Refactor imports: use file extension, improve tree shaking, update tests

This commit is contained in:
Alex Gleason 2023-05-12 11:00:14 -05:00 committed by fiatjaf_
parent 9f896479d0
commit 08885ab8da
42 changed files with 244 additions and 212 deletions

View File

@ -6,9 +6,9 @@ import {
validateEvent, validateEvent,
verifySignature, verifySignature,
getSignature, getSignature,
getPublicKey, Kind,
Kind } from './event.ts'
} from '.' import {getPublicKey} from './keys.ts'
describe('Event', () => { describe('Event', () => {
describe('getBlankEvent', () => { describe('getBlankEvent', () => {

View File

@ -2,8 +2,8 @@ import {schnorr} from '@noble/curves/secp256k1'
import {sha256} from '@noble/hashes/sha256' import {sha256} from '@noble/hashes/sha256'
import {bytesToHex} from '@noble/hashes/utils' import {bytesToHex} from '@noble/hashes/utils'
import {utf8Encoder} from './utils' import {getPublicKey} from './keys.ts'
import {getPublicKey} from './keys' import {utf8Encoder} from './utils.ts'
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
export enum Kind { export enum Kind {

View File

@ -1,15 +1,15 @@
import {fj} from '.' import {matchEventId, matchEventKind, getSubscriptionId} from './fakejson.ts'
test('match id', () => { test('match id', () => {
expect( expect(
fj.matchEventId( matchEventId(
`["EVENT","nostril-query",{"tags":[],"content":"so did we cut all corners and p2p stuff in order to make a decentralized social network that was fast and worked, but in the end what we got was a lot of very slow clients that can't handle the traffic of one jack dorsey tweet?","sig":"ca62629d189edebb8f0811cfa0ac53015013df5f305dcba3f411ba15cfc4074d8c2d517ee7d9e81c9eb72a7328bfbe31c9122156397565ac55e740404e2b1fe7","id":"fef2a50f7d9d3d5a5f38ee761bc087ec16198d3f0140df6d1e8193abf7c2b146","kind":1,"pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1671150419}]`, `["EVENT","nostril-query",{"tags":[],"content":"so did we cut all corners and p2p stuff in order to make a decentralized social network that was fast and worked, but in the end what we got was a lot of very slow clients that can't handle the traffic of one jack dorsey tweet?","sig":"ca62629d189edebb8f0811cfa0ac53015013df5f305dcba3f411ba15cfc4074d8c2d517ee7d9e81c9eb72a7328bfbe31c9122156397565ac55e740404e2b1fe7","id":"fef2a50f7d9d3d5a5f38ee761bc087ec16198d3f0140df6d1e8193abf7c2b146","kind":1,"pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1671150419}]`,
'fef2a50f7d9d3d5a5f38ee761bc087ec16198d3f0140df6d1e8193abf7c2b146' 'fef2a50f7d9d3d5a5f38ee761bc087ec16198d3f0140df6d1e8193abf7c2b146'
) )
).toBeTruthy() ).toBeTruthy()
expect( expect(
fj.matchEventId( matchEventId(
`["EVENT","nostril-query",{"content":"a bunch of mfs interacted with my post using what I assume were \"likes\": https://nostr.build/i/964.png","created_at":1672506879,"id":"f40bdd0905137ad60482537e260890ab50b0863bf16e67cf9383f203bd26c96f","kind":1,"pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","sig":"8b825d2d4096f0643b18ca39da59ec07a682cd8a3e717f119c845037573d98099f5bea94ec7ddedd5600c8020144a255ed52882a911f7f7ada6d6abb3c0a1eb4","tags":[]}]`, `["EVENT","nostril-query",{"content":"a bunch of mfs interacted with my post using what I assume were \"likes\": https://nostr.build/i/964.png","created_at":1672506879,"id":"f40bdd0905137ad60482537e260890ab50b0863bf16e67cf9383f203bd26c96f","kind":1,"pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","sig":"8b825d2d4096f0643b18ca39da59ec07a682cd8a3e717f119c845037573d98099f5bea94ec7ddedd5600c8020144a255ed52882a911f7f7ada6d6abb3c0a1eb4","tags":[]}]`,
'fef2a50f7d9d3d5a5f38ee761bc087ec16198d3f0140df6d1e8193abf7c2b146' 'fef2a50f7d9d3d5a5f38ee761bc087ec16198d3f0140df6d1e8193abf7c2b146'
) )
@ -18,14 +18,14 @@ test('match id', () => {
test('match kind', () => { test('match kind', () => {
expect( expect(
fj.matchEventKind( matchEventKind(
`["EVENT","nostril-query",{"tags":[],"content":"so did we cut all corners and p2p stuff in order to make a decentralized social network that was fast and worked, but in the end what we got was a lot of very slow clients that can't handle the traffic of one jack dorsey tweet?","sig":"ca62629d189edebb8f0811cfa0ac53015013df5f305dcba3f411ba15cfc4074d8c2d517ee7d9e81c9eb72a7328bfbe31c9122156397565ac55e740404e2b1fe7","id":"fef2a50f7d9d3d5a5f38ee761bc087ec16198d3f0140df6d1e8193abf7c2b146","kind":1,"pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1671150419}]`, `["EVENT","nostril-query",{"tags":[],"content":"so did we cut all corners and p2p stuff in order to make a decentralized social network that was fast and worked, but in the end what we got was a lot of very slow clients that can't handle the traffic of one jack dorsey tweet?","sig":"ca62629d189edebb8f0811cfa0ac53015013df5f305dcba3f411ba15cfc4074d8c2d517ee7d9e81c9eb72a7328bfbe31c9122156397565ac55e740404e2b1fe7","id":"fef2a50f7d9d3d5a5f38ee761bc087ec16198d3f0140df6d1e8193abf7c2b146","kind":1,"pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1671150419}]`,
1 1
) )
).toBeTruthy() ).toBeTruthy()
expect( expect(
fj.matchEventKind( matchEventKind(
`["EVENT","nostril-query",{"content":"{\"name\":\"fiatjaf\",\"about\":\"buy my merch at fiatjaf store\",\"picture\":\"https://fiatjaf.com/static/favicon.jpg\",\"nip05\":\"_@fiatjaf.com\"}","created_at":1671217411,"id":"b52f93f6dfecf9d81f59062827cd941412a0e8398dda60baf960b17499b88900","kind":12720,"pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","sig":"fc1ea5d45fa5ed0526faed06e8fc7a558e60d1b213e9714f440828584ee999b93407092f9b04deea7e504fa034fc0428f31f7f0f95417b3280ebe6004b80b470","tags":[]}]`, `["EVENT","nostril-query",{"content":"{\"name\":\"fiatjaf\",\"about\":\"buy my merch at fiatjaf store\",\"picture\":\"https://fiatjaf.com/static/favicon.jpg\",\"nip05\":\"_@fiatjaf.com\"}","created_at":1671217411,"id":"b52f93f6dfecf9d81f59062827cd941412a0e8398dda60baf960b17499b88900","kind":12720,"pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","sig":"fc1ea5d45fa5ed0526faed06e8fc7a558e60d1b213e9714f440828584ee999b93407092f9b04deea7e504fa034fc0428f31f7f0f95417b3280ebe6004b80b470","tags":[]}]`,
12720 12720
) )
@ -33,14 +33,14 @@ test('match kind', () => {
}) })
test('match subscription id', () => { test('match subscription id', () => {
expect(fj.getSubscriptionId('["EVENT","",{}]')).toEqual('') expect(getSubscriptionId('["EVENT","",{}]')).toEqual('')
expect(fj.getSubscriptionId('["EVENT","_",{}]')).toEqual('_') expect(getSubscriptionId('["EVENT","_",{}]')).toEqual('_')
expect(fj.getSubscriptionId('["EVENT","subname",{}]')).toEqual('subname') expect(getSubscriptionId('["EVENT","subname",{}]')).toEqual('subname')
expect(fj.getSubscriptionId('["EVENT", "kasjbdjkav", {}]')).toEqual( expect(getSubscriptionId('["EVENT", "kasjbdjkav", {}]')).toEqual(
'kasjbdjkav' 'kasjbdjkav'
) )
expect( expect(
fj.getSubscriptionId( getSubscriptionId(
' [ \n\n "EVENT" , \n\n "y4d5ow45gfwoiudfÇA VSADLKAN KLDASB[12312535]SFMZSNJKLH" , {}]' ' [ \n\n "EVENT" , \n\n "y4d5ow45gfwoiudfÇA VSADLKAN KLDASB[12312535]SFMZSNJKLH" , {}]'
) )
).toEqual('y4d5ow45gfwoiudfÇA VSADLKAN KLDASB[12312535]SFMZSNJKLH') ).toEqual('y4d5ow45gfwoiudfÇA VSADLKAN KLDASB[12312535]SFMZSNJKLH')

View File

@ -1,5 +1,5 @@
import {matchFilter, matchFilters} from '.' import {matchFilter, matchFilters} from './filter.ts'
import {buildEvent} from './test-helpers' import {buildEvent} from './test-helpers.ts'
describe('Filter', () => { describe('Filter', () => {
describe('matchFilter', () => { describe('matchFilter', () => {

View File

@ -1,4 +1,4 @@
import {Event, type Kind} from './event' import {Event, type Kind} from './event.ts'
export type Filter<K extends number = Kind> = { export type Filter<K extends number = Kind> = {
ids?: string[] ids?: string[]

View File

@ -1,24 +1,24 @@
export * from './keys' export * from './keys.ts'
export * from './relay' export * from './relay.ts'
export * from './event' export * from './event.ts'
export * from './filter' export * from './filter.ts'
export * from './pool' export * from './pool.ts'
export * from './references' export * from './references.ts'
export * as nip04 from './nip04' export * as nip04 from './nip04.ts'
export * as nip05 from './nip05' export * as nip05 from './nip05.ts'
export * as nip06 from './nip06' export * as nip06 from './nip06.ts'
export * as nip10 from './nip10' export * as nip10 from './nip10.ts'
export * as nip13 from './nip13' export * as nip13 from './nip13.ts'
export * as nip18 from './nip18' export * as nip18 from './nip18.ts'
export * as nip19 from './nip19' export * as nip19 from './nip19.ts'
export * as nip21 from './nip21' export * as nip21 from './nip21.ts'
export * as nip25 from './nip25' export * as nip25 from './nip25.ts'
export * as nip26 from './nip26' export * as nip26 from './nip26.ts'
export * as nip27 from './nip27' export * as nip27 from './nip27.ts'
export * as nip39 from './nip39' export * as nip39 from './nip39.ts'
export * as nip42 from './nip42' export * as nip42 from './nip42.ts'
export * as nip57 from './nip57' export * as nip57 from './nip57.ts'
export * as fj from './fakejson' export * as fj from './fakejson.ts'
export * as utils from './utils' export * as utils from './utils.ts'

View File

@ -1,4 +1,4 @@
import {generatePrivateKey, getPublicKey} from '.' import {generatePrivateKey, getPublicKey} from './keys.ts'
test('private key generation', () => { test('private key generation', () => {
expect(generatePrivateKey()).toMatch(/[a-f0-9]{64}/) expect(generatePrivateKey()).toMatch(/[a-f0-9]{64}/)

View File

@ -1,5 +1,7 @@
import {nip04, getPublicKey, generatePrivateKey} from '.' import crypto from 'node:crypto'
import crypto from 'crypto'
import {encrypt, decrypt} from './nip04.ts'
import {getPublicKey, generatePrivateKey} from './keys.ts'
// @ts-ignore // @ts-ignore
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
@ -12,6 +14,6 @@ test('encrypt and decrypt message', async () => {
let pk2 = getPublicKey(sk2) let pk2 = getPublicKey(sk2)
expect( expect(
await nip04.decrypt(sk2, pk1, await nip04.encrypt(sk1, pk2, 'hello')) await decrypt(sk2, pk1, await encrypt(sk1, pk2, 'hello'))
).toEqual('hello') ).toEqual('hello')
}) })

View File

@ -2,7 +2,7 @@ import {randomBytes} from '@noble/hashes/utils'
import {secp256k1} from '@noble/curves/secp256k1' import {secp256k1} from '@noble/curves/secp256k1'
import {base64} from '@scure/base' import {base64} from '@scure/base'
import {utf8Decoder, utf8Encoder} from './utils' import {utf8Decoder, utf8Encoder} from './utils.ts'
// @ts-ignore // @ts-ignore
if (typeof crypto !== 'undefined' && !crypto.subtle && crypto.webcrypto) { if (typeof crypto !== 'undefined' && !crypto.subtle && crypto.webcrypto) {

View File

@ -1,28 +1,29 @@
import fetch from 'node-fetch' import fetch from 'node-fetch'
import {nip05} from '.'
import {useFetchImplementation, queryProfile} from './nip05.ts'
test('fetch nip05 profiles', async () => { test('fetch nip05 profiles', async () => {
nip05.useFetchImplementation(fetch) useFetchImplementation(fetch)
let p1 = await nip05.queryProfile('jb55.com') let p1 = await queryProfile('jb55.com')
expect(p1!.pubkey).toEqual( expect(p1!.pubkey).toEqual(
'32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245' '32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'
) )
expect(p1!.relays).toEqual(['wss://relay.damus.io']) expect(p1!.relays).toEqual(['wss://relay.damus.io'])
let p2 = await nip05.queryProfile('jb55@jb55.com') let p2 = await queryProfile('jb55@jb55.com')
expect(p2!.pubkey).toEqual( expect(p2!.pubkey).toEqual(
'32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245' '32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'
) )
expect(p2!.relays).toEqual(['wss://relay.damus.io']) expect(p2!.relays).toEqual(['wss://relay.damus.io'])
let p3 = await nip05.queryProfile('channel.ninja@channel.ninja') let p3 = await queryProfile('channel.ninja@channel.ninja')
expect(p3!.pubkey).toEqual( expect(p3!.pubkey).toEqual(
'36e65b503eba8a6b698e724a59137603101166a1cddb45ddc704247fc8aa0fce' '36e65b503eba8a6b698e724a59137603101166a1cddb45ddc704247fc8aa0fce'
) )
expect(p3!.relays).toEqual(undefined) expect(p3!.relays).toEqual(undefined)
let p4 = await nip05.queryProfile('_@fiatjaf.com') let p4 = await queryProfile('_@fiatjaf.com')
expect(p4!.pubkey).toEqual( expect(p4!.pubkey).toEqual(
'3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d' '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d'
) )

View File

@ -1,4 +1,4 @@
import {ProfilePointer} from './nip19' import {ProfilePointer} from './nip19.ts'
/** /**
* NIP-05 regex. The localpart is optional, and should be assumed to be `_` otherwise. * NIP-05 regex. The localpart is optional, and should be assumed to be `_` otherwise.

View File

@ -1,8 +1,8 @@
import {nip06} from '.' import {privateKeyFromSeedWords} from './nip06.ts'
test('generate private key from a mnemonic', async () => { test('generate private key from a mnemonic', async () => {
const mnemonic = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' const mnemonic = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'
const privateKey = nip06.privateKeyFromSeedWords(mnemonic) const privateKey = privateKeyFromSeedWords(mnemonic)
expect(privateKey).toEqual( expect(privateKey).toEqual(
'c26cf31d8ba425b555ca27d00ca71b5008004f2f662470f8c8131822ec129fe2' 'c26cf31d8ba425b555ca27d00ca71b5008004f2f662470f8c8131822ec129fe2'
) )
@ -11,7 +11,7 @@ test('generate private key from a mnemonic', async () => {
test('generate private key from a mnemonic and passphrase', async () => { 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 mnemonic = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'
const passphrase = '123' const passphrase = '123'
const privateKey = nip06.privateKeyFromSeedWords(mnemonic, passphrase) const privateKey = privateKeyFromSeedWords(mnemonic, passphrase)
expect(privateKey).toEqual( expect(privateKey).toEqual(
'55a22b8203273d0aaf24c22c8fbe99608e70c524b17265641074281c8b978ae4' '55a22b8203273d0aaf24c22c8fbe99608e70c524b17265641074281c8b978ae4'
) )

View File

@ -1,4 +1,4 @@
import {nip10} from '.' import {parse} from './nip10.ts'
describe('parse NIP10-referenced events', () => { describe('parse NIP10-referenced events', () => {
test('legacy + a lot of events', () => { test('legacy + a lot of events', () => {
@ -47,7 +47,7 @@ describe('parse NIP10-referenced events', () => {
] ]
} }
expect(nip10.parse(event)).toEqual({ expect(parse(event)).toEqual({
mentions: [ mentions: [
{ {
id: 'bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631', id: 'bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631',
@ -128,7 +128,7 @@ describe('parse NIP10-referenced events', () => {
] ]
} }
expect(nip10.parse(event)).toEqual({ expect(parse(event)).toEqual({
mentions: [ mentions: [
{ {
id: 'bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631', id: 'bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631',
@ -189,7 +189,7 @@ describe('parse NIP10-referenced events', () => {
] ]
} }
expect(nip10.parse(event)).toEqual({ expect(parse(event)).toEqual({
mentions: [], mentions: [],
profiles: [ profiles: [
{ {
@ -233,7 +233,7 @@ describe('parse NIP10-referenced events', () => {
] ]
} }
expect(nip10.parse(event)).toEqual({ expect(parse(event)).toEqual({
mentions: [], mentions: [],
profiles: [ profiles: [
{ {
@ -302,7 +302,7 @@ describe('parse NIP10-referenced events', () => {
] ]
} }
expect(nip10.parse(event)).toEqual({ expect(parse(event)).toEqual({
mentions: [], mentions: [],
profiles: [ profiles: [
{ {

View File

@ -1,5 +1,5 @@
import type {Event} from './event' import type {Event} from './event.ts'
import type {EventPointer, ProfilePointer} from './nip19' import type {EventPointer, ProfilePointer} from './nip19.ts'
export type NIP10Result = { export type NIP10Result = {
/** /**

View File

@ -1,7 +1,7 @@
import {nip13} from '.' import {getPow} from './nip13.ts'
test('identifies proof-of-work difficulty', async () => { test('identifies proof-of-work difficulty', async () => {
const id = '000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358' const id = '000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358'
const difficulty = nip13.getPow(id) const difficulty = getPow(id)
expect(difficulty).toEqual(21) expect(difficulty).toEqual(21)
}) })

View File

@ -1,5 +1,7 @@
import {nip18, finishEvent, getPublicKey, Kind} from '.' import {finishEvent, Kind} from './event.ts'
import { buildEvent } from './test-helpers' import {getPublicKey} from './keys.ts'
import {finishRepostEvent, getRepostedEventPointer, getRepostedEvent} from './nip18.ts'
import {buildEvent} from './test-helpers.ts'
const relayUrl = 'https://relay.example.com' const relayUrl = 'https://relay.example.com'
@ -27,7 +29,7 @@ describe('finishRepostEvent + getRepostedEventPointer + getRepostedEvent', () =>
created_at: 1617932115 created_at: 1617932115
} }
const event = nip18.finishRepostEvent( const event = finishRepostEvent(
template, template,
repostedEvent, repostedEvent,
relayUrl, relayUrl,
@ -45,13 +47,13 @@ describe('finishRepostEvent + getRepostedEventPointer + getRepostedEvent', () =>
expect(typeof event.id).toEqual('string') expect(typeof event.id).toEqual('string')
expect(typeof event.sig).toEqual('string') expect(typeof event.sig).toEqual('string')
const repostedEventPointer = nip18.getRepostedEventPointer(event) const repostedEventPointer = getRepostedEventPointer(event)
expect(repostedEventPointer!.id).toEqual(repostedEvent.id) expect(repostedEventPointer!.id).toEqual(repostedEvent.id)
expect(repostedEventPointer!.author).toEqual(repostedEvent.pubkey) expect(repostedEventPointer!.author).toEqual(repostedEvent.pubkey)
expect(repostedEventPointer!.relays).toEqual([relayUrl]) expect(repostedEventPointer!.relays).toEqual([relayUrl])
const repostedEventFromContent = nip18.getRepostedEvent(event) const repostedEventFromContent = getRepostedEvent(event)
expect(repostedEventFromContent).toEqual(repostedEvent) expect(repostedEventFromContent).toEqual(repostedEvent)
}) })
@ -63,7 +65,7 @@ describe('finishRepostEvent + getRepostedEventPointer + getRepostedEvent', () =>
created_at: 1617932115 created_at: 1617932115
} }
const event = nip18.finishRepostEvent( const event = finishRepostEvent(
template, template,
repostedEvent, repostedEvent,
relayUrl, relayUrl,
@ -82,13 +84,13 @@ describe('finishRepostEvent + getRepostedEventPointer + getRepostedEvent', () =>
expect(typeof event.id).toEqual('string') expect(typeof event.id).toEqual('string')
expect(typeof event.sig).toEqual('string') expect(typeof event.sig).toEqual('string')
const repostedEventPointer = nip18.getRepostedEventPointer(event) const repostedEventPointer = getRepostedEventPointer(event)
expect(repostedEventPointer!.id).toEqual(repostedEvent.id) expect(repostedEventPointer!.id).toEqual(repostedEvent.id)
expect(repostedEventPointer!.author).toEqual(repostedEvent.pubkey) expect(repostedEventPointer!.author).toEqual(repostedEvent.pubkey)
expect(repostedEventPointer!.relays).toEqual([relayUrl]) expect(repostedEventPointer!.relays).toEqual([relayUrl])
const repostedEventFromContent = nip18.getRepostedEvent(event) const repostedEventFromContent = getRepostedEvent(event)
expect(repostedEventFromContent).toEqual(undefined) expect(repostedEventFromContent).toEqual(undefined)
}) })
@ -101,7 +103,7 @@ describe('getRepostedEventPointer', () => {
tags: [['e', 'reposted event id', relayUrl]], tags: [['e', 'reposted event id', relayUrl]],
}) })
const repostedEventPointer = nip18.getRepostedEventPointer(event) const repostedEventPointer = getRepostedEventPointer(event)
expect(repostedEventPointer!.id).toEqual('reposted event id') expect(repostedEventPointer!.id).toEqual('reposted event id')
expect(repostedEventPointer!.author).toEqual(undefined) expect(repostedEventPointer!.author).toEqual(undefined)

View File

@ -1,5 +1,5 @@
import { Event, finishEvent, Kind, verifySignature } from './event' import {Event, finishEvent, Kind, verifySignature} from './event.ts'
import { EventPointer } from './nip19' import {EventPointer} from './nip19.ts'
export type RepostEventTemplate = { export type RepostEventTemplate = {
/** /**

View File

@ -1,19 +1,29 @@
import {nip19, generatePrivateKey, getPublicKey} from '.' import {generatePrivateKey, getPublicKey} from './keys.ts'
import {
decode,
naddrEncode,
nprofileEncode,
npubEncode,
nrelayEncode,
nsecEncode,
type AddressPointer,
type ProfilePointer,
} from './nip19.ts'
test('encode and decode nsec', () => { test('encode and decode nsec', () => {
let sk = generatePrivateKey() let sk = generatePrivateKey()
let nsec = nip19.nsecEncode(sk) let nsec = nsecEncode(sk)
expect(nsec).toMatch(/nsec1\w+/) expect(nsec).toMatch(/nsec1\w+/)
let {type, data} = nip19.decode(nsec) let {type, data} = decode(nsec)
expect(type).toEqual('nsec') expect(type).toEqual('nsec')
expect(data).toEqual(sk) expect(data).toEqual(sk)
}) })
test('encode and decode npub', () => { test('encode and decode npub', () => {
let pk = getPublicKey(generatePrivateKey()) let pk = getPublicKey(generatePrivateKey())
let npub = nip19.npubEncode(pk) let npub = npubEncode(pk)
expect(npub).toMatch(/npub1\w+/) expect(npub).toMatch(/npub1\w+/)
let {type, data} = nip19.decode(npub) let {type, data} = decode(npub)
expect(type).toEqual('npub') expect(type).toEqual('npub')
expect(data).toEqual(pk) expect(data).toEqual(pk)
}) })
@ -24,11 +34,11 @@ test('encode and decode nprofile', () => {
'wss://relay.nostr.example.mydomain.example.com', 'wss://relay.nostr.example.mydomain.example.com',
'wss://nostr.banana.com' 'wss://nostr.banana.com'
] ]
let nprofile = nip19.nprofileEncode({pubkey: pk, relays}) let nprofile = nprofileEncode({pubkey: pk, relays})
expect(nprofile).toMatch(/nprofile1\w+/) expect(nprofile).toMatch(/nprofile1\w+/)
let {type, data} = nip19.decode(nprofile) let {type, data} = decode(nprofile)
expect(type).toEqual('nprofile') expect(type).toEqual('nprofile')
const pointer = data as nip19.ProfilePointer const pointer = data as ProfilePointer
expect(pointer.pubkey).toEqual(pk) expect(pointer.pubkey).toEqual(pk)
expect(pointer.relays).toContain(relays[0]) expect(pointer.relays).toContain(relays[0])
expect(pointer.relays).toContain(relays[1]) expect(pointer.relays).toContain(relays[1])
@ -36,8 +46,8 @@ test('encode and decode nprofile', () => {
test('decode nprofile without relays', () => { test('decode nprofile without relays', () => {
expect( expect(
nip19.decode( decode(
nip19.nprofileEncode({ nprofileEncode({
pubkey: pubkey:
'97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322', '97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322',
relays: [] relays: []
@ -55,16 +65,16 @@ test('encode and decode naddr', () => {
'wss://relay.nostr.example.mydomain.example.com', 'wss://relay.nostr.example.mydomain.example.com',
'wss://nostr.banana.com' 'wss://nostr.banana.com'
] ]
let naddr = nip19.naddrEncode({ let naddr = naddrEncode({
pubkey: pk, pubkey: pk,
relays, relays,
kind: 30023, kind: 30023,
identifier: 'banana' identifier: 'banana'
}) })
expect(naddr).toMatch(/naddr1\w+/) expect(naddr).toMatch(/naddr1\w+/)
let {type, data} = nip19.decode(naddr) let {type, data} = decode(naddr)
expect(type).toEqual('naddr') expect(type).toEqual('naddr')
const pointer = data as nip19.AddressPointer const pointer = data as AddressPointer
expect(pointer.pubkey).toEqual(pk) expect(pointer.pubkey).toEqual(pk)
expect(pointer.relays).toContain(relays[0]) expect(pointer.relays).toContain(relays[0])
expect(pointer.relays).toContain(relays[1]) expect(pointer.relays).toContain(relays[1])
@ -73,11 +83,11 @@ test('encode and decode naddr', () => {
}) })
test('decode naddr from habla.news', () => { test('decode naddr from habla.news', () => {
let {type, data} = nip19.decode( let {type, data} = decode(
'naddr1qq98yetxv4ex2mnrv4esygrl54h466tz4v0re4pyuavvxqptsejl0vxcmnhfl60z3rth2xkpjspsgqqqw4rsf34vl5' 'naddr1qq98yetxv4ex2mnrv4esygrl54h466tz4v0re4pyuavvxqptsejl0vxcmnhfl60z3rth2xkpjspsgqqqw4rsf34vl5'
) )
expect(type).toEqual('naddr') expect(type).toEqual('naddr')
const pointer = data as nip19.AddressPointer const pointer = data as AddressPointer
expect(pointer.pubkey).toEqual( expect(pointer.pubkey).toEqual(
'7fa56f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751ac194' '7fa56f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751ac194'
) )
@ -86,12 +96,12 @@ test('decode naddr from habla.news', () => {
}) })
test('decode naddr from go-nostr with different TLV ordering', () => { test('decode naddr from go-nostr with different TLV ordering', () => {
let {type, data} = nip19.decode( let {type, data} = decode(
'naddr1qqrxyctwv9hxzq3q80cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsxpqqqp65wqfwwaehxw309aex2mrp0yhxummnw3ezuetcv9khqmr99ekhjer0d4skjm3wv4uxzmtsd3jjucm0d5q3vamnwvaz7tmwdaehgu3wvfskuctwvyhxxmmd0zfmwx' 'naddr1qqrxyctwv9hxzq3q80cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsxpqqqp65wqfwwaehxw309aex2mrp0yhxummnw3ezuetcv9khqmr99ekhjer0d4skjm3wv4uxzmtsd3jjucm0d5q3vamnwvaz7tmwdaehgu3wvfskuctwvyhxxmmd0zfmwx'
) )
expect(type).toEqual('naddr') expect(type).toEqual('naddr')
const pointer = data as nip19.AddressPointer const pointer = data as AddressPointer
expect(pointer.pubkey).toEqual( expect(pointer.pubkey).toEqual(
'3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d' '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d'
) )
@ -105,9 +115,9 @@ test('decode naddr from go-nostr with different TLV ordering', () => {
test('encode and decode nrelay', () => { test('encode and decode nrelay', () => {
let url = 'wss://relay.nostr.example' let url = 'wss://relay.nostr.example'
let nrelay = nip19.nrelayEncode(url) let nrelay = nrelayEncode(url)
expect(nrelay).toMatch(/nrelay1\w+/) expect(nrelay).toMatch(/nrelay1\w+/)
let {type, data} = nip19.decode(nrelay) let {type, data} = decode(nrelay)
expect(type).toEqual('nrelay') expect(type).toEqual('nrelay')
expect(data).toEqual(url) expect(data).toEqual(url)
}) })

View File

@ -1,7 +1,7 @@
import {bytesToHex, concatBytes, hexToBytes} from '@noble/hashes/utils' import {bytesToHex, concatBytes, hexToBytes} from '@noble/hashes/utils'
import {bech32} from '@scure/base' import {bech32} from '@scure/base'
import {utf8Decoder, utf8Encoder} from './utils' import {utf8Decoder, utf8Encoder} from './utils.ts'
const Bech32MaxSize = 5000 const Bech32MaxSize = 5000

View File

@ -1,32 +1,32 @@
import {nip21} from '.' import {test as testRegex, parse} from './nip21.ts'
test('test()', () => { test('test()', () => {
expect( expect(
nip21.test( testRegex(
'nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6' 'nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6'
) )
).toBe(true) ).toBe(true)
expect( expect(
nip21.test( testRegex(
'nostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky' 'nostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky'
) )
).toBe(true) ).toBe(true)
expect( expect(
nip21.test( testRegex(
' nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6' ' nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6'
) )
).toBe(false) ).toBe(false)
expect(nip21.test('nostr:')).toBe(false) expect(testRegex('nostr:')).toBe(false)
expect( expect(
nip21.test( testRegex(
'nostr:npub108pv4cg5ag52nQq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6' 'nostr:npub108pv4cg5ag52nQq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6'
) )
).toBe(false) ).toBe(false)
expect(nip21.test('gggggg')).toBe(false) expect(testRegex('gggggg')).toBe(false)
}) })
test('parse', () => { test('parse', () => {
const result = nip21.parse( const result = parse(
'nostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky' 'nostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky'
) )

View File

@ -1,8 +1,7 @@
import * as nip19 from './nip19' import {BECH32_REGEX, decode, type DecodeResult} from './nip19.ts'
import * as nip21 from './nip21'
/** Nostr URI regex, eg `nostr:npub1...` */ /** Nostr URI regex, eg `nostr:npub1...` */
export const NOSTR_URI_REGEX = new RegExp(`nostr:(${nip19.BECH32_REGEX.source})`) export const NOSTR_URI_REGEX = new RegExp(`nostr:(${BECH32_REGEX.source})`)
/** Test whether the value is a Nostr URI. */ /** Test whether the value is a Nostr URI. */
export function test(value: unknown): value is `nostr:${string}` { export function test(value: unknown): value is `nostr:${string}` {
@ -19,16 +18,16 @@ export interface NostrURI {
/** The bech32-encoded data (eg `npub1...`). */ /** The bech32-encoded data (eg `npub1...`). */
value: string value: string
/** Decoded bech32 string, according to NIP-19. */ /** Decoded bech32 string, according to NIP-19. */
decoded: nip19.DecodeResult decoded: DecodeResult
} }
/** Parse and decode a Nostr URI. */ /** Parse and decode a Nostr URI. */
export function parse(uri: string): NostrURI { export function parse(uri: string): NostrURI {
const match = uri.match(new RegExp(`^${nip21.NOSTR_URI_REGEX.source}$`)) const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`))
if (!match) throw new Error(`Invalid Nostr URI: ${uri}`) if (!match) throw new Error(`Invalid Nostr URI: ${uri}`)
return { return {
uri: match[0] as `nostr:${string}`, uri: match[0] as `nostr:${string}`,
value: match[1], value: match[1],
decoded: nip19.decode(match[1]) decoded: decode(match[1])
} }
} }

View File

@ -1,4 +1,6 @@
import {nip25, finishEvent, getPublicKey, Kind} from '.' import {finishEvent, Kind} from './event.ts'
import {getPublicKey} from './keys.ts'
import {finishReactionEvent, getReactedEventPointer} from './nip25.ts'
describe('finishReactionEvent + getReactedEventPointer', () => { describe('finishReactionEvent + getReactedEventPointer', () => {
const privateKey = const privateKey =
@ -24,7 +26,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => {
created_at: 1617932115 created_at: 1617932115
} }
const event = nip25.finishReactionEvent(template, reactedEvent, privateKey) const event = finishReactionEvent(template, reactedEvent, privateKey)
expect(event.kind).toEqual(Kind.Reaction) expect(event.kind).toEqual(Kind.Reaction)
expect(event.tags).toEqual([ expect(event.tags).toEqual([
@ -39,7 +41,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => {
expect(typeof event.id).toEqual('string') expect(typeof event.id).toEqual('string')
expect(typeof event.sig).toEqual('string') expect(typeof event.sig).toEqual('string')
const reactedEventPointer = nip25.getReactedEventPointer(event) const reactedEventPointer = getReactedEventPointer(event)
expect(reactedEventPointer!.id).toEqual(reactedEvent.id) expect(reactedEventPointer!.id).toEqual(reactedEvent.id)
expect(reactedEventPointer!.author).toEqual(reactedEvent.pubkey) expect(reactedEventPointer!.author).toEqual(reactedEvent.pubkey)
@ -52,7 +54,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => {
created_at: 1617932115 created_at: 1617932115
} }
const event = nip25.finishReactionEvent(template, reactedEvent, privateKey) const event = finishReactionEvent(template, reactedEvent, privateKey)
expect(event.kind).toEqual(Kind.Reaction) expect(event.kind).toEqual(Kind.Reaction)
expect(event.tags).toEqual([ expect(event.tags).toEqual([
@ -68,7 +70,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => {
expect(typeof event.id).toEqual('string') expect(typeof event.id).toEqual('string')
expect(typeof event.sig).toEqual('string') expect(typeof event.sig).toEqual('string')
const reactedEventPointer = nip25.getReactedEventPointer(event) const reactedEventPointer = getReactedEventPointer(event)
expect(reactedEventPointer!.id).toEqual(reactedEvent.id) expect(reactedEventPointer!.id).toEqual(reactedEvent.id)
expect(reactedEventPointer!.author).toEqual(reactedEvent.pubkey) expect(reactedEventPointer!.author).toEqual(reactedEvent.pubkey)

View File

@ -1,5 +1,6 @@
import { Event, finishEvent, Kind } from './event' import {Event, finishEvent, Kind} from './event.ts'
import { EventPointer } from './nip19'
import type {EventPointer} from './nip19.ts'
export type ReactionEventTemplate = { export type ReactionEventTemplate = {
/** /**

View File

@ -1,9 +1,10 @@
import {nip26, getPublicKey, generatePrivateKey} from '.' import {getPublicKey, generatePrivateKey} from './keys.ts'
import { buildEvent } from './test-helpers' import {getDelegator, createDelegation} from './nip26.ts'
import {buildEvent} from './test-helpers.ts'
test('parse good delegation from NIP', async () => { test('parse good delegation from NIP', async () => {
expect( expect(
nip26.getDelegator({ getDelegator({
id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc', id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc',
pubkey: pubkey:
'62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49', '62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49',
@ -25,7 +26,7 @@ test('parse good delegation from NIP', async () => {
test('parse bad delegations', async () => { test('parse bad delegations', async () => {
expect( expect(
nip26.getDelegator({ getDelegator({
id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc', id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc',
pubkey: pubkey:
'62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49', '62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49',
@ -45,7 +46,7 @@ test('parse bad delegations', async () => {
).toEqual(null) ).toEqual(null)
expect( expect(
nip26.getDelegator({ getDelegator({
id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc', id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc',
pubkey: pubkey:
'62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49', '62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49',
@ -65,7 +66,7 @@ test('parse bad delegations', async () => {
).toEqual(null) ).toEqual(null)
expect( expect(
nip26.getDelegator({ getDelegator({
id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc', id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc',
pubkey: pubkey:
'62903b1ff41559daf9ee98ef1ae67c152f301bb5ce26d14baba3052f649c3f49', '62903b1ff41559daf9ee98ef1ae67c152f301bb5ce26d14baba3052f649c3f49',
@ -90,7 +91,7 @@ test('create and verify delegation', async () => {
let pk1 = getPublicKey(sk1) let pk1 = getPublicKey(sk1)
let sk2 = generatePrivateKey() let sk2 = generatePrivateKey()
let pk2 = getPublicKey(sk2) let pk2 = getPublicKey(sk2)
let delegation = nip26.createDelegation(sk1, {pubkey: pk2, kind: 1}) let delegation = createDelegation(sk1, {pubkey: pk2, kind: 1})
expect(delegation).toHaveProperty('from', pk1) expect(delegation).toHaveProperty('from', pk1)
expect(delegation).toHaveProperty('to', pk2) expect(delegation).toHaveProperty('to', pk2)
expect(delegation).toHaveProperty('cond', 'kind=1') expect(delegation).toHaveProperty('cond', 'kind=1')
@ -100,5 +101,5 @@ test('create and verify delegation', async () => {
tags: [['delegation', delegation.from, delegation.cond, delegation.sig]], tags: [['delegation', delegation.from, delegation.cond, delegation.sig]],
pubkey: pk2, pubkey: pk2,
}) })
expect(nip26.getDelegator(event)).toEqual(pk1) expect(getDelegator(event)).toEqual(pk1)
}) })

View File

@ -2,9 +2,10 @@ import {schnorr} from '@noble/curves/secp256k1'
import {bytesToHex} from '@noble/hashes/utils' import {bytesToHex} from '@noble/hashes/utils'
import {sha256} from '@noble/hashes/sha256' import {sha256} from '@noble/hashes/sha256'
import {Event} from './event' import {utf8Encoder} from './utils.ts'
import {utf8Encoder} from './utils' import {getPublicKey} from './keys.ts'
import {getPublicKey} from './keys'
import type {Event} from './event.ts'
export type Parameters = { export type Parameters = {
pubkey: string // the key to whom the delegation will be given pubkey: string // the key to whom the delegation will be given

View File

@ -1,7 +1,7 @@
import {nip27} from '.' import {matchAll, replaceAll} from './nip27.ts'
test('matchAll', () => { test('matchAll', () => {
const result = nip27.matchAll( const result = matchAll(
'Hello nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6!\n\nnostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky' 'Hello nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6!\n\nnostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky'
) )
@ -33,7 +33,7 @@ test('replaceAll', () => {
const content = const content =
'Hello nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6!\n\nnostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky' 'Hello nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6!\n\nnostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky'
const result = nip27.replaceAll(content, ({decoded, value}) => { const result = replaceAll(content, ({decoded, value}) => {
switch (decoded.type) { switch (decoded.type) {
case 'npub': case 'npub':
return '@alex' return '@alex'

View File

@ -1,12 +1,12 @@
import * as nip19 from './nip19' import {decode} from './nip19.ts'
import * as nip21 from './nip21' import {NOSTR_URI_REGEX, type NostrURI} from './nip21.ts'
/** Regex to find NIP-21 URIs inside event content. */ /** Regex to find NIP-21 URIs inside event content. */
export const regex = () => export const regex = () =>
new RegExp(`\\b${nip21.NOSTR_URI_REGEX.source}\\b`, 'g') new RegExp(`\\b${NOSTR_URI_REGEX.source}\\b`, 'g')
/** Match result for a Nostr URI in event content. */ /** Match result for a Nostr URI in event content. */
export interface NostrURIMatch extends nip21.NostrURI { export interface NostrURIMatch extends NostrURI {
/** Index where the URI begins in the event content. */ /** Index where the URI begins in the event content. */
start: number start: number
/** Index where the URI ends in the event content. */ /** Index where the URI ends in the event content. */
@ -23,7 +23,7 @@ export function * matchAll(content: string): Iterable<NostrURIMatch> {
yield { yield {
uri: uri as `nostr:${string}`, uri: uri as `nostr:${string}`,
value, value,
decoded: nip19.decode(value), decoded: decode(value),
start: match.index!, start: match.index!,
end: match.index! + uri.length end: match.index! + uri.length
} }
@ -51,13 +51,13 @@ export function * matchAll(content: string): Iterable<NostrURIMatch> {
*/ */
export function replaceAll( export function replaceAll(
content: string, content: string,
replacer: (match: nip21.NostrURI) => string replacer: (match: NostrURI) => string
): string { ): string {
return content.replaceAll(regex(), (uri, value) => { return content.replaceAll(regex(), (uri, value) => {
return replacer({ return replacer({
uri: uri as `nostr:${string}`, uri: uri as `nostr:${string}`,
value, value,
decoded: nip19.decode(value) decoded: decode(value)
}) })
}) })
} }

View File

@ -1,10 +1,11 @@
import fetch from 'node-fetch' import fetch from 'node-fetch'
import {nip39} from '.'
import {useFetchImplementation, validateGithub} from './nip39.ts'
test('validate github claim', async () => { test('validate github claim', async () => {
nip39.useFetchImplementation(fetch) useFetchImplementation(fetch)
let result = await nip39.validateGithub( let result = await validateGithub(
'npub1gcxzte5zlkncx26j68ez60fzkvtkm9e0vrwdcvsjakxf9mu9qewqlfnj5z', 'npub1gcxzte5zlkncx26j68ez60fzkvtkm9e0vrwdcvsjakxf9mu9qewqlfnj5z',
'vitorpamplona', 'vitorpamplona',
'cf19e2d1d7f8dac6348ad37b35ec8421' 'cf19e2d1d7f8dac6348ad37b35ec8421'

View File

@ -1,10 +1,9 @@
import 'websocket-polyfill' import 'websocket-polyfill'
import {
relayInit, import {finishEvent} from './event.ts'
generatePrivateKey, import {generatePrivateKey} from './keys.ts'
finishEvent, import {authenticate} from './nip42.ts'
nip42 import {relayInit} from './relay.ts'
} from '.'
test('auth flow', () => { test('auth flow', () => {
const relay = relayInit('wss://nostr.kollider.xyz') const relay = relayInit('wss://nostr.kollider.xyz')
@ -14,7 +13,7 @@ test('auth flow', () => {
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
relay.on('auth', async challenge => { relay.on('auth', async challenge => {
await expect( await expect(
nip42.authenticate({ authenticate({
challenge, challenge,
relay, relay,
sign: (e) => finishEvent(e, sk) sign: (e) => finishEvent(e, sk)

View File

@ -1,5 +1,5 @@
import {EventTemplate, Event, Kind} from './event' import {Kind, type EventTemplate, type Event} from './event.ts'
import {Relay} from './relay' import {Relay} from './relay.ts'
/** /**
* Authenticate via NIP-42 flow. * Authenticate via NIP-42 flow.

View File

@ -1,25 +1,28 @@
import {finishEvent} from './event.ts'
import {getPublicKey, generatePrivateKey} from './keys.ts'
import { import {
nip57, getZapEndpoint,
generatePrivateKey, makeZapReceipt,
getPublicKey, makeZapRequest,
finishEvent useFetchImplementation,
} from '.' validateZapRequest,
import { buildEvent } from './test-helpers' } from './nip57.ts'
import {buildEvent} from './test-helpers.ts'
describe('getZapEndpoint', () => { describe('getZapEndpoint', () => {
test('returns null if neither lud06 nor lud16 is present', async () => { test('returns null if neither lud06 nor lud16 is present', async () => {
const metadata = buildEvent({kind: 0, content: '{}'}) const metadata = buildEvent({kind: 0, content: '{}'})
const result = await nip57.getZapEndpoint(metadata) const result = await getZapEndpoint(metadata)
expect(result).toBeNull() expect(result).toBeNull()
}) })
test('returns null if fetch fails', async () => { test('returns null if fetch fails', async () => {
const fetchImplementation = jest.fn(() => Promise.reject(new Error())) const fetchImplementation = jest.fn(() => Promise.reject(new Error()))
nip57.useFetchImplementation(fetchImplementation) useFetchImplementation(fetchImplementation)
const metadata = buildEvent({kind: 0, content: '{"lud16": "name@domain"}'}) const metadata = buildEvent({kind: 0, content: '{"lud16": "name@domain"}'})
const result = await nip57.getZapEndpoint(metadata) const result = await getZapEndpoint(metadata)
expect(result).toBeNull() expect(result).toBeNull()
expect(fetchImplementation).toHaveBeenCalledWith( expect(fetchImplementation).toHaveBeenCalledWith(
@ -31,10 +34,10 @@ describe('getZapEndpoint', () => {
const fetchImplementation = jest.fn(() => const fetchImplementation = jest.fn(() =>
Promise.resolve({json: () => ({allowsNostr: false})}) Promise.resolve({json: () => ({allowsNostr: false})})
) )
nip57.useFetchImplementation(fetchImplementation) useFetchImplementation(fetchImplementation)
const metadata = buildEvent({kind: 0, content: '{"lud16": "name@domain"}'}) const metadata = buildEvent({kind: 0, content: '{"lud16": "name@domain"}'})
const result = await nip57.getZapEndpoint(metadata) const result = await getZapEndpoint(metadata)
expect(result).toBeNull() expect(result).toBeNull()
expect(fetchImplementation).toHaveBeenCalledWith( expect(fetchImplementation).toHaveBeenCalledWith(
@ -52,10 +55,10 @@ describe('getZapEndpoint', () => {
}) })
}) })
) )
nip57.useFetchImplementation(fetchImplementation) useFetchImplementation(fetchImplementation)
const metadata = buildEvent({kind: 0, content: '{"lud16": "name@domain"}'}) const metadata = buildEvent({kind: 0, content: '{"lud16": "name@domain"}'})
const result = await nip57.getZapEndpoint(metadata) const result = await getZapEndpoint(metadata)
expect(result).toBe('callback') expect(result).toBe('callback')
expect(fetchImplementation).toHaveBeenCalledWith( expect(fetchImplementation).toHaveBeenCalledWith(
@ -68,7 +71,7 @@ describe('makeZapRequest', () => {
test('throws an error if amount is not given', () => { test('throws an error if amount is not given', () => {
expect(() => expect(() =>
// @ts-expect-error // @ts-expect-error
nip57.makeZapRequest({ makeZapRequest({
profile: 'profile', profile: 'profile',
event: null, event: null,
relays: [], relays: [],
@ -80,7 +83,7 @@ describe('makeZapRequest', () => {
test('throws an error if profile is not given', () => { test('throws an error if profile is not given', () => {
expect(() => expect(() =>
// @ts-expect-error // @ts-expect-error
nip57.makeZapRequest({ makeZapRequest({
event: null, event: null,
amount: 100, amount: 100,
relays: [], relays: [],
@ -90,7 +93,7 @@ describe('makeZapRequest', () => {
}) })
test('returns a valid Zap request', () => { test('returns a valid Zap request', () => {
const result = nip57.makeZapRequest({ const result = makeZapRequest({
profile: 'profile', profile: 'profile',
event: 'event', event: 'event',
amount: 100, amount: 100,
@ -113,7 +116,7 @@ describe('makeZapRequest', () => {
describe('validateZapRequest', () => { describe('validateZapRequest', () => {
test('returns an error message for invalid JSON', () => { test('returns an error message for invalid JSON', () => {
expect(nip57.validateZapRequest('invalid JSON')).toBe( expect(validateZapRequest('invalid JSON')).toBe(
'Invalid zap request JSON.' 'Invalid zap request JSON.'
) )
}) })
@ -130,7 +133,7 @@ describe('validateZapRequest', () => {
] ]
} }
expect(nip57.validateZapRequest(JSON.stringify(zapRequest))).toBe( expect(validateZapRequest(JSON.stringify(zapRequest))).toBe(
'Zap request is not a valid Nostr event.' 'Zap request is not a valid Nostr event.'
) )
}) })
@ -151,7 +154,7 @@ describe('validateZapRequest', () => {
] ]
} }
expect(nip57.validateZapRequest(JSON.stringify(zapRequest))).toBe( expect(validateZapRequest(JSON.stringify(zapRequest))).toBe(
'Invalid signature on zap request.' 'Invalid signature on zap request.'
) )
}) })
@ -172,7 +175,7 @@ describe('validateZapRequest', () => {
privateKey privateKey
) )
expect(nip57.validateZapRequest(JSON.stringify(zapRequest))).toBe( expect(validateZapRequest(JSON.stringify(zapRequest))).toBe(
"Zap request doesn't have a 'p' tag." "Zap request doesn't have a 'p' tag."
) )
}) })
@ -194,7 +197,7 @@ describe('validateZapRequest', () => {
privateKey privateKey
) )
expect(nip57.validateZapRequest(JSON.stringify(zapRequest))).toBe( expect(validateZapRequest(JSON.stringify(zapRequest))).toBe(
"Zap request 'p' tag is not valid hex." "Zap request 'p' tag is not valid hex."
) )
}) })
@ -218,7 +221,7 @@ describe('validateZapRequest', () => {
privateKey privateKey
) )
expect(nip57.validateZapRequest(JSON.stringify(zapRequest))).toBe( expect(validateZapRequest(JSON.stringify(zapRequest))).toBe(
"Zap request 'e' tag is not valid hex." "Zap request 'e' tag is not valid hex."
) )
}) })
@ -240,7 +243,7 @@ describe('validateZapRequest', () => {
privateKey privateKey
) )
expect(nip57.validateZapRequest(JSON.stringify(zapRequest))).toBe( expect(validateZapRequest(JSON.stringify(zapRequest))).toBe(
"Zap request doesn't have a 'relays' tag." "Zap request doesn't have a 'relays' tag."
) )
}) })
@ -263,7 +266,7 @@ describe('validateZapRequest', () => {
privateKey privateKey
) )
expect(nip57.validateZapRequest(JSON.stringify(zapRequest))).toBeNull() expect(validateZapRequest(JSON.stringify(zapRequest))).toBeNull()
}) })
}) })
@ -291,7 +294,7 @@ describe('makeZapReceipt', () => {
const bolt11 = 'bolt11' const bolt11 = 'bolt11'
const paidAt = new Date() const paidAt = new Date()
const result = nip57.makeZapReceipt({zapRequest, preimage, bolt11, paidAt}) const result = makeZapReceipt({zapRequest, preimage, bolt11, paidAt})
expect(result.kind).toBe(9735) expect(result.kind).toBe(9735)
expect(result.created_at).toBeCloseTo(paidAt.getTime() / 1000, 0) expect(result.created_at).toBeCloseTo(paidAt.getTime() / 1000, 0)
@ -324,7 +327,7 @@ describe('makeZapReceipt', () => {
const bolt11 = 'bolt11' const bolt11 = 'bolt11'
const paidAt = new Date() const paidAt = new Date()
const result = nip57.makeZapReceipt({zapRequest, bolt11, paidAt}) const result = makeZapReceipt({zapRequest, bolt11, paidAt})
expect(result.kind).toBe(9735) expect(result.kind).toBe(9735)
expect(result.created_at).toBeCloseTo(paidAt.getTime() / 1000, 0) expect(result.created_at).toBeCloseTo(paidAt.getTime() / 1000, 0)

View File

@ -1,13 +1,13 @@
import {bech32} from '@scure/base' import {bech32} from '@scure/base'
import { import {
Event, Kind,
EventTemplate,
validateEvent, validateEvent,
verifySignature, verifySignature,
Kind type Event,
} from './event' type EventTemplate,
import {utf8Decoder} from './utils' } from './event.ts'
import {utf8Decoder} from './utils.ts'
var _fetch: any var _fetch: any

View File

@ -1,11 +1,8 @@
import 'websocket-polyfill' import 'websocket-polyfill'
import {
SimplePool, import {finishEvent, type Event} from './event.ts'
generatePrivateKey, import {generatePrivateKey, getPublicKey} from './keys.ts'
getPublicKey, import {SimplePool} from './pool.ts'
finishEvent,
type Event,
} from '.'
let pool = new SimplePool() let pool = new SimplePool()

15
pool.ts
View File

@ -1,9 +1,14 @@
import {Relay, relayInit} from './relay' import {
import {normalizeURL} from './utils' relayInit,
import {Filter} from './filter' type Pub,
import {Event} from './event' type Relay,
import {SubscriptionOptions, Sub, Pub} from './relay' type Sub,
type SubscriptionOptions,
} from './relay.ts'
import {normalizeURL} from './utils.ts'
import type {Event} from './event.ts'
import type {Filter} from './filter.ts'
export class SimplePool { export class SimplePool {
private _conn: {[url: string]: Relay} private _conn: {[url: string]: Relay}
private _seenOn: {[id: string]: Set<string>} = {} // a map of all events we've seen in each relay private _seenOn: {[id: string]: Set<string>} = {} // a map of all events we've seen in each relay

View File

@ -1,5 +1,5 @@
import {parseReferences} from '.' import {parseReferences} from './references.ts'
import { buildEvent } from './test-helpers' import {buildEvent} from './test-helpers.ts'
test('parse mentions', () => { test('parse mentions', () => {
let evt = buildEvent({ let evt = buildEvent({

View File

@ -1,5 +1,11 @@
import {Event} from './event' import {
import {decode, AddressPointer, ProfilePointer, EventPointer} from './nip19' decode,
type AddressPointer,
type ProfilePointer,
type EventPointer,
} from './nip19.ts'
import type {Event} from './event.ts'
type Reference = { type Reference = {
text: string text: string

View File

@ -1,10 +1,8 @@
import 'websocket-polyfill' import 'websocket-polyfill'
import {
relayInit, import {finishEvent} from './event.ts'
generatePrivateKey, import {generatePrivateKey, getPublicKey} from './keys.ts'
getPublicKey, import {relayInit} from './relay.ts'
finishEvent,
} from '.'
let relay = relayInit('wss://relay.damus.io/') let relay = relayInit('wss://relay.damus.io/')

View File

@ -1,8 +1,8 @@
/* global WebSocket */ /* global WebSocket */
import {Event, verifySignature, validateEvent} from './event' import {verifySignature, validateEvent, type Event} from './event.ts'
import {Filter, matchFilters} from './filter' import {matchFilters, type Filter} from './filter.ts'
import {getHex64, getSubscriptionId} from './fakejson' import {getHex64, getSubscriptionId} from './fakejson.ts'
type RelayEvent = { type RelayEvent = {
connect: () => void | Promise<void> connect: () => void | Promise<void>

View File

@ -1,4 +1,4 @@
import {type Event} from '.' import type {Event} from './event.ts'
type EventParams<K extends number> = Partial<Event<K>> type EventParams<K extends number> = Partial<Event<K>>

View File

@ -10,6 +10,7 @@
"esModuleInterop": true, "esModuleInterop": true,
"emitDeclarationOnly": true, "emitDeclarationOnly": true,
"outDir": "lib", "outDir": "lib",
"rootDir": "." "rootDir": ".",
"allowImportingTsExtensions": true
} }
} }

View File

@ -1,7 +1,10 @@
import {utils, type Event} from '.' import {buildEvent} from './test-helpers.ts'
import {buildEvent} from './test-helpers' import {
insertEventIntoAscendingList,
insertEventIntoDescendingList,
} from './utils.ts'
const {insertEventIntoAscendingList, insertEventIntoDescendingList} = utils import type {Event} from './event.ts'
describe('inserting into a desc sorted list of events', () => { describe('inserting into a desc sorted list of events', () => {
test('insert into an empty list', async () => { test('insert into an empty list', async () => {

View File

@ -1,4 +1,4 @@
import {Event} from './event' import type {Event} from './event.ts'
export const utf8Decoder = new TextDecoder('utf-8') export const utf8Decoder = new TextDecoder('utf-8')
export const utf8Encoder = new TextEncoder() export const utf8Encoder = new TextEncoder()