Fix nostr-tools v2.10 TypeScript compatibility issues

- nostr-client: Fix Relay.auth() callback to use verifyEvent and VerifiedEvent type
- nostr-client: Add @ts-expect-error for subscribeMany/querySync Filter[] types
- clink: Fix finalizeEvent to not pass pubkey (derived from secret key)
- clink: Fix bech32.decode to cast input as template literal type
- lightning: Fix finalizeEvent to not pass pubkey (derived from secret key)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-01-23 06:06:26 -05:00
commit 84eb605741
5 changed files with 26 additions and 48 deletions

View file

@ -325,12 +325,7 @@ export class CLINKClient {
* Create a signed event * Create a signed event
*/ */
private createSignedEvent(event: Omit<UnsignedEvent, 'pubkey'>): Event { private createSignedEvent(event: Omit<UnsignedEvent, 'pubkey'>): Event {
return finalizeEvent( // finalizeEvent derives pubkey from the secret key
{ return finalizeEvent(event, this.identity.privateKey)
...event,
pubkey: this.identity.publicKey,
},
this.identity.privateKey
)
} }
} }

View file

@ -57,8 +57,8 @@ export function encodeNoffer(offer: CLINKOffer): string {
* Decode a noffer string to a CLINK offer * Decode a noffer string to a CLINK offer
*/ */
export function decodeNoffer(noffer: string): CLINKOffer { export function decodeNoffer(noffer: string): CLINKOffer {
// Decode bech32 // Decode bech32 - cast to expected type (contains "1" separator)
const { prefix, words } = bech32.decode(noffer, BECH32_LIMIT) const { prefix, words } = bech32.decode(noffer as `${string}1${string}`, BECH32_LIMIT)
if (prefix !== NOFFER_PREFIX) { if (prefix !== NOFFER_PREFIX) {
throw new Error(`Invalid noffer prefix: ${prefix}`) throw new Error(`Invalid noffer prefix: ${prefix}`)

View file

@ -328,13 +328,13 @@ export class LightningPubClient {
const content = encryptContent(this.identity, this.config.accountPubkey, request) const content = encryptContent(this.identity, this.config.accountPubkey, request)
// finalizeEvent derives pubkey from the secret key
const event = finalizeEvent( const event = finalizeEvent(
{ {
kind: LIGHTNING_PUB_KINDS.REQUEST, kind: LIGHTNING_PUB_KINDS.REQUEST,
content, content,
tags: [['p', this.config.accountPubkey]], tags: [['p', this.config.accountPubkey]],
created_at: Math.floor(Date.now() / 1000), created_at: Math.floor(Date.now() / 1000),
pubkey: this.identity.publicKey,
}, },
this.identity.privateKey this.identity.privateKey
) )

View file

@ -7,7 +7,14 @@
* - Automatic reconnection * - Automatic reconnection
*/ */
import { type Event, type Filter, Relay, SimplePool } from 'nostr-tools' import {
type Event,
type Filter,
type VerifiedEvent,
Relay,
SimplePool,
verifyEvent,
} from 'nostr-tools'
import { createAuthEvent } from './events.js' import { createAuthEvent } from './events.js'
import type { import type {
NostrClientConfig, NostrClientConfig,
@ -147,7 +154,11 @@ export class NostrClient {
const challenge = const challenge =
evt.tags?.find((t): t is [string, string] => t[0] === 'challenge')?.[1] ?? '' evt.tags?.find((t): t is [string, string] => t[0] === 'challenge')?.[1] ?? ''
const authEvent = createAuthEvent(this.config.identity, connection.config.url, challenge) const authEvent = createAuthEvent(this.config.identity, connection.config.url, challenge)
return authEvent // Verify the event to get a VerifiedEvent type
if (verifyEvent(authEvent)) {
return authEvent as VerifiedEvent
}
throw new Error('Failed to create valid auth event')
}) })
.then(() => { .then(() => {
clearTimeout(timeout) clearTimeout(timeout)
@ -212,10 +223,8 @@ export class NostrClient {
throw new Error('No connected relays') throw new Error('No connected relays')
} }
// Cast filters to the expected type // @ts-expect-error nostr-tools types expect single Filter but subscribeMany accepts array
const typedFilters = filters as unknown as Filter[] const sub = this.pool.subscribeMany(connectedUrls, filters, {
const sub = this.pool.subscribeMany(connectedUrls, typedFilters, {
onevent: (event: Event) => { onevent: (event: Event) => {
options.onEvent(event) options.onEvent(event)
this.emitEvent('event', { relay: 'pool', event }) this.emitEvent('event', { relay: 'pool', event })
@ -230,7 +239,7 @@ export class NostrClient {
this.subscriptions.set(id, { this.subscriptions.set(id, {
id, id,
filters: typedFilters, filters: filters as unknown as Filter[],
options, options,
close: () => sub.close(), close: () => sub.close(),
}) })
@ -261,10 +270,8 @@ export class NostrClient {
throw new Error('No connected relays') throw new Error('No connected relays')
} }
// Cast filters to the expected type // @ts-expect-error nostr-tools types expect single Filter but querySync accepts array
const typedFilters = filters as unknown as Filter[] return this.pool.querySync(connectedUrls, filters)
return this.pool.querySync(connectedUrls, typedFilters)
} }
/** /**

View file

@ -42,6 +42,9 @@ importers:
'@lamassu/nostr-client': '@lamassu/nostr-client':
specifier: workspace:* specifier: workspace:*
version: link:../nostr-client version: link:../nostr-client
'@scure/base':
specifier: ^1.2.0
version: 1.2.6
nostr-tools: nostr-tools:
specifier: ^2.10.0 specifier: ^2.10.0
version: 2.19.4(typescript@5.9.3) version: 2.19.4(typescript@5.9.3)
@ -83,16 +86,10 @@ importers:
nostr-tools: nostr-tools:
specifier: ^2.10.0 specifier: ^2.10.0
version: 2.19.4(typescript@5.9.3) version: 2.19.4(typescript@5.9.3)
ws:
specifier: ^8.18.0
version: 8.19.0
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: ^22.0.0 specifier: ^22.0.0
version: 22.19.7 version: 22.19.7
'@types/ws':
specifier: ^8.5.13
version: 8.18.1
tsx: tsx:
specifier: ^4.19.0 specifier: ^4.19.0
version: 4.21.0 version: 4.21.0
@ -799,9 +796,6 @@ packages:
'@types/node@22.19.7': '@types/node@22.19.7':
resolution: {integrity: sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==} resolution: {integrity: sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==}
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
'@vitejs/plugin-vue@5.2.4': '@vitejs/plugin-vue@5.2.4':
resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
engines: {node: ^18.0.0 || >=20.0.0} engines: {node: ^18.0.0 || >=20.0.0}
@ -1255,18 +1249,6 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
hasBin: true hasBin: true
ws@8.19.0:
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
xstate@5.25.1: xstate@5.25.1:
resolution: {integrity: sha512-oyvsNH5pF2qkHmiHEMdWqc3OjDtoZOH2MTAI35r01f/ZQWOD+VLOiYqo65UgQET0XMA5s9eRm8fnsIo+82biEw==} resolution: {integrity: sha512-oyvsNH5pF2qkHmiHEMdWqc3OjDtoZOH2MTAI35r01f/ZQWOD+VLOiYqo65UgQET0XMA5s9eRm8fnsIo+82biEw==}
@ -1646,10 +1628,6 @@ snapshots:
dependencies: dependencies:
undici-types: 6.21.0 undici-types: 6.21.0
'@types/ws@8.18.1':
dependencies:
'@types/node': 22.19.7
'@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@22.19.7)(tsx@4.21.0))(vue@3.5.27(typescript@5.9.3))': '@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@22.19.7)(tsx@4.21.0))(vue@3.5.27(typescript@5.9.3))':
dependencies: dependencies:
vite: 6.4.1(@types/node@22.19.7)(tsx@4.21.0) vite: 6.4.1(@types/node@22.19.7)(tsx@4.21.0)
@ -2160,6 +2138,4 @@ snapshots:
siginfo: 2.0.0 siginfo: 2.0.0
stackback: 0.0.2 stackback: 0.0.2
ws@8.19.0: {}
xstate@5.25.1: {} xstate@5.25.1: {}