applesauce-sqlite 5.0.0 → 6.0.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # applesauce-sqlite
2
2
 
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.
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, INSERT_EVENT_TAG_STATEMENT, } from "../helpers/statements.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, 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(INSERT_EVENT_TAG_STATEMENT.sql);
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(":");
@@ -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, INSERT_EVENT_TAG_STATEMENT, } from "../helpers/statements.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, 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(INSERT_EVENT_TAG_STATEMENT.sql);
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(":");
@@ -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 (libsql, turso-wasm) */
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 (libsql, turso-wasm) */
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
  };
@@ -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, INSERT_EVENT_TAG_STATEMENT, } from "../helpers/statements.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, 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 db.execute({
72
- sql: INSERT_EVENT_TAG_STATEMENT.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
  }
@@ -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, INSERT_EVENT_TAG_STATEMENT, } from "../helpers/statements.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, 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(INSERT_EVENT_TAG_STATEMENT.sql);
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(":");
@@ -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
- // Try to insert the main event with OR IGNORE
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
- // Try to insert the main event with OR IGNORE
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": "5.0.0",
3
+ "version": "6.0.0",
4
4
  "description": "sqlite event databases for applesauce",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -102,19 +102,23 @@
102
102
  }
103
103
  },
104
104
  "dependencies": {
105
- "applesauce-core": "^5.0.0"
105
+ "applesauce-core": "^6.0.0"
106
106
  },
107
- "optionalDependencies": {
107
+ "peerDependencies": {
108
108
  "@libsql/client": "^0.15.15",
109
109
  "@tursodatabase/database": "^0.2.2",
110
110
  "@tursodatabase/database-wasm": "^0.2.2",
111
- "better-sqlite3": "^12.2.0"
111
+ "better-sqlite3": "^12.8.0"
112
112
  },
113
113
  "devDependencies": {
114
114
  "@hirez_io/observer-spy": "^2.2.0",
115
+ "@libsql/client": "^0.15.15",
116
+ "@tursodatabase/database": "^0.2.2",
117
+ "@tursodatabase/database-wasm": "^0.2.2",
115
118
  "@types/better-sqlite3": "^7.6.13",
116
119
  "@types/ws": "^8.18.1",
117
- "applesauce-signers": "^5.0.0",
120
+ "applesauce-signers": "^6.0.0",
121
+ "better-sqlite3": "^12.8.0",
118
122
  "rimraf": "^6.1.2",
119
123
  "typescript": "^5.9.3",
120
124
  "vitest": "^4.0.15",