Add memoize and batcher, bump versions

This commit is contained in:
Jon Staab
2024-08-12 11:17:27 -07:00
parent a8d0f5bc4f
commit 5d2186825b
15 changed files with 132 additions and 108 deletions
+23 -39
View File
@@ -2169,9 +2169,9 @@
} }
}, },
"node_modules/nostr-tools": { "node_modules/nostr-tools": {
"version": "2.7.0", "version": "2.7.2",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.7.0.tgz", "resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.7.2.tgz",
"integrity": "sha512-jJoL2J1CBiKDxaXZww27nY/Wsuxzx7AULxmGKFce4sskDu1tohNyfnzYQ8BvDyvkstU8kNZUAXPL32tre33uig==", "integrity": "sha512-Bq3Ug0SZFtgtL1+0wCnAe8AJtI7yx/00/a2nUug9SkhfOwlKS92Tef12iCK9FdwXw+oFZWMtRnSwcLayQso+xA==",
"dependencies": { "dependencies": {
"@noble/ciphers": "^0.5.1", "@noble/ciphers": "^0.5.1",
"@noble/curves": "1.2.0", "@noble/curves": "1.2.0",
@@ -3283,12 +3283,12 @@
}, },
"packages/dvm": { "packages/dvm": {
"name": "@welshman/dvm", "name": "@welshman/dvm",
"version": "0.0.3", "version": "0.0.4",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@welshman/lib": "0.0.13", "@welshman/lib": "0.0.14",
"@welshman/net": "0.0.16", "@welshman/net": "0.0.18",
"@welshman/util": "0.0.24", "@welshman/util": "0.0.25",
"nostr-tools": "^2.7.0" "nostr-tools": "^2.7.0"
}, },
"devDependencies": { "devDependencies": {
@@ -3297,29 +3297,11 @@
"typescript": "~5.1.6" "typescript": "~5.1.6"
} }
}, },
"packages/dvm/node_modules/@scure/base": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz",
"integrity": "sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==",
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"packages/dvm/node_modules/@welshman/net": {
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/@welshman/net/-/net-0.0.16.tgz",
"integrity": "sha512-9NoNKs3BxMs7biyfhLtQFs0rJP5e8raMMjCXYL0+WaHTSPi1VaKBzXGlsikF713pJ0xm5kigAxa6wquonsWHtg==",
"dependencies": {
"@welshman/lib": "0.0.12",
"@welshman/util": "0.0.23",
"isomorphic-ws": "^5.0.0",
"ws": "^8.16.0"
}
},
"packages/dvm/node_modules/@welshman/net/node_modules/@welshman/lib": { "packages/dvm/node_modules/@welshman/net/node_modules/@welshman/lib": {
"version": "0.0.12", "version": "0.0.12",
"resolved": "https://registry.npmjs.org/@welshman/lib/-/lib-0.0.12.tgz", "resolved": "https://registry.npmjs.org/@welshman/lib/-/lib-0.0.12.tgz",
"integrity": "sha512-865dbPRbXdpZXNkAXz0KDgW0Yq6krC9Pz59cycfe6k75fDO1w4jA30UlW4E1mJuGQycmBtyi9sAkUPP9LG4Srw==", "integrity": "sha512-865dbPRbXdpZXNkAXz0KDgW0Yq6krC9Pz59cycfe6k75fDO1w4jA30UlW4E1mJuGQycmBtyi9sAkUPP9LG4Srw==",
"extraneous": true,
"dependencies": { "dependencies": {
"@scure/base": "^1.1.6", "@scure/base": "^1.1.6",
"@types/events": "^3.0.3", "@types/events": "^3.0.3",
@@ -3332,6 +3314,7 @@
"version": "0.0.23", "version": "0.0.23",
"resolved": "https://registry.npmjs.org/@welshman/util/-/util-0.0.23.tgz", "resolved": "https://registry.npmjs.org/@welshman/util/-/util-0.0.23.tgz",
"integrity": "sha512-xV9RnPvO3XZ163TD/5ga/vCnLtejMpkIu9JlfiO/iyHJ1DdObq9WkyuHTgVDNxkrPBf74KuoF9gnub9rLMCj/w==", "integrity": "sha512-xV9RnPvO3XZ163TD/5ga/vCnLtejMpkIu9JlfiO/iyHJ1DdObq9WkyuHTgVDNxkrPBf74KuoF9gnub9rLMCj/w==",
"extraneous": true,
"dependencies": { "dependencies": {
"@welshman/lib": "0.0.12", "@welshman/lib": "0.0.12",
"nostr-tools": "^2.3.2" "nostr-tools": "^2.3.2"
@@ -3339,10 +3322,10 @@
}, },
"packages/feeds": { "packages/feeds": {
"name": "@welshman/feeds", "name": "@welshman/feeds",
"version": "0.0.13", "version": "0.0.14",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@welshman/util": "0.0.24" "@welshman/util": "0.0.25"
}, },
"devDependencies": { "devDependencies": {
"gts": "^5.0.1", "gts": "^5.0.1",
@@ -3352,7 +3335,7 @@
}, },
"packages/lib": { "packages/lib": {
"name": "@welshman/lib", "name": "@welshman/lib",
"version": "0.0.13", "version": "0.0.14",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@scure/base": "^1.1.6", "@scure/base": "^1.1.6",
@@ -3376,11 +3359,11 @@
}, },
"packages/net": { "packages/net": {
"name": "@welshman/net", "name": "@welshman/net",
"version": "0.0.17", "version": "0.0.18",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@welshman/lib": "0.0.13", "@welshman/lib": "0.0.14",
"@welshman/util": "0.0.24", "@welshman/util": "0.0.25",
"isomorphic-ws": "^5.0.0", "isomorphic-ws": "^5.0.0",
"ws": "^8.16.0" "ws": "^8.16.0"
}, },
@@ -3392,12 +3375,13 @@
}, },
"packages/signer": { "packages/signer": {
"name": "@welshman/signer", "name": "@welshman/signer",
"version": "0.0.1", "version": "0.0.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@welshman/lib": "^0.0.13", "@welshman/lib": "0.0.14",
"@welshman/net": "^0.0.17", "@welshman/net": "0.0.18",
"@welshman/util": "^0.0.24" "@welshman/util": "0.0.25",
"nostr-tools": "^2.7.2"
}, },
"devDependencies": { "devDependencies": {
"gts": "^5.0.1", "gts": "^5.0.1",
@@ -3407,7 +3391,7 @@
}, },
"packages/store": { "packages/store": {
"name": "@welshman/store", "name": "@welshman/store",
"version": "0.0.1", "version": "0.0.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"svelte": "^4.2.18" "svelte": "^4.2.18"
@@ -3420,10 +3404,10 @@
}, },
"packages/util": { "packages/util": {
"name": "@welshman/util", "name": "@welshman/util",
"version": "0.0.24", "version": "0.0.25",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@welshman/lib": "0.0.13", "@welshman/lib": "0.0.14",
"nostr-tools": "^2.3.2" "nostr-tools": "^2.3.2"
}, },
"devDependencies": { "devDependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/content", "name": "@welshman/content",
"version": "0.0.6", "version": "0.0.7",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of utilities for parsing nostr note content.", "description": "A collection of utilities for parsing nostr note content.",
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/dvm", "name": "@welshman/dvm",
"version": "0.0.3", "version": "0.0.4",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of utilities for building nostr DVMs.", "description": "A collection of utilities for building nostr DVMs.",
@@ -31,9 +31,9 @@
"typescript": "~5.1.6" "typescript": "~5.1.6"
}, },
"dependencies": { "dependencies": {
"@welshman/lib": "0.0.13", "@welshman/lib": "0.0.14",
"@welshman/net": "0.0.16", "@welshman/net": "0.0.18",
"@welshman/util": "0.0.24", "@welshman/util": "0.0.25",
"nostr-tools": "^2.7.0" "nostr-tools": "^2.7.0"
} }
} }
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/feeds", "name": "@welshman/feeds",
"version": "0.0.13", "version": "0.0.14",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "Utilities for building dynamic nostr feeds.", "description": "Utilities for building dynamic nostr feeds.",
@@ -31,6 +31,6 @@
"typescript": "~5.1.6" "typescript": "~5.1.6"
}, },
"dependencies": { "dependencies": {
"@welshman/util": "0.0.24" "@welshman/util": "0.0.25"
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/lib", "name": "@welshman/lib",
"version": "0.0.13", "version": "0.0.14",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of utilities.", "description": "A collection of utilities.",
+38
View File
@@ -355,6 +355,20 @@ export const once = (f: (...args: any) => void) => {
} }
} }
export const memoize = <T>(f: (...args: any[]) => T) => {
let prevArgs: any[]
let result: T
return (...args: any[]) => {
if (!equals(prevArgs, args)) {
prevArgs = args
result = f(...args)
}
return result
}
}
export const batch = <T>(t: number, f: (xs: T[]) => void) => { export const batch = <T>(t: number, f: (xs: T[]) => void) => {
const xs: T[] = [] const xs: T[] = []
const cb = throttle(t, () => xs.length > 0 && f(xs.splice(0))) const cb = throttle(t, () => xs.length > 0 && f(xs.splice(0)))
@@ -365,6 +379,30 @@ export const batch = <T>(t: number, f: (xs: T[]) => void) => {
} }
} }
export const batcher = <T, U>(t: number, execute: (request: T[]) => U[] | Promise<U[]>) => {
const queue: {request: T, resolve: (x: U) => void}[] = []
const _execute = async () => {
const items = queue.splice(0)
const results = await execute(items.map(item => item.request))
if (results.length !== items.length) {
throw new Error("Execute must return a promise for each request")
}
results.forEach(async (r, i) => items[i].resolve(await r))
}
return (request: T): Promise<U> =>
new Promise(resolve => {
if (queue.length === 0) {
setTimeout(_execute, t)
}
queue.push({request, resolve})
})
}
export const addToKey = <T>(m: Record<string, Set<T>>, k: string, v: T) => { export const addToKey = <T>(m: Record<string, Set<T>>, k: string, v: T) => {
const s = m[k] || new Set<T>() const s = m[k] || new Set<T>()
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/net", "name": "@welshman/net",
"version": "0.0.17", "version": "0.0.18",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "Utilities for connecting with nostr relays.", "description": "Utilities for connecting with nostr relays.",
@@ -31,8 +31,8 @@
"typescript": "~5.1.6" "typescript": "~5.1.6"
}, },
"dependencies": { "dependencies": {
"@welshman/lib": "0.0.13", "@welshman/lib": "0.0.14",
"@welshman/util": "0.0.24", "@welshman/util": "0.0.25",
"isomorphic-ws": "^5.0.0", "isomorphic-ws": "^5.0.0",
"ws": "^8.16.0" "ws": "^8.16.0"
} }
+3 -2
View File
@@ -1,9 +1,10 @@
import {Emitter} from '@welshman/lib' import {Emitter} from '@welshman/lib'
import type {TrustedEvent} from '@welshman/util'
import {Relay, LOCAL_RELAY_URL} from '@welshman/util' import {Relay, LOCAL_RELAY_URL} from '@welshman/util'
import type {Message} from '../Socket' import type {Message} from '../Socket'
export class Local extends Emitter { export class Local<T extends TrustedEvent> extends Emitter {
constructor(readonly relay: Relay) { constructor(readonly relay: Relay<T>) {
super() super()
relay.on('*', this.onMessage) relay.on('*', this.onMessage)
+5 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/signer", "name": "@welshman/signer",
"version": "0.0.1", "version": "0.0.2",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A nostr signer implemenation supporting several login methods.", "description": "A nostr signer implemenation supporting several login methods.",
@@ -31,8 +31,9 @@
"typescript": "~5.1.6" "typescript": "~5.1.6"
}, },
"dependencies": { "dependencies": {
"@welshman/lib": "^0.0.13", "@welshman/lib": "0.0.14",
"@welshman/net": "^0.0.17", "@welshman/net": "0.0.18",
"@welshman/util": "^0.0.24" "@welshman/util": "0.0.25",
"nostr-tools": "^2.7.2"
} }
} }
+11 -11
View File
@@ -55,7 +55,7 @@ export class Nip46Broker extends Emitter {
this.#sub = this.subscribe() this.#sub = this.subscribe()
} }
subscribe() { subscribe = () => {
const sub = subscribe({ const sub = subscribe({
relays: this.handler.relays, relays: this.handler.relays,
filters: [ filters: [
@@ -91,7 +91,7 @@ export class Nip46Broker extends Emitter {
return sub return sub
} }
async request(method: string, params: string[], admin = false) { request = async (method: string, params: string[], admin = false) => {
// nsecbunker has a race condition // nsecbunker has a race condition
await this.#ready await this.#ready
@@ -105,7 +105,7 @@ export class Nip46Broker extends Emitter {
publish({event, relays: this.handler.relays}) publish({event, relays: this.handler.relays})
this.once(`auth-${id}`, res => { this.once(`auth-${id}`, res => {
window.open(res.result, "Coracle", "width=600,height=800,popup=yes") window.open(res.error, "Coracle", "width=600,height=800,popup=yes")
}) })
return new Promise<string>((resolve, reject) => { return new Promise<string>((resolve, reject) => {
@@ -119,7 +119,7 @@ export class Nip46Broker extends Emitter {
}) })
} }
createAccount(username: string) { createAccount = (username: string) => {
if (!this.handler.domain) { if (!this.handler.domain) {
throw new Error("Unable to create an account without a handler domain") throw new Error("Unable to create an account without a handler domain")
} }
@@ -127,7 +127,7 @@ export class Nip46Broker extends Emitter {
return this.request("create_account", [username, this.handler.domain, "", Perms], true) return this.request("create_account", [username, this.handler.domain, "", Perms], true)
} }
async connect(token = "") { connect = async (token = "") => {
if (!this.#connectResult) { if (!this.#connectResult) {
const params = [this.pubkey, token, Perms] const params = [this.pubkey, token, Perms]
@@ -137,27 +137,27 @@ export class Nip46Broker extends Emitter {
return this.#connectResult === "ack" return this.#connectResult === "ack"
} }
async signEvent(event: EventTemplate) { signEvent = async (event: EventTemplate) => {
return JSON.parse(await this.request("sign_event", [JSON.stringify(event)]) as string) return JSON.parse(await this.request("sign_event", [JSON.stringify(event)]) as string)
} }
nip04Encrypt(pk: string, message: string) { nip04Encrypt = (pk: string, message: string) => {
return this.request("nip04_encrypt", [pk, message]) return this.request("nip04_encrypt", [pk, message])
} }
nip04Decrypt(pk: string, message: string) { nip04Decrypt = (pk: string, message: string) => {
return this.request("nip04_decrypt", [pk, message]) return this.request("nip04_decrypt", [pk, message])
} }
nip44Encrypt(pk: string, message: string) { nip44Encrypt = (pk: string, message: string) => {
return this.request("nip44_encrypt", [pk, message]) return this.request("nip44_encrypt", [pk, message])
} }
nip44Decrypt(pk: string, message: string) { nip44Decrypt = (pk: string, message: string) => {
return this.request("nip44_decrypt", [pk, message]) return this.request("nip44_decrypt", [pk, message])
} }
teardown() { teardown = () => {
this.#closed = true this.#closed = true
this.#sub?.close() this.#sub?.close()
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/store", "name": "@welshman/store",
"version": "0.0.1", "version": "0.0.2",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of utilities based on svelte/store for use with welshman", "description": "A collection of utilities based on svelte/store for use with welshman",
+14 -14
View File
@@ -68,8 +68,8 @@ export function withGetter<T>(store: Readable<T> | Writable<T>) {
export const throttled = <T>(delay: number, store: Readable<T>) => export const throttled = <T>(delay: number, store: Readable<T>) =>
custom(set => store.subscribe(throttle(delay, set))) custom(set => store.subscribe(throttle(delay, set)))
export const createEventStore = (repository: Repository) => { export const createEventStore = <E extends TrustedEvent>(repository: Repository<E>) => {
let subs: Sub<TrustedEvent[]>[] = [] let subs: Sub<E[]>[] = []
const onUpdate = throttle(300, () => { const onUpdate = throttle(300, () => {
const $events = repository.dump() const $events = repository.dump()
@@ -81,8 +81,8 @@ export const createEventStore = (repository: Repository) => {
return { return {
get: () => repository.dump(), get: () => repository.dump(),
set: (events: TrustedEvent[]) => repository.load(events), set: (events: E[]) => repository.load(events),
subscribe: (f: Sub<TrustedEvent[]>) => { subscribe: (f: Sub<E[]>) => {
f(repository.dump()) f(repository.dump())
subs.push(f) subs.push(f)
@@ -102,7 +102,7 @@ export const createEventStore = (repository: Repository) => {
} }
} }
export const deriveEventsMapped = <T>({ export const deriveEventsMapped = <T, E extends TrustedEvent>({
filters, filters,
repository, repository,
eventToItem, eventToItem,
@@ -110,9 +110,9 @@ export const deriveEventsMapped = <T>({
includeDeleted = false, includeDeleted = false,
}: { }: {
filters: Filter[] filters: Filter[]
repository: Repository, repository: Repository<E>,
eventToItem: (event: TrustedEvent) => T eventToItem: (event: E) => T
itemToEvent: (item: T) => TrustedEvent itemToEvent: (item: T) => E
includeDeleted?: boolean includeDeleted?: boolean
}) => }) =>
custom<T[]>(setter => { custom<T[]>(setter => {
@@ -120,7 +120,7 @@ export const deriveEventsMapped = <T>({
setter(data) setter(data)
const onUpdate = batch(300, (updates: {added: TrustedEvent[]; removed: Set<string>}[]) => { const onUpdate = batch(300, (updates: {added: E[]; removed: Set<string>}[]) => {
const removed = new Set() const removed = new Set()
const added = new Map() const added = new Map()
@@ -150,7 +150,7 @@ export const deriveEventsMapped = <T>({
if (!includeDeleted && removed.size > 0) { if (!includeDeleted && removed.size > 0) {
const [deleted, ok] = partition( const [deleted, ok] = partition(
(item: T) => getIdAndAddress(itemToEvent(item)).some(id => removed.has(id)), (item: T) => getIdAndAddress(itemToEvent(item)).some((id: string) => removed.has(id)),
data, data,
) )
@@ -170,15 +170,15 @@ export const deriveEventsMapped = <T>({
return () => repository.off("update", onUpdate) return () => repository.off("update", onUpdate)
}) })
export const deriveEvents = (repository: Repository, opts: {filters: Filter[]; includeDeleted?: boolean}) => export const deriveEvents = <E extends TrustedEvent>(repository: Repository<E>, opts: {filters: Filter[]; includeDeleted?: boolean}) =>
deriveEventsMapped<TrustedEvent>({ deriveEventsMapped<E, E>({
...opts, ...opts,
repository, repository,
eventToItem: identity, eventToItem: identity,
itemToEvent: identity, itemToEvent: identity,
}) })
export const deriveEvent = (repository: Repository, idOrAddress: string) => export const deriveEvent = <E extends TrustedEvent>(repository: Repository<E>, idOrAddress: string) =>
derived( derived(
deriveEvents(repository, { deriveEvents(repository, {
filters: getIdFilters([idOrAddress]), filters: getIdFilters([idOrAddress]),
@@ -187,7 +187,7 @@ export const deriveEvent = (repository: Repository, idOrAddress: string) =>
first first
) )
export const deriveIsDeletedByAddress = (repository: Repository, event: TrustedEvent) => export const deriveIsDeletedByAddress = <E extends TrustedEvent>(repository: Repository<E>, event: E) =>
custom<boolean>(setter => { custom<boolean>(setter => {
setter(repository.isDeletedByAddress(event)) setter(repository.isDeletedByAddress(event))
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/util", "name": "@welshman/util",
"version": "0.0.24", "version": "0.0.25",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of nostr-related utilities.", "description": "A collection of nostr-related utilities.",
@@ -31,7 +31,7 @@
"typescript": "~5.1.6" "typescript": "~5.1.6"
}, },
"dependencies": { "dependencies": {
"@welshman/lib": "0.0.13", "@welshman/lib": "0.0.14",
"nostr-tools": "^2.3.2" "nostr-tools": "^2.3.2"
} }
} }
+4 -4
View File
@@ -48,22 +48,22 @@ export const normalizeRelayUrl = (url: string, {allowInsecure = false}: Normaliz
return prefix + url return prefix + url
} }
export class Relay extends Emitter { export class Relay<T extends TrustedEvent> extends Emitter {
subs = new Map<string, Filter[]>() subs = new Map<string, Filter[]>()
constructor(readonly repository: Repository) { constructor(readonly repository: Repository<T>) {
super() super()
} }
send(type: string, ...message: any[]) { send(type: string, ...message: any[]) {
switch(type) { switch(type) {
case 'EVENT': return this.handleEVENT(message as [TrustedEvent]) case 'EVENT': return this.handleEVENT(message as [T])
case 'CLOSE': return this.handleCLOSE(message as [string]) case 'CLOSE': return this.handleCLOSE(message as [string])
case 'REQ': return this.handleREQ(message as [string, ...Filter[]]) case 'REQ': return this.handleREQ(message as [string, ...Filter[]])
} }
} }
handleEVENT([event]: [TrustedEvent]) { handleEVENT([event]: [T]) {
this.repository.publish(event) this.repository.publish(event)
// Callers generally expect async relays // Callers generally expect async relays
+20 -20
View File
@@ -10,13 +10,13 @@ export const DAY = 86400
const getDay = (ts: number) => Math.floor(ts / DAY) const getDay = (ts: number) => Math.floor(ts / DAY)
export class Repository extends Emitter { export class Repository<T extends TrustedEvent> extends Emitter {
eventsById = new Map<string, TrustedEvent>() eventsById = new Map<string, T>()
eventsByWrap = new Map<string, TrustedEvent>() eventsByWrap = new Map<string, T>()
eventsByAddress = new Map<string, TrustedEvent>() eventsByAddress = new Map<string, T>()
eventsByTag = new Map<string, TrustedEvent[]>() eventsByTag = new Map<string, T[]>()
eventsByDay = new Map<number, TrustedEvent[]>() eventsByDay = new Map<number, T[]>()
eventsByAuthor = new Map<string, TrustedEvent[]>() eventsByAuthor = new Map<string, T[]>()
deletes = new Map<string, number>() deletes = new Map<string, number>()
// Dump/load/clear // Dump/load/clear
@@ -25,7 +25,7 @@ export class Repository extends Emitter {
return Array.from(this.eventsById.values()) return Array.from(this.eventsById.values())
} }
load = async (events: TrustedEvent[], chunkSize = 1000) => { load = async (events: T[], chunkSize = 1000) => {
this.clear() this.clear()
const added = [] const added = []
@@ -69,7 +69,7 @@ export class Repository extends Emitter {
: this.eventsById.get(idOrAddress) : this.eventsById.get(idOrAddress)
} }
hasEvent = (event: TrustedEvent) => { hasEvent = (event: T) => {
const duplicate = ( const duplicate = (
this.eventsById.get(event.id) || this.eventsById.get(event.id) ||
this.eventsByAddress.get(getAddress(event)) this.eventsByAddress.get(getAddress(event))
@@ -79,12 +79,12 @@ export class Repository extends Emitter {
} }
query = (filters: Filter[], {includeDeleted = false} = {}) => { query = (filters: Filter[], {includeDeleted = false} = {}) => {
const result: TrustedEvent[][] = [] const result: T[][] = []
for (let filter of filters) { for (let filter of filters) {
let events: TrustedEvent[] = Array.from(this.eventsById.values()) let events: T[] = Array.from(this.eventsById.values())
if (filter.ids) { if (filter.ids) {
events = filter.ids!.map(id => this.eventsById.get(id)).filter(identity) as TrustedEvent[] events = filter.ids!.map(id => this.eventsById.get(id)).filter(identity) as T[]
filter = omit(['ids'], filter) filter = omit(['ids'], filter)
} else if (filter.authors) { } else if (filter.authors) {
events = uniq(filter.authors!.flatMap(pubkey => this.eventsByAuthor.get(pubkey) || [])) events = uniq(filter.authors!.flatMap(pubkey => this.eventsByAuthor.get(pubkey) || []))
@@ -112,8 +112,8 @@ export class Repository extends Emitter {
} }
} }
const chunk: TrustedEvent[] = [] const chunk: T[] = []
for (const event of sortBy((e: TrustedEvent) => -e.created_at, events)) { for (const event of sortBy((e: T) => -e.created_at, events)) {
if (filter.limit && chunk.length >= filter.limit) { if (filter.limit && chunk.length >= filter.limit) {
break break
} }
@@ -133,7 +133,7 @@ export class Repository extends Emitter {
return uniq(flatten(result)) return uniq(flatten(result))
} }
publish = (event: TrustedEvent, {shouldNotify = true} = {}): boolean => { publish = (event: T, {shouldNotify = true} = {}): boolean => {
if (!isTrustedEvent(event)) { if (!isTrustedEvent(event)) {
throw new Error("Invalid event published to Repository", event) throw new Error("Invalid event published to Repository", event)
} }
@@ -203,19 +203,19 @@ export class Repository extends Emitter {
return true return true
} }
isDeletedByAddress = (event: TrustedEvent) => (this.deletes.get(getAddress(event)) || 0) > event.created_at isDeletedByAddress = (event: T) => (this.deletes.get(getAddress(event)) || 0) > event.created_at
isDeletedById = (event: TrustedEvent) => (this.deletes.get(event.id) || 0) > event.created_at isDeletedById = (event: T) => (this.deletes.get(event.id) || 0) > event.created_at
isDeleted = (event: TrustedEvent) => this.isDeletedByAddress(event) || this.isDeletedById(event) isDeleted = (event: T) => this.isDeletedByAddress(event) || this.isDeletedById(event)
// Utilities // Utilities
_updateIndex<K>(m: Map<K, TrustedEvent[]>, k: K, e: TrustedEvent, duplicate?: TrustedEvent) { _updateIndex<K>(m: Map<K, T[]>, k: K, e: T, duplicate?: T) {
let a = m.get(k) || [] let a = m.get(k) || []
if (duplicate) { if (duplicate) {
a = a.filter((x: TrustedEvent) => x !== duplicate) a = a.filter((x: T) => x !== duplicate)
} }
a.push(e) a.push(e)