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.
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/dist/better-sqlite3/event-database.d.ts +52 -0
- package/dist/better-sqlite3/event-database.js +104 -0
- package/dist/better-sqlite3/index.d.ts +2 -0
- package/dist/better-sqlite3/index.js +2 -0
- package/dist/better-sqlite3/methods.d.ts +31 -0
- package/dist/better-sqlite3/methods.js +149 -0
- package/dist/bun/event-database.d.ts +52 -0
- package/dist/bun/event-database.js +105 -0
- package/dist/bun/index.d.ts +2 -0
- package/dist/bun/index.js +2 -0
- package/dist/bun/methods.d.ts +31 -0
- package/dist/bun/methods.js +152 -0
- package/dist/helpers/index.d.ts +3 -0
- package/dist/helpers/index.js +3 -0
- package/dist/helpers/search.d.ts +16 -0
- package/dist/helpers/search.js +60 -0
- package/dist/helpers/sql.d.ts +15 -0
- package/dist/helpers/sql.js +122 -0
- package/dist/helpers/sqlite.d.ts +66 -0
- package/dist/helpers/sqlite.js +367 -0
- package/dist/helpers/statements.d.ts +47 -0
- package/dist/helpers/statements.js +65 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/libsql/event-database.d.ts +54 -0
- package/dist/libsql/event-database.js +106 -0
- package/dist/libsql/index.d.ts +2 -0
- package/dist/libsql/index.js +2 -0
- package/dist/libsql/methods.d.ts +31 -0
- package/dist/libsql/methods.js +249 -0
- package/dist/native/event-database.d.ts +52 -0
- package/dist/native/event-database.js +104 -0
- package/dist/native/index.d.ts +2 -0
- package/dist/native/index.js +2 -0
- package/dist/native/methods.d.ts +31 -0
- package/dist/native/methods.js +174 -0
- package/dist/relay.d.ts +1 -0
- package/dist/relay.js +166 -0
- package/dist/sqlite-event-database.d.ts +53 -0
- package/dist/sqlite-event-database.js +105 -0
- package/package.json +113 -0
package/dist/relay.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { EventStore } from "applesauce-core";
|
|
2
|
+
import { matchFilter, verifyEvent } from "applesauce-core/helpers";
|
|
3
|
+
import { createServer } from "http";
|
|
4
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
5
|
+
import { BetterSqlite3EventDatabase } from "./better-sqlite3/event-database.js";
|
|
6
|
+
// Create the event store with SQLite backend
|
|
7
|
+
const database = new BetterSqlite3EventDatabase(process.env.DATABASE_PATH || ":memory:");
|
|
8
|
+
const eventStore = new EventStore(database);
|
|
9
|
+
// Set validation method for event store
|
|
10
|
+
eventStore.verifyEvent = verifyEvent;
|
|
11
|
+
const subscriptions = new Map();
|
|
12
|
+
// Create HTTP server and WebSocket server
|
|
13
|
+
const server = createServer();
|
|
14
|
+
const wss = new WebSocketServer({ server });
|
|
15
|
+
// Handle WebSocket connections
|
|
16
|
+
wss.on("connection", (ws) => {
|
|
17
|
+
console.log("New client connected");
|
|
18
|
+
ws.on("message", async (data) => {
|
|
19
|
+
try {
|
|
20
|
+
const message = JSON.parse(data.toString());
|
|
21
|
+
if (!Array.isArray(message) || message.length < 2) {
|
|
22
|
+
ws.send(JSON.stringify(["NOTICE", "Invalid message format"]));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const [type, ...args] = message;
|
|
26
|
+
switch (type) {
|
|
27
|
+
case "EVENT":
|
|
28
|
+
await handleEvent(ws, args[0]);
|
|
29
|
+
break;
|
|
30
|
+
case "REQ":
|
|
31
|
+
await handleReq(ws, args[0], args.slice(1));
|
|
32
|
+
break;
|
|
33
|
+
case "CLOSE":
|
|
34
|
+
handleClose(ws, args[0]);
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
ws.send(JSON.stringify(["NOTICE", `Unknown message type: ${type}`]));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error("Error processing message:", error);
|
|
42
|
+
ws.send(JSON.stringify(["NOTICE", "Error processing message"]));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
ws.on("close", () => {
|
|
46
|
+
console.log("Client disconnected");
|
|
47
|
+
// Clean up subscriptions for this WebSocket
|
|
48
|
+
for (const [subId, sub] of subscriptions.entries()) {
|
|
49
|
+
if (sub.ws === ws) {
|
|
50
|
+
subscriptions.delete(subId);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
ws.on("error", (error) => {
|
|
55
|
+
console.error("WebSocket error:", error);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
// Handle EVENT messages
|
|
59
|
+
async function handleEvent(ws, event) {
|
|
60
|
+
try {
|
|
61
|
+
// Basic event validation
|
|
62
|
+
if (typeof event !== "object" || event === null)
|
|
63
|
+
throw new Error("invalid: event is not valid");
|
|
64
|
+
let added = null;
|
|
65
|
+
try {
|
|
66
|
+
added = eventStore.add(event);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
ws.send(JSON.stringify(["OK", event.id, false, "error: failed to validate event"]));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!added) {
|
|
73
|
+
ws.send(JSON.stringify(["OK", event.id, false, "error: rejected event"]));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (added === event) {
|
|
77
|
+
// Its a new event because the current instance was returned
|
|
78
|
+
ws.send(JSON.stringify(["OK", event.id, true, ""]));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// It was a duplicate because the "real" instance was returned
|
|
82
|
+
ws.send(JSON.stringify(["OK", event.id, true, "duplicate: event already exists"]));
|
|
83
|
+
}
|
|
84
|
+
// Broadcast to subscribers
|
|
85
|
+
broadcastToSubscribers(event);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error("Error handling event:", error);
|
|
89
|
+
if (error instanceof Error)
|
|
90
|
+
ws.send(JSON.stringify(["OK", event.id, false, error.message]));
|
|
91
|
+
else
|
|
92
|
+
ws.send(JSON.stringify(["OK", event.id, false, "error: failed to process event"]));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Handle REQ messages
|
|
96
|
+
async function handleReq(ws, subscriptionId, filters) {
|
|
97
|
+
try {
|
|
98
|
+
// Store subscription
|
|
99
|
+
subscriptions.set(subscriptionId, {
|
|
100
|
+
id: subscriptionId,
|
|
101
|
+
filters,
|
|
102
|
+
ws,
|
|
103
|
+
});
|
|
104
|
+
// Get existing events that match filters
|
|
105
|
+
const events = eventStore.getByFilters(filters);
|
|
106
|
+
// Send matching events
|
|
107
|
+
for (const event of events) {
|
|
108
|
+
ws.send(JSON.stringify(["EVENT", subscriptionId, event]));
|
|
109
|
+
}
|
|
110
|
+
// Send EOSE (End of Stored Events)
|
|
111
|
+
ws.send(JSON.stringify(["EOSE", subscriptionId]));
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
console.error("Error handling REQ:", error);
|
|
115
|
+
ws.send(JSON.stringify(["NOTICE", "Error processing subscription"]));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Handle CLOSE messages
|
|
119
|
+
function handleClose(ws, subscriptionId) {
|
|
120
|
+
const sub = subscriptions.get(subscriptionId);
|
|
121
|
+
if (sub && sub.ws === ws) {
|
|
122
|
+
subscriptions.delete(subscriptionId);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Broadcast event to all subscribers with matching filters
|
|
126
|
+
function broadcastToSubscribers(event) {
|
|
127
|
+
for (const [subId, sub] of subscriptions.entries()) {
|
|
128
|
+
if (sub.ws.readyState === WebSocket.OPEN) {
|
|
129
|
+
// Skip this subscription if it has a search filter (cant match search filters)
|
|
130
|
+
if (sub.filters.some((filter) => filter.search))
|
|
131
|
+
continue;
|
|
132
|
+
// Check if event matches any of the subscription filters
|
|
133
|
+
const matches = sub.filters.some((filter) => matchFilter(filter, event));
|
|
134
|
+
if (matches)
|
|
135
|
+
sub.ws.send(JSON.stringify(["EVENT", subId, event]));
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// Clean up closed connections
|
|
139
|
+
subscriptions.delete(subId);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Start the server
|
|
144
|
+
const PORT = process.env.PORT || 8080;
|
|
145
|
+
server.listen(PORT, () => {
|
|
146
|
+
console.log(`Nostr relay server listening on port ${PORT}`);
|
|
147
|
+
console.log(`WebSocket endpoint: ws://localhost:${PORT}`);
|
|
148
|
+
});
|
|
149
|
+
// Subscribe to new events from the event store to broadcast them
|
|
150
|
+
eventStore.insert$.subscribe((event) => {
|
|
151
|
+
broadcastToSubscribers(event);
|
|
152
|
+
});
|
|
153
|
+
// Graceful shutdown
|
|
154
|
+
process.on("SIGINT", () => {
|
|
155
|
+
console.log("\nShutting down relay server...");
|
|
156
|
+
wss.close(() => {
|
|
157
|
+
server.close(() => {
|
|
158
|
+
database.close();
|
|
159
|
+
process.exit(0);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
console.log("🚀 Nostr relay started!");
|
|
164
|
+
console.log("📡 WebSocket endpoint: ws://localhost:" + (process.env.PORT || 8080));
|
|
165
|
+
console.log("💾 Database: " + (process.env.DATABASE_PATH || ":memory:"));
|
|
166
|
+
console.log("🛑 Press Ctrl+C to stop the server");
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { IEventDatabase } from "applesauce-core";
|
|
2
|
+
import { Filter, NostrEvent } from "applesauce-core/helpers";
|
|
3
|
+
import { type Database as TDatabase } from "better-sqlite3";
|
|
4
|
+
import { defaultSearchContentFormatter, enhancedSearchContentFormatter, type SearchContentFormatter } from "./helpers/sqlite.js";
|
|
5
|
+
export { defaultSearchContentFormatter, enhancedSearchContentFormatter, type SearchContentFormatter };
|
|
6
|
+
/** Options for the SqliteEventDatabase */
|
|
7
|
+
export type SqliteEventDatabaseOptions = {
|
|
8
|
+
search?: boolean;
|
|
9
|
+
searchContentFormatter?: SearchContentFormatter;
|
|
10
|
+
};
|
|
11
|
+
export declare class SqliteEventDatabase implements IEventDatabase {
|
|
12
|
+
db: TDatabase;
|
|
13
|
+
/** If search is enabled */
|
|
14
|
+
private search;
|
|
15
|
+
/** The search content formatter */
|
|
16
|
+
private searchContentFormatter;
|
|
17
|
+
constructor(database?: string | TDatabase, options?: SqliteEventDatabaseOptions);
|
|
18
|
+
/** Store a Nostr event in the database */
|
|
19
|
+
add(event: NostrEvent): NostrEvent;
|
|
20
|
+
/** Delete an event by ID */
|
|
21
|
+
remove(id: string): boolean;
|
|
22
|
+
/** Checks if an event exists */
|
|
23
|
+
hasEvent(id: string): boolean;
|
|
24
|
+
/** Get an event by its ID */
|
|
25
|
+
getEvent(id: string): NostrEvent | undefined;
|
|
26
|
+
/** Get the latest replaceable event For replaceable events (10000-19999), returns the most recent event */
|
|
27
|
+
getReplaceable(kind: number, pubkey: string, identifier?: string): NostrEvent | undefined;
|
|
28
|
+
/** Checks if a replaceable event exists */
|
|
29
|
+
hasReplaceable(kind: number, pubkey: string, identifier?: string): boolean;
|
|
30
|
+
/** Returns all the versions of a replaceable event */
|
|
31
|
+
getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[];
|
|
32
|
+
/** Get all events that match the filters (supports NIP-50 search field) */
|
|
33
|
+
getByFilters(filters: (Filter & {
|
|
34
|
+
search?: string;
|
|
35
|
+
}) | (Filter & {
|
|
36
|
+
search?: string;
|
|
37
|
+
})[]): Set<NostrEvent>;
|
|
38
|
+
/** Get a timeline of events that match the filters (returns array in chronological order, supports NIP-50 search) */
|
|
39
|
+
getTimeline(filters: (Filter & {
|
|
40
|
+
search?: string;
|
|
41
|
+
}) | (Filter & {
|
|
42
|
+
search?: string;
|
|
43
|
+
})[]): NostrEvent[];
|
|
44
|
+
/** Set the search content formatter */
|
|
45
|
+
setSearchContentFormatter(formatter: SearchContentFormatter): void;
|
|
46
|
+
/** Get the current search content formatter */
|
|
47
|
+
getSearchContentFormatter(): SearchContentFormatter;
|
|
48
|
+
/** Rebuild the search index for all events */
|
|
49
|
+
rebuildSearchIndex(): void;
|
|
50
|
+
/** Close the database connection */
|
|
51
|
+
close(): void;
|
|
52
|
+
[Symbol.dispose](): void;
|
|
53
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { logger } from "applesauce-core";
|
|
2
|
+
import { insertEventIntoDescendingList } from "applesauce-core/helpers";
|
|
3
|
+
import Database from "better-sqlite3";
|
|
4
|
+
import { createTables, defaultSearchContentFormatter, deleteEvent, enhancedSearchContentFormatter, getEvent, getEventsByFilters, getReplaceable, getReplaceableHistory, hasEvent, hasReplaceable, insertEvent, rebuildSearchIndex, } from "./helpers/sqlite.js";
|
|
5
|
+
const log = logger.extend("SqliteEventDatabase");
|
|
6
|
+
// Export the search content formatters and types for external use
|
|
7
|
+
export { defaultSearchContentFormatter, enhancedSearchContentFormatter };
|
|
8
|
+
export class SqliteEventDatabase {
|
|
9
|
+
db;
|
|
10
|
+
/** If search is enabled */
|
|
11
|
+
search;
|
|
12
|
+
/** The search content formatter */
|
|
13
|
+
searchContentFormatter;
|
|
14
|
+
constructor(database = ":memory:", options) {
|
|
15
|
+
this.db = typeof database === "string" ? new Database(database) : database;
|
|
16
|
+
this.search = options?.search ?? true;
|
|
17
|
+
this.searchContentFormatter = options?.searchContentFormatter ?? enhancedSearchContentFormatter;
|
|
18
|
+
// Setup the database tables and indexes
|
|
19
|
+
createTables(this.db, this.search);
|
|
20
|
+
}
|
|
21
|
+
/** Store a Nostr event in the database */
|
|
22
|
+
add(event) {
|
|
23
|
+
try {
|
|
24
|
+
insertEvent(this.db, event, this.search ? this.searchContentFormatter : undefined);
|
|
25
|
+
return event;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
log("Error inserting event:", error);
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Delete an event by ID */
|
|
33
|
+
remove(id) {
|
|
34
|
+
try {
|
|
35
|
+
// Remove event from database
|
|
36
|
+
return deleteEvent(this.db, id);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Checks if an event exists */
|
|
43
|
+
hasEvent(id) {
|
|
44
|
+
return hasEvent(this.db, id);
|
|
45
|
+
}
|
|
46
|
+
/** Get an event by its ID */
|
|
47
|
+
getEvent(id) {
|
|
48
|
+
return getEvent(this.db, id);
|
|
49
|
+
}
|
|
50
|
+
/** Get the latest replaceable event For replaceable events (10000-19999), returns the most recent event */
|
|
51
|
+
getReplaceable(kind, pubkey, identifier = "") {
|
|
52
|
+
return getReplaceable(this.db, kind, pubkey, identifier);
|
|
53
|
+
}
|
|
54
|
+
/** Checks if a replaceable event exists */
|
|
55
|
+
hasReplaceable(kind, pubkey, identifier = "") {
|
|
56
|
+
return hasReplaceable(this.db, kind, pubkey, identifier);
|
|
57
|
+
}
|
|
58
|
+
/** Returns all the versions of a replaceable event */
|
|
59
|
+
getReplaceableHistory(kind, pubkey, identifier = "") {
|
|
60
|
+
return getReplaceableHistory(this.db, kind, pubkey, identifier);
|
|
61
|
+
}
|
|
62
|
+
/** Get all events that match the filters (supports NIP-50 search field) */
|
|
63
|
+
getByFilters(filters) {
|
|
64
|
+
try {
|
|
65
|
+
// If search is disabled, remove the search field from the filters
|
|
66
|
+
if (!this.search && (Array.isArray(filters) ? filters.some((f) => "search" in f) : "search" in filters))
|
|
67
|
+
throw new Error("Search is disabled");
|
|
68
|
+
return getEventsByFilters(this.db, filters);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return new Set();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Get a timeline of events that match the filters (returns array in chronological order, supports NIP-50 search) */
|
|
75
|
+
getTimeline(filters) {
|
|
76
|
+
const events = this.getByFilters(filters);
|
|
77
|
+
const timeline = [];
|
|
78
|
+
for (const event of events)
|
|
79
|
+
insertEventIntoDescendingList(timeline, event);
|
|
80
|
+
return timeline;
|
|
81
|
+
}
|
|
82
|
+
/** Set the search content formatter */
|
|
83
|
+
setSearchContentFormatter(formatter) {
|
|
84
|
+
this.searchContentFormatter = formatter;
|
|
85
|
+
}
|
|
86
|
+
/** Get the current search content formatter */
|
|
87
|
+
getSearchContentFormatter() {
|
|
88
|
+
return this.searchContentFormatter;
|
|
89
|
+
}
|
|
90
|
+
/** Rebuild the search index for all events */
|
|
91
|
+
rebuildSearchIndex() {
|
|
92
|
+
if (!this.search)
|
|
93
|
+
throw new Error("Search is disabled");
|
|
94
|
+
rebuildSearchIndex(this.db, this.searchContentFormatter);
|
|
95
|
+
log("Search index rebuilt successfully");
|
|
96
|
+
}
|
|
97
|
+
/** Close the database connection */
|
|
98
|
+
close() {
|
|
99
|
+
log("Closing database connection");
|
|
100
|
+
this.db.close();
|
|
101
|
+
}
|
|
102
|
+
[Symbol.dispose]() {
|
|
103
|
+
this.close();
|
|
104
|
+
}
|
|
105
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "applesauce-sqlite",
|
|
3
|
+
"version": "0.0.0-next-20250915145415",
|
|
4
|
+
"description": "sqlite event databases for applesauce",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"nostr",
|
|
10
|
+
"applesauce"
|
|
11
|
+
],
|
|
12
|
+
"author": "hzrd149",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"require": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts"
|
|
22
|
+
},
|
|
23
|
+
"./helpers": {
|
|
24
|
+
"import": "./dist/helpers/index.js",
|
|
25
|
+
"require": "./dist/helpers/index.js",
|
|
26
|
+
"types": "./dist/helpers/index.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"./helpers/*": {
|
|
29
|
+
"import": "./dist/helpers/*.js",
|
|
30
|
+
"require": "./dist/helpers/*.js",
|
|
31
|
+
"types": "./dist/helpers/*.d.ts"
|
|
32
|
+
},
|
|
33
|
+
"./better-sqlite3": {
|
|
34
|
+
"import": "./dist/better-sqlite3/index.js",
|
|
35
|
+
"require": "./dist/better-sqlite3/index.js",
|
|
36
|
+
"types": "./dist/better-sqlite3/index.d.ts"
|
|
37
|
+
},
|
|
38
|
+
"./better-sqlite3/*": {
|
|
39
|
+
"import": "./dist/better-sqlite3/*.js",
|
|
40
|
+
"require": "./dist/better-sqlite3/*.js",
|
|
41
|
+
"types": "./dist/better-sqlite3/*.d.ts"
|
|
42
|
+
},
|
|
43
|
+
"./native": {
|
|
44
|
+
"import": "./dist/native/index.js",
|
|
45
|
+
"require": "./dist/native/index.js",
|
|
46
|
+
"types": "./dist/native/index.d.ts"
|
|
47
|
+
},
|
|
48
|
+
"./native/*": {
|
|
49
|
+
"import": "./dist/native/*.js",
|
|
50
|
+
"require": "./dist/native/*.js",
|
|
51
|
+
"types": "./dist/native/*.d.ts"
|
|
52
|
+
},
|
|
53
|
+
"./deno": {
|
|
54
|
+
"import": "./dist/native/index.js",
|
|
55
|
+
"require": "./dist/native/index.js",
|
|
56
|
+
"types": "./dist/native/index.d.ts"
|
|
57
|
+
},
|
|
58
|
+
"./deno/*": {
|
|
59
|
+
"import": "./dist/native/*.js",
|
|
60
|
+
"require": "./dist/native/*.js",
|
|
61
|
+
"types": "./dist/native/*.d.ts"
|
|
62
|
+
},
|
|
63
|
+
"./bun": {
|
|
64
|
+
"import": "./dist/bun/index.js",
|
|
65
|
+
"require": "./dist/bun/index.js",
|
|
66
|
+
"types": "./dist/bun/index.d.ts"
|
|
67
|
+
},
|
|
68
|
+
"./bun/*": {
|
|
69
|
+
"import": "./dist/bun/*.js",
|
|
70
|
+
"require": "./dist/bun/*.js",
|
|
71
|
+
"types": "./dist/bun/*.d.ts"
|
|
72
|
+
},
|
|
73
|
+
"./libsql": {
|
|
74
|
+
"import": "./dist/libsql/index.js",
|
|
75
|
+
"require": "./dist/libsql/index.js",
|
|
76
|
+
"types": "./dist/libsql/index.d.ts"
|
|
77
|
+
},
|
|
78
|
+
"./libsql/*": {
|
|
79
|
+
"import": "./dist/libsql/*.js",
|
|
80
|
+
"require": "./dist/libsql/*.js",
|
|
81
|
+
"types": "./dist/libsql/*.d.ts"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"dependencies": {
|
|
85
|
+
"applesauce-core": "0.0.0-next-20250915145415"
|
|
86
|
+
},
|
|
87
|
+
"optionalDependencies": {
|
|
88
|
+
"better-sqlite3": "^12.2.0",
|
|
89
|
+
"@libsql/client": "^0.15.15"
|
|
90
|
+
},
|
|
91
|
+
"devDependencies": {
|
|
92
|
+
"@hirez_io/observer-spy": "^2.2.0",
|
|
93
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
94
|
+
"@types/bun": "^1.2.22",
|
|
95
|
+
"@types/ws": "^8.5.13",
|
|
96
|
+
"applesauce-signers": "0.0.0-next-20250915145415",
|
|
97
|
+
"typescript": "^5.7.3",
|
|
98
|
+
"vitest": "^3.2.4",
|
|
99
|
+
"vitest-websocket-mock": "^0.5.0",
|
|
100
|
+
"ws": "^8.18.3"
|
|
101
|
+
},
|
|
102
|
+
"funding": {
|
|
103
|
+
"type": "lightning",
|
|
104
|
+
"url": "lightning:nostrudel@geyser.fund"
|
|
105
|
+
},
|
|
106
|
+
"scripts": {
|
|
107
|
+
"build": "tsc",
|
|
108
|
+
"watch:build": "tsc --watch > /dev/null",
|
|
109
|
+
"test": "vitest run --passWithNoTests",
|
|
110
|
+
"watch:test": "vitest",
|
|
111
|
+
"relay": "node dist/relay.js"
|
|
112
|
+
}
|
|
113
|
+
}
|