applesauce-core 3.1.0 → 4.1.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 (88) hide show
  1. package/dist/event-store/async-event-store.d.ts +136 -0
  2. package/dist/event-store/async-event-store.js +364 -0
  3. package/dist/event-store/{event-set.d.ts → event-memory.d.ts} +17 -25
  4. package/dist/event-store/{event-set.js → event-memory.js} +54 -53
  5. package/dist/event-store/event-store.d.ts +59 -63
  6. package/dist/event-store/event-store.js +126 -190
  7. package/dist/event-store/index.d.ts +2 -1
  8. package/dist/event-store/index.js +2 -1
  9. package/dist/event-store/interface.d.ts +115 -38
  10. package/dist/event-store/model-mixin.d.ts +59 -0
  11. package/dist/event-store/model-mixin.js +147 -0
  12. package/dist/helpers/app-data.d.ts +39 -0
  13. package/dist/helpers/app-data.js +68 -0
  14. package/dist/helpers/bookmarks.d.ts +11 -1
  15. package/dist/helpers/bookmarks.js +29 -4
  16. package/dist/helpers/comment.d.ts +13 -20
  17. package/dist/helpers/comment.js +16 -27
  18. package/dist/helpers/contacts.d.ts +10 -1
  19. package/dist/helpers/contacts.js +30 -3
  20. package/dist/helpers/encrypted-content-cache.js +7 -7
  21. package/dist/helpers/encrypted-content.d.ts +9 -2
  22. package/dist/helpers/encrypted-content.js +12 -9
  23. package/dist/helpers/event-cache.d.ts +3 -1
  24. package/dist/helpers/event-cache.js +3 -1
  25. package/dist/helpers/event-tags.d.ts +6 -0
  26. package/dist/helpers/event-tags.js +4 -0
  27. package/dist/helpers/event.d.ts +8 -1
  28. package/dist/helpers/event.js +6 -0
  29. package/dist/helpers/file-metadata.d.ts +4 -9
  30. package/dist/helpers/file-metadata.js +2 -10
  31. package/dist/helpers/filter.d.ts +4 -3
  32. package/dist/helpers/filter.js +3 -3
  33. package/dist/helpers/gift-wraps.d.ts +35 -14
  34. package/dist/helpers/gift-wraps.js +59 -50
  35. package/dist/helpers/groups.d.ts +2 -5
  36. package/dist/helpers/groups.js +2 -5
  37. package/dist/helpers/hidden-content.d.ts +14 -5
  38. package/dist/helpers/hidden-content.js +19 -8
  39. package/dist/helpers/hidden-tags.d.ts +16 -7
  40. package/dist/helpers/hidden-tags.js +47 -26
  41. package/dist/helpers/index.d.ts +1 -0
  42. package/dist/helpers/index.js +1 -0
  43. package/dist/helpers/legacy-messages.d.ts +17 -13
  44. package/dist/helpers/legacy-messages.js +21 -19
  45. package/dist/helpers/lists.js +2 -1
  46. package/dist/helpers/lnurl.d.ts +4 -0
  47. package/dist/helpers/lnurl.js +7 -3
  48. package/dist/helpers/mailboxes.d.ts +2 -6
  49. package/dist/helpers/mailboxes.js +26 -20
  50. package/dist/helpers/mutes.d.ts +11 -1
  51. package/dist/helpers/mutes.js +30 -5
  52. package/dist/helpers/picture-post.d.ts +2 -1
  53. package/dist/helpers/pointers.d.ts +3 -1
  54. package/dist/helpers/pointers.js +4 -1
  55. package/dist/helpers/profile.d.ts +7 -3
  56. package/dist/helpers/profile.js +7 -8
  57. package/dist/helpers/relay-selection.d.ts +17 -0
  58. package/dist/helpers/relay-selection.js +102 -0
  59. package/dist/helpers/relays.d.ts +3 -1
  60. package/dist/helpers/relays.js +18 -2
  61. package/dist/helpers/url.js +3 -3
  62. package/dist/helpers/wrapped-messages.d.ts +5 -3
  63. package/dist/helpers/wrapped-messages.js +5 -3
  64. package/dist/helpers/zap.d.ts +18 -14
  65. package/dist/helpers/zap.js +26 -28
  66. package/dist/models/common.d.ts +4 -4
  67. package/dist/models/common.js +79 -40
  68. package/dist/models/gift-wrap.d.ts +1 -1
  69. package/dist/models/gift-wrap.js +4 -4
  70. package/dist/models/index.d.ts +1 -0
  71. package/dist/models/index.js +1 -0
  72. package/dist/models/legacy-messages.d.ts +2 -2
  73. package/dist/models/legacy-messages.js +13 -10
  74. package/dist/models/outbox.d.ts +13 -0
  75. package/dist/models/outbox.js +18 -0
  76. package/dist/models/profile.d.ts +1 -1
  77. package/dist/models/zaps.d.ts +5 -4
  78. package/dist/models/zaps.js +2 -2
  79. package/dist/observable/index.d.ts +4 -3
  80. package/dist/observable/index.js +5 -4
  81. package/dist/observable/map-events-to-store.d.ts +5 -3
  82. package/dist/observable/map-events-to-store.js +14 -3
  83. package/dist/observable/map-events-to-timeline.js +12 -0
  84. package/dist/observable/relay-selection.d.ts +9 -0
  85. package/dist/observable/relay-selection.js +43 -0
  86. package/package.json +5 -3
  87. package/dist/observable/map-events-timeline.js +0 -9
  88. /package/dist/observable/{map-events-timeline.d.ts → map-events-to-timeline.d.ts} +0 -0
