diff --git a/package.json b/package.json index ab68bab..96e9862 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,6 @@ "@swc-node/register": "^1.10.10", "@swc/core": "^1.11.13", "@types/better-sqlite3": "^7.6.12", - "@types/bun": "^1.2.7", "@types/cors": "^2.8.17", "@types/debug": "^4.1.12", "@types/express": "^4.17.21", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc51cf2..25cf3c2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -141,9 +141,6 @@ importers: '@types/better-sqlite3': specifier: ^7.6.12 version: 7.6.12 - '@types/bun': - specifier: ^1.2.7 - version: 1.2.7 '@types/cors': specifier: ^2.8.17 version: 2.8.17 @@ -1153,9 +1150,6 @@ packages: '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - '@types/bun@1.2.7': - resolution: {integrity: sha512-Xf/nkx1V03GvWEzWrYsOGB1VUvWVtU5p6jHalCxixhhfLgsJN59iT7CRvfBXyEBnYoGCa2orzWj6car/QfTjxw==} - '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -1523,9 +1517,6 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - bun-types@1.2.7: - resolution: {integrity: sha512-P4hHhk7kjF99acXqKvltyuMQ2kf/rzIw3ylEDpCxDS9Xa0X0Yp/gJu/vDCucmWpiur5qJ0lwB2bWzOXa2GlHqA==} - bytes@3.0.0: resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} engines: {node: '>= 0.8'} @@ -2871,8 +2862,8 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} - serve-static@2.1.0: - resolution: {integrity: sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==} + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} engines: {node: '>= 18'} set-function-length@1.2.2: @@ -4355,10 +4346,6 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 22.13.14 - '@types/bun@1.2.7': - dependencies: - bun-types: 1.2.7 - '@types/connect@3.4.38': dependencies: '@types/node': 22.13.14 @@ -4869,11 +4856,6 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - bun-types@1.2.7: - dependencies: - '@types/node': 22.13.14 - '@types/ws': 8.18.0 - bytes@3.0.0: {} bytes@3.1.2: {} @@ -5284,7 +5266,7 @@ snapshots: router: 2.2.0 safe-buffer: 5.2.1 send: 1.2.0 - serve-static: 2.1.0 + serve-static: 2.2.0 setprototypeof: 1.2.0 statuses: 2.0.1 type-is: 2.0.1 @@ -6463,7 +6445,7 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@2.1.0: + serve-static@2.2.0: dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 diff --git a/src/sqlite/event-store.ts b/src/sqlite/event-store.ts index 324ab33..764dd62 100644 --- a/src/sqlite/event-store.ts +++ b/src/sqlite/event-store.ts @@ -1,3 +1,4 @@ +import { ISyncEventStore } from "applesauce-core"; import { Database } from "better-sqlite3"; import { Filter, NostrEvent, kinds } from "nostr-tools"; import EventEmitter from "events"; @@ -184,7 +185,7 @@ type EventMap = { "event:removed": [string]; }; -export class SQLiteEventStore extends EventEmitter { +export class SQLiteEventStore extends EventEmitter implements ISyncEventStore { db: Database; log = logger.extend("sqlite-event-store"); @@ -330,20 +331,7 @@ export class SQLiteEventStore extends EventEmitter { } } - removeEvent(id: string) { - const results = this.db.transaction(() => { - this.db.prepare(`DELETE FROM tags WHERE e = ?`).run(id); - this.db.prepare(`DELETE FROM events_fts WHERE id = ?`).run(id); - - return this.db.prepare(`DELETE FROM events WHERE events.id = ?`).run(id); - })(); - - if (results.changes > 0) this.emit("event:removed", id); - - return results.changes > 0; - } - - buildConditionsForFilters(filter: Filter) { + protected buildConditionsForFilters(filter: Filter) { const joins: string[] = []; const conditions: string[] = []; const parameters: (string | number)[] = []; @@ -484,6 +472,39 @@ export class SQLiteEventStore extends EventEmitter { return { sql, parameters }; } + hasEvent(id: string): boolean { + return this.db.prepare<[string], { id: string }>(`SELECT id FROM events WHERE id = ?`).get(id) !== undefined; + } + + getEvent(id: string): NostrEvent | undefined { + const row = this.db.prepare<[string], EventRow>(`SELECT * FROM events WHERE id = ?`).get(id); + if (!row) return undefined; + return parseEventRow(row); + } + + hasReplaceable(kind: number, pubkey: string, identifier?: string): boolean { + return this.getReplaceable(kind, pubkey, identifier) !== undefined; + } + + getReplaceable(kind: number, pubkey: string, identifier?: string): NostrEvent | undefined { + const filter: Filter = { kinds: [kind], authors: [pubkey], limit: 1 }; + if (identifier) filter["#d"] = [identifier]; + return this.getEventsForFilters([filter])[0]; + } + + getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] { + const filter: Filter = { kinds: [kind], authors: [pubkey] }; + if (identifier) filter["#d"] = [identifier]; + return this.getEventsForFilters([filter]); + } + getTimeline(filters: Filter | Filter[]): NostrEvent[] { + return this.getEventsForFilters(Array.isArray(filters) ? filters : [filters]); + } + + getAll(filters: Filter | Filter[]): Set { + return new Set(this.getEventsForFilters(Array.isArray(filters) ? filters : [filters])); + } + getEventsForFilters(filters: Filter[]) { const { sql, parameters } = this.buildSQLQueryForFilters(filters);