applesauce-core 3.1.0 → 4.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.
Files changed (88) hide show
  1. package/dist/event-store/async-event-store.d.ts +134 -0
  2. package/dist/event-store/async-event-store.js +349 -0
  3. package/dist/event-store/{event-set.d.ts → event-memory.d.ts} +15 -25
  4. package/dist/event-store/{event-set.js → event-memory.js} +43 -53
  5. package/dist/event-store/event-store.d.ts +57 -63
  6. package/dist/event-store/event-store.js +111 -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 +111 -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 +1 -1
  54. package/dist/helpers/pointers.js +2 -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 +13 -0
  58. package/dist/helpers/relay-selection.js +84 -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 +16 -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 +7 -0
  85. package/dist/observable/relay-selection.js +38 -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,22 @@ 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
+ /** Notify the database that an event has updated */
124
+ update(_event) {
125
+ // Do nothing
126
+ }
153
127
  /** A weak map of events that are claimed by other things */
154
128
  claims = new WeakMap();
129
+ /** Moves an event to the top of the LRU cache */
130
+ touch(event) {
131
+ // Make sure the event is in the database before adding it to the LRU
132
+ if (!this.events.has(event.id))
133
+ return;
134
+ // Move to the top of the LRU
135
+ this.events.set(event.id, event);
136
+ }
155
137
  /** Sets the claim on the event and touches it */
156
138
  claim(event, claim) {
157
139
  if (!this.claims.has(event)) {
@@ -174,6 +156,30 @@ export class EventSet {
174
156
  clearClaim(event) {
175
157
  this.claims.delete(event);
176
158
  }
159
+ /** Returns a generator of unclaimed events in order of least used */
160
+ *unclaimed() {
161
+ let removed = 0;
162
+ let cursor = this.events.first;
163
+ while (cursor) {
164
+ const event = cursor.value;
165
+ if (!this.isClaimed(event))
166
+ yield event;
167
+ cursor = cursor.next;
168
+ }
169
+ return removed;
170
+ }
171
+ /** Removes events that are not claimed (free up memory) */
172
+ prune(limit) {
173
+ let removed = 0;
174
+ const unclaimed = this.unclaimed();
175
+ for (const event of unclaimed) {
176
+ this.remove(event);
177
+ removed++;
178
+ if (limit && removed >= limit)
179
+ break;
180
+ }
181
+ return removed;
182
+ }
177
183
  /** Index helper methods */
178
184
  getKindIndex(kind) {
179
185
  if (!this.kinds.has(kind))
@@ -321,7 +327,7 @@ export class EventSet {
321
327
  /** Returns all events that match the filters */
322
328
  getEventsForFilters(filters) {
323
329
  if (filters.length === 0)
324
- throw new Error("No Filters");
330
+ return new Set();
325
331
  let events = new Set();
326
332
  for (const filter of filters) {
327
333
  const filtered = this.getEventsForFilter(filter);
@@ -330,22 +336,6 @@ export class EventSet {
330
336
  }
331
337
  return events;
332
338
  }
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
339
  /** Resets the event set */
350
340
  reset() {
351
341
  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,10 @@ 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;
62
99
  /** Add an event to the store and notifies all subscribes it has updated */
63
100
  update(event: NostrEvent): boolean;
64
- /** Removes any event that is not being used by a subscription */
65
- prune(max?: number): number;
66
101
  /** Check if the store has an event by id */
67
102
  hasEvent(id: string): boolean;
68
103
  /** Get an event by id from the store */
@@ -74,9 +109,11 @@ export declare class EventStore implements IEventStore {
74
109
  /** Returns all versions of a replaceable event */
75
110
  getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
76
111
  /** Get all events matching a filter */
77
- getByFilters(filters: Filter | Filter[]): Set<NostrEvent>;
112
+ getByFilters(filters: Filter | Filter[]): NostrEvent[];
78
113
  /** Returns a timeline of events that match filters */
79
114
  getTimeline(filters: Filter | Filter[]): NostrEvent[];
115
+ /** Passthrough method for the database.touch */
116
+ touch(event: NostrEvent): void | undefined;
80
117
  /** Sets the claim on the event and touches it */
81
118
  claim(event: NostrEvent, claim: any): void;
82
119
  /** Checks if an event is claimed by anything */
@@ -85,56 +122,13 @@ export declare class EventStore implements IEventStore {
85
122
  removeClaim(event: NostrEvent, claim: any): void;
86
123
  /** Removes all claims on an event */
87
124
  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>;
125
+ /** Pass through method for the database.unclaimed */
126
+ unclaimed(): Generator<NostrEvent>;
127
+ /** Removes any event that is not being used by a subscription */
128
+ prune(limit?: number): number;
100
129
  /** Returns an observable that completes when an event is removed */
101
130
  removed(id: string): Observable<never>;
102
131
  /** Creates an observable that emits when event is updated */
103
132
  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
133
  }
134
+ export {};