applesauce-sqlite 0.0.0-next-20250915145415

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/dist/better-sqlite3/event-database.d.ts +52 -0
  4. package/dist/better-sqlite3/event-database.js +104 -0
  5. package/dist/better-sqlite3/index.d.ts +2 -0
  6. package/dist/better-sqlite3/index.js +2 -0
  7. package/dist/better-sqlite3/methods.d.ts +31 -0
  8. package/dist/better-sqlite3/methods.js +149 -0
  9. package/dist/bun/event-database.d.ts +52 -0
  10. package/dist/bun/event-database.js +105 -0
  11. package/dist/bun/index.d.ts +2 -0
  12. package/dist/bun/index.js +2 -0
  13. package/dist/bun/methods.d.ts +31 -0
  14. package/dist/bun/methods.js +152 -0
  15. package/dist/helpers/index.d.ts +3 -0
  16. package/dist/helpers/index.js +3 -0
  17. package/dist/helpers/search.d.ts +16 -0
  18. package/dist/helpers/search.js +60 -0
  19. package/dist/helpers/sql.d.ts +15 -0
  20. package/dist/helpers/sql.js +122 -0
  21. package/dist/helpers/sqlite.d.ts +66 -0
  22. package/dist/helpers/sqlite.js +367 -0
  23. package/dist/helpers/statements.d.ts +47 -0
  24. package/dist/helpers/statements.js +65 -0
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.js +1 -0
  27. package/dist/libsql/event-database.d.ts +54 -0
  28. package/dist/libsql/event-database.js +106 -0
  29. package/dist/libsql/index.d.ts +2 -0
  30. package/dist/libsql/index.js +2 -0
  31. package/dist/libsql/methods.d.ts +31 -0
  32. package/dist/libsql/methods.js +249 -0
  33. package/dist/native/event-database.d.ts +52 -0
  34. package/dist/native/event-database.js +104 -0
  35. package/dist/native/index.d.ts +2 -0
  36. package/dist/native/index.js +2 -0
  37. package/dist/native/methods.d.ts +31 -0
  38. package/dist/native/methods.js +174 -0
  39. package/dist/relay.d.ts +1 -0
  40. package/dist/relay.js +166 -0
  41. package/dist/sqlite-event-database.d.ts +53 -0
  42. package/dist/sqlite-event-database.js +105 -0
  43. package/package.json +113 -0
