diff --git a/event.test.ts b/event.test.ts index ff91ac6..7b68a3f 100644 --- a/event.test.ts +++ b/event.test.ts @@ -1,44 +1,23 @@ import { - getBlankEvent, finishEvent, serializeEvent, getEventHash, validateEvent, verifySignature, getSignature, - Kind, verifiedSymbol, } from './event.ts' import { getPublicKey } from './keys.ts' +import { ShortTextNote } from './kinds.ts' describe('Event', () => { - describe('getBlankEvent', () => { - it('should return a blank event object', () => { - expect(getBlankEvent()).toEqual({ - kind: 255, - content: '', - tags: [], - created_at: 0, - }) - }) - - it('should return a blank event object with defined kind', () => { - expect(getBlankEvent(Kind.Text)).toEqual({ - kind: 1, - content: '', - tags: [], - created_at: 0, - }) - }) - }) - describe('finishEvent', () => { it('should create a signed event from a template', () => { const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' const publicKey = getPublicKey(privateKey) const template = { - kind: Kind.Text, + kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, @@ -64,7 +43,7 @@ describe('Event', () => { const unsignedEvent = { pubkey: publicKey, created_at: 1617932115, - kind: Kind.Text, + kind: ShortTextNote, tags: [], content: 'Hello, world!', } @@ -88,7 +67,7 @@ describe('Event', () => { const publicKey = getPublicKey(privateKey) const invalidEvent = { - kind: Kind.Text, + kind: ShortTextNote, tags: [], created_at: 1617932115, pubkey: publicKey, // missing content @@ -107,7 +86,7 @@ describe('Event', () => { const publicKey = getPublicKey(privateKey) const unsignedEvent = { - kind: Kind.Text, + kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, @@ -127,7 +106,7 @@ describe('Event', () => { const publicKey = getPublicKey(privateKey) const unsignedEvent = { - kind: Kind.Text, + kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, @@ -149,7 +128,7 @@ describe('Event', () => { it('should return false for an event object with missing properties', () => { const invalidEvent = { - kind: Kind.Text, + kind: ShortTextNote, tags: [], created_at: 1617932115, // missing content and pubkey } @@ -221,7 +200,7 @@ describe('Event', () => { const event = finishEvent( { - kind: Kind.Text, + kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, @@ -239,7 +218,7 @@ describe('Event', () => { const { [verifiedSymbol]: _, ...event } = finishEvent( { - kind: Kind.Text, + kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, @@ -263,7 +242,7 @@ describe('Event', () => { const { [verifiedSymbol]: _, ...event } = finishEvent( { - kind: Kind.Text, + kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, @@ -308,7 +287,7 @@ describe('Event', () => { const publicKey = getPublicKey(privateKey) const unsignedEvent = { - kind: Kind.Text, + kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, @@ -336,7 +315,7 @@ describe('Event', () => { const wrongPrivateKey = 'a91e2a9d9e0f70f0877bea0dbf034e8f95d7392a27a7f07da0d14b9e9d456be7' const unsignedEvent = { - kind: Kind.Text, + kind: ShortTextNote, tags: [], content: 'Hello, world!', created_at: 1617932115, diff --git a/event.ts b/event.ts index dfb0b56..1632548 100644 --- a/event.ts +++ b/event.ts @@ -8,8 +8,8 @@ import { utf8Encoder } from './utils.ts' /** Designates a verified event signature. */ export const verifiedSymbol = Symbol('verified') -export interface Event { - kind: K +export interface Event { + kind: number tags: string[][] content: string created_at: number @@ -19,30 +19,16 @@ export interface Event { [verifiedSymbol]?: boolean } -export type EventTemplate = Pick, 'kind' | 'tags' | 'content' | 'created_at'> -export type UnsignedEvent = Pick< - Event, - 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey' -> +export type EventTemplate = Pick +export type UnsignedEvent = Pick /** An event whose signature has been verified. */ -export interface VerifiedEvent extends Event { +export interface VerifiedEvent extends Event { [verifiedSymbol]: true } -export function getBlankEvent(): EventTemplate -export function getBlankEvent(kind: K): EventTemplate -export function getBlankEvent(kind: K | Kind.Blank = Kind.Blank) { - return { - kind, - content: '', - tags: [], - created_at: 0, - } -} - -export function finishEvent(t: EventTemplate, privateKey: string): VerifiedEvent { - const event = t as VerifiedEvent +export function finishEvent(t: EventTemplate, privateKey: string): VerifiedEvent { + const event = t as VerifiedEvent event.pubkey = getPublicKey(privateKey) event.id = getEventHash(event) event.sig = getSignature(event, privateKey) @@ -50,20 +36,20 @@ export function finishEvent(t: EventTemplate, priv return event } -export function serializeEvent(evt: UnsignedEvent): string { +export function serializeEvent(evt: UnsignedEvent): string { if (!validateEvent(evt)) throw new Error("can't serialize event with wrong or missing properties") return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]) } -export function getEventHash(event: UnsignedEvent): string { +export function getEventHash(event: UnsignedEvent): string { let eventHash = sha256(utf8Encoder.encode(serializeEvent(event))) return bytesToHex(eventHash) } const isRecord = (obj: unknown): obj is Record => obj instanceof Object -export function validateEvent(event: T): event is T & UnsignedEvent { +export function validateEvent(event: T): event is T & UnsignedEvent { if (!isRecord(event)) return false if (typeof event.kind !== 'number') return false if (typeof event.content !== 'string') return false @@ -84,7 +70,7 @@ export function validateEvent(event: T): event is T & UnsignedEvent { } /** Verify the event's signature. This function mutates the event with a `verified` symbol, making it idempotent. */ -export function verifySignature(event: Event): event is VerifiedEvent { +export function verifySignature(event: Event): event is VerifiedEvent { if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol] const hash = getEventHash(event) @@ -100,7 +86,7 @@ export function verifySignature(event: Event): event is Ver } /** @deprecated Use `getSignature` instead. */ -export function signEvent(event: UnsignedEvent, key: string): string { +export function signEvent(event: UnsignedEvent, key: string): string { console.warn( 'nostr-tools: `signEvent` is deprecated and will be removed or changed in the future. Please use `getSignature` instead.', ) @@ -108,6 +94,6 @@ export function signEvent(event: UnsignedEvent, key: string): string { } /** Calculate the signature for an event. */ -export function getSignature(event: UnsignedEvent, key: string): string { +export function getSignature(event: UnsignedEvent, key: string): string { return bytesToHex(schnorr.sign(getEventHash(event), key)) } diff --git a/filter.ts b/filter.ts index deea4af..203f6ec 100644 --- a/filter.ts +++ b/filter.ts @@ -1,8 +1,8 @@ import { Event } from './event.ts' -export type Filter = { +export type Filter = { ids?: string[] - kinds?: K[] + kinds?: number[] authors?: string[] since?: number until?: number @@ -11,7 +11,7 @@ export type Filter = { [key: `#${string}`]: string[] | undefined } -export function matchFilter(filter: Filter, event: Event): boolean { +export function matchFilter(filter: Filter, event: Event): boolean { if (filter.ids && filter.ids.indexOf(event.id) === -1) { if (!filter.ids.some(prefix => event.id.startsWith(prefix))) { return false @@ -38,15 +38,15 @@ export function matchFilter(filter: Filter, event: Event): boole return true } -export function matchFilters(filters: Filter[], event: Event): boolean { +export function matchFilters(filters: Filter[], event: Event): boolean { for (let i = 0; i < filters.length; i++) { if (matchFilter(filters[i], event)) return true } return false } -export function mergeFilters(...filters: Filter[]): Filter { - let result: Filter = {} +export function mergeFilters(...filters: Filter[]): Filter { + let result: Filter = {} for (let i = 0; i < filters.length; i++) { let filter = filters[i] Object.entries(filter).forEach(([property, values]) => { diff --git a/kinds.ts b/kinds.ts index f4deabe..0d6f6a8 100644 --- a/kinds.ts +++ b/kinds.ts @@ -39,6 +39,11 @@ export const EventDeletion = 5 export const Repost = 6 export const Reaction = 7 export const BadgeAward = 8 +export const ChannelCreation = 40 +export const ChannelMetadata = 41 +export const ChannelMessage = 42 +export const ChannelHideMessage = 43 +export const ChannelMuteUser = 44 export const Report = 1984 export const ZapRequest = 9734 export const Zap = 9735 @@ -68,10 +73,10 @@ export const BlockedRelaysList = 10006 export const SearchRelaysList = 10007 export const InterestsList = 10015 export const UserEmojiList = 10030 -export const WalletInfo = 13194 +export const NWCWalletInfo = 13194 export const LightningPubRPC = 21000 -export const WalletRequest = 23194 -export const WalletResponse = 23195 +export const NWCWalletRequest = 23194 +export const NWCWalletResponse = 23195 export const NostrConnect = 24133 export const HTTPAuth = 27235 export const Followsets = 30000 diff --git a/nip13.ts b/nip13.ts index 3b2bab9..c3ef403 100644 --- a/nip13.ts +++ b/nip13.ts @@ -23,10 +23,10 @@ export function getPow(hex: string): number { * * Adapted from Snort: https://git.v0l.io/Kieran/snort/src/commit/4df6c19248184218c4c03728d61e94dae5f2d90c/packages/system/src/pow-util.ts#L14-L36 */ -export function minePow(unsigned: UnsignedEvent, difficulty: number): Omit, 'sig'> { +export function minePow(unsigned: UnsignedEvent, difficulty: number): Omit { let count = 0 - const event = unsigned as Omit, 'sig'> + const event = unsigned as Omit const tag = ['nonce', count.toString(), difficulty.toString()] event.tags.push(tag) diff --git a/nip18.test.ts b/nip18.test.ts index e813abd..76bfcca 100644 --- a/nip18.test.ts +++ b/nip18.test.ts @@ -1,5 +1,6 @@ -import { finishEvent, Kind } from './event.ts' +import { finishEvent } from './event.ts' import { getPublicKey } from './keys.ts' +import { Repost, ShortTextNote } from './kinds.ts' import { finishRepostEvent, getRepostedEventPointer, getRepostedEvent } from './nip18.ts' import { buildEvent } from './test-helpers.ts' @@ -12,7 +13,7 @@ describe('finishRepostEvent + getRepostedEventPointer + getRepostedEvent', () => const repostedEvent = finishEvent( { - kind: Kind.Text, + kind: ShortTextNote, tags: [ ['e', 'replied event id'], ['p', 'replied event pubkey'], @@ -30,7 +31,7 @@ describe('finishRepostEvent + getRepostedEventPointer + getRepostedEvent', () => const event = finishRepostEvent(template, repostedEvent, relayUrl, privateKey) - expect(event.kind).toEqual(Kind.Repost) + expect(event.kind).toEqual(Repost) expect(event.tags).toEqual([ ['e', repostedEvent.id, relayUrl], ['p', repostedEvent.pubkey], @@ -61,7 +62,7 @@ describe('finishRepostEvent + getRepostedEventPointer + getRepostedEvent', () => const event = finishRepostEvent(template, repostedEvent, relayUrl, privateKey) - expect(event.kind).toEqual(Kind.Repost) + expect(event.kind).toEqual(Repost) expect(event.tags).toEqual([ ['nonstandard', 'tag'], ['e', repostedEvent.id, relayUrl], @@ -88,7 +89,7 @@ describe('finishRepostEvent + getRepostedEventPointer + getRepostedEvent', () => describe('getRepostedEventPointer', () => { it('should parse an event with only an `e` tag', () => { const event = buildEvent({ - kind: Kind.Repost, + kind: Repost, tags: [['e', 'reposted event id', relayUrl]], }) diff --git a/nip18.ts b/nip18.ts index 3a7e6da..df5e963 100644 --- a/nip18.ts +++ b/nip18.ts @@ -1,4 +1,5 @@ -import { Event, finishEvent, Kind, verifySignature } from './event.ts' +import { Event, finishEvent, verifySignature } from './event.ts' +import { Repost } from './kinds.ts' import { EventPointer } from './nip19.ts' export type RepostEventTemplate = { @@ -20,13 +21,13 @@ export type RepostEventTemplate = { export function finishRepostEvent( t: RepostEventTemplate, - reposted: Event, + reposted: Event, relayUrl: string, privateKey: string, -): Event { +): Event { return finishEvent( { - kind: Kind.Repost, + kind: Repost, tags: [...(t.tags ?? []), ['e', reposted.id, relayUrl], ['p', reposted.pubkey]], content: t.content === '' ? '' : JSON.stringify(reposted), created_at: t.created_at, @@ -35,8 +36,8 @@ export function finishRepostEvent( ) } -export function getRepostedEventPointer(event: Event): undefined | EventPointer { - if (event.kind !== Kind.Repost) { +export function getRepostedEventPointer(event: Event): undefined | EventPointer { + if (event.kind !== Repost) { return undefined } @@ -69,20 +70,17 @@ export type GetRepostedEventOptions = { skipVerification?: boolean } -export function getRepostedEvent( - event: Event, - { skipVerification }: GetRepostedEventOptions = {}, -): undefined | Event { +export function getRepostedEvent(event: Event, { skipVerification }: GetRepostedEventOptions = {}): undefined | Event { const pointer = getRepostedEventPointer(event) if (pointer === undefined || event.content === '') { return undefined } - let repostedEvent: undefined | Event + let repostedEvent: undefined | Event try { - repostedEvent = JSON.parse(event.content) as Event + repostedEvent = JSON.parse(event.content) as Event } catch (error) { return undefined } diff --git a/nip25.test.ts b/nip25.test.ts index d397b82..e365771 100644 --- a/nip25.test.ts +++ b/nip25.test.ts @@ -1,5 +1,6 @@ -import { finishEvent, Kind } from './event.ts' +import { finishEvent } from './event.ts' import { getPublicKey } from './keys.ts' +import { Reaction, ShortTextNote } from './kinds.ts' import { finishReactionEvent, getReactedEventPointer } from './nip25.ts' describe('finishReactionEvent + getReactedEventPointer', () => { @@ -9,7 +10,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => { const reactedEvent = finishEvent( { - kind: Kind.Text, + kind: ShortTextNote, tags: [ ['e', 'replied event id'], ['p', 'replied event pubkey'], @@ -27,7 +28,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => { const event = finishReactionEvent(template, reactedEvent, privateKey) - expect(event.kind).toEqual(Kind.Reaction) + expect(event.kind).toEqual(Reaction) expect(event.tags).toEqual([ ['e', 'replied event id'], ['p', 'replied event pubkey'], @@ -55,7 +56,7 @@ describe('finishReactionEvent + getReactedEventPointer', () => { const event = finishReactionEvent(template, reactedEvent, privateKey) - expect(event.kind).toEqual(Kind.Reaction) + expect(event.kind).toEqual(Reaction) expect(event.tags).toEqual([ ['nonstandard', 'tag'], ['e', 'replied event id'], diff --git a/nip25.ts b/nip25.ts index 5e10c47..ffd97b7 100644 --- a/nip25.ts +++ b/nip25.ts @@ -1,4 +1,5 @@ -import { Event, finishEvent, Kind } from './event.ts' +import { Event, finishEvent } from './event.ts' +import { Reaction } from './kinds.ts' import type { EventPointer } from './nip19.ts' @@ -16,17 +17,13 @@ export type ReactionEventTemplate = { created_at: number } -export function finishReactionEvent( - t: ReactionEventTemplate, - reacted: Event, - privateKey: string, -): Event { +export function finishReactionEvent(t: ReactionEventTemplate, reacted: Event, privateKey: string): Event { const inheritedTags = reacted.tags.filter(tag => tag.length >= 2 && (tag[0] === 'e' || tag[0] === 'p')) return finishEvent( { ...t, - kind: Kind.Reaction, + kind: Reaction, tags: [...(t.tags ?? []), ...inheritedTags, ['e', reacted.id], ['p', reacted.pubkey]], content: t.content ?? '+', }, @@ -34,8 +31,8 @@ export function finishReactionEvent( ) } -export function getReactedEventPointer(event: Event): undefined | EventPointer { - if (event.kind !== Kind.Reaction) { +export function getReactedEventPointer(event: Event): undefined | EventPointer { + if (event.kind !== Reaction) { return undefined } diff --git a/nip28.test.ts b/nip28.test.ts index cee94f5..2ea5f29 100644 --- a/nip28.test.ts +++ b/nip28.test.ts @@ -1,5 +1,5 @@ -import { Kind } from './event.ts' import { getPublicKey } from './keys.ts' +import * as Kind from './kinds.ts' import { channelCreateEvent, channelMetadataEvent, diff --git a/nip28.ts b/nip28.ts index fba812f..38383c9 100644 --- a/nip28.ts +++ b/nip28.ts @@ -1,4 +1,5 @@ -import { Event, finishEvent, Kind } from './event.ts' +import { Event, finishEvent } from './event.ts' +import { ChannelCreation, ChannelHideMessage, ChannelMessage, ChannelMetadata, ChannelMuteUser } from './kinds.ts' export interface ChannelMetadata { name: string @@ -44,10 +45,7 @@ export interface ChannelMuteUserEventTemplate { tags?: string[][] } -export const channelCreateEvent = ( - t: ChannelCreateEventTemplate, - privateKey: string, -): Event | undefined => { +export const channelCreateEvent = (t: ChannelCreateEventTemplate, privateKey: string): Event | undefined => { let content: string if (typeof t.content === 'object') { content = JSON.stringify(t.content) @@ -59,7 +57,7 @@ export const channelCreateEvent = ( return finishEvent( { - kind: Kind.ChannelCreation, + kind: ChannelCreation, tags: [...(t.tags ?? [])], content: content, created_at: t.created_at, @@ -68,10 +66,7 @@ export const channelCreateEvent = ( ) } -export const channelMetadataEvent = ( - t: ChannelMetadataEventTemplate, - privateKey: string, -): Event | undefined => { +export const channelMetadataEvent = (t: ChannelMetadataEventTemplate, privateKey: string): Event | undefined => { let content: string if (typeof t.content === 'object') { content = JSON.stringify(t.content) @@ -83,7 +78,7 @@ export const channelMetadataEvent = ( return finishEvent( { - kind: Kind.ChannelMetadata, + kind: ChannelMetadata, tags: [['e', t.channel_create_event_id], ...(t.tags ?? [])], content: content, created_at: t.created_at, @@ -92,7 +87,7 @@ export const channelMetadataEvent = ( ) } -export const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: string): Event => { +export const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: string): Event => { const tags = [['e', t.channel_create_event_id, t.relay_url, 'root']] if (t.reply_to_channel_message_event_id) { @@ -101,7 +96,7 @@ export const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: return finishEvent( { - kind: Kind.ChannelMessage, + kind: ChannelMessage, tags: [...tags, ...(t.tags ?? [])], content: t.content, created_at: t.created_at, @@ -111,10 +106,7 @@ export const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: } /* "e" tag should be the kind 42 event to hide */ -export const channelHideMessageEvent = ( - t: ChannelHideMessageEventTemplate, - privateKey: string, -): Event | undefined => { +export const channelHideMessageEvent = (t: ChannelHideMessageEventTemplate, privateKey: string): Event | undefined => { let content: string if (typeof t.content === 'object') { content = JSON.stringify(t.content) @@ -126,7 +118,7 @@ export const channelHideMessageEvent = ( return finishEvent( { - kind: Kind.ChannelHideMessage, + kind: ChannelHideMessage, tags: [['e', t.channel_message_event_id], ...(t.tags ?? [])], content: content, created_at: t.created_at, @@ -135,10 +127,7 @@ export const channelHideMessageEvent = ( ) } -export const channelMuteUserEvent = ( - t: ChannelMuteUserEventTemplate, - privateKey: string, -): Event | undefined => { +export const channelMuteUserEvent = (t: ChannelMuteUserEventTemplate, privateKey: string): Event | undefined => { let content: string if (typeof t.content === 'object') { content = JSON.stringify(t.content) @@ -150,7 +139,7 @@ export const channelMuteUserEvent = ( return finishEvent( { - kind: Kind.ChannelMuteUser, + kind: ChannelMuteUser, tags: [['p', t.pubkey_to_mute], ...(t.tags ?? [])], content: content, created_at: t.created_at, diff --git a/nip42.ts b/nip42.ts index 167d6e3..9451116 100644 --- a/nip42.ts +++ b/nip42.ts @@ -1,4 +1,5 @@ -import { Kind, type EventTemplate, type Event } from './event.ts' +import { type EventTemplate, type Event } from './event.ts' +import { ClientAuth } from './kinds.ts' import { Relay } from './relay.ts' /** @@ -17,10 +18,10 @@ export const authenticate = async ({ }: { challenge: string relay: Relay - sign: (e: EventTemplate) => Promise> | Event + sign: (e: EventTemplate) => Promise | Event }): Promise => { - const e: EventTemplate = { - kind: Kind.ClientAuth, + const evt: EventTemplate = { + kind: ClientAuth, created_at: Math.floor(Date.now() / 1000), tags: [ ['relay', relay.url], @@ -28,5 +29,5 @@ export const authenticate = async ({ ], content: '', } - return relay.auth(await sign(e)) + return relay.auth(await sign(evt)) } diff --git a/nip47.test.ts b/nip47.test.ts index 15e4679..31197b5 100644 --- a/nip47.test.ts +++ b/nip47.test.ts @@ -1,7 +1,7 @@ import { makeNwcRequestEvent, parseConnectionString } from './nip47' -import { Kind } from './event' import { decrypt } from './nip04.ts' import crypto from 'node:crypto' +import { NWCWalletRequest } from './kinds.ts' // @ts-ignore // eslint-disable-next-line no-undef @@ -53,7 +53,7 @@ describe('makeNwcRequestEvent', () => { invoice, }) const timeAfter = Date.now() / 1000 - expect(result.kind).toBe(Kind.NwcRequest) + expect(result.kind).toBe(NWCWalletRequest) expect(result.created_at).toBeGreaterThan(timeBefore) expect(result.created_at).toBeLessThan(timeAfter) expect(await decrypt(secret, pubkey, result.content)).toEqual( diff --git a/nip47.ts b/nip47.ts index 16b11b0..f8915a5 100644 --- a/nip47.ts +++ b/nip47.ts @@ -1,6 +1,6 @@ import { finishEvent } from './event.ts' +import { NWCWalletRequest } from './kinds.ts' import { encrypt } from './nip04.ts' -import { Kind } from './event' export function parseConnectionString(connectionString: string) { const { pathname, searchParams } = new URL(connectionString) @@ -32,7 +32,7 @@ export async function makeNwcRequestEvent({ } const encryptedContent = await encrypt(secret, pubkey, JSON.stringify(content)) const eventTemplate = { - kind: Kind.NwcRequest, + kind: NWCWalletRequest, created_at: Math.round(Date.now() / 1000), content: encryptedContent, tags: [['p', pubkey]], diff --git a/nip57.ts b/nip57.ts index 94fd488..cdebd72 100644 --- a/nip57.ts +++ b/nip57.ts @@ -1,6 +1,6 @@ import { bech32 } from '@scure/base' -import { Kind, validateEvent, verifySignature, type Event, type EventTemplate } from './event.ts' +import { validateEvent, verifySignature, type Event, type EventTemplate } from './event.ts' import { utf8Decoder } from './utils.ts' var _fetch: any @@ -13,7 +13,7 @@ export function useFetchImplementation(fetchImplementation: any) { _fetch = fetchImplementation } -export async function getZapEndpoint(metadata: Event): Promise { +export async function getZapEndpoint(metadata: Event): Promise { try { let lnurl: string = '' let { lud06, lud16 } = JSON.parse(metadata.content) @@ -53,11 +53,11 @@ export function makeZapRequest({ amount: number comment: string relays: string[] -}): EventTemplate { +}): EventTemplate { if (!amount) throw new Error('amount not given') if (!profile) throw new Error('profile not given') - let zr: EventTemplate = { + let zr: EventTemplate = { kind: 9734, created_at: Math.round(Date.now() / 1000), content: comment, @@ -111,11 +111,11 @@ export function makeZapReceipt({ preimage?: string bolt11: string paidAt: Date -}): EventTemplate { - let zr: Event = JSON.parse(zapRequest) +}): EventTemplate { + let zr: Event = JSON.parse(zapRequest) let tagsFromZapRequest = zr.tags.filter(([t]) => t === 'e' || t === 'p' || t === 'a') - let zap: EventTemplate = { + let zap: EventTemplate = { kind: 9735, created_at: Math.round(paidAt.getTime() / 1000), content: '', diff --git a/nip98.test.ts b/nip98.test.ts index 9114437..fac6c4d 100644 --- a/nip98.test.ts +++ b/nip98.test.ts @@ -1,9 +1,10 @@ import { getToken, unpackEventFromToken, validateEvent, validateToken } from './nip98.ts' -import { Event, Kind, finishEvent } from './event.ts' +import { Event, finishEvent } from './event.ts' import { generatePrivateKey, getPublicKey } from './keys.ts' import { sha256 } from '@noble/hashes/sha256' import { utf8Encoder } from './utils.ts' import { bytesToHex } from '@noble/hashes/utils' +import { HTTPAuth } from './kinds.ts' const sk = generatePrivateKey() @@ -15,7 +16,7 @@ describe('getToken', () => { expect(decodedResult.created_at).toBeGreaterThan(0) expect(decodedResult.content).toBe('') - expect(decodedResult.kind).toBe(Kind.HttpAuth) + expect(decodedResult.kind).toBe(HTTPAuth) expect(decodedResult.pubkey).toBe(getPublicKey(sk)) expect(decodedResult.tags).toStrictEqual([ ['u', 'http://test.com'], @@ -30,7 +31,7 @@ describe('getToken', () => { expect(decodedResult.created_at).toBeGreaterThan(0) expect(decodedResult.content).toBe('') - expect(decodedResult.kind).toBe(Kind.HttpAuth) + expect(decodedResult.kind).toBe(HTTPAuth) expect(decodedResult.pubkey).toBe(getPublicKey(sk)) expect(decodedResult.tags).toStrictEqual([ ['u', 'http://test.com'], @@ -49,7 +50,7 @@ describe('getToken', () => { expect(decodedResult.created_at).toBeGreaterThan(0) expect(decodedResult.content).toBe('') - expect(decodedResult.kind).toBe(Kind.HttpAuth) + expect(decodedResult.kind).toBe(HTTPAuth) expect(decodedResult.pubkey).toBe(getPublicKey(sk)) expect(decodedResult.tags).toStrictEqual([ ['u', 'http://test.com'], @@ -76,7 +77,7 @@ describe('getToken', () => { expect(decodedResult.created_at).toBeGreaterThan(0) expect(decodedResult.content).toBe('') - expect(decodedResult.kind).toBe(Kind.HttpAuth) + expect(decodedResult.kind).toBe(HTTPAuth) expect(decodedResult.pubkey).toBe(getPublicKey(sk)) expect(decodedResult.tags).toStrictEqual([ ['u', 'http://test.com'], diff --git a/nip98.ts b/nip98.ts index 6ea3937..79bce62 100644 --- a/nip98.ts +++ b/nip98.ts @@ -1,12 +1,13 @@ import { bytesToHex } from '@noble/hashes/utils' import { sha256 } from '@noble/hashes/sha256' import { base64 } from '@scure/base' -import { Event, EventTemplate, Kind, getBlankEvent, verifySignature } from './event' +import { Event, EventTemplate, verifySignature } from './event' import { utf8Decoder, utf8Encoder } from './utils' +import { HTTPAuth } from './kinds' const _authorizationScheme = 'Nostr ' -function hashPayload(payload: any): string { +export function hashPayload(payload: any): string { const hash = sha256(utf8Encoder.encode(JSON.stringify(payload))) return bytesToHex(hash) } @@ -21,28 +22,29 @@ function hashPayload(payload: any): string { export async function getToken( loginUrl: string, httpMethod: string, - sign: (e: EventTemplate) => Promise> | Event, + sign: (e: EventTemplate) => Promise | Event, includeAuthorizationScheme: boolean = false, payload?: Record, ): Promise { if (!loginUrl || !httpMethod) throw new Error('Missing loginUrl or httpMethod') - const event = getBlankEvent(Kind.HttpAuth) - - event.tags = [ - ['u', loginUrl], - ['method', httpMethod], - ] + const event: EventTemplate = { + kind: HTTPAuth, + tags: [ + ['u', loginUrl], + ['method', httpMethod], + ], + created_at: Math.round(new Date().getTime() / 1000), + content: '', + } if (payload) { event.tags.push(['payload', bytesToHex(sha256(utf8Encoder.encode(JSON.stringify(payload))))]) } - event.created_at = Math.round(new Date().getTime() / 1000) - const signedEvent = await sign(event) - const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : '' + return authorizationScheme + base64.encode(utf8Encoder.encode(JSON.stringify(signedEvent))) } @@ -86,7 +88,7 @@ export async function validateEvent(event: Event, url: string, method: string, b if (!verifySignature(event)) { throw new Error('Invalid nostr event, signature invalid') } - if (event.kind !== Kind.HttpAuth) { + if (event.kind !== HTTPAuth) { throw new Error('Invalid nostr event, kind invalid') } diff --git a/pool.ts b/pool.ts index 1de6a70..2370739 100644 --- a/pool.ts +++ b/pool.ts @@ -5,10 +5,10 @@ import type { Event } from './event.ts' import { matchFilters, mergeFilters, type Filter } from './filter.ts' type BatchedRequest = { - filters: Filter[] + filters: Filter[] relays: string[] - resolve: (events: Event[]) => void - events: Event[] + resolve: (events: Event[]) => void + events: Event[] } export class SimplePool { @@ -58,7 +58,7 @@ export class SimplePool { return relay } - sub(relays: string[], filters: Filter[], opts?: SubscriptionOptions): Sub { + sub(relays: string[], filters: Filter[], opts?: SubscriptionOptions): Sub { let _knownIds: Set = new Set() let modifiedOpts = { ...(opts || {}) } modifiedOpts.alreadyHaveEvent = (id, url) => { @@ -118,7 +118,7 @@ export class SimplePool { } }) - let greaterSub: Sub = { + let greaterSub: Sub = { sub(filters, opts) { subs.forEach(sub => sub.sub(filters, opts)) return greaterSub as any @@ -146,11 +146,7 @@ export class SimplePool { return greaterSub } - get( - relays: string[], - filter: Filter, - opts?: SubscriptionOptions, - ): Promise | null> { + get(relays: string[], filter: Filter, opts?: SubscriptionOptions): Promise { return new Promise(resolve => { let sub = this.sub(relays, [filter], opts) let timeout = setTimeout(() => { @@ -165,13 +161,9 @@ export class SimplePool { }) } - list( - relays: string[], - filters: Filter[], - opts?: SubscriptionOptions, - ): Promise[]> { + list(relays: string[], filters: Filter[], opts?: SubscriptionOptions): Promise { return new Promise(resolve => { - let events: Event[] = [] + let events: Event[] = [] let sub = this.sub(relays, filters, opts) sub.on('event', event => { @@ -186,11 +178,7 @@ export class SimplePool { }) } - batchedList( - batchKey: string, - relays: string[], - filters: Filter[], - ): Promise[]> { + batchedList(batchKey: string, relays: string[], filters: Filter[]): Promise { return new Promise(resolve => { if (!this.batchedByKey[batchKey]) { this.batchedByKey[batchKey] = [ @@ -236,7 +224,7 @@ export class SimplePool { }) } - publish(relays: string[], event: Event): Promise[] { + publish(relays: string[], event: Event): Promise[] { return relays.map(async relay => { let r = await this.ensureRelay(relay) return r.publish(event) diff --git a/relay.ts b/relay.ts index 87cdd20..9d06a46 100644 --- a/relay.ts +++ b/relay.ts @@ -15,8 +15,8 @@ type RelayEvent = { export type CountPayload = { count: number } -export type SubEvent = { - event: (event: Event) => void | Promise +export type SubEvent = { + event: (event: Event) => void | Promise count: (payload: CountPayload) => void | Promise eose: () => void | Promise } @@ -25,21 +25,21 @@ export type Relay = { status: number connect: () => Promise close: () => void - sub: (filters: Filter[], opts?: SubscriptionOptions) => Sub - list: (filters: Filter[], opts?: SubscriptionOptions) => Promise[]> - get: (filter: Filter, opts?: SubscriptionOptions) => Promise | null> + sub: (filters: Filter[], opts?: SubscriptionOptions) => Sub + list: (filters: Filter[], opts?: SubscriptionOptions) => Promise + get: (filter: Filter, opts?: SubscriptionOptions) => Promise count: (filters: Filter[], opts?: SubscriptionOptions) => Promise - publish: (event: Event) => Promise - auth: (event: Event) => Promise + publish: (event: Event) => Promise + auth: (event: Event) => Promise off: (event: T, listener: U) => void on: (event: T, listener: U) => void } -export type Sub = { - sub: (filters: Filter[], opts: SubscriptionOptions) => Sub +export type Sub = { + sub: (filters: Filter[], opts: SubscriptionOptions) => Sub unsub: () => void - on: , U extends SubEvent[T]>(event: T, listener: U) => void - off: , U extends SubEvent[T]>(event: T, listener: U) => void - events: AsyncGenerator, void, unknown> + on: (event: T, listener: U) => void + off: (event: T, listener: U) => void + events: AsyncGenerator } export type SubscriptionOptions = { @@ -72,7 +72,7 @@ export function relayInit( var openSubs: { [id: string]: { filters: Filter[] } & SubscriptionOptions } = {} var listeners = newListeners() var subListeners: { - [subid: string]: { [TK in keyof SubEvent]: SubEvent[TK][] } + [subid: string]: { [TK in keyof SubEvent]: SubEvent[TK][] } } = {} var pubListeners: { [eventid: string]: { @@ -223,15 +223,15 @@ export function relayInit( } } - const sub = ( - filters: Filter[], + const sub = ( + filters: Filter[], { verb = 'REQ', skipVerification = false, alreadyHaveEvent = null, id = Math.random().toString().slice(2), }: SubscriptionOptions = {}, - ): Sub => { + ): Sub => { let subid = id openSubs[subid] = { @@ -242,7 +242,7 @@ export function relayInit( } trySend([verb, subid, ...filters]) - let subscription: Sub = { + let subscription: Sub = { sub: (newFilters, newOpts = {}) => sub(newFilters || filters, { skipVerification: newOpts.skipVerification || skipVerification, @@ -275,7 +275,7 @@ export function relayInit( return subscription } - function _publishEvent(event: Event, type: string) { + function _publishEvent(event: Event, type: string) { return new Promise((resolve, reject) => { if (!event.id) { reject(new Error(`event ${event} has no id`)) @@ -305,7 +305,7 @@ export function relayInit( list: (filters, opts?: SubscriptionOptions) => new Promise(resolve => { let s = sub(filters, opts) - let events: Event[] = [] + let events: Event[] = [] let timeout = setTimeout(() => { s.unsub() resolve(events) @@ -366,11 +366,11 @@ export function relayInit( } } -export async function* eventsGenerator(sub: Sub): AsyncGenerator, void, unknown> { - let nextResolve: ((event: Event) => void) | undefined - const eventQueue: Event[] = [] +export async function* eventsGenerator(sub: Sub): AsyncGenerator { + let nextResolve: ((event: Event) => void) | undefined + const eventQueue: Event[] = [] - const pushToQueue = (event: Event) => { + const pushToQueue = (event: Event) => { if (nextResolve) { nextResolve(event) nextResolve = undefined @@ -386,7 +386,7 @@ export async function* eventsGenerator(sub: Sub): AsyncGene if (eventQueue.length > 0) { yield eventQueue.shift()! } else { - const event = await new Promise>(resolve => { + const event = await new Promise(resolve => { nextResolve = resolve }) yield event diff --git a/test-helpers.ts b/test-helpers.ts index 98a8bab..6169aa6 100644 --- a/test-helpers.ts +++ b/test-helpers.ts @@ -1,12 +1,10 @@ import type { Event } from './event.ts' -type EventParams = Partial> - /** Build an event for testing purposes. */ -export function buildEvent(params: EventParams): Event { +export function buildEvent(params: Partial): Event { return { id: '', - kind: 1 as K, + kind: 1, pubkey: '', created_at: 0, content: '', diff --git a/utils.ts b/utils.ts index cae4d5f..affe6e8 100644 --- a/utils.ts +++ b/utils.ts @@ -16,7 +16,7 @@ export function normalizeURL(url: string): string { // // fast insert-into-sorted-array functions adapted from https://github.com/terrymorse58/fast-sorted-array // -export function insertEventIntoDescendingList(sortedArray: Event[], event: Event) { +export function insertEventIntoDescendingList(sortedArray: Event[], event: Event) { let start = 0 let end = sortedArray.length - 1 let midPoint @@ -54,7 +54,7 @@ export function insertEventIntoDescendingList(sortedArray: Event[], even return sortedArray } -export function insertEventIntoAscendingList(sortedArray: Event[], event: Event) { +export function insertEventIntoAscendingList(sortedArray: Event[], event: Event) { let start = 0 let end = sortedArray.length - 1 let midPoint