diff --git a/packages/net/src/ConnectionMeta.ts b/packages/net/src/ConnectionMeta.ts index 6c3a0e3..f6e001d 100644 --- a/packages/net/src/ConnectionMeta.ts +++ b/packages/net/src/ConnectionMeta.ts @@ -100,7 +100,7 @@ export class ConnectionMeta { if (ok) { this.authStatus = AuthStatus.Ok } else if (notice?.startsWith('auth-required:')) { - // Re-enqueue pending reqs when auth challenge is received + // Re-enqueue pending events when auth challenge is received const pub = this.pendingPublishes.get(eventId) if (pub) { diff --git a/packages/signer/src/signers/nip46.ts b/packages/signer/src/signers/nip46.ts index 88ed2b2..953cb06 100644 --- a/packages/signer/src/signers/nip46.ts +++ b/packages/signer/src/signers/nip46.ts @@ -3,7 +3,7 @@ import {hexToBytes} from '@noble/hashes/utils' import {Emitter, tryCatch, randomId, sleep, equals, now} from "@welshman/lib" import {createEvent, TrustedEvent, EventTemplate, NOSTR_CONNECT} from "@welshman/util" import {subscribe, publish, Subscription} from "@welshman/net" -import {ISigner, decrypt} from '../util' +import {ISigner, decrypt, hash, own} from '../util' import {Nip01Signer} from './nip01' export type Nip46Handler = { @@ -168,7 +168,8 @@ export class Nip46Signer implements ISigner { getPubkey = async () => this.broker.pubkey - sign = (event: EventTemplate) => this.broker.signEvent(event) + sign = (template: EventTemplate) => + this.broker.signEvent(hash(own(this.broker.pubkey, template))) nip04 = { encrypt: this.broker.nip04Encrypt, diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts index ecc1fff..bfdff35 100644 --- a/packages/store/src/index.ts +++ b/packages/store/src/index.ts @@ -110,18 +110,46 @@ export const deriveEventsMapped = ({ repository, eventToItem, itemToEvent, + throttle = 300, includeDeleted = false, }: { filters: Filter[] repository: Repository, eventToItem: (event: CustomEvent) => T | Promise itemToEvent: (item: T) => CustomEvent + throttle?: number includeDeleted?: boolean }) => custom(setter => { - let data = repository.query(filters, {includeDeleted}).map(eventToItem).filter(identity) as T[] + let data: T[] = [] const deferred = new Set() + const defer = (event: CustomEvent, item: Promise) => { + deferred.add(event.id) + + item.then($item => { + if (deferred.has(event.id)) { + deferred.delete(event.id) + data.push($item) + setter(data) + } + }) + } + + for (const event of repository.query(filters, {includeDeleted})) { + const item = eventToItem(event) + + if (!item) { + continue + } + + if (item instanceof Promise) { + defer(event, item) + } else { + data.push(item) + } + } + setter(data) const onUpdate = batch(300, (updates: {added: CustomEvent[]; removed: Set}[]) => { @@ -147,18 +175,7 @@ export const deriveEventsMapped = ({ const item = eventToItem(event) if (item instanceof Promise) { - // If it's a promise, resolve it before adding it to data - deferred.add(event.id) - - // Wait for the promise to resolve. If it hasn't since been removed, - // we're clear to add it - item.then($item => { - if (deferred.has(event.id)) { - deferred.delete(event.id) - data.push($item) - setter(data) - } - }) + defer(event, item) } else if (item) { dirty = true data.push(item as T) @@ -186,9 +203,7 @@ export const deriveEventsMapped = ({ repository.on("update", onUpdate) return () => repository.off("update", onUpdate) - }, { - throttle: 300, - }) + }, {throttle}) export const deriveEvents = (opts: {repository: Repository, filters: Filter[], includeDeleted?: boolean}) => deriveEventsMapped({ diff --git a/packages/util/src/Encryptable.ts b/packages/util/src/Encryptable.ts index cf6c21f..be0bed2 100644 --- a/packages/util/src/Encryptable.ts +++ b/packages/util/src/Encryptable.ts @@ -2,6 +2,18 @@ import type {EventContent, CustomEvent} from './Events' export type Encrypt = (x: string) => Promise +export type EncryptableParams = { + kind: number, + tags?: string[][] + content?: string +} + +export type EncryptableResult = { + kind: number, + tags: string[][] + content: string +} + export type DecryptedEvent = CustomEvent & { plaintext: Partial } @@ -9,10 +21,10 @@ export type DecryptedEvent = CustomEvent & { export const asDecryptedEvent = (event: CustomEvent, plaintext: Partial) => ({...event, plaintext}) as DecryptedEvent -export class Encryptable> { - constructor(readonly event: E, readonly updates: E) {} +export class Encryptable { + constructor(readonly event: EncryptableParams, readonly updates: Partial) {} - async reconcile(encrypt: Encrypt) { + async reconcile(encrypt: Encrypt): Promise { const encryptContent = () => { if (!this.updates.content) return null diff --git a/packages/util/src/Handler.ts b/packages/util/src/Handler.ts index 0b38767..b63c272 100644 --- a/packages/util/src/Handler.ts +++ b/packages/util/src/Handler.ts @@ -33,7 +33,7 @@ export const readHandlers = (event: CustomEvent) => { } return getKindTagValues(event.tags) - .map(kind => ({...normalizedMeta, kind: parseInt(kind), identifier, event})) as Handler[] + .map(kind => ({...normalizedMeta, kind, identifier, event})) as Handler[] } export const getHandlerKey = (handler: Handler) => `${handler.kind}:${getAddress(handler.event)}` diff --git a/packages/util/src/Tags.ts b/packages/util/src/Tags.ts index ed9f1aa..d8f25af 100644 --- a/packages/util/src/Tags.ts +++ b/packages/util/src/Tags.ts @@ -1,6 +1,6 @@ import {EventTemplate} from 'nostr-tools' import type {OmitStatics} from '@welshman/lib' -import {Fluent, ensurePlural} from '@welshman/lib' +import {Fluent, nth, ensurePlural} from '@welshman/lib' import {isRelayUrl, normalizeRelayUrl} from './Relay' import {Address, isContextAddress} from './Address' import {GROUP, COMMUNITY} from './Kinds' @@ -208,46 +208,35 @@ export class Tags extends (Fluent as OmitStatics, 'from' // New, simpler version -export const getTags = - (types: string[], testValue?: (v: string) => boolean) => - (tags: string[][]) => - tags.filter(t => types.includes(t[0]) && (!testValue || testValue(t[1] || ""))) +export const getEventTags = (tags: string[][]) => + tags.filter(t => ["e"].includes(t[0]) && t[1].length === 64) -export const getTagValues = (types: string[], testValue?: (v: string) => boolean) => { - const _getTags = getTags(types, testValue) +export const getEventTagValues = (tags: string[][]) => getEventTags(tags).map(nth(1)) - return (tags: string[][]) => _getTags(tags).map(t => t[1] || "") -} +export const getAddressTags = (tags: string[][]) => + tags.filter(t => ["a"].includes(t[0]) && Address.isAddress(t[1])) -export const getTagValue = (types: string[], testValue?: (v: string) => boolean) => { - const _getTagValues = getTagValues(types, testValue) - - return (tags: string[][]) => _getTagValues(tags)[0] -} - -export const getEventTags = getTags(["e"], id => id.length === 64) - -export const getEventTagValues = getTagValues(["e"], id => id.length === 64) - -export const getAddressTags = getTags(["a"], Address.isAddress) - -export const getAddressTagValues = getTagValues(["a"], Address.isAddress) +export const getAddressTagValues = (tags: string[][]) => getAddressTags(tags).map(nth(1)) export const getContextTagValues = (tags: string[][]) => getAddressTagValues(tags).filter(isContextAddress) -export const getPubkeyTags = getTags(["p"], pk => pk.length === 64) +export const getPubkeyTags = (tags: string[][]) => + tags.filter(t => ["p"].includes(t[0]) && t[1].length === 64) -export const getPubkeyTagValues = getTagValues(["p"], pk => pk.length === 64) +export const getPubkeyTagValues = (tags: string[][]) => getPubkeyTags(tags).map(nth(1)) -export const getRelayTags = getTags(["r", "relay"], isRelayUrl) +export const getRelayTags = (tags: string[][]) => + tags.filter(t => ["r", "relay"].includes(t[0]) && isRelayUrl(t[1])) -export const getRelayTagValues = getTagValues(["r", "relay"], isRelayUrl) +export const getRelayTagValues = (tags: string[][]) => getRelayTags(tags).map(nth(1)) -export const getGroupTags = getTags(["h", "group"], h => Boolean(h.match(/^(.+)'(.+)$/))) +export const getGroupTags = (tags: string[][]) => + tags.filter(t => ["h", "group"].includes(t[0]) && t[1] && isRelayUrl(t[2])) -export const getGroupTagValues = getTagValues(["h", "group"], h => Boolean(h.match(/^(.+)'(.+)$/))) +export const getGroupTagValues = (tags: string[][]) => getGroupTags(tags).map(nth(1)) -export const getKindTags = getTags(["k"], h => Boolean(h.match(/^\d+$/))) +export const getKindTags = (tags: string[][]) => + tags.filter(t => ["k"].includes(t[0]) && t[1].match(/^\d+$/)) -export const getKindTagValues = getTagValues(["k"], h => Boolean(h.match(/^\d+$/))) +export const getKindTagValues = (tags: string[][]) => getKindTags(tags).map(t => parseInt(t[1]))