@@ -1,15 +1,11 @@
1
1
  import { binarySearch, insertEventIntoDescendingList } from "nostr-tools/utils";
2
- import { Subject } from "rxjs";
3
2
  import { getIndexableTags, INDEXABLE_TAGS } from "../helpers/event-tags.js";
4
3
  import { createReplaceableAddress, isReplaceable } from "../helpers/event.js";
5
4
  import { LRU } from "../helpers/lru.js";
6
5
  import { logger } from "../logger.js";
7
- /**
8
- * A set of nostr events that can be queried and subscribed to
9
- * NOTE: does not handle replaceable events or any deletion logic
10
- */
11
- export class EventSet {
12
- log = logger.extend("EventSet");
6
+ /** An in-memory database of events */
7
+ export class EventMemory {
8
+ log = logger.extend("EventMemory");
13
9
  /** Indexes */
14
10
  kinds = new Map();
15
11
  authors = new Map();
@@ -19,22 +15,10 @@ export class EventSet {
19
15
  events = new LRU();
20
16
  /** A sorted array of replaceable events by address */
21
17
  replaceable = new Map();
22
- /** A stream of events inserted into the database */
23
- insert$ = new Subject();
24
- /** A stream of events that have been updated */
25
- update$ = new Subject();
26
- /** A stream of events removed from the database */
27
- remove$ = new Subject();
28
- /** A method thats called before a new event is inserted */
29
- onBeforeInsert;
30
18
  /** The number of events in the event set */
31
19
  get size() {
32
20
  return this.events.size;
33
21
  }
34
- /** Moves an event to the top of the LRU cache */
35
- touch(event) {
36
- this.events.set(event.id, event);
37
- }
38
22
  /** Checks if the database contains an event without touching it */
39
23
  hasEvent(id) {
40
24
  return this.events.has(id);
@@ -61,12 +45,12 @@ export class EventSet {
61
45
  }
62
46
  /** Gets all events that match the filters */
63
47
  getByFilters(filters) {
64
- return this.getEventsForFilters(Array.isArray(filters) ? filters : [filters]);
48
+ return Array.from(this.getEventsForFilters(Array.isArray(filters) ? filters : [filters]));
65
49
  }
66
50
  /** Gets a timeline of events that match the filters */
67
51
  getTimeline(filters) {
68
52
  const timeline = [];
69
- const events = this.getEventsForFilters(Array.isArray(filters) ? filters : [filters]);
53
+ const events = this.getByFilters(filters);
70
54
  for (const event of events)
71
55
  insertEventIntoDescendingList(timeline, event);
72
56
  return timeline;
@@ -77,9 +61,6 @@ export class EventSet {
77
61
  const current = this.events.get(id);
78
62
  if (current)
79
63
  return current;
80
- // Ignore events if before insert returns false
81
- if (this.onBeforeInsert?.(event) === false)
82
- return null;
83
64
  this.events.set(id, event);
84
65
  this.getKindIndex(event.kind).add(event);
85
66
  this.getAuthorsIndex(event.pubkey).add(event);
@@ -103,22 +84,13 @@ export class EventSet {
103
84
  // insert the event into the sorted array
104
85
  insertEventIntoDescendingList(array, event);
105
86
  }
106
- // Notify subscribers that the event was inserted
107
- this.insert$.next(event);
108
87
  return event;
109
88
  }
110
- /** Inserts and event into the database and notifies all subscriptions that the event has updated */
111
- update(event) {
112
- const inserted = this.add(event);
113
- if (inserted)
114
- this.update$.next(inserted);
115
- return inserted !== null;
116
- }
117
89
  /** Removes an event from the database and notifies all subscriptions */
118
90
  remove(eventOrId) {
119
91
  let event = typeof eventOrId === "string" ? this.events.get(eventOrId) : eventOrId;
120
92
  if (!event)
121
- throw new Error("Missing event");
93
+ return false;
122
94
  const id = event.id;
123
95
  // only remove events that are known
124
96
  if (!this.events.has(id))
@@ -146,12 +118,33 @@ export class EventSet {
146
118
  }
147
119
  // remove any claims this event has
148
120
  this.claims.delete(event);
149
- // notify subscribers this event was removed
150
- this.remove$.next(event);
151
121
  return true;
152
122
  }
123
+ /** Remove multiple events that match the given filters */
124
+ removeByFilters(filters) {
125
+ const eventsToRemove = this.getByFilters(filters);
126
+ let removedCount = 0;
127
+ for (const event of eventsToRemove) {
128
+ if (this.remove(event)) {
129
+ removedCount++;
130
+ }
131
+ }
132
+ return removedCount;
133
+ }
134
+ /** Notify the database that an event has updated */
135
+ update(_event) {
136
+ // Do nothing
137
+ }
153
138
  /** A weak map of events that are claimed by other things */
154
139
  claims = new WeakMap();
140
+ /** Moves an event to the top of the LRU cache */
141
+ touch(event) {
142
+ // Make sure the event is in the database before adding it to the LRU
143
+ if (!this.events.has(event.id))
144
+ return;
145
+ // Move to the top of the LRU
146
+ this.events.set(event.id, event);
147
+ }
155
148
  /** Sets the claim on the event and touches it */
156
149
  claim(event, claim) {
157
150
  if (!this.claims.has(event)) {
@@ -174,6 +167,30 @@ export class EventSet {
174
167
  clearClaim(event) {
175
168
  this.claims.delete(event);
176
169
  }
170
+ /** Returns a generator of unclaimed events in order of least used */
171
+ *unclaimed() {
172
+ let removed = 0;
173
+ let cursor = this.events.first;
174
+ while (cursor) {
175
+ const event = cursor.value;
176
+ if (!this.isClaimed(event))
177
+ yield event;
178
+ cursor = cursor.next;
179
+ }
180
+ return removed;
181
+ }
182
+ /** Removes events that are not claimed (free up memory) */
183
+ prune(limit) {
184
+ let removed = 0;
185
+ const unclaimed = this.unclaimed();
186
+ for (const event of unclaimed) {
187
+ this.remove(event);
188
+ removed++;
189
+ if (limit && removed >= limit)
190
+ break;
191
+ }
192
+ return removed;
193
+ }
177
194
  /** Index helper methods */
178
195
  getKindIndex(kind) {
179
196
  if (!this.kinds.has(kind))
@@ -321,7 +338,7 @@ export class EventSet {
321
338
  /** Returns all events that match the filters */
322
339
  getEventsForFilters(filters) {
323
340
  if (filters.length === 0)
324
- throw new Error("No Filters");
341
+ return new Set();
325
342
  let events = new Set();
326
343
  for (const filter of filters) {
327
344
  const filtered = this.getEventsForFilter(filter);
@@ -330,22 +347,6 @@ export class EventSet {
330
347
  }
331
348
  return events;
332
349
  }
333
- /** Remove the oldest events that are not claimed */
334
- prune(limit = 1000) {
335
- let removed = 0;
336
- let cursor = this.events.first;
337
- while (cursor) {
338
- const event = cursor.value;
339
- if (!this.isClaimed(event)) {
340
- this.remove(event);
341
- removed++;
342
- if (removed >= limit)
343
- break;
344
- }
345
- cursor = cursor.next;
346
- }
347
- return removed;
348
- }
349
350
  /** Resets the event set */
350
351
  reset() {
351
352
  this.events.clear();
@@ -1,12 +1,47 @@
1
1
  import { Filter, NostrEvent } from "nostr-tools";
2
- import { Observable } from "rxjs";
3
- import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19";
2
+ import { AddressPointer, EventPointer } from "nostr-tools/nip19";
3
+ import { Observable, Subject } from "rxjs";
4
4
  import { AddressPointerWithoutD } from "../helpers/pointers.js";
5
- import { EventSet } from "./event-set.js";
6
- import { IEventStore, ModelConstructor } from "./interface.js";
7
- /** An extended {@link EventSet} that handles replaceable events, delets, and models */
8
- export declare class EventStore implements IEventStore {
9
- database: EventSet;
5
+ import { EventMemory } from "./event-memory.js";
6
+ import { IEventDatabase, IEventStore } from "./interface.js";
7
+ declare const EventStore_base: {
8
+ new (...args: any[]): {
9
+ [x: string]: any;
10
+ models: Map<import("./interface.js").ModelConstructor<any, any[], IEventStore | import("./interface.js").IAsyncEventStore>, Map<string, Observable<any>>>;
11
+ modelKeepWarm: number;
12
+ model<T extends unknown, Args extends Array<any>>(constructor: import("./interface.js").ModelConstructor<T, Args, IEventStore | import("./interface.js").IAsyncEventStore>, ...args: Args): Observable<T>;
13
+ filters(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent>;
14
+ event(pointer: string | EventPointer): Observable<NostrEvent | undefined>;
15
+ replaceable(pointer: AddressPointer | AddressPointerWithoutD): Observable<NostrEvent | undefined>;
16
+ replaceable(kind: number, pubkey: string, identifier?: string): Observable<NostrEvent | undefined>;
17
+ addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
18
+ timeline(filters: Filter | Filter[], includeOldVersion?: boolean): Observable<NostrEvent[]>;
19
+ profile(user: string | import("nostr-tools/nip19").ProfilePointer): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
20
+ contacts(user: string | import("nostr-tools/nip19").ProfilePointer): Observable<import("nostr-tools/nip19").ProfilePointer[]>;
21
+ mutes(user: string | import("nostr-tools/nip19").ProfilePointer): Observable<import("../helpers/mutes.js").Mutes | undefined>;
22
+ mailboxes(user: string | import("nostr-tools/nip19").ProfilePointer): Observable<{
23
+ inboxes: string[];
24
+ outboxes: string[];
25
+ } | undefined>;
26
+ blossomServers(user: string | import("nostr-tools/nip19").ProfilePointer): Observable<URL[]>;
27
+ reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
28
+ thread(root: string | EventPointer | AddressPointer): Observable<import("../models/thread.js").Thread>;
29
+ comments(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
30
+ events(ids: string[]): Observable<Record<string, NostrEvent | undefined>>;
31
+ replaceableSet(pointers: {
32
+ kind: number;
33
+ pubkey: string;
34
+ identifier?: string;
35
+ }[]): Observable<Record<string, NostrEvent | undefined>>;
36
+ };
37
+ } & {
38
+ new (): {};
39
+ };
40
+ /** A wrapper around an event database that handles replaceable events, deletes, and models */
41
+ export declare class EventStore extends EventStore_base implements IEventStore {
42
+ database: IEventDatabase;
43
+ /** Optional memory database for ensuring single event instances */
44
+ memory?: EventMemory;
10
45
  /** Enable this to keep old versions of replaceable events */
11
46
  keepOldVersions: boolean;
12
47
  /** Enable this to keep expired events */
@@ -17,11 +52,11 @@ export declare class EventStore implements IEventStore {
17
52
  */
18
53
  verifyEvent?: (event: NostrEvent) => boolean;
19
54
  /** A stream of new events added to the store */
20
- insert$: Observable<NostrEvent>;
55
+ insert$: Subject<import("nostr-tools").Event>;
21
56
  /** A stream of events that have been updated */
22
- update$: Observable<NostrEvent>;
57
+ update$: Subject<import("nostr-tools").Event>;
23
58
  /** A stream of events that have been removed */
24
- remove$: Observable<NostrEvent>;
59
+ remove$: Subject<import("nostr-tools").Event>;
25
60
  /**
26
61
  * A method that will be called when an event isn't found in the store
27
62
  * @experimental
@@ -37,7 +72,9 @@ export declare class EventStore implements IEventStore {
37
72
  * @experimental
38
73
  */
39
74
  addressableLoader?: (pointer: AddressPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
40
- constructor();
75
+ constructor(database?: IEventDatabase);
76
+ /** A method to add all events to memory to ensure there is only ever a single instance of an event */
77
+ private mapToMemory;
41
78
  protected deletedIds: Set<string>;
42
79
  protected deletedCoords: Map<string, number>;
43
80
  protected checkDeleted(event: string | NostrEvent): boolean;
@@ -57,12 +94,12 @@ export declare class EventStore implements IEventStore {
57
94
  * @returns The existing event or the event that was added, if it was ignored returns null
58
95
  */
59
96
  add(event: NostrEvent, fromRelay?: string): NostrEvent | null;
60
- /** Removes an event from the database and updates subscriptions */
97
+ /** Removes an event from the store and updates subscriptions */
61
98
  remove(event: string | NostrEvent): boolean;
99
+ /** Remove multiple events that match the given filters */
100
+ removeByFilters(filters: Filter | Filter[]): number;
62
101
  /** Add an event to the store and notifies all subscribes it has updated */
63
102
  update(event: NostrEvent): boolean;
64
- /** Removes any event that is not being used by a subscription */
65
- prune(max?: number): number;
66
103
  /** Check if the store has an event by id */
67
104
  hasEvent(id: string): boolean;
68
105
  /** Get an event by id from the store */
@@ -74,9 +111,11 @@ export declare class EventStore implements IEventStore {
74
111
  /** Returns all versions of a replaceable event */
75
112
  getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
76
113
  /** Get all events matching a filter */
77
- getByFilters(filters: Filter | Filter[]): Set<NostrEvent>;
114
+ getByFilters(filters: Filter | Filter[]): NostrEvent[];
78
115
  /** Returns a timeline of events that match filters */
79
116
  getTimeline(filters: Filter | Filter[]): NostrEvent[];
117
+ /** Passthrough method for the database.touch */
118
+ touch(event: NostrEvent): void | undefined;
80
119
  /** Sets the claim on the event and touches it */
81
120
  claim(event: NostrEvent, claim: any): void;
82
121
  /** Checks if an event is claimed by anything */
@@ -85,56 +124,13 @@ export declare class EventStore implements IEventStore {
85
124
  removeClaim(event: NostrEvent, claim: any): void;
86
125
  /** Removes all claims on an event */
87
126
  clearClaim(event: NostrEvent): void;
88
- /** A directory of all active models */
89
- protected models: Map<ModelConstructor<any, any[]>, Map<string, Observable<any>>>;
90
- /** How long a model should be kept "warm" while nothing is subscribed to it */
91
- modelKeepWarm: number;
92
- /** Get or create a model on the event store */
93
- model<T extends unknown, Args extends Array<any>>(constructor: ModelConstructor<T, Args>, ...args: Args): Observable<T>;
94
- /**
95
- * Creates an observable that streams all events that match the filter
96
- * @param filters
97
- * @param [onlyNew=false] Only subscribe to new events
98
- */
99
- filters(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent>;
127
+ /** Pass through method for the database.unclaimed */
128
+ unclaimed(): Generator<NostrEvent>;
129
+ /** Removes any event that is not being used by a subscription */
130
+ prune(limit?: number): number;
100
131
  /** Returns an observable that completes when an event is removed */
101
132
  removed(id: string): Observable<never>;
102
133
  /** Creates an observable that emits when event is updated */
103
134
  updated(event: string | NostrEvent): Observable<NostrEvent>;
104
- /** Creates a {@link EventModel} */
105
- event(pointer: string | EventPointer): Observable<NostrEvent | undefined>;
106
- /** Creates a {@link ReplaceableModel} */
107
- replaceable(pointer: AddressPointer | AddressPointerWithoutD): Observable<NostrEvent | undefined>;
108
- replaceable(kind: number, pubkey: string, identifier?: string): Observable<NostrEvent | undefined>;
109
- /** Subscribe to an addressable event by pointer */
110
- addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
111
- /** Creates a {@link TimelineModel} */
112
- timeline(filters: Filter | Filter[], includeOldVersion?: boolean): Observable<NostrEvent[]>;
113
- /** Subscribe to a users profile */
114
- profile(user: string | ProfilePointer): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
115
- /** Subscribe to a users contacts */
116
- contacts(user: string | ProfilePointer): Observable<ProfilePointer[]>;
117
- /** Subscribe to a users mutes */
118
- mutes(user: string | ProfilePointer): Observable<import("../helpers/mutes.js").Mutes | undefined>;
119
- /** Subscribe to a users NIP-65 mailboxes */
120
- mailboxes(user: string | ProfilePointer): Observable<{
121
- inboxes: string[];
122
- outboxes: string[];
123
- } | undefined>;
124
- /** Subscribe to a users blossom servers */
125
- blossomServers(user: string | ProfilePointer): Observable<URL[]>;
126
- /** Subscribe to an event's reactions */
127
- reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
128
- /** Subscribe to a thread */
129
- thread(root: string | EventPointer | AddressPointer): Observable<import("../models/thread.js").Thread>;
130
- /** Subscribe to a event's comments */
131
- comments(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
132
- /** @deprecated use multiple {@link EventModel} instead */
133
- events(ids: string[]): Observable<Record<string, NostrEvent | undefined>>;
134
- /** @deprecated use multiple {@link ReplaceableModel} instead */
135
- replaceableSet(pointers: {
136
- kind: number;
137
- pubkey: string;
138
- identifier?: string;
139
- }[]): Observable<Record<string, NostrEvent | undefined>>;
140
135
  }
136
+ export {};