Upgrade NSpec, update `count` interface
This commit is contained in:
parent
676bd04229
commit
cb80770fc7
|
@ -96,7 +96,7 @@ function connectStream(socket: WebSocket) {
|
||||||
|
|
||||||
/** Handle COUNT. Return the number of events matching the filters. */
|
/** Handle COUNT. Return the number of events matching the filters. */
|
||||||
async function handleCount([_, subId, ...rest]: ClientCOUNT): Promise<void> {
|
async function handleCount([_, subId, ...rest]: ClientCOUNT): Promise<void> {
|
||||||
const count = await eventsDB.count(prepareFilters(rest));
|
const { count } = await eventsDB.count(prepareFilters(rest));
|
||||||
send(['COUNT', subId, { count, approximate: false }]);
|
send(['COUNT', subId, { count, approximate: false }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,6 @@ export { default as Debug } from 'https://gitlab.com/soapbox-pub/stickynotes/-/r
|
||||||
export {
|
export {
|
||||||
LNURL,
|
LNURL,
|
||||||
type LNURLDetails,
|
type LNURLDetails,
|
||||||
type MapCache,
|
|
||||||
NCache,
|
NCache,
|
||||||
NIP05,
|
NIP05,
|
||||||
type NostrEvent,
|
type NostrEvent,
|
||||||
|
@ -93,6 +92,6 @@ export {
|
||||||
NSet,
|
NSet,
|
||||||
type NStore,
|
type NStore,
|
||||||
type NStoreOpts,
|
type NStoreOpts,
|
||||||
} from 'https://gitlab.com/soapbox-pub/NSpec/-/raw/8ce84d9acd9925f51b51c00374eae81a5031559b/mod.ts';
|
} from 'https://gitlab.com/soapbox-pub/NSpec/-/raw/v0.1.0/mod.ts';
|
||||||
|
|
||||||
export type * as TypeFest from 'npm:type-fest@^4.3.0';
|
export type * as TypeFest from 'npm:type-fest@^4.3.0';
|
||||||
|
|
|
@ -46,7 +46,7 @@ async function handleEvent(event: DittoEvent, signal: AbortSignal): Promise<void
|
||||||
|
|
||||||
/** Encounter the event, and return whether it has already been encountered. */
|
/** Encounter the event, and return whether it has already been encountered. */
|
||||||
async function encounterEvent(event: NostrEvent, signal: AbortSignal): Promise<boolean> {
|
async function encounterEvent(event: NostrEvent, signal: AbortSignal): Promise<boolean> {
|
||||||
const preexisting = (await cache.count([{ ids: [event.id] }])) > 0;
|
const preexisting = (await cache.count([{ ids: [event.id] }])).count > 0;
|
||||||
cache.event(event);
|
cache.event(event);
|
||||||
reqmeister.event(event, { signal });
|
reqmeister.event(event, { signal });
|
||||||
return preexisting;
|
return preexisting;
|
||||||
|
@ -72,10 +72,10 @@ async function storeEvent(event: DittoEvent, opts: StoreEventOpts): Promise<void
|
||||||
const { force = false, signal } = opts;
|
const { force = false, signal } = opts;
|
||||||
|
|
||||||
if (force || event.user || isAdminEvent(event) || await isLocallyFollowed(event.pubkey)) {
|
if (force || event.user || isAdminEvent(event) || await isLocallyFollowed(event.pubkey)) {
|
||||||
const isDeleted = await eventsDB.count(
|
const isDeleted = (await eventsDB.count(
|
||||||
[{ kinds: [5], authors: [Conf.pubkey, event.pubkey], '#e': [event.id], limit: 1 }],
|
[{ kinds: [5], authors: [Conf.pubkey, event.pubkey], '#e': [event.id], limit: 1 }],
|
||||||
opts,
|
opts,
|
||||||
) > 0;
|
)).count > 0;
|
||||||
|
|
||||||
if (isDeleted) {
|
if (isDeleted) {
|
||||||
return Promise.reject(new RelayError('blocked', 'event was deleted'));
|
return Promise.reject(new RelayError('blocked', 'event was deleted'));
|
||||||
|
@ -145,16 +145,20 @@ function trackRelays(event: NostrEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Queue related events to fetch. */
|
/** Queue related events to fetch. */
|
||||||
function fetchRelatedEvents(event: DittoEvent, signal: AbortSignal) {
|
async function fetchRelatedEvents(event: DittoEvent, signal: AbortSignal) {
|
||||||
if (!event.user) {
|
if (!event.user) {
|
||||||
reqmeister.req({ kinds: [0], authors: [event.pubkey] }, { signal }).catch(() => {});
|
reqmeister.req({ kinds: [0], authors: [event.pubkey] }, { signal }).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [name, id, relay] of event.tags) {
|
for (const [name, id, relay] of event.tags) {
|
||||||
if (name === 'e' && !cache.count([{ ids: [id] }])) {
|
if (name === 'e') {
|
||||||
|
const { count } = await cache.count([{ ids: [id] }]);
|
||||||
|
if (!count) {
|
||||||
reqmeister.req({ ids: [id] }, { relays: [relay] }).catch(() => {});
|
reqmeister.req({ ids: [id] }, { relays: [relay] }).catch(() => {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Delete unattached media entries that are attached to the event. */
|
/** Delete unattached media entries that are attached to the event. */
|
||||||
function processMedia({ tags, pubkey, user }: DittoEvent) {
|
function processMedia({ tags, pubkey, user }: DittoEvent) {
|
||||||
|
|
|
@ -10,9 +10,9 @@ import { EventsDB } from './events-db.ts';
|
||||||
const eventsDB = new EventsDB(db);
|
const eventsDB = new EventsDB(db);
|
||||||
|
|
||||||
Deno.test('count filters', async () => {
|
Deno.test('count filters', async () => {
|
||||||
assertEquals(await eventsDB.count([{ kinds: [1] }]), 0);
|
assertEquals((await eventsDB.count([{ kinds: [1] }])).count, 0);
|
||||||
await eventsDB.event(event1);
|
await eventsDB.event(event1);
|
||||||
assertEquals(await eventsDB.count([{ kinds: [1] }]), 1);
|
assertEquals((await eventsDB.count([{ kinds: [1] }])).count, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('insert and filter events', async () => {
|
Deno.test('insert and filter events', async () => {
|
||||||
|
@ -55,11 +55,11 @@ Deno.test('query events with local filter', async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('inserting replaceable events', async () => {
|
Deno.test('inserting replaceable events', async () => {
|
||||||
assertEquals(await eventsDB.count([{ kinds: [0], authors: [event0.pubkey] }]), 0);
|
assertEquals((await eventsDB.count([{ kinds: [0], authors: [event0.pubkey] }])).count, 0);
|
||||||
|
|
||||||
await eventsDB.event(event0);
|
await eventsDB.event(event0);
|
||||||
await assertRejects(() => eventsDB.event(event0));
|
await assertRejects(() => eventsDB.event(event0));
|
||||||
assertEquals(await eventsDB.count([{ kinds: [0], authors: [event0.pubkey] }]), 1);
|
assertEquals((await eventsDB.count([{ kinds: [0], authors: [event0.pubkey] }])).count, 1);
|
||||||
|
|
||||||
const changeEvent = { ...event0, id: '123', created_at: event0.created_at + 1 };
|
const changeEvent = { ...event0, id: '123', created_at: event0.created_at + 1 };
|
||||||
await eventsDB.event(changeEvent);
|
await eventsDB.event(changeEvent);
|
||||||
|
|
|
@ -345,9 +345,9 @@ class EventsDB implements NStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get number of events that would be returned by filters. */
|
/** Get number of events that would be returned by filters. */
|
||||||
async count(filters: DittoFilter[], opts: NStoreOpts = {}): Promise<number> {
|
async count(filters: DittoFilter[], opts: NStoreOpts = {}): Promise<{ count: number; approximate: boolean }> {
|
||||||
if (opts.signal?.aborted) return Promise.reject(abortError());
|
if (opts.signal?.aborted) return Promise.reject(abortError());
|
||||||
if (!filters.length) return Promise.resolve(0);
|
if (!filters.length) return Promise.resolve({ count: 0, approximate: false });
|
||||||
|
|
||||||
this.#debug('COUNT', JSON.stringify(filters));
|
this.#debug('COUNT', JSON.stringify(filters));
|
||||||
const query = this.getEventsQuery(filters);
|
const query = this.getEventsQuery(filters);
|
||||||
|
@ -357,7 +357,10 @@ class EventsDB implements NStore {
|
||||||
.select((eb) => eb.fn.count('id').as('count'))
|
.select((eb) => eb.fn.count('id').as('count'))
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
return Number(count);
|
return {
|
||||||
|
count: Number(count),
|
||||||
|
approximate: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,14 +94,6 @@ class Optimizer implements NStore {
|
||||||
|
|
||||||
return getResults();
|
return getResults();
|
||||||
}
|
}
|
||||||
|
|
||||||
count(_filters: DittoFilter[]): Promise<number> {
|
|
||||||
return Promise.reject(new Error('COUNT not implemented.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(_filters: DittoFilter[]): Promise<void> {
|
|
||||||
return Promise.reject(new Error('DELETE not implemented.'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Optimizer };
|
export { Optimizer };
|
||||||
|
|
|
@ -92,14 +92,6 @@ class PoolStore implements NStore {
|
||||||
opts.signal?.addEventListener('abort', onAbort);
|
opts.signal?.addEventListener('abort', onAbort);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
count() {
|
|
||||||
return Promise.reject(new Error('COUNT not implemented'));
|
|
||||||
}
|
|
||||||
|
|
||||||
remove() {
|
|
||||||
return Promise.reject(new Error('Cannot delete events from relays. Create a kind 5 event instead.'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { PoolStore };
|
export { PoolStore };
|
||||||
|
|
|
@ -133,14 +133,6 @@ class Reqmeister extends EventEmitter<{ [filterId: string]: (event: NostrEvent)
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
count(_filters: NostrFilter[]): Promise<number> {
|
|
||||||
return Promise.reject(new Error('COUNT not implemented.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(_filters: NostrFilter[]): Promise<void> {
|
|
||||||
return Promise.reject(new Error('DELETE not implemented.'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Reqmeister };
|
export { Reqmeister };
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { NiceRelay } from 'https://gitlab.com/soapbox-pub/nostr-machina/-/raw/5f4fb59c90c092e5aa59c01e6556a4bec264c167/mod.ts';
|
import { NiceRelay } from 'https://gitlab.com/soapbox-pub/nostr-machina/-/raw/5f4fb59c90c092e5aa59c01e6556a4bec264c167/mod.ts';
|
||||||
|
|
||||||
import { Debug, type NostrEvent, type NostrFilter, NSet, type NStore, type NStoreOpts } from '@/deps.ts';
|
import { Debug, type NostrEvent, NSet, type NStore, type NStoreOpts } from '@/deps.ts';
|
||||||
import { normalizeFilters } from '@/filter.ts';
|
import { normalizeFilters } from '@/filter.ts';
|
||||||
import { type DittoEvent } from '@/interfaces/DittoEvent.ts';
|
import { type DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||||
import { type DittoFilter } from '@/interfaces/DittoFilter.ts';
|
import { type DittoFilter } from '@/interfaces/DittoFilter.ts';
|
||||||
|
@ -68,14 +68,6 @@ class SearchStore implements NStore {
|
||||||
return this.#fallback.query(filters, opts);
|
return this.#fallback.query(filters, opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
count(_filters: NostrFilter[]): Promise<number> {
|
|
||||||
return Promise.reject(new Error('COUNT not implemented.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(_filters: NostrFilter[]): Promise<void> {
|
|
||||||
return Promise.reject(new Error('DELETE not implemented.'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { SearchStore };
|
export { SearchStore };
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// deno-lint-ignore-file ban-types
|
// deno-lint-ignore-file ban-types
|
||||||
|
|
||||||
import { LRUCache, type MapCache } from '@/deps.ts';
|
import { LRUCache } from '@/deps.ts';
|
||||||
|
|
||||||
type FetchFn<K extends {}, V extends {}, O extends {}> = (key: K, opts: O) => Promise<V>;
|
type FetchFn<K extends {}, V extends {}, O extends {}> = (key: K, opts: O) => Promise<V>;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ export class SimpleLRU<
|
||||||
K extends {},
|
K extends {},
|
||||||
V extends {},
|
V extends {},
|
||||||
O extends {} = FetchFnOpts,
|
O extends {} = FetchFnOpts,
|
||||||
> implements MapCache<K, V, O> {
|
> {
|
||||||
protected cache: LRUCache<K, V, void>;
|
protected cache: LRUCache<K, V, void>;
|
||||||
|
|
||||||
constructor(fetchFn: FetchFn<K, V, { signal: AbortSignal }>, opts: LRUCache.Options<K, V, void>) {
|
constructor(fetchFn: FetchFn<K, V, { signal: AbortSignal }>, opts: LRUCache.Options<K, V, void>) {
|
||||||
|
|
Loading…
Reference in New Issue