applesauce-core 0.8.0 → 0.10.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/event-store/database.d.ts +17 -14
- package/dist/event-store/database.js +50 -33
- package/dist/event-store/event-store.d.ts +37 -14
- package/dist/event-store/event-store.js +249 -96
- package/dist/helpers/bolt11.d.ts +9 -0
- package/dist/helpers/bolt11.js +15 -0
- package/dist/helpers/cache.js +9 -9
- package/dist/helpers/comment.d.ts +48 -0
- package/dist/helpers/comment.js +116 -0
- package/dist/helpers/content.d.ts +3 -0
- package/dist/helpers/content.js +8 -0
- package/dist/helpers/delete.d.ts +3 -0
- package/dist/helpers/delete.js +7 -0
- package/dist/helpers/emoji.d.ts +10 -1
- package/dist/helpers/emoji.js +12 -0
- package/dist/helpers/event.d.ts +9 -1
- package/dist/helpers/event.js +25 -1
- package/dist/helpers/external-id.d.ts +29 -0
- package/dist/helpers/external-id.js +20 -0
- package/dist/helpers/filter.d.ts +0 -2
- package/dist/helpers/filter.js +3 -7
- package/dist/helpers/hidden-tags.d.ts +48 -0
- package/dist/helpers/hidden-tags.js +108 -0
- package/dist/helpers/hidden-tags.test.d.ts +1 -0
- package/dist/helpers/hidden-tags.test.js +28 -0
- package/dist/helpers/index.d.ts +17 -7
- package/dist/helpers/index.js +17 -7
- package/dist/helpers/json.d.ts +1 -0
- package/dist/helpers/json.js +1 -0
- package/dist/helpers/lnurl.d.ts +4 -0
- package/dist/helpers/lnurl.js +40 -0
- package/dist/helpers/mailboxes.d.ts +0 -6
- package/dist/helpers/mailboxes.js +7 -8
- package/dist/helpers/mailboxes.test.js +13 -12
- package/dist/helpers/media-attachment.d.ts +33 -0
- package/dist/helpers/media-attachment.js +60 -0
- package/dist/helpers/pointers.d.ts +39 -6
- package/dist/helpers/pointers.js +102 -20
- package/dist/helpers/profile.d.ts +2 -7
- package/dist/helpers/profile.js +26 -23
- package/dist/helpers/string.d.ts +6 -0
- package/dist/helpers/string.js +2 -0
- package/dist/helpers/tags.d.ts +6 -0
- package/dist/helpers/tags.js +6 -0
- package/dist/helpers/threading.d.ts +10 -10
- package/dist/helpers/threading.js +52 -19
- package/dist/helpers/threading.test.d.ts +1 -0
- package/dist/helpers/threading.test.js +41 -0
- package/dist/helpers/url.d.ts +4 -1
- package/dist/helpers/url.js +4 -3
- package/dist/helpers/zap.d.ts +39 -0
- package/dist/helpers/zap.js +95 -0
- package/dist/observable/{getValue.d.ts → get-value.d.ts} +1 -0
- package/dist/observable/{getValue.js → get-value.js} +3 -0
- package/dist/observable/index.d.ts +1 -1
- package/dist/observable/index.js +1 -1
- package/dist/promise/deferred.d.ts +1 -0
- package/dist/promise/deferred.js +1 -0
- package/dist/queries/comments.d.ts +4 -0
- package/dist/queries/comments.js +14 -0
- package/dist/queries/index.d.ts +4 -2
- package/dist/queries/index.js +4 -2
- package/dist/queries/mailboxes.d.ts +1 -0
- package/dist/queries/mailboxes.js +1 -0
- package/dist/queries/profile.d.ts +1 -0
- package/dist/queries/profile.js +4 -3
- package/dist/queries/reactions.d.ts +1 -1
- package/dist/queries/reactions.js +1 -1
- package/dist/queries/simple.d.ts +3 -3
- package/dist/queries/simple.js +13 -13
- package/dist/queries/thread.d.ts +2 -0
- package/dist/queries/thread.js +29 -3
- package/dist/queries/zaps.d.ts +5 -0
- package/dist/queries/zaps.js +21 -0
- package/dist/query-store/index.d.ts +22 -12
- package/dist/query-store/index.js +36 -30
- package/package.json +14 -16
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# applesauce-core
|
|
2
2
|
|
|
3
|
-
AppleSauce Core is an interpretation layer for nostr clients, Push events into the in-memory [database](https://hzrd149.github.io/applesauce/classes/Database.html) and get nicely formatted data out with [queries](https://hzrd149.github.io/applesauce/modules/Queries)
|
|
3
|
+
AppleSauce Core is an interpretation layer for nostr clients, Push events into the in-memory [database](https://hzrd149.github.io/applesauce/typedoc/classes/Database.html) and get nicely formatted data out with [queries](https://hzrd149.github.io/applesauce/typedoc/modules/Queries)
|
|
4
4
|
|
|
5
5
|
# Example
|
|
6
6
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/// <reference types="debug" />
|
|
2
1
|
import { Filter, NostrEvent } from "nostr-tools";
|
|
3
2
|
import { Subject } from "rxjs";
|
|
4
3
|
import { LRU } from "../helpers/lru.js";
|
|
5
4
|
/**
|
|
6
5
|
* An in-memory database for nostr events
|
|
6
|
+
* NOTE: does not handle replaceable events
|
|
7
7
|
*/
|
|
8
8
|
export declare class Database {
|
|
9
9
|
protected log: import("debug").Debugger;
|
|
@@ -14,6 +14,7 @@ export declare class Database {
|
|
|
14
14
|
protected created_at: NostrEvent[];
|
|
15
15
|
/** LRU cache of last events touched */
|
|
16
16
|
events: LRU<import("nostr-tools").Event>;
|
|
17
|
+
protected replaceable: Map<string, import("nostr-tools").Event[]>;
|
|
17
18
|
/** A stream of events inserted into the database */
|
|
18
19
|
inserted: Subject<import("nostr-tools").Event>;
|
|
19
20
|
/** A stream of events that have been updated */
|
|
@@ -28,18 +29,20 @@ export declare class Database {
|
|
|
28
29
|
protected getTagIndex(tagAndValue: string): Set<import("nostr-tools").Event>;
|
|
29
30
|
/** Moves an event to the top of the LRU cache */
|
|
30
31
|
touch(event: NostrEvent): void;
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
/** Checks if the database contains an event without touching it */
|
|
33
|
+
hasEvent(id: string): boolean;
|
|
34
|
+
/** Gets a single event based on id */
|
|
35
|
+
getEvent(id: string): NostrEvent | undefined;
|
|
33
36
|
/** Checks if the database contains a replaceable event without touching it */
|
|
34
37
|
hasReplaceable(kind: number, pubkey: string, d?: string): boolean;
|
|
35
|
-
/** Gets
|
|
36
|
-
getReplaceable(kind: number, pubkey: string, d?: string):
|
|
38
|
+
/** Gets an array of replaceable events */
|
|
39
|
+
getReplaceable(kind: number, pubkey: string, d?: string): NostrEvent[] | undefined;
|
|
37
40
|
/** Inserts an event into the database and notifies all subscriptions */
|
|
38
|
-
addEvent(event: NostrEvent):
|
|
41
|
+
addEvent(event: NostrEvent): NostrEvent;
|
|
39
42
|
/** Inserts and event into the database and notifies all subscriptions that the event has updated */
|
|
40
|
-
updateEvent(event: NostrEvent):
|
|
43
|
+
updateEvent(event: NostrEvent): NostrEvent;
|
|
41
44
|
/** Deletes an event from the database and notifies all subscriptions */
|
|
42
|
-
deleteEvent(
|
|
45
|
+
deleteEvent(eventOrId: string | NostrEvent): boolean;
|
|
43
46
|
/** Sets the claim on the event and touches it */
|
|
44
47
|
claimEvent(event: NostrEvent, claim: any): void;
|
|
45
48
|
/** Checks if an event is claimed by anything */
|
|
@@ -48,14 +51,14 @@ export declare class Database {
|
|
|
48
51
|
removeClaim(event: NostrEvent, claim: any): void;
|
|
49
52
|
/** Removes all claims on an event */
|
|
50
53
|
clearClaim(event: NostrEvent): void;
|
|
51
|
-
iterateAuthors(authors: Iterable<string>): Generator<
|
|
52
|
-
iterateTag(tag: string, values: Iterable<string>): Generator<
|
|
53
|
-
iterateKinds(kinds: Iterable<number>): Generator<
|
|
54
|
-
iterateTime(since: number | undefined, until: number | undefined): Generator<
|
|
55
|
-
iterateIds(ids: Iterable<string>): Generator<
|
|
54
|
+
iterateAuthors(authors: Iterable<string>): Generator<NostrEvent>;
|
|
55
|
+
iterateTag(tag: string, values: Iterable<string>): Generator<NostrEvent>;
|
|
56
|
+
iterateKinds(kinds: Iterable<number>): Generator<NostrEvent>;
|
|
57
|
+
iterateTime(since: number | undefined, until: number | undefined): Generator<NostrEvent>;
|
|
58
|
+
iterateIds(ids: Iterable<string>): Generator<NostrEvent>;
|
|
56
59
|
/** Returns all events that match the filter */
|
|
57
60
|
getEventsForFilter(filter: Filter): Set<NostrEvent>;
|
|
58
|
-
getForFilters(filters: Filter[]): Set<
|
|
61
|
+
getForFilters(filters: Filter[]): Set<NostrEvent>;
|
|
59
62
|
/** Remove the oldest events that are not claimed */
|
|
60
63
|
prune(limit?: number): number;
|
|
61
64
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { binarySearch, insertEventIntoDescendingList } from "nostr-tools/utils";
|
|
2
2
|
import { Subject } from "rxjs";
|
|
3
|
-
import { FromCacheSymbol, getEventUID, getIndexableTags, getReplaceableUID } from "../helpers/event.js";
|
|
3
|
+
import { FromCacheSymbol, getEventUID, getIndexableTags, getReplaceableUID, isReplaceable } from "../helpers/event.js";
|
|
4
4
|
import { INDEXABLE_TAGS } from "./common.js";
|
|
5
5
|
import { logger } from "../logger.js";
|
|
6
6
|
import { LRU } from "../helpers/lru.js";
|
|
7
7
|
/**
|
|
8
8
|
* An in-memory database for nostr events
|
|
9
|
+
* NOTE: does not handle replaceable events
|
|
9
10
|
*/
|
|
10
11
|
export class Database {
|
|
11
12
|
log = logger.extend("Database");
|
|
@@ -16,6 +17,7 @@ export class Database {
|
|
|
16
17
|
created_at = [];
|
|
17
18
|
/** LRU cache of last events touched */
|
|
18
19
|
events = new LRU();
|
|
20
|
+
replaceable = new Map();
|
|
19
21
|
/** A stream of events inserted into the database */
|
|
20
22
|
inserted = new Subject();
|
|
21
23
|
/** A stream of events that have been updated */
|
|
@@ -56,35 +58,36 @@ export class Database {
|
|
|
56
58
|
}
|
|
57
59
|
/** Moves an event to the top of the LRU cache */
|
|
58
60
|
touch(event) {
|
|
59
|
-
this.events.set(
|
|
61
|
+
this.events.set(event.id, event);
|
|
60
62
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
/** Checks if the database contains an event without touching it */
|
|
64
|
+
hasEvent(id) {
|
|
65
|
+
return this.events.has(id);
|
|
63
66
|
}
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
/** Gets a single event based on id */
|
|
68
|
+
getEvent(id) {
|
|
69
|
+
return this.events.get(id);
|
|
66
70
|
}
|
|
67
71
|
/** Checks if the database contains a replaceable event without touching it */
|
|
68
72
|
hasReplaceable(kind, pubkey, d) {
|
|
69
|
-
|
|
73
|
+
const events = this.replaceable.get(getReplaceableUID(kind, pubkey, d));
|
|
74
|
+
return !!events && events.length > 0;
|
|
70
75
|
}
|
|
71
|
-
/** Gets
|
|
76
|
+
/** Gets an array of replaceable events */
|
|
72
77
|
getReplaceable(kind, pubkey, d) {
|
|
73
|
-
return this.
|
|
78
|
+
return this.replaceable.get(getReplaceableUID(kind, pubkey, d));
|
|
74
79
|
}
|
|
75
80
|
/** Inserts an event into the database and notifies all subscriptions */
|
|
76
81
|
addEvent(event) {
|
|
77
|
-
const
|
|
78
|
-
const current = this.events.get(
|
|
79
|
-
if (current
|
|
82
|
+
const id = event.id;
|
|
83
|
+
const current = this.events.get(id);
|
|
84
|
+
if (current) {
|
|
80
85
|
// if this is a duplicate event, transfer some import symbols
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
current[FromCacheSymbol] = event[FromCacheSymbol];
|
|
84
|
-
}
|
|
86
|
+
if (event[FromCacheSymbol])
|
|
87
|
+
current[FromCacheSymbol] = event[FromCacheSymbol];
|
|
85
88
|
return current;
|
|
86
89
|
}
|
|
87
|
-
this.events.set(
|
|
90
|
+
this.events.set(id, event);
|
|
88
91
|
this.getKindIndex(event.kind).add(event);
|
|
89
92
|
this.getAuthorsIndex(event.pubkey).add(event);
|
|
90
93
|
for (const tag of getIndexableTags(event)) {
|
|
@@ -92,7 +95,18 @@ export class Database {
|
|
|
92
95
|
this.getTagIndex(tag).add(event);
|
|
93
96
|
}
|
|
94
97
|
}
|
|
98
|
+
// insert into time index
|
|
95
99
|
insertEventIntoDescendingList(this.created_at, event);
|
|
100
|
+
// insert into replaceable index
|
|
101
|
+
if (isReplaceable(event.kind)) {
|
|
102
|
+
const uid = getEventUID(event);
|
|
103
|
+
let array = this.replaceable.get(uid);
|
|
104
|
+
if (!this.replaceable.has(uid)) {
|
|
105
|
+
array = [];
|
|
106
|
+
this.replaceable.set(uid, array);
|
|
107
|
+
}
|
|
108
|
+
insertEventIntoDescendingList(array, event);
|
|
109
|
+
}
|
|
96
110
|
this.inserted.next(event);
|
|
97
111
|
return event;
|
|
98
112
|
}
|
|
@@ -103,13 +117,13 @@ export class Database {
|
|
|
103
117
|
return inserted;
|
|
104
118
|
}
|
|
105
119
|
/** Deletes an event from the database and notifies all subscriptions */
|
|
106
|
-
deleteEvent(
|
|
107
|
-
let event = typeof
|
|
120
|
+
deleteEvent(eventOrId) {
|
|
121
|
+
let event = typeof eventOrId === "string" ? this.events.get(eventOrId) : eventOrId;
|
|
108
122
|
if (!event)
|
|
109
123
|
throw new Error("Missing event");
|
|
110
|
-
const
|
|
124
|
+
const id = event.id;
|
|
111
125
|
// only remove events that are known
|
|
112
|
-
if (!this.events.has(
|
|
126
|
+
if (!this.events.has(id))
|
|
113
127
|
return false;
|
|
114
128
|
this.getAuthorsIndex(event.pubkey).delete(event);
|
|
115
129
|
this.getKindIndex(event.kind).delete(event);
|
|
@@ -121,7 +135,16 @@ export class Database {
|
|
|
121
135
|
// remove from created_at index
|
|
122
136
|
const i = this.created_at.indexOf(event);
|
|
123
137
|
this.created_at.splice(i, 1);
|
|
124
|
-
this.events.delete(
|
|
138
|
+
this.events.delete(id);
|
|
139
|
+
// remove from replaceable index
|
|
140
|
+
if (isReplaceable(event.kind)) {
|
|
141
|
+
const uid = getEventUID(event);
|
|
142
|
+
const array = this.replaceable.get(uid);
|
|
143
|
+
if (array && array.includes(event)) {
|
|
144
|
+
const idx = array.indexOf(event);
|
|
145
|
+
array.splice(idx, 1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
125
148
|
this.deleted.next(event);
|
|
126
149
|
return true;
|
|
127
150
|
}
|
|
@@ -179,27 +202,21 @@ export class Database {
|
|
|
179
202
|
let sinceIndex = this.created_at.length - 1;
|
|
180
203
|
let start = until
|
|
181
204
|
? binarySearch(this.created_at, (mid) => {
|
|
182
|
-
if (mid.created_at === until)
|
|
183
|
-
return -1;
|
|
184
205
|
return mid.created_at - until;
|
|
185
206
|
})
|
|
186
207
|
: undefined;
|
|
187
|
-
if (start
|
|
208
|
+
if (start)
|
|
188
209
|
untilIndex = start[0];
|
|
189
210
|
const end = since
|
|
190
211
|
? binarySearch(this.created_at, (mid) => {
|
|
191
|
-
|
|
192
|
-
return 1;
|
|
193
|
-
return since - mid.created_at;
|
|
212
|
+
return mid.created_at - since;
|
|
194
213
|
})
|
|
195
214
|
: undefined;
|
|
196
|
-
if (end
|
|
215
|
+
if (end)
|
|
197
216
|
sinceIndex = end[0];
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
events.add(this.created_at[i]);
|
|
217
|
+
for (let i = untilIndex; i < sinceIndex; i++) {
|
|
218
|
+
yield this.created_at[i];
|
|
201
219
|
}
|
|
202
|
-
return events;
|
|
203
220
|
}
|
|
204
221
|
*iterateIds(ids) {
|
|
205
222
|
for (const id of ids) {
|
|
@@ -3,24 +3,47 @@ import { Observable } from "rxjs";
|
|
|
3
3
|
import { Database } from "./database.js";
|
|
4
4
|
export declare class EventStore {
|
|
5
5
|
database: Database;
|
|
6
|
+
/** Enable this to keep old versions of replaceable events */
|
|
7
|
+
keepOldVersions: boolean;
|
|
6
8
|
constructor();
|
|
7
|
-
/** Adds an event to the database */
|
|
8
|
-
add(event: NostrEvent, fromRelay?: string):
|
|
9
|
+
/** Adds an event to the database and update subscriptions */
|
|
10
|
+
add(event: NostrEvent, fromRelay?: string): NostrEvent;
|
|
11
|
+
/** Removes an event from the database and updates subscriptions */
|
|
12
|
+
remove(event: string | NostrEvent): boolean;
|
|
13
|
+
protected deletedIds: Set<string>;
|
|
14
|
+
protected deletedCoords: Map<string, number>;
|
|
15
|
+
protected handleDeleteEvent(deleteEvent: NostrEvent): void;
|
|
16
|
+
protected checkDeleted(event: NostrEvent): boolean;
|
|
17
|
+
/** Removes any event that is not being used by a subscription */
|
|
18
|
+
prune(max?: number): number;
|
|
9
19
|
/** Add an event to the store and notifies all subscribes it has updated */
|
|
10
|
-
update(event: NostrEvent):
|
|
11
|
-
getAll(filters: Filter[]): Set<
|
|
12
|
-
hasEvent(uid: string):
|
|
13
|
-
getEvent(uid: string):
|
|
20
|
+
update(event: NostrEvent): NostrEvent;
|
|
21
|
+
getAll(filters: Filter[]): Set<NostrEvent>;
|
|
22
|
+
hasEvent(uid: string): boolean;
|
|
23
|
+
getEvent(uid: string): NostrEvent | undefined;
|
|
14
24
|
hasReplaceable(kind: number, pubkey: string, d?: string): boolean;
|
|
15
|
-
|
|
25
|
+
/** Gets the latest version of a replaceable event */
|
|
26
|
+
getReplaceable(kind: number, pubkey: string, d?: string): NostrEvent | undefined;
|
|
27
|
+
/** Returns all versions of a replaceable event */
|
|
28
|
+
getReplaceableHistory(kind: number, pubkey: string, d?: string): NostrEvent[] | undefined;
|
|
16
29
|
/** Creates an observable that updates a single event */
|
|
17
|
-
event(
|
|
30
|
+
event(id: string): Observable<NostrEvent | undefined>;
|
|
18
31
|
/** Creates an observable that subscribes to multiple events */
|
|
19
|
-
events(
|
|
20
|
-
/** Creates an observable
|
|
21
|
-
replaceable(kind: number, pubkey: string, d?: string): Observable<
|
|
22
|
-
/** Creates an observable
|
|
23
|
-
|
|
32
|
+
events(ids: string[]): Observable<Map<string, NostrEvent>>;
|
|
33
|
+
/** Creates an observable with the latest version of a replaceable event */
|
|
34
|
+
replaceable(kind: number, pubkey: string, d?: string): Observable<NostrEvent | undefined>;
|
|
35
|
+
/** Creates an observable with the latest versions of replaceable events */
|
|
36
|
+
replaceableSet(pointers: {
|
|
37
|
+
kind: number;
|
|
38
|
+
pubkey: string;
|
|
39
|
+
identifier?: string;
|
|
40
|
+
}[]): Observable<Map<string, NostrEvent>>;
|
|
41
|
+
/**
|
|
42
|
+
* Creates an observable that streams all events that match the filter
|
|
43
|
+
* @param filters
|
|
44
|
+
* @param [onlyNew=false] Only subscribe to new events
|
|
45
|
+
*/
|
|
46
|
+
stream(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent>;
|
|
24
47
|
/** Creates an observable that updates with an array of sorted events */
|
|
25
|
-
timeline(filters: Filter[]): Observable<
|
|
48
|
+
timeline(filters: Filter | Filter[], keepOldVersions?: boolean): Observable<NostrEvent[]>;
|
|
26
49
|
}
|