@@ -0,0 +1,31 @@
1
+ import { Filter, NostrEvent } from "applesauce-core/helpers";
2
+ import { Database } from "bun:sqlite";
3
+ import { FilterWithSearch, SearchContentFormatter } from "../helpers/search.js";
4
+ /** Create and migrate the `events`, `event_tags`, and search tables */
5
+ export declare function createTables(db: Database, search?: boolean): void;
6
+ /** Inserts search content for an event */
7
+ export declare function insertSearchContent(db: Database, event: NostrEvent, contentFormatter: SearchContentFormatter): void;
8
+ /** Removes search content for an event */
9
+ export declare function deleteSearchContent(db: Database, eventId: string): void;
10
+ /** Inserts an event into the `events`, `event_tags`, and search tables of a database */
11
+ export declare function insertEvent(db: Database, event: NostrEvent, contentFormatter?: SearchContentFormatter): boolean;
12
+ /** Insert indexable tags for an event into the event_tags table */
13
+ export declare function insertEventTags(db: Database, event: NostrEvent): void;
14
+ /** Removes an event by id from the `events`, `event_tags`, and search tables of a database */
15
+ export declare function deleteEvent(db: Database, id: string): boolean;
16
+ /** Checks if an event exists */
17
+ export declare function hasEvent(db: Database, id: string): boolean;
18
+ /** Gets a single event from a database */
19
+ export declare function getEvent(db: Database, id: string): NostrEvent | undefined;
20
+ /** Gets the latest replaceable event from a database */
21
+ export declare function getReplaceable(db: Database, kind: number, pubkey: string, identifier: string): NostrEvent | undefined;
22
+ /** Gets the history of a replaceable event from a database */
23
+ export declare function getReplaceableHistory(db: Database, kind: number, pubkey: string, identifier: string): NostrEvent[];
24
+ /** Checks if a replaceable event exists in a database */
25
+ export declare function hasReplaceable(db: Database, kind: number, pubkey: string, identifier?: string): boolean;
26
+ /** Get all events that match the filters (includes NIP-50 search support) */
27
+ export declare function getEventsByFilters(db: Database, filters: FilterWithSearch | FilterWithSearch[]): Set<NostrEvent>;
28
+ /** Search events using FTS5 full-text search (convenience wrapper around getEventsByFilters) */
29
+ export declare function searchEvents(db: Database, search: string, options?: Filter): NostrEvent[];
30
+ /** Rebuild the FTS5 search index for all events */
31
+ export declare function rebuildSearchIndex(db: Database, contentFormatter: SearchContentFormatter): void;
@@ -0,0 +1,152 @@
1
+ import { getIndexableTags, getReplaceableIdentifier } from "applesauce-core/helpers";
2
+ import { CREATE_SEARCH_TABLE_STATEMENT, DELETE_SEARCH_CONTENT_STATEMENT, INSERT_SEARCH_CONTENT_STATEMENT, } from "../helpers/search.js";
3
+ import { buildFiltersQuery, rowToEvent } from "../helpers/sql.js";
4
+ import { CREATE_EVENT_TAGS_TABLE_STATEMENT, CREATE_EVENTS_TABLE_STATEMENT, CREATE_INDEXES_STATEMENTS, DELETE_EVENT_STATEMENT, DELETE_EVENT_TAGS_STATEMENT, GET_ALL_EVENTS_STATEMENT, GET_EVENT_STATEMENT, GET_REPLACEABLE_HISTORY_STATEMENT, GET_REPLACEABLE_STATEMENT, HAS_EVENT_STATEMENT, HAS_REPLACEABLE_STATEMENT, INSERT_EVENT_STATEMENT, INSERT_EVENT_TAG_STATEMENT, } from "../helpers/statements.js";
5
+ /** Create and migrate the `events`, `event_tags`, and search tables */
6
+ export function createTables(db, search = true) {
7
+ // Create the events table
8
+ db.exec(CREATE_EVENTS_TABLE_STATEMENT.sql);
9
+ // Create the event_tags table
10
+ db.exec(CREATE_EVENT_TAGS_TABLE_STATEMENT.sql);
11
+ // Create the FTS5 search table
12
+ if (search) {
13
+ db.exec(CREATE_SEARCH_TABLE_STATEMENT.sql);
14
+ }
15
+ // Create indexes
16
+ CREATE_INDEXES_STATEMENTS.forEach((indexStatement) => {
17
+ db.exec(indexStatement.sql);
18
+ });
19
+ }
20
+ /** Inserts search content for an event */
21
+ export function insertSearchContent(db, event, contentFormatter) {
22
+ const searchableContent = contentFormatter(event);
23
+ // Insert/update directly into the FTS5 table
24
+ const stmt = db.query(INSERT_SEARCH_CONTENT_STATEMENT.sql);
25
+ stmt.run(event.id, searchableContent, event.kind, event.pubkey, event.created_at);
26
+ }
27
+ /** Removes search content for an event */
28
+ export function deleteSearchContent(db, eventId) {
29
+ const stmt = db.query(DELETE_SEARCH_CONTENT_STATEMENT.sql);
30
+ stmt.run(eventId);
31
+ }
32
+ /** Inserts an event into the `events`, `event_tags`, and search tables of a database */
33
+ export function insertEvent(db, event, contentFormatter) {
34
+ const identifier = getReplaceableIdentifier(event);
35
+ const transaction = db.transaction(() => {
36
+ // Insert/update the main event
37
+ const stmt = db.query(INSERT_EVENT_STATEMENT.sql);
38
+ const result = stmt.run(event.id, event.kind, event.pubkey, event.created_at, event.content, JSON.stringify(event.tags), event.sig, identifier);
39
+ // Insert indexable tags into the event_tags table
40
+ insertEventTags(db, event);
41
+ // Insert searchable content into the search tables
42
+ if (contentFormatter)
43
+ insertSearchContent(db, event, contentFormatter);
44
+ return result.changes > 0;
45
+ });
46
+ return transaction();
47
+ }
48
+ /** Insert indexable tags for an event into the event_tags table */
49
+ export function insertEventTags(db, event) {
50
+ // Clear existing tags for this event first
51
+ const deleteStmt = db.query(DELETE_EVENT_TAGS_STATEMENT.sql);
52
+ deleteStmt.run(event.id);
53
+ // Get only the indexable tags using applesauce-core helper
54
+ const indexableTags = getIndexableTags(event);
55
+ if (indexableTags && indexableTags.size > 0) {
56
+ const insertStmt = db.query(INSERT_EVENT_TAG_STATEMENT.sql);
57
+ for (const tagString of indexableTags) {
58
+ // Parse the "tagName:tagValue" format
59
+ const [name, value] = tagString.split(":");
60
+ if (name && value)
61
+ insertStmt.run(event.id, name, value);
62
+ }
63
+ }
64
+ }
65
+ /** Removes an event by id from the `events`, `event_tags`, and search tables of a database */
66
+ export function deleteEvent(db, id) {
67
+ const transaction = db.transaction(() => {
68
+ // Delete from event_tags first (foreign key constraint)
69
+ const deleteTagsStmt = db.query(DELETE_EVENT_TAGS_STATEMENT.sql);
70
+ deleteTagsStmt.run(id);
71
+ // Delete from search tables
72
+ deleteSearchContent(db, id);
73
+ // Delete from events table
74
+ const deleteEventStmt = db.query(DELETE_EVENT_STATEMENT.sql);
75
+ const result = deleteEventStmt.run(id);
76
+ return result.changes > 0;
77
+ });
78
+ return transaction();
79
+ }
80
+ /** Checks if an event exists */
81
+ export function hasEvent(db, id) {
82
+ const stmt = db.query(HAS_EVENT_STATEMENT.sql);
83
+ const result = stmt.get(id);
84
+ if (!result)
85
+ return false;
86
+ return result.count > 0;
87
+ }
88
+ /** Gets a single event from a database */
89
+ export function getEvent(db, id) {
90
+ const stmt = db.query(GET_EVENT_STATEMENT.sql);
91
+ const row = stmt.get(id);
92
+ return row ? rowToEvent(row) : undefined;
93
+ }
94
+ /** Gets the latest replaceable event from a database */
95
+ export function getReplaceable(db, kind, pubkey, identifier) {
96
+ const stmt = db.query(GET_REPLACEABLE_STATEMENT.sql);
97
+ const row = stmt.get(kind, pubkey, identifier);
98
+ return row ? rowToEvent(row) : undefined;
99
+ }
100
+ /** Gets the history of a replaceable event from a database */
101
+ export function getReplaceableHistory(db, kind, pubkey, identifier) {
102
+ const stmt = db.query(GET_REPLACEABLE_HISTORY_STATEMENT.sql);
103
+ return stmt.all(kind, pubkey, identifier).map(rowToEvent);
104
+ }
105
+ /** Checks if a replaceable event exists in a database */
106
+ export function hasReplaceable(db, kind, pubkey, identifier = "") {
107
+ const stmt = db.query(HAS_REPLACEABLE_STATEMENT.sql);
108
+ const result = stmt.get(kind, pubkey, identifier);
109
+ if (!result)
110
+ return false;
111
+ return result.count > 0;
112
+ }
113
+ /** Get all events that match the filters (includes NIP-50 search support) */
114
+ export function getEventsByFilters(db, filters) {
115
+ const query = buildFiltersQuery(filters);
116
+ if (!query)
117
+ return new Set();
118
+ const eventSet = new Set();
119
+ const stmt = db.query(query.sql);
120
+ const rows = stmt.all(...query.params);
121
+ // Convert rows to events and add to set
122
+ for (const row of rows)
123
+ eventSet.add(rowToEvent(row));
124
+ return eventSet;
125
+ }
126
+ /** Search events using FTS5 full-text search (convenience wrapper around getEventsByFilters) */
127
+ export function searchEvents(db, search, options) {
128
+ if (!search.trim())
129
+ return [];
130
+ // Build filter with search and other options
131
+ const filter = {
132
+ search: search.trim(),
133
+ ...options,
134
+ };
135
+ // Use the main filter system which now supports search
136
+ const results = getEventsByFilters(db, filter);
137
+ return Array.from(results);
138
+ }
139
+ /** Rebuild the FTS5 search index for all events */
140
+ export function rebuildSearchIndex(db, contentFormatter) {
141
+ const transaction = db.transaction(() => {
142
+ // Clear existing search data
143
+ db.exec(`DELETE FROM events_search;`);
144
+ // Rebuild from all events
145
+ const stmt = db.query(GET_ALL_EVENTS_STATEMENT.sql);
146
+ const events = stmt.all().map(rowToEvent);
147
+ for (const event of events) {
148
+ insertSearchContent(db, event, contentFormatter);
149
+ }
150
+ });
151
+ transaction();
152
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./search.js";
2
+ export * from "./sql.js";
3
+ export * from "./statements.js";
@@ -0,0 +1,3 @@
1
+ export * from "./search.js";
2
+ export * from "./sql.js";
3
+ export * from "./statements.js";
@@ -0,0 +1,16 @@
1
+ import { Filter, NostrEvent } from "applesauce-core/helpers";
2
+ import type { Statement } from "./statements.js";
3
+ export declare const CREATE_SEARCH_TABLE_STATEMENT: Statement<[]>;
4
+ export declare const INSERT_SEARCH_CONTENT_STATEMENT: Statement<[string, string, number, string, number]>;
5
+ export declare const DELETE_SEARCH_CONTENT_STATEMENT: Statement<[string]>;
6
+ /** Filter with search field */
7
+ export type FilterWithSearch = Filter & {
8
+ search?: string;
9
+ order?: "created_at" | "rank";
10
+ };
11
+ /** Content formatter function type for search indexing */
12
+ export type SearchContentFormatter = (event: NostrEvent) => string;
13
+ /** Default search content formatter - returns the raw content */
14
+ export declare const defaultSearchContentFormatter: SearchContentFormatter;
15
+ /** Enhanced search content formatter that includes tags and special handling for kind 0 events */
16
+ export declare const enhancedSearchContentFormatter: SearchContentFormatter;
@@ -0,0 +1,60 @@
1
+ // SQL schema for FTS5 search table - stores formatted searchable content directly
2
+ export const CREATE_SEARCH_TABLE_STATEMENT = {
3
+ sql: `CREATE VIRTUAL TABLE IF NOT EXISTS events_search USING fts5(
4
+ event_id UNINDEXED,
5
+ content,
6
+ kind UNINDEXED,
7
+ pubkey UNINDEXED,
8
+ created_at UNINDEXED
9
+ )`,
10
+ };
11
+ export const INSERT_SEARCH_CONTENT_STATEMENT = {
12
+ sql: `INSERT OR REPLACE INTO events_search (event_id, content, kind, pubkey, created_at)
13
+ VALUES (?, ?, ?, ?, ?)`,
14
+ };
15
+ export const DELETE_SEARCH_CONTENT_STATEMENT = {
16
+ sql: `DELETE FROM events_search WHERE event_id = ?`,
17
+ };
18
+ /** Default search content formatter - returns the raw content */
19
+ export const defaultSearchContentFormatter = (event) => {
20
+ return event.content;
21
+ };
22
+ /** Enhanced search content formatter that includes tags and special handling for kind 0 events */
23
+ export const enhancedSearchContentFormatter = (event) => {
24
+ let searchableContent = event.content;
25
+ // Special handling for kind 0 (profile metadata) events
26
+ if (event.kind === 0) {
27
+ try {
28
+ const profile = JSON.parse(event.content);
29
+ const profileFields = [];
30
+ // Include common profile fields in search
31
+ if (profile.name)
32
+ profileFields.push(profile.name);
33
+ if (profile.display_name)
34
+ profileFields.push(profile.display_name);
35
+ if (profile.about)
36
+ profileFields.push(profile.about);
37
+ if (profile.nip05)
38
+ profileFields.push(profile.nip05);
39
+ if (profile.lud16)
40
+ profileFields.push(profile.lud16);
41
+ searchableContent = profileFields.join(" ");
42
+ }
43
+ catch (e) {
44
+ // If JSON parsing fails, use the raw content
45
+ searchableContent = event.content;
46
+ }
47
+ }
48
+ // Include relevant tags in the searchable content
49
+ const relevantTags = ["t", "subject", "title", "summary", "description", "d"];
50
+ const tagContent = [];
51
+ for (const tag of event.tags) {
52
+ if (tag.length >= 2 && relevantTags.includes(tag[0]))
53
+ tagContent.push(tag[1]);
54
+ }
55
+ // Combine content with tag content
56
+ if (tagContent.length > 0) {
57
+ searchableContent += " " + tagContent.join(" ");
58
+ }
59
+ return searchableContent;
60
+ };
@@ -0,0 +1,15 @@
1
+ import { NostrEvent } from "applesauce-core/helpers";
2
+ import { FilterWithSearch } from "./search.js";
3
+ import { EventRow } from "./statements.js";
4
+ /** Convert database row to NostrEvent */
5
+ export declare function rowToEvent(row: EventRow): NostrEvent;
6
+ /** Builds conditions for a single filter */
7
+ export declare function buildFilterConditions(filter: FilterWithSearch): {
8
+ conditions: string[];
9
+ params: any[];
10
+ search: boolean;
11
+ };
12
+ export declare function buildFiltersQuery(filters: FilterWithSearch | FilterWithSearch[]): {
13
+ sql: string;
14
+ params: any[];
15
+ } | null;
@@ -0,0 +1,122 @@
1
+ /** Convert database row to NostrEvent */
2
+ export function rowToEvent(row) {
3
+ return {
4
+ id: row.id,
5
+ kind: row.kind,
6
+ pubkey: row.pubkey,
7
+ created_at: row.created_at,
8
+ content: row.content,
9
+ tags: JSON.parse(row.tags || "[]"),
10
+ sig: row.sig,
11
+ };
12
+ }
13
+ /** Builds conditions for a single filter */
14
+ export function buildFilterConditions(filter) {
15
+ const conditions = [];
16
+ const params = [];
17
+ let search = false;
18
+ // Handle NIP-50 search filter
19
+ if (filter.search && filter.search.trim()) {
20
+ conditions.push(`events_search MATCH ?`);
21
+ params.push(filter.search.trim());
22
+ search = true;
23
+ }
24
+ // Handle IDs filter
25
+ if (filter.ids && filter.ids.length > 0) {
26
+ const placeholders = filter.ids.map(() => `?`).join(", ");
27
+ conditions.push(`events.id IN (${placeholders})`);
28
+ params.push(...filter.ids);
29
+ }
30
+ // Handle kinds filter
31
+ if (filter.kinds && filter.kinds.length > 0) {
32
+ const placeholders = filter.kinds.map(() => `?`).join(", ");
33
+ conditions.push(`events.kind IN (${placeholders})`);
34
+ params.push(...filter.kinds);
35
+ }
36
+ // Handle authors filter (pubkeys)
37
+ if (filter.authors && filter.authors.length > 0) {
38
+ const placeholders = filter.authors.map(() => `?`).join(", ");
39
+ conditions.push(`events.pubkey IN (${placeholders})`);
40
+ params.push(...filter.authors);
41
+ }
42
+ // Handle since filter (timestamp >= since)
43
+ if (filter.since !== undefined) {
44
+ conditions.push(`events.created_at >= ?`);
45
+ params.push(filter.since);
46
+ }
47
+ // Handle until filter (timestamp <= until)
48
+ if (filter.until !== undefined) {
49
+ conditions.push(`events.created_at <= ?`);
50
+ params.push(filter.until);
51
+ }
52
+ // Handle tag filters (e.g., #e, #p, #t, #d, etc.)
53
+ for (const [key, values] of Object.entries(filter)) {
54
+ if (key.startsWith("#") && values && Array.isArray(values) && values.length > 0) {
55
+ const tagName = key.slice(1); // Remove the '#' prefix
56
+ // Use the event_tags table for efficient tag filtering
57
+ const placeholders = values.map(() => "?").join(", ");
58
+ conditions.push(`events.id IN (
59
+ SELECT DISTINCT event_id
60
+ FROM event_tags
61
+ WHERE tag_name = ? AND tag_value IN (${placeholders})
62
+ )`);
63
+ // Add parameters: tagName first, then all the tag values
64
+ params.push(tagName, ...values);
65
+ }
66
+ }
67
+ return { conditions, params, search };
68
+ }
69
+ export function buildFiltersQuery(filters) {
70
+ const filterArray = Array.isArray(filters) ? filters : [filters];
71
+ if (filterArray.length === 0)
72
+ return null;
73
+ // Build queries for each filter (OR logic between filters)
74
+ const filterQueries = [];
75
+ const allParams = [];
76
+ let globalLimit;
77
+ // Build the final query with proper ordering and limit
78
+ let fromClause = "events";
79
+ let orderBy = "events.created_at DESC, events.id ASC";
80
+ for (const filter of filterArray) {
81
+ const { conditions, params, search } = buildFilterConditions(filter);
82
+ if (search) {
83
+ // Override the from clause to join the events_search table
84
+ fromClause = "events INNER JOIN events_search ON events.id = events_search.event_id";
85
+ // Set the order by clause based on the filter order
86
+ switch (filter.order) {
87
+ case "created_at":
88
+ orderBy = "events.created_at DESC, events.id ASC";
89
+ break;
90
+ case "rank":
91
+ orderBy = "events_search.rank, events.created_at DESC";
92
+ break;
93
+ }
94
+ }
95
+ if (conditions.length === 0) {
96
+ // If no conditions, this filter matches all events
97
+ filterQueries.push("1=1");
98
+ }
99
+ else {
100
+ // AND logic within a single filter
101
+ filterQueries.push(`(${conditions.join(" AND ")})`);
102
+ }
103
+ allParams.push(...params);
104
+ // Track the most restrictive limit across all filters
105
+ if (filter.limit !== undefined) {
106
+ globalLimit = globalLimit === undefined ? filter.limit : Math.min(globalLimit, filter.limit);
107
+ }
108
+ }
109
+ // Combine all filter conditions with OR logic
110
+ const whereClause = filterQueries.length > 0 ? `WHERE ${filterQueries.join(" OR ")}` : "";
111
+ let query = `
112
+ SELECT DISTINCT events.* FROM ${fromClause}
113
+ ${whereClause}
114
+ ORDER BY ${orderBy}
115
+ `;
116
+ // Apply global limit if specified
117
+ if (globalLimit !== undefined && globalLimit > 0) {
118
+ query += ` LIMIT ?`;
119
+ allParams.push(globalLimit);
120
+ }
121
+ return { sql: query, params: allParams };
122
+ }
@@ -0,0 +1,66 @@
1
+ import { Filter, NostrEvent } from "applesauce-core/helpers";
2
+ import { Database } from "better-sqlite3";
3
+ export declare const CREATE_EVENTS_TABLE = "\nCREATE TABLE IF NOT EXISTS events (\n id TEXT PRIMARY KEY,\n kind INTEGER NOT NULL,\n pubkey TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n content TEXT NOT NULL,\n tags TEXT,\n sig TEXT NOT NULL,\n identifier TEXT NOT NULL DEFAULT ''\n);\n";
4
+ export declare const CREATE_EVENT_TAGS_TABLE = "\nCREATE TABLE IF NOT EXISTS event_tags (\n event_id TEXT NOT NULL,\n tag_name TEXT NOT NULL,\n tag_value TEXT NOT NULL,\n FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE,\n PRIMARY KEY (event_id, tag_name, tag_value)\n);\n";
5
+ export declare const CREATE_SEARCH_TABLE = "\nCREATE VIRTUAL TABLE IF NOT EXISTS events_search USING fts5(\n event_id UNINDEXED,\n content,\n kind UNINDEXED,\n pubkey UNINDEXED,\n created_at UNINDEXED\n);\n";
6
+ export declare const CREATE_INDEXES: string[];
7
+ export type EventRow = {
8
+ id: string;
9
+ kind: number;
10
+ pubkey: string;
11
+ created_at: number;
12
+ content: string;
13
+ tags: string;
14
+ sig: string;
15
+ };
16
+ /** Filter with search field */
17
+ export type FilterWithSearch = Filter & {
18
+ search?: string;
19
+ order?: "created_at" | "rank";
20
+ };
21
+ /** Content formatter function type for search indexing */
22
+ export type SearchContentFormatter = (event: NostrEvent) => string;
23
+ /** Default search content formatter - returns the raw content */
24
+ export declare const defaultSearchContentFormatter: SearchContentFormatter;
25
+ /** Enhanced search content formatter that includes tags and special handling for kind 0 events */
26
+ export declare const enhancedSearchContentFormatter: SearchContentFormatter;
27
+ /** Create and migrate the `events`, `event_tags`, and search tables */
28
+ export declare function createTables(db: Database, search?: boolean): void;
29
+ /** Inserts search content for an event */
30
+ export declare function insertSearchContent(db: Database, event: NostrEvent, contentFormatter: SearchContentFormatter): void;
31
+ /** Removes search content for an event */
32
+ export declare function deleteSearchContent(db: Database, eventId: string): void;
33
+ /** Inserts an event into the `events`, `event_tags`, and search tables of a database */
34
+ export declare function insertEvent(db: Database, event: NostrEvent, contentFormatter?: SearchContentFormatter): boolean;
35
+ /** Insert indexable tags for an event into the event_tags table */
36
+ export declare function insertEventTags(db: Database, event: NostrEvent): void;
37
+ /** Removes an event by id from the `events`, `event_tags`, and search tables of a database */
38
+ export declare function deleteEvent(db: Database, id: string): boolean;
39
+ /** Checks if an event exists */
40
+ export declare function hasEvent(db: Database, id: string): boolean;
41
+ /** Gets a single event from a database */
42
+ export declare function getEvent(db: Database, id: string): NostrEvent | undefined;
43
+ /** Gets the latest replaceable event from a database */
44
+ export declare function getReplaceable(db: Database, kind: number, pubkey: string, identifier: string): NostrEvent | undefined;
45
+ /** Gets the history of a replaceable event from a database */
46
+ export declare function getReplaceableHistory(db: Database, kind: number, pubkey: string, identifier: string): NostrEvent[];
47
+ /** Checks if a replaceable event exists in a database */
48
+ export declare function hasReplaceable(db: Database, kind: number, pubkey: string, identifier?: string): boolean;
49
+ /** Convert database row to NostrEvent */
50
+ export declare function rowToEvent(row: EventRow): NostrEvent;
51
+ /** Builds conditions for a single filter */
52
+ export declare function buildFilterConditions(filter: FilterWithSearch): {
53
+ conditions: string[];
54
+ params: any[];
55
+ search: boolean;
56
+ };
57
+ export declare function buildFiltersQuery(filters: FilterWithSearch | FilterWithSearch[]): {
58
+ sql: string;
59
+ params: any[];
60
+ } | null;
61
+ /** Get all events that match the filters (includes NIP-50 search support) */
62
+ export declare function getEventsByFilters(db: Database, filters: FilterWithSearch | FilterWithSearch[]): Set<NostrEvent>;
63
+ /** Search events using FTS5 full-text search (convenience wrapper around getEventsByFilters) */
64
+ export declare function searchEvents(db: Database, search: string, options?: Filter): NostrEvent[];
65
+ /** Rebuild the FTS5 search index for all events */
66
+ export declare function rebuildSearchIndex(db: Database, contentFormatter: SearchContentFormatter): void;