mirror of
https://github.com/nbd-wtf/nostr-tools.git
synced 2025-12-08 16:28:49 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a55fb8465f | ||
|
|
472a01af6a | ||
|
|
bb5acfc197 | ||
|
|
5b15237b95 | ||
|
|
4184609a00 | ||
|
|
97287cad74 | ||
|
|
fa21f71ab5 |
@@ -1,4 +1,4 @@
|
||||
import {matchFilter, matchFilters} from './filter.ts'
|
||||
import {matchFilter, matchFilters, mergeFilters} from './filter.ts'
|
||||
import {buildEvent} from './test-helpers.ts'
|
||||
|
||||
describe('Filter', () => {
|
||||
@@ -18,7 +18,7 @@ describe('Filter', () => {
|
||||
kind: 1,
|
||||
pubkey: 'abc',
|
||||
created_at: 150,
|
||||
tags: [['tag', 'value']],
|
||||
tags: [['tag', 'value']]
|
||||
})
|
||||
|
||||
const result = matchFilter(filter, event)
|
||||
@@ -162,7 +162,12 @@ describe('Filter', () => {
|
||||
{authors: ['abc'], limit: 3}
|
||||
]
|
||||
|
||||
const event = buildEvent({id: '123', kind: 1, pubkey: 'abc', created_at: 150})
|
||||
const event = buildEvent({
|
||||
id: '123',
|
||||
kind: 1,
|
||||
pubkey: 'abc',
|
||||
created_at: 150
|
||||
})
|
||||
|
||||
const result = matchFilters(filters, event)
|
||||
|
||||
@@ -189,11 +194,35 @@ describe('Filter', () => {
|
||||
{kinds: [1], limit: 2},
|
||||
{authors: ['abc'], limit: 3}
|
||||
]
|
||||
const event = buildEvent({id: '456', kind: 2, pubkey: 'def', created_at: 200})
|
||||
const event = buildEvent({
|
||||
id: '456',
|
||||
kind: 2,
|
||||
pubkey: 'def',
|
||||
created_at: 200
|
||||
})
|
||||
|
||||
const result = matchFilters(filters, event)
|
||||
|
||||
expect(result).toEqual(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('mergeFilters', () => {
|
||||
it('should merge filters', () => {
|
||||
expect(
|
||||
mergeFilters(
|
||||
{ids: ['a', 'b'], limit: 3},
|
||||
{authors: ['x'], ids: ['b', 'c']}
|
||||
)
|
||||
).toEqual({ids: ['a', 'b', 'c'], limit: 3, authors: ['x']})
|
||||
|
||||
expect(
|
||||
mergeFilters(
|
||||
{kinds: [1], since: 15, until: 30},
|
||||
{since: 10, kinds: [7], until: 15},
|
||||
{kinds: [9, 10]}
|
||||
)
|
||||
).toEqual({kinds: [1, 7, 9, 10], since: 10, until: 30})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
34
filter.ts
34
filter.ts
@@ -56,3 +56,37 @@ export function matchFilters(
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function mergeFilters(...filters: Filter<number>[]): Filter<number> {
|
||||
let result: Filter<number> = {}
|
||||
for (let i = 0; i < filters.length; i++) {
|
||||
let filter = filters[i]
|
||||
Object.entries(filter).forEach(([property, values]) => {
|
||||
if (
|
||||
property === 'kinds' ||
|
||||
property === 'ids' ||
|
||||
property === 'authors' ||
|
||||
property[0] === '#'
|
||||
) {
|
||||
// @ts-ignore
|
||||
result[property] = result[property] || []
|
||||
// @ts-ignore
|
||||
for (let v = 0; v < values.length; v++) {
|
||||
// @ts-ignore
|
||||
let value = values[v]
|
||||
// @ts-ignore
|
||||
if (!result[property].includes(value)) result[property].push(value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (filter.limit && (!result.limit || filter.limit > result.limit))
|
||||
result.limit = filter.limit
|
||||
if (filter.until && (!result.until || filter.until > result.until))
|
||||
result.until = filter.until
|
||||
if (filter.since && (!result.since || filter.since < result.since))
|
||||
result.since = filter.since
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
7
nip19.ts
7
nip19.ts
@@ -69,9 +69,7 @@ export function decode(nip19: string): DecodeResult {
|
||||
data: {
|
||||
id: bytesToHex(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],
|
||||
author: tlv[2]?.[0]
|
||||
? bytesToHex(tlv[2][0])
|
||||
: undefined
|
||||
author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,9 +121,10 @@ function parseTLV(data: Uint8Array): TLV {
|
||||
while (rest.length > 0) {
|
||||
let t = rest[0]
|
||||
let l = rest[1]
|
||||
if (!l) throw new Error(`malformed TLV ${t}`)
|
||||
let v = rest.slice(2, 2 + l)
|
||||
rest = rest.slice(2 + l)
|
||||
if (v.length < l) continue
|
||||
if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)
|
||||
result[t] = result[t] || []
|
||||
result[t].push(v)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nostr-tools",
|
||||
"version": "1.11.1",
|
||||
"version": "1.12.0",
|
||||
"description": "Tools for making a Nostr client.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
9
relay.ts
9
relay.ts
@@ -3,6 +3,7 @@
|
||||
import {verifySignature, validateEvent, type Event} from './event.ts'
|
||||
import {matchFilters, type Filter} from './filter.ts'
|
||||
import {getHex64, getSubscriptionId} from './fakejson.ts'
|
||||
import { MessageQueue } from './utils.ts'
|
||||
|
||||
type RelayEvent = {
|
||||
connect: () => void | Promise<void>
|
||||
@@ -122,24 +123,24 @@ export function relayInit(
|
||||
listeners.disconnect.forEach(cb => cb())
|
||||
}
|
||||
|
||||
let incomingMessageQueue: string[] = []
|
||||
let incomingMessageQueue: MessageQueue = new MessageQueue()
|
||||
let handleNextInterval: any
|
||||
|
||||
ws.onmessage = e => {
|
||||
incomingMessageQueue.push(e.data)
|
||||
incomingMessageQueue.enqueue(e.data)
|
||||
if (!handleNextInterval) {
|
||||
handleNextInterval = setInterval(handleNext, 0)
|
||||
}
|
||||
}
|
||||
|
||||
function handleNext() {
|
||||
if (incomingMessageQueue.length === 0) {
|
||||
if (incomingMessageQueue.size === 0) {
|
||||
clearInterval(handleNextInterval)
|
||||
handleNextInterval = null
|
||||
return
|
||||
}
|
||||
|
||||
var json = incomingMessageQueue.shift()
|
||||
var json = incomingMessageQueue.dequeue()
|
||||
if (!json) return
|
||||
|
||||
let subid = getSubscriptionId(json)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {buildEvent} from './test-helpers.ts'
|
||||
import {
|
||||
MessageQueue,
|
||||
insertEventIntoAscendingList,
|
||||
insertEventIntoDescendingList,
|
||||
} from './utils.ts'
|
||||
@@ -191,3 +192,48 @@ describe('inserting into a asc sorted list of events', () => {
|
||||
expect(list1).toHaveLength(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('enque a message into MessageQueue', () => {
|
||||
test('enque into an empty queue', () => {
|
||||
const queue = new MessageQueue()
|
||||
queue.enqueue('node1')
|
||||
expect(queue.first!.value).toBe('node1')
|
||||
})
|
||||
test('enque into a non-empty queue', () => {
|
||||
const queue = new MessageQueue()
|
||||
queue.enqueue('node1')
|
||||
queue.enqueue('node3')
|
||||
queue.enqueue('node2')
|
||||
expect(queue.first!.value).toBe('node1')
|
||||
expect(queue.last!.value).toBe('node2')
|
||||
expect(queue.size).toBe(3)
|
||||
})
|
||||
test('dequeue from an empty queue', () => {
|
||||
const queue = new MessageQueue()
|
||||
const item1 = queue.dequeue()
|
||||
expect(item1).toBe(null)
|
||||
expect(queue.size).toBe(0)
|
||||
})
|
||||
test('dequeue from a non-empty queue', () => {
|
||||
const queue = new MessageQueue()
|
||||
queue.enqueue('node1')
|
||||
queue.enqueue('node3')
|
||||
queue.enqueue('node2')
|
||||
const item1 = queue.dequeue()
|
||||
expect(item1).toBe('node1')
|
||||
const item2 = queue.dequeue()
|
||||
expect(item2).toBe('node3')
|
||||
})
|
||||
test('dequeue more than in queue', () => {
|
||||
const queue = new MessageQueue()
|
||||
queue.enqueue('node1')
|
||||
queue.enqueue('node3')
|
||||
const item1 = queue.dequeue()
|
||||
expect(item1).toBe('node1')
|
||||
const item2 = queue.dequeue()
|
||||
expect(item2).toBe('node3')
|
||||
expect(queue.size).toBe(0)
|
||||
const item3 = queue.dequeue()
|
||||
expect(item3).toBe(null)
|
||||
})
|
||||
})
|
||||
|
||||
76
utils.ts
76
utils.ts
@@ -109,3 +109,79 @@ export function insertEventIntoAscendingList(
|
||||
|
||||
return sortedArray
|
||||
}
|
||||
|
||||
export class MessageNode {
|
||||
private _value: string
|
||||
private _next: MessageNode | null
|
||||
|
||||
public get value(): string {
|
||||
return this._value
|
||||
}
|
||||
public set value(message: string) {
|
||||
this._value = message
|
||||
}
|
||||
public get next(): MessageNode | null {
|
||||
return this._next
|
||||
}
|
||||
public set next(node: MessageNode | null) {
|
||||
this._next = node
|
||||
}
|
||||
|
||||
constructor(message: string) {
|
||||
this._value = message
|
||||
this._next = null
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageQueue {
|
||||
private _first: MessageNode | null
|
||||
private _last: MessageNode | null
|
||||
|
||||
public get first(): MessageNode | null {
|
||||
return this._first
|
||||
}
|
||||
public set first(messageNode: MessageNode | null) {
|
||||
this._first = messageNode
|
||||
}
|
||||
public get last(): MessageNode | null {
|
||||
return this._last
|
||||
}
|
||||
public set last(messageNode: MessageNode | null) {
|
||||
this._last = messageNode
|
||||
}
|
||||
private _size: number
|
||||
public get size(): number {
|
||||
return this._size
|
||||
}
|
||||
public set size(v: number) {
|
||||
this._size = v
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this._first = null
|
||||
this._last = null
|
||||
this._size = 0
|
||||
}
|
||||
enqueue(message: string): boolean {
|
||||
const newNode = new MessageNode(message)
|
||||
if (this._size === 0 || !this._last) {
|
||||
this._first = newNode
|
||||
this._last = newNode
|
||||
} else {
|
||||
this._last.next = newNode
|
||||
this._last = newNode
|
||||
}
|
||||
this._size++
|
||||
return true
|
||||
}
|
||||
dequeue(): string | null {
|
||||
if (this._size === 0 || !this._first) return null
|
||||
|
||||
let prev = this._first
|
||||
this._first = prev.next
|
||||
prev.next = null
|
||||
|
||||
this._size--
|
||||
return prev.value
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user