mirror of
https://github.com/nbd-wtf/nostr-tools.git
synced 2025-12-09 08:38:50 +00:00
refactor previous commit a little, add fakejson module for simple parsing that doesn't use regex.
This commit is contained in:
35
fakejson.test.js
Normal file
35
fakejson.test.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/* eslint-env jest */
|
||||||
|
|
||||||
|
const {fj} = require('./lib/nostr.cjs')
|
||||||
|
|
||||||
|
test('match id', () => {
|
||||||
|
expect(
|
||||||
|
fj.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(
|
||||||
|
`["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'
|
||||||
|
)
|
||||||
|
).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('match kind', () => {
|
||||||
|
expect(
|
||||||
|
fj.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(
|
||||||
|
`["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
|
||||||
|
)
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
26
fakejson.ts
Normal file
26
fakejson.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
export function getHex64(json: string, field: string): string {
|
||||||
|
let len = field.length + 3
|
||||||
|
let idx = json.indexOf(`"${field}":`) + len
|
||||||
|
let s = json.slice(idx).indexOf(`"`) + idx + 1
|
||||||
|
return json.slice(s, s + 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInt(json: string, field: string): number {
|
||||||
|
let len = field.length
|
||||||
|
let idx = json.indexOf(`"${field}":`) + len + 3
|
||||||
|
let sliced = json.slice(idx)
|
||||||
|
let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))
|
||||||
|
return parseInt(sliced.slice(0, end), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function matchEventId(json: string, id: string): boolean {
|
||||||
|
return id === getHex64(json, 'id')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function matchEventPubkey(json: string, pubkey: string): boolean {
|
||||||
|
return pubkey === getHex64(json, 'pubkey')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function matchEventKind(json: string, kind: number): boolean {
|
||||||
|
return kind === getInt(json, 'kind')
|
||||||
|
}
|
||||||
2
index.ts
2
index.ts
@@ -3,6 +3,8 @@ export * from './relay'
|
|||||||
export * from './event'
|
export * from './event'
|
||||||
export * from './filter'
|
export * from './filter'
|
||||||
|
|
||||||
|
export * as fj from './fakejson'
|
||||||
|
|
||||||
export * as nip04 from './nip04'
|
export * as nip04 from './nip04'
|
||||||
export * as nip05 from './nip05'
|
export * as nip05 from './nip05'
|
||||||
export * as nip06 from './nip06'
|
export * as nip06 from './nip06'
|
||||||
|
|||||||
70
relay.ts
70
relay.ts
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import {Event, verifySignature, validateEvent} from './event'
|
import {Event, verifySignature, validateEvent} from './event'
|
||||||
import {Filter, matchFilters} from './filter'
|
import {Filter, matchFilters} from './filter'
|
||||||
|
import {getHex64} from './fakejson'
|
||||||
|
|
||||||
type RelayEvent = 'connect' | 'disconnect' | 'error' | 'notice'
|
type RelayEvent = 'connect' | 'disconnect' | 'error' | 'notice'
|
||||||
|
|
||||||
@@ -31,11 +32,16 @@ type SubscriptionOptions = {
|
|||||||
id?: string
|
id?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function relayInit(url: string, alreadyHaveEvent?: (id: string) => boolean): Relay {
|
export function relayInit(
|
||||||
|
url: string,
|
||||||
|
alreadyHaveEvent: (id: string) => boolean = () => false
|
||||||
|
): Relay {
|
||||||
var ws: WebSocket
|
var ws: WebSocket
|
||||||
var resolveClose: () => void
|
var resolveClose: () => void
|
||||||
var setOpen: (value: (PromiseLike<void> | void)) => void
|
var setOpen: (value: PromiseLike<void> | void) => void
|
||||||
var untilOpen = new Promise<void>((resolve) => { setOpen = resolve })
|
var untilOpen = new Promise<void>(resolve => {
|
||||||
|
setOpen = resolve
|
||||||
|
})
|
||||||
var openSubs: {[id: string]: {filters: Filter[]} & SubscriptionOptions} = {}
|
var openSubs: {[id: string]: {filters: Filter[]} & SubscriptionOptions} = {}
|
||||||
var listeners: {
|
var listeners: {
|
||||||
connect: Array<() => void>
|
connect: Array<() => void>
|
||||||
@@ -61,7 +67,6 @@ export function relayInit(url: string, alreadyHaveEvent?: (id: string) => boolea
|
|||||||
failed: Array<(reason: string) => void>
|
failed: Array<(reason: string) => void>
|
||||||
}
|
}
|
||||||
} = {}
|
} = {}
|
||||||
let idRegex = /"id":"([a-fA-F0-9]+)"/;
|
|
||||||
|
|
||||||
async function connectRelay(): Promise<void> {
|
async function connectRelay(): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -81,35 +86,36 @@ export function relayInit(url: string, alreadyHaveEvent?: (id: string) => boolea
|
|||||||
resolveClose && resolveClose()
|
resolveClose && resolveClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
let incomingMessageQueue: any[] = []
|
let incomingMessageQueue: string[] = []
|
||||||
let handleNextInterval: any
|
let handleNextInterval: any
|
||||||
|
|
||||||
const handleNext = () => {
|
ws.onmessage = e => {
|
||||||
|
incomingMessageQueue.push(e.data)
|
||||||
|
if (!handleNextInterval) {
|
||||||
|
handleNextInterval = setInterval(handleNext, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNext() {
|
||||||
if (incomingMessageQueue.length === 0) {
|
if (incomingMessageQueue.length === 0) {
|
||||||
clearInterval(handleNextInterval)
|
clearInterval(handleNextInterval)
|
||||||
handleNextInterval = null
|
handleNextInterval = null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = incomingMessageQueue.shift()
|
var json = incomingMessageQueue.shift()
|
||||||
if (data && !!alreadyHaveEvent) {
|
if (!json || alreadyHaveEvent(getHex64(json, 'id'))) {
|
||||||
const match = idRegex.exec(data)
|
return
|
||||||
if (match) {
|
|
||||||
const id = match[1];
|
|
||||||
if (alreadyHaveEvent(id)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
data = JSON.parse(data)
|
|
||||||
} catch (err) {}
|
|
||||||
|
|
||||||
if (data.length >= 1) {
|
try {
|
||||||
|
let data = JSON.parse(json)
|
||||||
|
|
||||||
|
// we won't do any checks against the data since all failures (i.e. invalid messages from relays)
|
||||||
|
// will naturally be caught by the encompassing try..catch block
|
||||||
|
|
||||||
switch (data[0]) {
|
switch (data[0]) {
|
||||||
case 'EVENT':
|
case 'EVENT':
|
||||||
if (data.length !== 3) return // ignore empty or malformed EVENT
|
|
||||||
|
|
||||||
let id = data[1]
|
let id = data[1]
|
||||||
let event = data[2]
|
let event = data[2]
|
||||||
if (
|
if (
|
||||||
@@ -123,13 +129,11 @@ export function relayInit(url: string, alreadyHaveEvent?: (id: string) => boolea
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
case 'EOSE': {
|
case 'EOSE': {
|
||||||
if (data.length !== 2) return // ignore empty or malformed EOSE
|
|
||||||
let id = data[1]
|
let id = data[1]
|
||||||
;(subListeners[id]?.eose || []).forEach(cb => cb())
|
;(subListeners[id]?.eose || []).forEach(cb => cb())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case 'OK': {
|
case 'OK': {
|
||||||
if (data.length < 3) return // ignore empty or malformed OK
|
|
||||||
let id: string = data[1]
|
let id: string = data[1]
|
||||||
let ok: boolean = data[2]
|
let ok: boolean = data[2]
|
||||||
let reason: string = data[3] || ''
|
let reason: string = data[3] || ''
|
||||||
@@ -138,18 +142,12 @@ export function relayInit(url: string, alreadyHaveEvent?: (id: string) => boolea
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
case 'NOTICE':
|
case 'NOTICE':
|
||||||
if (data.length !== 2) return // ignore empty or malformed NOTICE
|
|
||||||
let notice = data[1]
|
let notice = data[1]
|
||||||
listeners.notice.forEach(cb => cb(notice))
|
listeners.notice.forEach(cb => cb(notice))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
} catch (err) {
|
||||||
}
|
return
|
||||||
|
|
||||||
ws.onmessage = e => {
|
|
||||||
incomingMessageQueue.push(e.data)
|
|
||||||
if (!handleNextInterval) {
|
|
||||||
handleNextInterval = setInterval(handleNext, 0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -216,19 +214,13 @@ export function relayInit(url: string, alreadyHaveEvent?: (id: string) => boolea
|
|||||||
return {
|
return {
|
||||||
url,
|
url,
|
||||||
sub,
|
sub,
|
||||||
on: (
|
on: (type: RelayEvent, cb: any): void => {
|
||||||
type: RelayEvent,
|
|
||||||
cb: any
|
|
||||||
): void => {
|
|
||||||
listeners[type].push(cb)
|
listeners[type].push(cb)
|
||||||
if (type === 'connect' && ws?.readyState === 1) {
|
if (type === 'connect' && ws?.readyState === 1) {
|
||||||
cb()
|
cb()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
off: (
|
off: (type: RelayEvent, cb: any): void => {
|
||||||
type: RelayEvent,
|
|
||||||
cb: any
|
|
||||||
): void => {
|
|
||||||
let index = listeners[type].indexOf(cb)
|
let index = listeners[type].indexOf(cb)
|
||||||
if (index !== -1) listeners[type].splice(index, 1)
|
if (index !== -1) listeners[type].splice(index, 1)
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user