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,
verifySignature,
getSignature,
getPublicKey,
Kind
} from '.'
Kind,
} from './event.ts'
import {getPublicKey} from './keys.ts'
describe('Event', () => {
describe('getBlankEvent', () => {

View File

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

View File

@ -1,15 +1,15 @@
import {fj} from '.'
import {matchEventId, matchEventKind, getSubscriptionId} from './fakejson.ts'
test('match id', () => {
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}]`,
'fef2a50f7d9d3d5a5f38ee761bc087ec16198d3f0140df6d1e8193abf7c2b146'
)
).toBeTruthy()
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":[]}]`,
'fef2a50f7d9d3d5a5f38ee761bc087ec16198d3f0140df6d1e8193abf7c2b146'
)
@ -18,14 +18,14 @@ test('match id', () => {
test('match kind', () => {
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}]`,
1
)
).toBeTruthy()
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":[]}]`,
12720
)
@ -33,14 +33,14 @@ test('match kind', () => {
})
test('match subscription id', () => {
expect(fj.getSubscriptionId('["EVENT","",{}]')).toEqual('')
expect(fj.getSubscriptionId('["EVENT","_",{}]')).toEqual('_')
expect(fj.getSubscriptionId('["EVENT","subname",{}]')).toEqual('subname')
expect(fj.getSubscriptionId('["EVENT", "kasjbdjkav", {}]')).toEqual(
expect(getSubscriptionId('["EVENT","",{}]')).toEqual('')
expect(getSubscriptionId('["EVENT","_",{}]')).toEqual('_')
expect(getSubscriptionId('["EVENT","subname",{}]')).toEqual('subname')
expect(getSubscriptionId('["EVENT", "kasjbdjkav", {}]')).toEqual(
'kasjbdjkav'
)
expect(
fj.getSubscriptionId(
getSubscriptionId(
' [ \n\n "EVENT" , \n\n "y4d5ow45gfwoiudfÇA VSADLKAN KLDASB[12312535]SFMZSNJKLH" , {}]'
)
).toEqual('y4d5ow45gfwoiudfÇA VSADLKAN KLDASB[12312535]SFMZSNJKLH')

View File

@ -1,5 +1,5 @@
import {matchFilter, matchFilters} from '.'
import {buildEvent} from './test-helpers'
import {matchFilter, matchFilters} from './filter.ts'
import {buildEvent} from './test-helpers.ts'
describe('Filter', () => {
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> = {
ids?: string[]

View File

@ -1,24 +1,24 @@
export * from './keys'
export * from './relay'
export * from './event'
export * from './filter'
export * from './pool'
export * from './references'
export * from './keys.ts'
export * from './relay.ts'
export * from './event.ts'
export * from './filter.ts'
export * from './pool.ts'
export * from './references.ts'
export * as nip04 from './nip04'
export * as nip05 from './nip05'
export * as nip06 from './nip06'
export * as nip10 from './nip10'
export * as nip13 from './nip13'
export * as nip18 from './nip18'
export * as nip19 from './nip19'
export * as nip21 from './nip21'
export * as nip25 from './nip25'
export * as nip26 from './nip26'
export * as nip27 from './nip27'
export * as nip39 from './nip39'
export * as nip42 from './nip42'
export * as nip57 from './nip57'
export * as nip04 from './nip04.ts'
export * as nip05 from './nip05.ts'
export * as nip06 from './nip06.ts'
export * as nip10 from './nip10.ts'
export * as nip13 from './nip13.ts'
export * as nip18 from './nip18.ts'
export * as nip19 from './nip19.ts'
export * as nip21 from './nip21.ts'
export * as nip25 from './nip25.ts'
export * as nip26 from './nip26.ts'
export * as nip27 from './nip27.ts'
export * as nip39 from './nip39.ts'
export * as nip42 from './nip42.ts'
export * as nip57 from './nip57.ts'
export * as fj from './fakejson'
export * as utils from './utils'
export * as fj from './fakejson.ts'
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', () => {
expect(generatePrivateKey()).toMatch(/[a-f0-9]{64}/)

View File

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

View File

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

View File

@ -1,28 +1,29 @@
import fetch from 'node-fetch'
import {nip05} from '.'
import {useFetchImplementation, queryProfile} from './nip05.ts'
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(
'32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'
)
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(
'32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'
)
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(
'36e65b503eba8a6b698e724a59137603101166a1cddb45ddc704247fc8aa0fce'
)
expect(p3!.relays).toEqual(undefined)
let p4 = await nip05.queryProfile('_@fiatjaf.com')
let p4 = await queryProfile('_@fiatjaf.com')
expect(p4!.pubkey).toEqual(
'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.

View File

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

View File

@ -1,4 +1,4 @@
import {nip10} from '.'
import {parse} from './nip10.ts'
describe('parse NIP10-referenced 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: [
{
id: 'bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631',
@ -128,7 +128,7 @@ describe('parse NIP10-referenced events', () => {
]
}
expect(nip10.parse(event)).toEqual({
expect(parse(event)).toEqual({
mentions: [
{
id: 'bbd72f0ae14374aa8fb166b483cfcf99b57d7f4cf1600ccbf17c350040834631',
@ -189,7 +189,7 @@ describe('parse NIP10-referenced events', () => {
]
}
expect(nip10.parse(event)).toEqual({
expect(parse(event)).toEqual({
mentions: [],
profiles: [
{
@ -233,7 +233,7 @@ describe('parse NIP10-referenced events', () => {
]
}
expect(nip10.parse(event)).toEqual({
expect(parse(event)).toEqual({
mentions: [],
profiles: [
{
@ -302,7 +302,7 @@ describe('parse NIP10-referenced events', () => {
]
}
expect(nip10.parse(event)).toEqual({
expect(parse(event)).toEqual({
mentions: [],
profiles: [
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,7 @@
import * as nip19 from './nip19'
import * as nip21 from './nip21'
import {BECH32_REGEX, decode, type DecodeResult} from './nip19.ts'
/** 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. */
export function test(value: unknown): value is `nostr:${string}` {
@ -19,16 +18,16 @@ export interface NostrURI {
/** The bech32-encoded data (eg `npub1...`). */
value: string
/** Decoded bech32 string, according to NIP-19. */
decoded: nip19.DecodeResult
decoded: DecodeResult
}
/** Parse and decode a Nostr URI. */
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}`)
return {
uri: match[0] as `nostr:${string}`,
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', () => {
const privateKey =
@ -24,7 +26,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => {
created_at: 1617932115
}
const event = nip25.finishReactionEvent(template, reactedEvent, privateKey)
const event = finishReactionEvent(template, reactedEvent, privateKey)
expect(event.kind).toEqual(Kind.Reaction)
expect(event.tags).toEqual([
@ -39,7 +41,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => {
expect(typeof event.id).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!.author).toEqual(reactedEvent.pubkey)
@ -52,7 +54,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => {
created_at: 1617932115
}
const event = nip25.finishReactionEvent(template, reactedEvent, privateKey)
const event = finishReactionEvent(template, reactedEvent, privateKey)
expect(event.kind).toEqual(Kind.Reaction)
expect(event.tags).toEqual([
@ -68,7 +70,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => {
expect(typeof event.id).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!.author).toEqual(reactedEvent.pubkey)

View File

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

View File

@ -1,9 +1,10 @@
import {nip26, getPublicKey, generatePrivateKey} from '.'
import { buildEvent } from './test-helpers'
import {getPublicKey, generatePrivateKey} from './keys.ts'
import {getDelegator, createDelegation} from './nip26.ts'
import {buildEvent} from './test-helpers.ts'
test('parse good delegation from NIP', async () => {
expect(
nip26.getDelegator({
getDelegator({
id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc',
pubkey:
'62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49',
@ -25,7 +26,7 @@ test('parse good delegation from NIP', async () => {
test('parse bad delegations', async () => {
expect(
nip26.getDelegator({
getDelegator({
id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc',
pubkey:
'62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49',
@ -45,7 +46,7 @@ test('parse bad delegations', async () => {
).toEqual(null)
expect(
nip26.getDelegator({
getDelegator({
id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc',
pubkey:
'62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49',
@ -65,7 +66,7 @@ test('parse bad delegations', async () => {
).toEqual(null)
expect(
nip26.getDelegator({
getDelegator({
id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc',
pubkey:
'62903b1ff41559daf9ee98ef1ae67c152f301bb5ce26d14baba3052f649c3f49',
@ -90,7 +91,7 @@ test('create and verify delegation', async () => {
let pk1 = getPublicKey(sk1)
let sk2 = generatePrivateKey()
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('to', pk2)
expect(delegation).toHaveProperty('cond', 'kind=1')
@ -100,5 +101,5 @@ test('create and verify delegation', async () => {
tags: [['delegation', delegation.from, delegation.cond, delegation.sig]],
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 {sha256} from '@noble/hashes/sha256'
import {Event} from './event'
import {utf8Encoder} from './utils'
import {getPublicKey} from './keys'
import {utf8Encoder} from './utils.ts'
import {getPublicKey} from './keys.ts'
import type {Event} from './event.ts'
export type Parameters = {
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', () => {
const result = nip27.matchAll(
const result = matchAll(
'Hello nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6!\n\nnostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky'
)
@ -33,7 +33,7 @@ test('replaceAll', () => {
const content =
'Hello nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6!\n\nnostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky'
const result = nip27.replaceAll(content, ({decoded, value}) => {
const result = replaceAll(content, ({decoded, value}) => {
switch (decoded.type) {
case 'npub':
return '@alex'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

15
pool.ts
View File

@ -1,9 +1,14 @@
import {Relay, relayInit} from './relay'
import {normalizeURL} from './utils'
import {Filter} from './filter'
import {Event} from './event'
import {SubscriptionOptions, Sub, Pub} from './relay'
import {
relayInit,
type Pub,
type 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 {
private _conn: {[url: string]: 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 { buildEvent } from './test-helpers'
import {parseReferences} from './references.ts'
import {buildEvent} from './test-helpers.ts'
test('parse mentions', () => {
let evt = buildEvent({

View File

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

View File

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

View File

@ -1,8 +1,8 @@
/* global WebSocket */
import {Event, verifySignature, validateEvent} from './event'
import {Filter, matchFilters} from './filter'
import {getHex64, getSubscriptionId} from './fakejson'
import {verifySignature, validateEvent, type Event} from './event.ts'
import {matchFilters, type Filter} from './filter.ts'
import {getHex64, getSubscriptionId} from './fakejson.ts'
type RelayEvent = {
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>>

View File

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

View File

@ -1,7 +1,10 @@
import {utils, type Event} from '.'
import {buildEvent} from './test-helpers'
import {buildEvent} from './test-helpers.ts'
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', () => {
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 utf8Encoder = new TextEncoder()