Fix: Replace pool.sub() with createSubscription() for nostr-tools 2.19.4 compatibility

This commit is contained in:
Nicolas Cantu 2025-12-28 22:56:34 +01:00
parent b551e257f9
commit a2656ef3f8
18 changed files with 116 additions and 75 deletions

View File

@ -65,7 +65,8 @@ export function fetchAuthorPresentationFromPool(
return new Promise((resolve) => {
let resolved = false
const relayUrl = getPrimaryRelaySync()
const sub = pool.sub([relayUrl], filters)
const { createSubscription } = require('@/types/nostr-tools-extended')
const sub = createSubscription(pool, [relayUrl], filters)
const finalize = (value: import('@/types/nostr').AuthorPresentationArticle | null) => {
if (resolved) {

View File

@ -1,6 +1,7 @@
import { nostrService } from './nostr'
import { getPrimaryRelaySync } from './config'
import type { Event } from 'nostr-tools'
import { createSubscription } from '@/types/nostr-tools-extended'
export function createMessageVerificationFilters(messageEventId: string, authorPubkey: string, recipientPubkey: string, articleId: string) {
return [
@ -33,7 +34,7 @@ export function handleMessageVerificationEvent(
}
export function setupMessageVerificationHandlers(
sub: import('@/types/nostr-tools-extended').SimplePoolWithSub['sub'] extends (...args: any[]) => infer R ? R : never,
sub: import('@/types/nostr-tools-extended').Subscription,
messageEventId: string,
articleId: string,
recipientPubkey: string,
@ -69,7 +70,7 @@ export function setupMessageVerificationHandlers(
}
function createMessageVerificationSubscription(
pool: import('@/types/nostr-tools-extended').SimplePoolWithSub,
pool: import('nostr-tools').SimplePool,
messageEventId: string,
authorPubkey: string,
recipientPubkey: string,
@ -77,11 +78,11 @@ function createMessageVerificationSubscription(
) {
const filters = createMessageVerificationFilters(messageEventId, authorPubkey, recipientPubkey, articleId)
const relayUrl = getPrimaryRelaySync()
return pool.sub([relayUrl], filters)
return createSubscription(pool, [relayUrl], filters)
}
function createVerificationPromise(
sub: import('@/types/nostr-tools-extended').SimplePoolWithSub['sub'] extends (...args: any[]) => infer R ? R : never,
sub: import('@/types/nostr-tools-extended').Subscription,
messageEventId: string,
articleId: string,
recipientPubkey: string,
@ -121,7 +122,7 @@ export function verifyPrivateMessagePublished(
}
const sub = createMessageVerificationSubscription(
pool as import('@/types/nostr-tools-extended').SimplePoolWithSub,
pool,
messageEventId,
authorPubkey,
recipientPubkey,

View File

@ -1,12 +1,13 @@
import type { Event } from 'nostr-tools'
import { nostrService } from './nostr'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import { SimplePool } from 'nostr-tools'
import { createSubscription } from '@/types/nostr-tools-extended'
import type { Article } from '@/types/nostr'
import { parseArticleFromEvent } from './nostrEventParsing'
import { buildTagFilter } from './nostrTagSystem'
import { getPrimaryRelaySync } from './config'
function createSeriesSubscription(poolWithSub: SimplePoolWithSub, seriesId: string, limit: number) {
function createSeriesSubscription(pool: SimplePool, seriesId: string, limit: number) {
const filters = [
{
...buildTagFilter({
@ -17,7 +18,7 @@ function createSeriesSubscription(poolWithSub: SimplePoolWithSub, seriesId: stri
},
]
const relayUrl = getPrimaryRelaySync()
return poolWithSub.sub([relayUrl], filters)
return createSubscription(pool, [relayUrl], filters)
}
export function getArticlesBySeries(seriesId: string, timeoutMs: number = 5000, limit: number = 100): Promise<Article[]> {
@ -25,8 +26,7 @@ export function getArticlesBySeries(seriesId: string, timeoutMs: number = 5000,
if (!pool) {
throw new Error('Pool not initialized')
}
const poolWithSub = pool as SimplePoolWithSub
const sub = createSeriesSubscription(poolWithSub, seriesId, limit)
const sub = createSeriesSubscription(pool, seriesId, limit)
return new Promise<Article[]>((resolve) => {
const results: Article[] = []

View File

@ -1,5 +1,5 @@
import { nostrService } from './nostr'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import { createSubscription } from '@/types/nostr-tools-extended'
import { getPrimaryRelaySync } from './config'
import type { Event } from 'nostr-tools'
@ -40,7 +40,7 @@ function createContentDeliveryFilters(authorPubkey: string, recipientPubkey: str
}
function setupContentDeliveryHandlers(
sub: SimplePoolWithSub['sub'] extends (...args: any[]) => infer R ? R : never,
sub: import('@/types/nostr-tools-extended').Subscription,
status: ContentDeliveryStatus,
finalize: (result: ContentDeliveryStatus) => void,
isResolved: () => boolean
@ -71,7 +71,7 @@ function setupContentDeliveryHandlers(
}
function createContentDeliverySubscription(
pool: SimplePoolWithSub,
pool: import('nostr-tools').SimplePool,
authorPubkey: string,
recipientPubkey: string,
articleId: string,
@ -79,11 +79,11 @@ function createContentDeliverySubscription(
) {
const filters = createContentDeliveryFilters(authorPubkey, recipientPubkey, articleId, messageEventId)
const relayUrl = getPrimaryRelaySync()
return pool.sub([relayUrl], filters)
return createSubscription(pool, [relayUrl], filters)
}
function createContentDeliveryPromise(
sub: SimplePoolWithSub['sub'] extends (...args: any[]) => infer R ? R : never,
sub: import('@/types/nostr-tools-extended').Subscription,
status: ContentDeliveryStatus
): Promise<ContentDeliveryStatus> {
return new Promise((resolve) => {
@ -122,8 +122,7 @@ export function verifyContentDelivery(
return Promise.resolve(status)
}
const poolWithSub = pool as SimplePoolWithSub
const sub = createContentDeliverySubscription(poolWithSub, authorPubkey, recipientPubkey, articleId, messageEventId)
const sub = createContentDeliverySubscription(pool, authorPubkey, recipientPubkey, articleId, messageEventId)
return createContentDeliveryPromise(sub, status)
} catch (error) {
status.error = error instanceof Error ? error.message : 'Unknown error'

View File

@ -1,7 +1,7 @@
import { Event, EventTemplate, finalizeEvent, nip19, SimplePool } from 'nostr-tools'
import { hexToBytes } from 'nostr-tools/utils'
import type { Article, NostrProfile } from '@/types/nostr'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import { createSubscription } from '@/types/nostr-tools-extended'
import { parseArticleFromEvent } from './nostrEventParsing'
import {
getPrivateContent as getPrivateContentFromPool,
@ -83,7 +83,7 @@ class NostrService {
}
}
private createArticleSubscription(pool: SimplePoolWithSub, limit: number) {
private createArticleSubscription(pool: SimplePool, limit: number) {
const filters = [
{
...buildTagFilter({
@ -93,7 +93,7 @@ class NostrService {
},
]
const relayUrl = getPrimaryRelaySync()
return pool.sub([relayUrl], filters)
return createSubscription(pool, [relayUrl], filters)
}
subscribeToArticles(callback: (article: Article) => void, limit: number = 100): () => void {
@ -109,8 +109,7 @@ class NostrService {
throw new Error('Pool not initialized')
}
const pool = this.pool as SimplePoolWithSub
const sub = this.createArticleSubscription(pool, limit)
const sub = this.createArticleSubscription(this.pool, limit)
sub.on('event', (event: Event) => {
try {

View File

@ -1,6 +1,5 @@
import { Event, nip04 } from 'nostr-tools'
import { SimplePool } from 'nostr-tools'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import { decryptArticleContent, type DecryptionKey } from './articleEncryption'
import { getPrimaryRelaySync } from './config'
@ -40,7 +39,8 @@ export function getPrivateContent(
return new Promise((resolve) => {
let resolved = false
const relayUrl = getPrimaryRelaySync()
const sub = (pool as SimplePoolWithSub).sub([relayUrl], createPrivateMessageFilters(eventId, publicKey, authorPubkey))
const { createSubscription } = require('@/types/nostr-tools-extended')
const sub = createSubscription(pool, [relayUrl], createPrivateMessageFilters(eventId, publicKey, authorPubkey))
const finalize = (result: string | null) => {
if (resolved) {
@ -116,7 +116,8 @@ export async function getDecryptionKey(
return new Promise((resolve) => {
let resolved = false
const relayUrl = getPrimaryRelaySync()
const sub = (pool as SimplePoolWithSub).sub([relayUrl], createPrivateMessageFilters(eventId, recipientPublicKey, authorPubkey))
const { createSubscription } = require('@/types/nostr-tools-extended')
const sub = createSubscription(pool, [relayUrl], createPrivateMessageFilters(eventId, recipientPublicKey, authorPubkey))
const finalize = (result: DecryptionKey | null) => {
if (resolved) {

View File

@ -1,7 +1,7 @@
import type { Event, Filter } from 'nostr-tools'
import { SimplePool } from 'nostr-tools'
import { getPrimaryRelaySync } from './config'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import { createSubscription } from '@/types/nostr-tools-extended'
/**
* Subscribe to events with timeout
@ -15,7 +15,7 @@ export function subscribeWithTimeout<T>(
return new Promise((resolve) => {
const resolved = { value: false }
const relayUrl = getPrimaryRelaySync()
const sub = (pool as SimplePoolWithSub).sub([relayUrl], filters)
const sub = createSubscription(pool, [relayUrl], filters)
let timeoutId: NodeJS.Timeout | null = null
const cleanup = () => {

View File

@ -1,6 +1,6 @@
import type { Event } from 'nostr-tools'
import { SimplePool } from 'nostr-tools'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import { createSubscription } from '@/types/nostr-tools-extended'
import { getPrimaryRelaySync } from './config'
function createZapFilters(targetPubkey: string, targetEventId: string, userPubkey: string) {
@ -63,7 +63,7 @@ export function checkZapReceipt(
return new Promise((resolve) => {
let resolved = false
const relayUrl = getPrimaryRelaySync()
const sub = (pool as SimplePoolWithSub).sub([relayUrl], createZapFilters(targetPubkey, targetEventId, userPubkey))
const sub = createSubscription(pool, [relayUrl], createZapFilters(targetPubkey, targetEventId, userPubkey))
const finalize = (value: boolean) => {
if (resolved) {

View File

@ -2,7 +2,7 @@ import type { Event } from 'nostr-tools'
import { nostrService } from './nostr'
import { zapVerificationService } from './zapVerification'
import type { Notification } from '@/types/notifications'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import { createSubscription } from '@/types/nostr-tools-extended'
import { getPrimaryRelaySync } from './config'
function createZapReceiptFilters(userPubkey: string) {
@ -48,7 +48,7 @@ async function buildPaymentNotification(event: Event, userPubkey: string): Promi
}
function registerZapSubscription(
sub: ReturnType<SimplePoolWithSub['sub']>,
sub: import('@/types/nostr-tools-extended').Subscription,
userPubkey: string,
onNotification: (notification: Notification) => void
) {
@ -84,9 +84,8 @@ export class NotificationService {
}
const filters = createZapReceiptFilters(userPubkey)
const poolWithSub = pool as SimplePoolWithSub
const relayUrl = getPrimaryRelaySync()
const sub = poolWithSub.sub([relayUrl], filters)
const sub = createSubscription(pool, [relayUrl], filters)
registerZapSubscription(sub, userPubkey, onNotification)

View File

@ -7,7 +7,7 @@ export function parseZapAmount(event: import('nostr-tools').Event): number {
return amountTag ? Math.floor(parseInt(amountTag, 10) / 1000) : 0
}
export function createZapReceiptSubscription(poolWithSub: import('@/types/nostr-tools-extended').SimplePoolWithSub, articlePubkey: string, articleId: string) {
export function createZapReceiptSubscription(pool: import('nostr-tools').SimplePool, articlePubkey: string, articleId: string) {
const filters = [
{
kinds: [9735],
@ -17,7 +17,8 @@ export function createZapReceiptSubscription(poolWithSub: import('@/types/nostr-
},
]
const relayUrl = getPrimaryRelaySync()
return poolWithSub.sub([relayUrl], filters)
const { createSubscription } = require('@/types/nostr-tools-extended')
return createSubscription(pool, [relayUrl], filters)
}
export function handleZapReceiptEvent(
@ -33,7 +34,7 @@ export function handleZapReceiptEvent(
}
export function createZapReceiptPromise(
sub: import('@/types/nostr-tools-extended').SimplePoolWithSub['sub'] extends (...args: any[]) => infer R ? R : never,
sub: import('@/types/nostr-tools-extended').Subscription,
amount: number,
recipientPubkey: string
): Promise<string | undefined> {
@ -70,8 +71,7 @@ export async function getZapReceiptId(
return undefined
}
const poolWithSub = pool as import('@/types/nostr-tools-extended').SimplePoolWithSub
const sub = createZapReceiptSubscription(poolWithSub, articlePubkey, articleId)
const sub = createZapReceiptSubscription(pool, articlePubkey, articleId)
return createZapReceiptPromise(sub, amount, recipientPubkey)
} catch (error) {
console.error('Error getting zap receipt ID', {

View File

@ -21,10 +21,9 @@ export class PlatformTrackingService {
if (!pool) {
throw new Error('Pool not initialized')
}
const poolWithSub = pool as SimplePoolWithSub
const { getPrimaryRelaySync } = await import('./config')
const relayUrl = getPrimaryRelaySync()
const pubs = poolWithSub.publish([relayUrl], event)
const pubs = pool.publish([relayUrl], event)
await Promise.all(pubs)
}
@ -99,8 +98,7 @@ export class PlatformTrackingService {
return new Promise((resolve) => {
const deliveries: ContentDeliveryTracking[] = []
let resolved = false
const poolWithSub = pool as SimplePoolWithSub
const sub = createArticleDeliveriesSubscription(poolWithSub, articleId, this.platformPubkey)
const sub = createArticleDeliveriesSubscription(pool, articleId, this.platformPubkey)
const finalize = () => {
if (resolved) {
@ -143,8 +141,7 @@ export class PlatformTrackingService {
return new Promise((resolve) => {
const deliveries: ContentDeliveryTracking[] = []
let resolved = false
const poolWithSub = pool as SimplePoolWithSub
const sub = createRecipientDeliveriesSubscription(poolWithSub, recipientPubkey, this.platformPubkey)
const sub = createRecipientDeliveriesSubscription(pool, recipientPubkey, this.platformPubkey)
const finalize = () => {
if (resolved) {

View File

@ -1,5 +1,6 @@
import { Event } from 'nostr-tools'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import { SimplePool } from 'nostr-tools'
import { createSubscription } from '@/types/nostr-tools-extended'
import { getPrimaryRelaySync } from './config'
import type { ContentDeliveryTracking } from './platformTrackingTypes'
import { getTrackingKind } from './platformTrackingEvents'
@ -37,7 +38,7 @@ export function parseTrackingEvent(event: Event): ContentDeliveryTracking | null
}
}
export function createArticleDeliveriesSubscription(pool: SimplePoolWithSub, articleId: string, platformPubkey: string) {
export function createArticleDeliveriesSubscription(pool: SimplePool, articleId: string, platformPubkey: string) {
const filters = [
{
kinds: [getTrackingKind()],
@ -47,10 +48,10 @@ export function createArticleDeliveriesSubscription(pool: SimplePoolWithSub, art
},
]
const relayUrl = getPrimaryRelaySync()
return pool.sub([relayUrl], filters)
return createSubscription(pool, [relayUrl], filters)
}
export function createRecipientDeliveriesSubscription(pool: SimplePoolWithSub, recipientPubkey: string, platformPubkey: string) {
export function createRecipientDeliveriesSubscription(pool: SimplePool, recipientPubkey: string, platformPubkey: string) {
const filters = [
{
kinds: [getTrackingKind()],
@ -60,5 +61,5 @@ export function createRecipientDeliveriesSubscription(pool: SimplePoolWithSub, r
},
]
const relayUrl = getPrimaryRelaySync()
return pool.sub([relayUrl], filters)
return createSubscription(pool, [relayUrl], filters)
}

View File

@ -8,8 +8,8 @@ export async function fetchOriginalReviewEvent(reviewId: string): Promise<Event
throw new Error('Pool not initialized')
}
const poolWithSub = pool as import('@/types/nostr-tools-extended').SimplePoolWithSub
const { getPrimaryRelaySync } = await import('./config')
const { createSubscription } = await import('@/types/nostr-tools-extended')
const relayUrl = getPrimaryRelaySync()
const filters = [
{
@ -21,7 +21,7 @@ export async function fetchOriginalReviewEvent(reviewId: string): Promise<Event
return new Promise<Event | null>((resolve) => {
let resolved = false
const sub = poolWithSub.sub([relayUrl], filters)
const sub = createSubscription(pool, [relayUrl], filters)
const finalize = (value: Event | null) => {
if (resolved) {

View File

@ -1,6 +1,5 @@
import type { Event } from 'nostr-tools'
import { nostrService } from './nostr'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import type { Review } from '@/types/nostr'
import { parseReviewFromEvent } from './nostrEventParsing'
import { buildTagFilter } from './nostrTagSystem'
@ -33,13 +32,13 @@ export function getReviewsForArticle(articleId: string, timeoutMs: number = 5000
if (!pool) {
throw new Error('Pool not initialized')
}
const poolWithSub = pool as SimplePoolWithSub
const filters = buildReviewFilters(articleId)
const { createSubscription } = require('@/types/nostr-tools-extended')
return new Promise<Review[]>((resolve) => {
const results: Review[] = []
const relayUrl = getPrimaryRelaySync()
const sub = poolWithSub.sub([relayUrl], filters)
const sub = createSubscription(pool, [relayUrl], filters)
let finished = false
const done = () => {

View File

@ -1,6 +1,5 @@
import type { Event } from 'nostr-tools'
import { nostrService } from './nostr'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import type { Series } from '@/types/nostr'
import { parseSeriesFromEvent } from './nostrEventParsing'
import { buildTagFilter } from './nostrTagSystem'
@ -26,13 +25,13 @@ export function getSeriesByAuthor(authorPubkey: string, timeoutMs: number = 5000
if (!pool) {
throw new Error('Pool not initialized')
}
const poolWithSub = pool as SimplePoolWithSub
const filters = buildSeriesFilters(authorPubkey)
const { createSubscription } = require('@/types/nostr-tools-extended')
return new Promise<Series[]>((resolve) => {
const results: Series[] = []
const relayUrl = getPrimaryRelaySync()
const sub = poolWithSub.sub([relayUrl], filters)
const sub = createSubscription(pool, [relayUrl], filters)
let finished = false
const done = () => {
@ -73,12 +72,12 @@ export function getSeriesById(seriesId: string, timeoutMs: number = 5000): Promi
if (!pool) {
throw new Error('Pool not initialized')
}
const poolWithSub = pool as SimplePoolWithSub
const filters = buildSeriesByIdFilters(seriesId)
const { createSubscription } = require('@/types/nostr-tools-extended')
return new Promise<Series | null>((resolve) => {
const relayUrl = getPrimaryRelaySync()
const sub = poolWithSub.sub([relayUrl], filters)
const sub = createSubscription(pool, [relayUrl], filters)
let finished = false
const done = (value: Series | null) => {

View File

@ -1,10 +1,9 @@
import { nostrService } from './nostr'
import { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import type { Article } from '@/types/nostr'
import { getPrimaryRelaySync } from './config'
import { buildTagFilter, extractTagsFromEvent } from './nostrTagSystem'
function subscribeToPresentation(pool: SimplePoolWithSub, pubkey: string): Promise<number> {
function subscribeToPresentation(pool: import('nostr-tools').SimplePool, pubkey: string): Promise<number> {
const filters = [
{
...buildTagFilter({
@ -18,7 +17,8 @@ function subscribeToPresentation(pool: SimplePoolWithSub, pubkey: string): Promi
return new Promise((resolve) => {
let resolved = false
const relayUrl = getPrimaryRelaySync()
const sub = pool.sub([relayUrl], filters)
const { createSubscription } = require('@/types/nostr-tools-extended')
const sub = createSubscription(pool, [relayUrl], filters)
const finalize = (value: number) => {
if (resolved) {
@ -53,7 +53,7 @@ export function getAuthorSponsoring(pubkey: string): Promise<number> {
return Promise.resolve(0)
}
return subscribeToPresentation(pool as SimplePoolWithSub, pubkey)
return subscribeToPresentation(pool, pubkey)
}
/**

View File

@ -1,6 +1,6 @@
import type { Event } from 'nostr-tools'
import { nostrService } from './nostr'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import type { Subscription } from '@/types/nostr-tools-extended'
import { getPrimaryRelaySync } from './config'
interface ZapAggregationFilter {
@ -42,17 +42,17 @@ export function aggregateZapSats(params: ZapAggregationFilter): Promise<number>
if (!pool) {
throw new Error('Nostr pool not initialized')
}
const poolWithSub = pool as SimplePoolWithSub
const filters = buildFilters(params)
const relay = getPrimaryRelaySync()
const timeout = params.timeoutMs ?? 5000
const { createSubscription } = require('@/types/nostr-tools-extended')
const sub = poolWithSub.sub([relay], filters)
const sub = createSubscription(pool, [relay], filters)
return collectZap(sub, timeout)
}
function collectZap(
sub: ReturnType<SimplePoolWithSub['sub']>,
sub: Subscription,
timeout: number
): Promise<number> {
return new Promise<number>((resolve, reject) => {

View File

@ -12,13 +12,58 @@ export interface Subscription {
}
/**
* Alias for SimplePool with typed sub method from nostr-tools definitions.
* In nostr-tools 2.x, SimplePool has a sub method but it's not properly typed.
* Type for SimplePool with subscribe method (for backward compatibility)
* Note: SimplePool already has subscribe method, this is just for type compatibility
*/
export interface SimplePoolWithSub extends SimplePool {
sub(relays: string[], filters: Filter[]): Subscription
}
export type SimplePoolWithSub = SimplePool
export function hasSubMethod(pool: SimplePool): pool is SimplePoolWithSub {
return typeof (pool as any).sub === 'function'
/**
* Helper to create a subscription compatible with the old API
*/
export function createSubscription(
pool: SimplePool,
relays: string[],
filters: Filter[]
): Subscription {
const events: Event[] = []
let eoseReceived = false
const eventCallbacks: Array<(event: Event) => void> = []
const eoseCallbacks: Array<() => void> = []
const subscription = pool.subscribe(
relays,
filters[0] || {},
{
onevent: (event: Event) => {
events.push(event)
eventCallbacks.forEach((cb) => cb(event))
},
oneose: () => {
eoseReceived = true
eoseCallbacks.forEach((cb) => cb())
},
}
)
const subscriptionWrapper: Subscription = {
on(event: 'event' | 'eose', callback: ((event: Event) => void) | (() => void)): void {
if (event === 'event') {
const eventCb = callback as (event: Event) => void
eventCallbacks.push(eventCb)
// Emit any events that were already received
events.forEach((e) => eventCb(e))
} else if (event === 'eose') {
const eoseCb = callback as () => void
if (eoseReceived) {
eoseCb()
} else {
eoseCallbacks.push(eoseCb)
}
}
},
unsub(): void {
subscription.close()
},
}
return subscriptionWrapper
}