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.
Files changed (78) hide show
  1. package/README.md +1 -1
  2. package/dist/event-store/database.d.ts +17 -14
  3. package/dist/event-store/database.js +50 -33
  4. package/dist/event-store/event-store.d.ts +37 -14
  5. package/dist/event-store/event-store.js +249 -96
  6. package/dist/helpers/bolt11.d.ts +9 -0
  7. package/dist/helpers/bolt11.js +15 -0
  8. package/dist/helpers/cache.js +9 -9
  9. package/dist/helpers/comment.d.ts +48 -0
  10. package/dist/helpers/comment.js +116 -0
  11. package/dist/helpers/content.d.ts +3 -0
  12. package/dist/helpers/content.js +8 -0
  13. package/dist/helpers/delete.d.ts +3 -0
  14. package/dist/helpers/delete.js +7 -0
  15. package/dist/helpers/emoji.d.ts +10 -1
  16. package/dist/helpers/emoji.js +12 -0
  17. package/dist/helpers/event.d.ts +9 -1
  18. package/dist/helpers/event.js +25 -1
  19. package/dist/helpers/external-id.d.ts +29 -0
  20. package/dist/helpers/external-id.js +20 -0
  21. package/dist/helpers/filter.d.ts +0 -2
  22. package/dist/helpers/filter.js +3 -7
  23. package/dist/helpers/hidden-tags.d.ts +48 -0
  24. package/dist/helpers/hidden-tags.js +108 -0
  25. package/dist/helpers/hidden-tags.test.d.ts +1 -0
  26. package/dist/helpers/hidden-tags.test.js +28 -0
  27. package/dist/helpers/index.d.ts +17 -7
  28. package/dist/helpers/index.js +17 -7
  29. package/dist/helpers/json.d.ts +1 -0
  30. package/dist/helpers/json.js +1 -0
  31. package/dist/helpers/lnurl.d.ts +4 -0
  32. package/dist/helpers/lnurl.js +40 -0
  33. package/dist/helpers/mailboxes.d.ts +0 -6
  34. package/dist/helpers/mailboxes.js +7 -8
  35. package/dist/helpers/mailboxes.test.js +13 -12
  36. package/dist/helpers/media-attachment.d.ts +33 -0
  37. package/dist/helpers/media-attachment.js +60 -0
  38. package/dist/helpers/pointers.d.ts +39 -6
  39. package/dist/helpers/pointers.js +102 -20
  40. package/dist/helpers/profile.d.ts +2 -7
  41. package/dist/helpers/profile.js +26 -23
  42. package/dist/helpers/string.d.ts +6 -0
  43. package/dist/helpers/string.js +2 -0
  44. package/dist/helpers/tags.d.ts +6 -0
  45. package/dist/helpers/tags.js +6 -0
  46. package/dist/helpers/threading.d.ts +10 -10
  47. package/dist/helpers/threading.js +52 -19
  48. package/dist/helpers/threading.test.d.ts +1 -0
  49. package/dist/helpers/threading.test.js +41 -0
  50. package/dist/helpers/url.d.ts +4 -1
  51. package/dist/helpers/url.js +4 -3
  52. package/dist/helpers/zap.d.ts +39 -0
  53. package/dist/helpers/zap.js +95 -0
  54. package/dist/observable/{getValue.d.ts → get-value.d.ts} +1 -0
  55. package/dist/observable/{getValue.js → get-value.js} +3 -0
  56. package/dist/observable/index.d.ts +1 -1
  57. package/dist/observable/index.js +1 -1
  58. package/dist/promise/deferred.d.ts +1 -0
  59. package/dist/promise/deferred.js +1 -0
  60. package/dist/queries/comments.d.ts +4 -0
  61. package/dist/queries/comments.js +14 -0
  62. package/dist/queries/index.d.ts +4 -2
  63. package/dist/queries/index.js +4 -2
  64. package/dist/queries/mailboxes.d.ts +1 -0
  65. package/dist/queries/mailboxes.js +1 -0
  66. package/dist/queries/profile.d.ts +1 -0
  67. package/dist/queries/profile.js +4 -3
  68. package/dist/queries/reactions.d.ts +1 -1
  69. package/dist/queries/reactions.js +1 -1
  70. package/dist/queries/simple.d.ts +3 -3
  71. package/dist/queries/simple.js +13 -13
  72. package/dist/queries/thread.d.ts +2 -0
  73. package/dist/queries/thread.js +29 -3
  74. package/dist/queries/zaps.d.ts +5 -0
  75. package/dist/queries/zaps.js +21 -0
  76. package/dist/query-store/index.d.ts +22 -12
  77. package/dist/query-store/index.js +36 -30
  78. 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
