applesauce-sqlite 4.4.0 → 5.2.0
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.
- package/README.md +1 -1
- package/dist/better-sqlite3/methods.js +2 -2
- package/dist/bun/methods.js +2 -2
- package/dist/helpers/search.d.ts +3 -3
- package/dist/helpers/sql.js +3 -3
- package/dist/helpers/statements.d.ts +4 -1
- package/dist/helpers/statements.js +6 -1
- package/dist/libsql/methods.js +3 -3
- package/dist/native/methods.js +2 -2
- package/dist/relay.js +1 -1
- package/dist/turso/methods.js +5 -1
- package/dist/turso-wasm/methods.js +5 -1
- package/package.json +7 -8
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# applesauce-sqlite
|
|
2
2
|
|
|
3
|
-
A SQLite3 event database implementation for
|
|
3
|
+
A SQLite3 event database implementation for Applesauce, providing persistent storage for Nostr events. This package extends the core `applesauce-core` functionality by replacing the default in-memory event database with a persistent SQLite database.
|
|
4
4
|
|
|
5
5
|
## Key Features
|
|
6
6
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getIndexableTags, getReplaceableIdentifier } from "applesauce-core/helpers";
|
|
2
2
|
import { CREATE_SEARCH_TABLE_STATEMENT, DELETE_SEARCH_CONTENT_STATEMENT, INSERT_SEARCH_CONTENT_STATEMENT, } from "../helpers/search.js";
|
|
3
3
|
import { buildFiltersQuery, buildDeleteFiltersQuery, rowToEvent } from "../helpers/sql.js";
|
|
4
|
-
import { CREATE_EVENT_TAGS_TABLE_STATEMENT, CREATE_EVENTS_TABLE_STATEMENT, CREATE_INDEXES_STATEMENTS, DELETE_EVENT_STATEMENT, GET_ALL_EVENTS_STATEMENT, GET_EVENT_STATEMENT, GET_REPLACEABLE_HISTORY_STATEMENT, GET_REPLACEABLE_STATEMENT, HAS_EVENT_STATEMENT, HAS_REPLACEABLE_STATEMENT, INSERT_EVENT_STATEMENT_WITH_IGNORE,
|
|
4
|
+
import { CREATE_EVENT_TAGS_TABLE_STATEMENT, CREATE_EVENTS_TABLE_STATEMENT, CREATE_INDEXES_STATEMENTS, DELETE_EVENT_STATEMENT, GET_ALL_EVENTS_STATEMENT, GET_EVENT_STATEMENT, GET_REPLACEABLE_HISTORY_STATEMENT, GET_REPLACEABLE_STATEMENT, HAS_EVENT_STATEMENT, HAS_REPLACEABLE_STATEMENT, INSERT_EVENT_STATEMENT_WITH_IGNORE, INSERT_EVENT_TAG_STATEMENT_WITH_IGNORE, } from "../helpers/statements.js";
|
|
5
5
|
/** Create and migrate the `events`, `event_tags`, and search tables */
|
|
6
6
|
export function createTables(db, search = true) {
|
|
7
7
|
// Create the events table
|
|
@@ -43,7 +43,7 @@ export function insertEvent(db, event, contentFormatter) {
|
|
|
43
43
|
// Event was inserted, continue with tags and search content
|
|
44
44
|
const indexableTags = getIndexableTags(event);
|
|
45
45
|
if (indexableTags && indexableTags.size > 0) {
|
|
46
|
-
const insertStmt = db.prepare(
|
|
46
|
+
const insertStmt = db.prepare(INSERT_EVENT_TAG_STATEMENT_WITH_IGNORE.sql);
|
|
47
47
|
for (const tagString of indexableTags) {
|
|
48
48
|
// Parse the "tagName:tagValue" format
|
|
49
49
|
const [name, value] = tagString.split(":");
|
package/dist/bun/methods.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getIndexableTags, getReplaceableIdentifier } from "applesauce-core/helpers";
|
|
2
2
|
import { CREATE_SEARCH_TABLE_STATEMENT, DELETE_SEARCH_CONTENT_STATEMENT, INSERT_SEARCH_CONTENT_STATEMENT, } from "../helpers/search.js";
|
|
3
3
|
import { buildFiltersQuery, buildDeleteFiltersQuery, rowToEvent } from "../helpers/sql.js";
|
|
4
|
-
import { CREATE_EVENT_TAGS_TABLE_STATEMENT, CREATE_EVENTS_TABLE_STATEMENT, CREATE_INDEXES_STATEMENTS, DELETE_EVENT_STATEMENT, GET_ALL_EVENTS_STATEMENT, GET_EVENT_STATEMENT, GET_REPLACEABLE_HISTORY_STATEMENT, GET_REPLACEABLE_STATEMENT, HAS_EVENT_STATEMENT, HAS_REPLACEABLE_STATEMENT, INSERT_EVENT_STATEMENT_WITH_IGNORE,
|
|
4
|
+
import { CREATE_EVENT_TAGS_TABLE_STATEMENT, CREATE_EVENTS_TABLE_STATEMENT, CREATE_INDEXES_STATEMENTS, DELETE_EVENT_STATEMENT, GET_ALL_EVENTS_STATEMENT, GET_EVENT_STATEMENT, GET_REPLACEABLE_HISTORY_STATEMENT, GET_REPLACEABLE_STATEMENT, HAS_EVENT_STATEMENT, HAS_REPLACEABLE_STATEMENT, INSERT_EVENT_STATEMENT_WITH_IGNORE, INSERT_EVENT_TAG_STATEMENT_WITH_IGNORE, } from "../helpers/statements.js";
|
|
5
5
|
/** Create and migrate the `events`, `event_tags`, and search tables */
|
|
6
6
|
export function createTables(db, search = true) {
|
|
7
7
|
// Create the events table
|
|
@@ -43,7 +43,7 @@ export function insertEvent(db, event, contentFormatter) {
|
|
|
43
43
|
// Event was inserted, continue with tags and search content
|
|
44
44
|
const indexableTags = getIndexableTags(event);
|
|
45
45
|
if (indexableTags && indexableTags.size > 0) {
|
|
46
|
-
const insertStmt = db.query(
|
|
46
|
+
const insertStmt = db.query(INSERT_EVENT_TAG_STATEMENT_WITH_IGNORE.sql);
|
|
47
47
|
for (const tagString of indexableTags) {
|
|
48
48
|
// Parse the "tagName:tagValue" format
|
|
49
49
|
const [name, value] = tagString.split(":");
|
package/dist/helpers/search.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Filter, NostrEvent } from "applesauce-core/helpers";
|
|
2
2
|
import type { Statement } from "./statements.js";
|
|
3
3
|
export declare const CREATE_SEARCH_TABLE_STATEMENT: Statement<[]>;
|
|
4
4
|
export declare const INSERT_SEARCH_CONTENT_STATEMENT: Statement<[string, string, number, string, number]>;
|
|
5
5
|
export declare const DELETE_SEARCH_CONTENT_STATEMENT: Statement<[string]>;
|
|
6
|
-
/** Filter with search field and NIP-
|
|
7
|
-
export type FilterWithSearch =
|
|
6
|
+
/** Filter with search field and NIP-91 AND operator support */
|
|
7
|
+
export type FilterWithSearch = Filter & {
|
|
8
8
|
search?: string;
|
|
9
9
|
order?: "created_at" | "rank";
|
|
10
10
|
};
|
package/dist/helpers/sql.js
CHANGED
|
@@ -49,7 +49,7 @@ export function buildFilterConditions(filter) {
|
|
|
49
49
|
conditions.push(`events.created_at <= ?`);
|
|
50
50
|
params.push(filter.until);
|
|
51
51
|
}
|
|
52
|
-
// Handle AND tag filters (& prefix) first - NIP-
|
|
52
|
+
// Handle AND tag filters (& prefix) first - NIP-91
|
|
53
53
|
// AND takes precedence and requires ALL values to be present
|
|
54
54
|
for (const [key, values] of Object.entries(filter)) {
|
|
55
55
|
if (key.startsWith("&") && values && Array.isArray(values) && values.length > 0) {
|
|
@@ -69,14 +69,14 @@ export function buildFilterConditions(filter) {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
// Handle OR tag filters (# prefix)
|
|
72
|
-
// Skip values that are in AND tags (NIP-
|
|
72
|
+
// Skip values that are in AND tags (NIP-91 rule)
|
|
73
73
|
for (const [key, values] of Object.entries(filter)) {
|
|
74
74
|
if (key.startsWith("#") && values && Array.isArray(values) && values.length > 0) {
|
|
75
75
|
const tagName = key.slice(1); // Remove the '#' prefix
|
|
76
76
|
// Check if there's a corresponding AND filter for this tag
|
|
77
77
|
const andKey = `&${tagName}`;
|
|
78
78
|
const andValues = filter[andKey];
|
|
79
|
-
// Filter out values that are in AND tags (NIP-
|
|
79
|
+
// Filter out values that are in AND tags (NIP-91 rule)
|
|
80
80
|
const filteredValues = andValues
|
|
81
81
|
? values.filter((v) => !andValues.includes(v))
|
|
82
82
|
: values;
|
|
@@ -38,10 +38,13 @@ export declare const INSERT_EVENT_STATEMENT_WITH_IGNORE: Statement<[
|
|
|
38
38
|
string,
|
|
39
39
|
string
|
|
40
40
|
]>;
|
|
41
|
-
/** For implementations that don't support OR IGNORE (
|
|
41
|
+
/** For implementations that don't support INSERT OR IGNORE (Turso embedded backends) */
|
|
42
42
|
export declare const INSERT_EVENT_STATEMENT: Statement<[string, number, string, number, string, string, string, string]>;
|
|
43
43
|
export declare const DELETE_EVENT_TAGS_STATEMENT: Statement<[string]>;
|
|
44
|
+
/** For implementations that don't support INSERT OR IGNORE (Turso embedded backends) */
|
|
44
45
|
export declare const INSERT_EVENT_TAG_STATEMENT: Statement<[string, string, string]>;
|
|
46
|
+
/** For sqlite implementations that support idempotent tag inserts with INSERT OR IGNORE */
|
|
47
|
+
export declare const INSERT_EVENT_TAG_STATEMENT_WITH_IGNORE: Statement<[string, string, string]>;
|
|
45
48
|
export declare const DELETE_EVENT_STATEMENT: Statement<[string]>;
|
|
46
49
|
export declare const HAS_EVENT_STATEMENT: Statement<[string], {
|
|
47
50
|
count: number;
|
|
@@ -3,7 +3,7 @@ export const INSERT_EVENT_STATEMENT_WITH_IGNORE = {
|
|
|
3
3
|
sql: `INSERT OR IGNORE INTO events (id, kind, pubkey, created_at, content, tags, sig, identifier)
|
|
4
4
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
5
5
|
};
|
|
6
|
-
/** For implementations that don't support OR IGNORE (
|
|
6
|
+
/** For implementations that don't support INSERT OR IGNORE (Turso embedded backends) */
|
|
7
7
|
export const INSERT_EVENT_STATEMENT = {
|
|
8
8
|
sql: `INSERT INTO events (id, kind, pubkey, created_at, content, tags, sig, identifier)
|
|
9
9
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -11,9 +11,14 @@ export const INSERT_EVENT_STATEMENT = {
|
|
|
11
11
|
export const DELETE_EVENT_TAGS_STATEMENT = {
|
|
12
12
|
sql: `DELETE FROM event_tags WHERE event_id = ?`,
|
|
13
13
|
};
|
|
14
|
+
/** For implementations that don't support INSERT OR IGNORE (Turso embedded backends) */
|
|
14
15
|
export const INSERT_EVENT_TAG_STATEMENT = {
|
|
15
16
|
sql: `INSERT INTO event_tags (event_id, tag_name, tag_value) VALUES (?, ?, ?)`,
|
|
16
17
|
};
|
|
18
|
+
/** For sqlite implementations that support idempotent tag inserts with INSERT OR IGNORE */
|
|
19
|
+
export const INSERT_EVENT_TAG_STATEMENT_WITH_IGNORE = {
|
|
20
|
+
sql: `INSERT OR IGNORE INTO event_tags (event_id, tag_name, tag_value) VALUES (?, ?, ?)`,
|
|
21
|
+
};
|
|
17
22
|
export const DELETE_EVENT_STATEMENT = {
|
|
18
23
|
sql: `DELETE FROM events WHERE id = ?`,
|
|
19
24
|
};
|
package/dist/libsql/methods.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getIndexableTags, getReplaceableIdentifier } from "applesauce-core/helpers";
|
|
2
2
|
import { CREATE_SEARCH_TABLE_STATEMENT, DELETE_SEARCH_CONTENT_STATEMENT, INSERT_SEARCH_CONTENT_STATEMENT, } from "../helpers/search.js";
|
|
3
3
|
import { buildFiltersQuery, buildDeleteFiltersQuery, rowToEvent } from "../helpers/sql.js";
|
|
4
|
-
import { CREATE_EVENT_TAGS_TABLE_STATEMENT, CREATE_EVENTS_TABLE_STATEMENT, CREATE_INDEXES_STATEMENTS, DELETE_EVENT_STATEMENT, GET_ALL_EVENTS_STATEMENT, GET_EVENT_STATEMENT, GET_REPLACEABLE_HISTORY_STATEMENT, GET_REPLACEABLE_STATEMENT, HAS_EVENT_STATEMENT, HAS_REPLACEABLE_STATEMENT, INSERT_EVENT_STATEMENT,
|
|
4
|
+
import { CREATE_EVENT_TAGS_TABLE_STATEMENT, CREATE_EVENTS_TABLE_STATEMENT, CREATE_INDEXES_STATEMENTS, DELETE_EVENT_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_WITH_IGNORE, } from "../helpers/statements.js";
|
|
5
5
|
/** Create and migrate the `events`, `event_tags`, and search tables */
|
|
6
6
|
export async function createTables(db, search = true) {
|
|
7
7
|
// Create the events table
|
|
@@ -68,8 +68,8 @@ export async function insertEvent(db, event, contentFormatter) {
|
|
|
68
68
|
// Parse the "tagName:tagValue" format
|
|
69
69
|
const [name, value] = tagString.split(":");
|
|
70
70
|
if (name && value) {
|
|
71
|
-
await
|
|
72
|
-
sql:
|
|
71
|
+
await transaction.execute({
|
|
72
|
+
sql: INSERT_EVENT_TAG_STATEMENT_WITH_IGNORE.sql,
|
|
73
73
|
args: [event.id, name, value],
|
|
74
74
|
});
|
|
75
75
|
}
|
package/dist/native/methods.js
CHANGED
|
@@ -2,7 +2,7 @@ import { logger } from "applesauce-core";
|
|
|
2
2
|
import { getIndexableTags, getReplaceableIdentifier } from "applesauce-core/helpers";
|
|
3
3
|
import { CREATE_SEARCH_TABLE_STATEMENT, DELETE_SEARCH_CONTENT_STATEMENT, INSERT_SEARCH_CONTENT_STATEMENT, } from "../helpers/search.js";
|
|
4
4
|
import { buildFiltersQuery, buildDeleteFiltersQuery, rowToEvent } from "../helpers/sql.js";
|
|
5
|
-
import { CREATE_EVENT_TAGS_TABLE_STATEMENT, CREATE_EVENTS_TABLE_STATEMENT, CREATE_INDEXES_STATEMENTS, DELETE_EVENT_STATEMENT, GET_ALL_EVENTS_STATEMENT, GET_EVENT_STATEMENT, GET_REPLACEABLE_HISTORY_STATEMENT, GET_REPLACEABLE_STATEMENT, HAS_EVENT_STATEMENT, HAS_REPLACEABLE_STATEMENT, INSERT_EVENT_STATEMENT_WITH_IGNORE,
|
|
5
|
+
import { CREATE_EVENT_TAGS_TABLE_STATEMENT, CREATE_EVENTS_TABLE_STATEMENT, CREATE_INDEXES_STATEMENTS, DELETE_EVENT_STATEMENT, GET_ALL_EVENTS_STATEMENT, GET_EVENT_STATEMENT, GET_REPLACEABLE_HISTORY_STATEMENT, GET_REPLACEABLE_STATEMENT, HAS_EVENT_STATEMENT, HAS_REPLACEABLE_STATEMENT, INSERT_EVENT_STATEMENT_WITH_IGNORE, INSERT_EVENT_TAG_STATEMENT_WITH_IGNORE, } from "../helpers/statements.js";
|
|
6
6
|
const log = logger.extend("sqlite:tables");
|
|
7
7
|
/** Create and migrate the `events`, `event_tags`, and search tables */
|
|
8
8
|
export function createTables(db, search = true) {
|
|
@@ -52,7 +52,7 @@ export function insertEvent(db, event, contentFormatter) {
|
|
|
52
52
|
// Get only the indexable tags using applesauce-core helper
|
|
53
53
|
const indexableTags = getIndexableTags(event);
|
|
54
54
|
if (indexableTags && indexableTags.size > 0) {
|
|
55
|
-
const insertStmt = db.prepare(
|
|
55
|
+
const insertStmt = db.prepare(INSERT_EVENT_TAG_STATEMENT_WITH_IGNORE.sql);
|
|
56
56
|
for (const tagString of indexableTags) {
|
|
57
57
|
// Parse the "tagName:tagValue" format
|
|
58
58
|
const [name, value] = tagString.split(":");
|
package/dist/relay.js
CHANGED
|
@@ -5,7 +5,7 @@ import { WebSocket, WebSocketServer } from "ws";
|
|
|
5
5
|
import { BetterSqlite3EventDatabase } from "./better-sqlite3/event-database.js";
|
|
6
6
|
// Create the event store with SQLite backend
|
|
7
7
|
const database = new BetterSqlite3EventDatabase(process.env.DATABASE_PATH || ":memory:");
|
|
8
|
-
const eventStore = new EventStore(database);
|
|
8
|
+
const eventStore = new EventStore({ database });
|
|
9
9
|
// Set validation method for event store
|
|
10
10
|
eventStore.verifyEvent = verifyEvent;
|
|
11
11
|
const subscriptions = new Map();
|
package/dist/turso/methods.js
CHANGED
|
@@ -21,7 +21,11 @@ export async function createTables(db, search = false) {
|
|
|
21
21
|
export async function insertEvent(db, event, searchContentFormatter) {
|
|
22
22
|
const identifier = getReplaceableIdentifier(event);
|
|
23
23
|
return await db.transaction(async () => {
|
|
24
|
-
//
|
|
24
|
+
// Turso embedded does not support INSERT OR IGNORE, so check for duplicates first
|
|
25
|
+
const existing = await db.prepare(HAS_EVENT_STATEMENT.sql).get(event.id);
|
|
26
|
+
if (existing && existing.count > 0)
|
|
27
|
+
return false;
|
|
28
|
+
// Insert the main event
|
|
25
29
|
const result = await db
|
|
26
30
|
.prepare(INSERT_EVENT_STATEMENT.sql)
|
|
27
31
|
.run(event.id, event.kind, event.pubkey, event.created_at, event.content, JSON.stringify(event.tags), event.sig, identifier);
|
|
@@ -16,7 +16,11 @@ export async function createTables(db) {
|
|
|
16
16
|
export async function insertEvent(db, event) {
|
|
17
17
|
const identifier = getReplaceableIdentifier(event);
|
|
18
18
|
return await db.transaction(async () => {
|
|
19
|
-
//
|
|
19
|
+
// Turso embedded does not support INSERT OR IGNORE, so check for duplicates first
|
|
20
|
+
const existing = await db.prepare(HAS_EVENT_STATEMENT.sql).get(event.id);
|
|
21
|
+
if (existing && existing.count > 0)
|
|
22
|
+
return false;
|
|
23
|
+
// Insert the main event
|
|
20
24
|
const result = await db
|
|
21
25
|
.prepare(INSERT_EVENT_STATEMENT.sql)
|
|
22
26
|
.run(event.id, event.kind, event.pubkey, event.created_at, event.content, JSON.stringify(event.tags), event.sig, identifier);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-sqlite",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "sqlite event databases for applesauce",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
}
|
|
103
103
|
},
|
|
104
104
|
"dependencies": {
|
|
105
|
-
"applesauce-core": "^
|
|
105
|
+
"applesauce-core": "^5.2.0"
|
|
106
106
|
},
|
|
107
107
|
"optionalDependencies": {
|
|
108
108
|
"@libsql/client": "^0.15.15",
|
|
@@ -113,12 +113,11 @@
|
|
|
113
113
|
"devDependencies": {
|
|
114
114
|
"@hirez_io/observer-spy": "^2.2.0",
|
|
115
115
|
"@types/better-sqlite3": "^7.6.13",
|
|
116
|
-
"@types/ws": "^8.
|
|
117
|
-
"applesauce-signers": "^
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"vitest": "^3.2.4",
|
|
116
|
+
"@types/ws": "^8.18.1",
|
|
117
|
+
"applesauce-signers": "^5.2.0",
|
|
118
|
+
"rimraf": "^6.1.2",
|
|
119
|
+
"typescript": "^5.9.3",
|
|
120
|
+
"vitest": "^4.0.15",
|
|
122
121
|
"vitest-websocket-mock": "^0.5.0",
|
|
123
122
|
"ws": "^8.18.3"
|
|
124
123
|
},
|