- hasEvent(uid: string): import("nostr-tools").Event | undefined;
32
- getEvent(uid: string): import("nostr-tools").Event | undefined;
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 a replaceable event and touches it */
36
- getReplaceable(kind: number, pubkey: string, d?: string): import("nostr-tools").Event | undefined;
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): import("nostr-tools").Event;
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): import("nostr-tools").Event;
43
+ updateEvent(event: NostrEvent): NostrEvent;
41
44
  /** Deletes an event from the database and notifies all subscriptions */
42
- deleteEvent(eventOrUID: string | NostrEvent): boolean;
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<import("nostr-tools").Event, void, unknown>;
52
- iterateTag(tag: string, values: Iterable<string>): Generator<import("nostr-tools").Event, void, unknown>;
53
- iterateKinds(kinds: Iterable<number>): Generator<import("nostr-tools").Event, void, unknown>;
54
- iterateTime(since: number | undefined, until: number | undefined): Generator<never, Set<import("nostr-tools").Event>, unknown>;
55
- iterateIds(ids: Iterable<string>): Generator<import("nostr-tools").Event, void, unknown>;
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<import("nostr-tools").Event>;
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(getEventUID(event), event);
61
+ this.events.set(event.id, event);
60
62
  }
61
- hasEvent(uid) {
62
- return this.events.get(uid);
63
+ /** Checks if the database contains an event without touching it */
64
+ hasEvent(id) {
65
+ return this.events.has(id);
63
66
  }
64
- getEvent(uid) {
65
- return this.events.get(uid);
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
- return this.events.has(getReplaceableUID(kind, pubkey, d));
73
+ const events = this.replaceable.get(getReplaceableUID(kind, pubkey, d));
74
+ return !!events && events.length > 0;
70
75
  }
71
- /** Gets a replaceable event and touches it */
76
+ /** Gets an array of replaceable events */
72
77
  getReplaceable(kind, pubkey, d) {
73
- return this.events.get(getReplaceableUID(kind, pubkey, d));
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 uid = getEventUID(event);
78
- const current = this.events.get(uid);
79
- if (current && event.created_at <= current.created_at) {
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 (current.id === event.id) {
82
- if (event[FromCacheSymbol])
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(uid, event);
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(eventOrUID) {
107
- let event = typeof eventOrUID === "string" ? this.events.get(eventOrUID) : eventOrUID;
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 uid = getEventUID(event);
124
+ const id = event.id;
111
125
  // only remove events that are known
112
- if (!this.events.has(uid))
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(uid);
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 && start[1])
208
+ if (start)
188
209
  untilIndex = start[0];
189
210
  const end = since
190
211
  ? binarySearch(this.created_at, (mid) => {
191
- if (mid.created_at === since)
192
- return 1;
193
- return since - mid.created_at;
212
+ return mid.created_at - since;
194
213
  })
195
214
  : undefined;
196
- if (end && end[1])
215
+ if (end)
197
216
  sinceIndex = end[0];
198
- const events = new Set();
199
- for (let i = untilIndex; i <= sinceIndex; i++) {
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): import("nostr-tools").Event;
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): import("nostr-tools").Event;
11
- getAll(filters: Filter[]): Set<import("nostr-tools").Event>;
12
- hasEvent(uid: string): import("nostr-tools").Event | undefined;
13
- getEvent(uid: string): import("nostr-tools").Event | undefined;
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
- getReplaceable(kind: number, pubkey: string, d?: string): import("nostr-tools").Event | undefined;
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(uid: string): Observable<import("nostr-tools").Event | undefined>;
30
+ event(id: string): Observable<NostrEvent | undefined>;
18
31
  /** Creates an observable that subscribes to multiple events */
19
- events(uids: string[]): Observable<Map<string, import("nostr-tools").Event>>;
20
- /** Creates an observable that updates a single replaceable event */
21
- replaceable(kind: number, pubkey: string, d?: string): Observable<import("nostr-tools").Event | undefined>;
22
- /** Creates an observable that streams all events that match the filter */
23
- stream(filters: Filter[]): Observable<import("nostr-tools").Event>;
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<import("nostr-tools").Event[]>;
48
+ timeline(filters: Filter | Filter[], keepOldVersions?: boolean): Observable<NostrEvent[]>;
26
49
  }