applesauce-core 0.0.0-next-20250913205403 → 0.0.0-next-20250916120818

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.
@@ -2,6 +2,7 @@ import { Filter, NostrEvent } from "nostr-tools";
2
2
  import { AddressPointer, EventPointer } from "nostr-tools/nip19";
3
3
  import { Observable, Subject } from "rxjs";
4
4
  import { AddressPointerWithoutD } from "../helpers/pointers.js";
5
+ import { EventMemory } from "./event-memory.js";
5
6
  import { IAsyncEventDatabase, IAsyncEventStore } from "./interface.js";
6
7
  declare const AsyncEventStore_base: {
7
8
  new (...args: any[]): {
@@ -39,6 +40,8 @@ declare const AsyncEventStore_base: {
39
40
  /** An async wrapper around an async event database that handles replaceable events, deletes, and models */
40
41
  export declare class AsyncEventStore extends AsyncEventStore_base implements IAsyncEventStore {
41
42
  database: IAsyncEventDatabase;
43
+ /** Optional memory database for ensuring single event instances */
44
+ memory?: EventMemory;
42
45
  /** Enable this to keep old versions of replaceable events */
43
46
  keepOldVersions: boolean;
44
47
  /** Enable this to keep expired events */
@@ -70,6 +73,8 @@ export declare class AsyncEventStore extends AsyncEventStore_base implements IAs
70
73
  */
71
74
  addressableLoader?: (pointer: AddressPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
72
75
  constructor(database: IAsyncEventDatabase);
76
+ /** A method to add all events to memory to ensure there is only ever a single instance of an event */
77
+ private mapToMemory;
73
78
  protected deletedIds: Set<string>;
74
79
  protected deletedCoords: Map<string, number>;
75
80
  protected checkDeleted(event: string | NostrEvent): boolean;
@@ -93,12 +98,6 @@ export declare class AsyncEventStore extends AsyncEventStore_base implements IAs
93
98
  remove(event: string | NostrEvent): Promise<boolean>;
94
99
  /** Add an event to the store and notifies all subscribes it has updated */
95
100
  update(event: NostrEvent): Promise<void>;
96
- /** Passthrough method for the database.touch */
97
- touch(event: NostrEvent): Promise<void>;
98
- /** Pass through method for the database.unclaimed */
99
- unclaimed(): AsyncGenerator<NostrEvent>;
100
- /** Removes any event that is not being used by a subscription */
101
- prune(limit?: number): Promise<number>;
102
101
  /** Check if the store has an event by id */
103
102
  hasEvent(id: string): Promise<boolean>;
104
103
  /** Get an event by id from the store */
@@ -110,17 +109,23 @@ export declare class AsyncEventStore extends AsyncEventStore_base implements IAs
110
109
  /** Returns all versions of a replaceable event */
111
110
  getReplaceableHistory(kind: number, pubkey: string, identifier?: string): Promise<NostrEvent[] | undefined>;
112
111
  /** Get all events matching a filter */
113
- getByFilters(filters: Filter | Filter[]): Promise<Set<NostrEvent>>;
112
+ getByFilters(filters: Filter | Filter[]): Promise<NostrEvent[]>;
114
113
  /** Returns a timeline of events that match filters */
115
114
  getTimeline(filters: Filter | Filter[]): Promise<NostrEvent[]>;
115
+ /** Passthrough method for the database.touch */
116
+ touch(event: NostrEvent): void | undefined;
116
117
  /** Sets the claim on the event and touches it */
117
- claim(event: NostrEvent, claim: any): Promise<void>;
118
+ claim(event: NostrEvent, claim: any): void;
118
119
  /** Checks if an event is claimed by anything */
119
- isClaimed(event: NostrEvent): Promise<boolean>;
120
+ isClaimed(event: NostrEvent): boolean;
120
121
  /** Removes a claim from an event */
121
- removeClaim(event: NostrEvent, claim: any): Promise<void>;
122
+ removeClaim(event: NostrEvent, claim: any): void;
122
123
  /** Removes all claims on an event */
123
- clearClaim(event: NostrEvent): Promise<void>;
124
+ clearClaim(event: NostrEvent): void;
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;
124
129
  /** Returns an observable that completes when an event is removed */
125
130
  removed(id: string): Observable<never>;
126
131
  /** Creates an observable that emits when event is updated */
@@ -7,12 +7,14 @@ import { getExpirationTimestamp } from "../helpers/expiration.js";
7
7
  import { parseCoordinate } from "../helpers/pointers.js";
8
8
  import { addSeenRelay, getSeenRelays } from "../helpers/relays.js";
9
9
  import { unixNow } from "../helpers/time.js";
10
- // Import common models
10
+ import { EventMemory } from "./event-memory.js";
11
11
  import { EventStoreModelMixin } from "./model-mixin.js";
12
12
  /** An async wrapper around an async event database that handles replaceable events, deletes, and models */
13
13
  export class AsyncEventStore extends EventStoreModelMixin(class {
14
14
  }) {
15
15
  database;
16
+ /** Optional memory database for ensuring single event instances */
17
+ memory;
16
18
  /** Enable this to keep old versions of replaceable events */
17
19
  keepOldVersions = false;
18
20
  /** Enable this to keep expired events */
@@ -46,6 +48,7 @@ export class AsyncEventStore extends EventStoreModelMixin(class {
46
48
  constructor(database) {
47
49
  super();
48
50
  this.database = database;
51
+ this.memory = new EventMemory();
49
52
  // when events are added to the database, add the symbol
50
53
  this.insert$.subscribe((event) => {
51
54
  Reflect.set(event, EventStoreSymbol, this);
@@ -55,6 +58,13 @@ export class AsyncEventStore extends EventStoreModelMixin(class {
55
58
  Reflect.deleteProperty(event, EventStoreSymbol);
56
59
  });
57
60
  }
61
+ mapToMemory(event) {
62
+ if (event === undefined)
63
+ return undefined;
64
+ if (!this.memory)
65
+ return event;
66
+ return this.memory.add(event);
67
+ }
58
68
  // delete state
59
69
  deletedIds = new Set();
60
70
  deletedCoords = new Map();
@@ -176,22 +186,22 @@ export class AsyncEventStore extends EventStoreModelMixin(class {
176
186
  return existing[0];
177
187
  }
178
188
  }
179
- else if (await this.database.hasEvent(event.id)) {
180
- // Duplicate event, copy symbols and return existing event
181
- const existing = await this.database.getEvent(event.id);
182
- if (existing) {
183
- AsyncEventStore.mergeDuplicateEvent(event, existing);
184
- return existing;
185
- }
186
- }
187
189
  // Verify event before inserting into the database
188
190
  if (this.verifyEvent && this.verifyEvent(event) === false)
189
191
  return null;
192
+ // Always add event to memory
193
+ const existing = this.memory?.add(event);
194
+ // If the memory returned a different instance, this is a duplicate event
195
+ if (existing && existing !== event) {
196
+ // Copy cached symbols and return existing event
197
+ AsyncEventStore.mergeDuplicateEvent(event, existing);
198
+ // attach relay this event was from
199
+ if (fromRelay)
200
+ addSeenRelay(existing, fromRelay);
201
+ return existing;
202
+ }
190
203
  // Insert event into database
191
- const inserted = await this.database.add(event);
192
- // If the event was ignored, return null
193
- if (inserted === null)
194
- return null;
204
+ const inserted = this.mapToMemory(await this.database.add(event));
195
205
  // Copy cached data if its a duplicate event
196
206
  if (event !== inserted)
197
207
  AsyncEventStore.mergeDuplicateEvent(event, inserted);
@@ -225,6 +235,9 @@ export class AsyncEventStore extends EventStoreModelMixin(class {
225
235
  const e = await this.database.getEvent(typeof event === "string" ? event : event.id);
226
236
  if (!e)
227
237
  return false;
238
+ // Remove from memory if available
239
+ if (this.memory)
240
+ this.memory.remove(typeof event === "string" ? event : event.id);
228
241
  const removed = await this.database.remove(event);
229
242
  if (removed && e)
230
243
  this.remove$.next(e);
@@ -236,72 +249,85 @@ export class AsyncEventStore extends EventStoreModelMixin(class {
236
249
  const e = await this.database.add(event);
237
250
  if (!e)
238
251
  return;
239
- await this.database.update(event);
252
+ // Notify the database that the event has updated
253
+ this.database.update?.(event);
240
254
  this.update$.next(event);
241
255
  }
242
- /** Passthrough method for the database.touch */
243
- async touch(event) {
244
- return await this.database.touch(event);
245
- }
246
- /** Pass through method for the database.unclaimed */
247
- unclaimed() {
248
- return this.database.unclaimed();
249
- }
250
- /** Removes any event that is not being used by a subscription */
251
- async prune(limit) {
252
- let removed = 0;
253
- const generator = this.database.unclaimed();
254
- for await (const event of generator) {
255
- await this.remove(event);
256
- removed++;
257
- if (limit && removed >= limit)
258
- break;
259
- }
260
- return removed;
261
- }
262
256
  /** Check if the store has an event by id */
263
257
  async hasEvent(id) {
264
- return await this.database.hasEvent(id);
258
+ // Check if the event exists in memory first, then in the database
259
+ return (this.memory?.hasEvent(id) ?? false) || (await this.database.hasEvent(id));
265
260
  }
266
261
  /** Get an event by id from the store */
267
262
  async getEvent(id) {
268
- return await this.database.getEvent(id);
263
+ // Get the event from memory first, then from the database
264
+ return this.memory?.getEvent(id) ?? this.mapToMemory(await this.database.getEvent(id));
269
265
  }
270
266
  /** Check if the store has a replaceable event */
271
267
  async hasReplaceable(kind, pubkey, d) {
272
- return await this.database.hasReplaceable(kind, pubkey, d);
268
+ // Check if the event exists in memory first, then in the database
269
+ return ((this.memory?.hasReplaceable(kind, pubkey, d) ?? false) || (await this.database.hasReplaceable(kind, pubkey, d)));
273
270
  }
274
271
  /** Gets the latest version of a replaceable event */
275
272
  async getReplaceable(kind, pubkey, identifier) {
276
- return await this.database.getReplaceable(kind, pubkey, identifier);
273
+ // Get the event from memory first, then from the database
274
+ return (this.memory?.getReplaceable(kind, pubkey, identifier) ??
275
+ this.mapToMemory(await this.database.getReplaceable(kind, pubkey, identifier)));
277
276
  }
278
277
  /** Returns all versions of a replaceable event */
279
278
  async getReplaceableHistory(kind, pubkey, identifier) {
280
- return await this.database.getReplaceableHistory(kind, pubkey, identifier);
279
+ // Get the events from memory first, then from the database
280
+ const memoryEvents = this.memory?.getReplaceableHistory(kind, pubkey, identifier);
281
+ if (memoryEvents)
282
+ return memoryEvents;
283
+ const dbEvents = await this.database.getReplaceableHistory(kind, pubkey, identifier);
284
+ return dbEvents?.map((e) => this.mapToMemory(e) ?? e);
281
285
  }
282
286
  /** Get all events matching a filter */
283
287
  async getByFilters(filters) {
284
- return await this.database.getByFilters(filters);
288
+ // NOTE: no way to read from memory since memory won't have the full set of events
289
+ const events = await this.database.getByFilters(filters);
290
+ // Map events to memory if available for better performance
291
+ if (this.memory)
292
+ return events.map((e) => this.mapToMemory(e) ?? e);
293
+ else
294
+ return events;
285
295
  }
286
296
  /** Returns a timeline of events that match filters */
287
297
  async getTimeline(filters) {
288
- return await this.database.getTimeline(filters);
298
+ const events = await this.database.getTimeline(filters);
299
+ if (this.memory)
300
+ return events.map((e) => this.mapToMemory(e));
301
+ else
302
+ return events;
303
+ }
304
+ /** Passthrough method for the database.touch */
305
+ touch(event) {
306
+ return this.memory?.touch(event);
289
307
  }
290
308
  /** Sets the claim on the event and touches it */
291
- async claim(event, claim) {
292
- return await this.database.claim(event, claim);
309
+ claim(event, claim) {
310
+ return this.memory?.claim(event, claim);
293
311
  }
294
312
  /** Checks if an event is claimed by anything */
295
- async isClaimed(event) {
296
- return await this.database.isClaimed(event);
313
+ isClaimed(event) {
314
+ return this.memory?.isClaimed(event) ?? false;
297
315
  }
298
316
  /** Removes a claim from an event */
299
- async removeClaim(event, claim) {
300
- return await this.database.removeClaim(event, claim);
317
+ removeClaim(event, claim) {
318
+ return this.memory?.removeClaim(event, claim);
301
319
  }
302
320
  /** Removes all claims on an event */
303
- async clearClaim(event) {
304
- return await this.database.clearClaim(event);
321
+ clearClaim(event) {
322
+ return this.memory?.clearClaim(event);
323
+ }
324
+ /** Pass through method for the database.unclaimed */
325
+ unclaimed() {
326
+ return this.memory?.unclaimed() || (function* () { })();
327
+ }
328
+ /** Removes any event that is not being used by a subscription */
329
+ prune(limit) {
330
+ return this.memory?.prune(limit) ?? 0;
305
331
  }
306
332
  /** Returns an observable that completes when an event is removed */
307
333
  removed(id) {
@@ -0,0 +1,74 @@
1
+ import { Filter, NostrEvent } from "nostr-tools";
2
+ import { LRU } from "../helpers/lru.js";
3
+ import { IEventMemory } from "./interface.js";
4
+ /** An in-memory database of events */
5
+ export declare class EventMemory implements IEventMemory {
6
+ protected log: import("debug").Debugger;
7
+ /** Indexes */
8
+ protected kinds: Map<number, Set<import("nostr-tools").Event>>;
9
+ protected authors: Map<string, Set<import("nostr-tools").Event>>;
10
+ protected tags: LRU<Set<import("nostr-tools").Event>>;
11
+ protected created_at: NostrEvent[];
12
+ /** LRU cache of last events touched */
13
+ events: LRU<import("nostr-tools").Event>;
14
+ /** A sorted array of replaceable events by address */
15
+ protected replaceable: Map<string, import("nostr-tools").Event[]>;
16
+ /** The number of events in the event set */
17
+ get size(): number;
18
+ /** Checks if the database contains an event without touching it */
19
+ hasEvent(id: string): boolean;
20
+ /** Gets a single event based on id */
21
+ getEvent(id: string): NostrEvent | undefined;
22
+ /** Checks if the event set has a replaceable event */
23
+ hasReplaceable(kind: number, pubkey: string, identifier?: string): boolean;
24
+ /** Gets the latest replaceable event */
25
+ getReplaceable(kind: number, pubkey: string, identifier?: string): NostrEvent | undefined;
26
+ /** Gets the history of a replaceable event */
27
+ getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
28
+ /** Gets all events that match the filters */
29
+ getByFilters(filters: Filter | Filter[]): NostrEvent[];
30
+ /** Gets a timeline of events that match the filters */
31
+ getTimeline(filters: Filter | Filter[]): NostrEvent[];
32
+ /** Inserts an event into the database and notifies all subscriptions */
33
+ add(event: NostrEvent): NostrEvent;
34
+ /** Removes an event from the database and notifies all subscriptions */
35
+ remove(eventOrId: string | NostrEvent): boolean;
36
+ /** Notify the database that an event has updated */
37
+ update(_event: NostrEvent): void;
38
+ /** A weak map of events that are claimed by other things */
39
+ protected claims: WeakMap<import("nostr-tools").Event, any>;
40
+ /** Moves an event to the top of the LRU cache */
41
+ touch(event: NostrEvent): void;
42
+ /** Sets the claim on the event and touches it */
43
+ claim(event: NostrEvent, claim: any): void;
44
+ /** Checks if an event is claimed by anything */
45
+ isClaimed(event: NostrEvent): boolean;
46
+ /** Removes a claim from an event */
47
+ removeClaim(event: NostrEvent, claim: any): void;
48
+ /** Removes all claims on an event */
49
+ clearClaim(event: NostrEvent): void;
50
+ /** Returns a generator of unclaimed events in order of least used */
51
+ unclaimed(): Generator<NostrEvent>;
52
+ /** Removes events that are not claimed (free up memory) */
53
+ prune(limit?: number): number;
54
+ /** Index helper methods */
55
+ protected getKindIndex(kind: number): Set<import("nostr-tools").Event>;
56
+ protected getAuthorsIndex(author: string): Set<import("nostr-tools").Event>;
57
+ protected getTagIndex(tagAndValue: string): Set<import("nostr-tools").Event>;
58
+ /** Iterates over all events by author */
59
+ iterateAuthors(authors: Iterable<string>): Generator<NostrEvent>;
60
+ /** Iterates over all events by indexable tag and value */
61
+ iterateTag(tag: string, values: Iterable<string>): Generator<NostrEvent>;
62
+ /** Iterates over all events by kind */
63
+ iterateKinds(kinds: Iterable<number>): Generator<NostrEvent>;
64
+ /** Iterates over all events by time */
65
+ iterateTime(since: number | undefined, until: number | undefined): Generator<NostrEvent>;
66
+ /** Iterates over all events by id */
67
+ iterateIds(ids: Iterable<string>): Generator<NostrEvent>;
68
+ /** Returns all events that match the filter */
69
+ getEventsForFilter(filter: Filter): Set<NostrEvent>;
70
+ /** Returns all events that match the filters */
71
+ getEventsForFilters(filters: Filter[]): Set<NostrEvent>;
72
+ /** Resets the event set */
73
+ reset(): void;
74
+ }
@@ -0,0 +1,349 @@
1
+ import { binarySearch, insertEventIntoDescendingList } from "nostr-tools/utils";
2
+ import { getIndexableTags, INDEXABLE_TAGS } from "../helpers/event-tags.js";
3
+ import { createReplaceableAddress, isReplaceable } from "../helpers/event.js";
4
+ import { LRU } from "../helpers/lru.js";
5
+ import { logger } from "../logger.js";
6
+ /** An in-memory database of events */
7
+ export class EventMemory {
8
+ log = logger.extend("EventMemory");
9
+ /** Indexes */
10
+ kinds = new Map();
11
+ authors = new Map();
12
+ tags = new LRU();
13
+ created_at = [];
14
+ /** LRU cache of last events touched */
15
+ events = new LRU();
16
+ /** A sorted array of replaceable events by address */
17
+ replaceable = new Map();
18
+ /** The number of events in the event set */
19
+ get size() {
20
+ return this.events.size;
21
+ }
22
+ /** Checks if the database contains an event without touching it */
23
+ hasEvent(id) {
24
+ return this.events.has(id);
25
+ }
26
+ /** Gets a single event based on id */
27
+ getEvent(id) {
28
+ return this.events.get(id);
29
+ }
30
+ /** Checks if the event set has a replaceable event */
31
+ hasReplaceable(kind, pubkey, identifier) {
32
+ const events = this.replaceable.get(createReplaceableAddress(kind, pubkey, identifier));
33
+ return !!events && events.length > 0;
34
+ }
35
+ /** Gets the latest replaceable event */
36
+ getReplaceable(kind, pubkey, identifier) {
37
+ const address = createReplaceableAddress(kind, pubkey, identifier);
38
+ const events = this.replaceable.get(address);
39
+ return events?.[0];
40
+ }
41
+ /** Gets the history of a replaceable event */
42
+ getReplaceableHistory(kind, pubkey, identifier) {
43
+ const address = createReplaceableAddress(kind, pubkey, identifier);
44
+ return this.replaceable.get(address);
45
+ }
46
+ /** Gets all events that match the filters */
47
+ getByFilters(filters) {
48
+ return Array.from(this.getEventsForFilters(Array.isArray(filters) ? filters : [filters]));
49
+ }
50
+ /** Gets a timeline of events that match the filters */
51
+ getTimeline(filters) {
52
+ const timeline = [];
53
+ const events = this.getByFilters(filters);
54
+ for (const event of events)
55
+ insertEventIntoDescendingList(timeline, event);
56
+ return timeline;
57
+ }
58
+ /** Inserts an event into the database and notifies all subscriptions */
59
+ add(event) {
60
+ const id = event.id;
61
+ const current = this.events.get(id);
62
+ if (current)
63
+ return current;
64
+ this.events.set(id, event);
65
+ this.getKindIndex(event.kind).add(event);
66
+ this.getAuthorsIndex(event.pubkey).add(event);
67
+ // Add the event to the tag indexes if they exist
68
+ for (const tag of getIndexableTags(event)) {
69
+ if (this.tags.has(tag))
70
+ this.getTagIndex(tag).add(event);
71
+ }
72
+ // Insert into time index
73
+ insertEventIntoDescendingList(this.created_at, event);
74
+ // Insert into replaceable index
75
+ if (isReplaceable(event.kind)) {
76
+ const identifier = event.tags.find((t) => t[0] === "d")?.[1];
77
+ const address = createReplaceableAddress(event.kind, event.pubkey, identifier);
78
+ let array = this.replaceable.get(address);
79
+ if (!this.replaceable.has(address)) {
80
+ // add an empty array if there is no array
81
+ array = [];
82
+ this.replaceable.set(address, array);
83
+ }
84
+ // insert the event into the sorted array
85
+ insertEventIntoDescendingList(array, event);
86
+ }
87
+ return event;
88
+ }
89
+ /** Removes an event from the database and notifies all subscriptions */
90
+ remove(eventOrId) {
91
+ let event = typeof eventOrId === "string" ? this.events.get(eventOrId) : eventOrId;
92
+ if (!event)
93
+ throw new Error("Missing event");
94
+ const id = event.id;
95
+ // only remove events that are known
96
+ if (!this.events.has(id))
97
+ return false;
98
+ this.getAuthorsIndex(event.pubkey).delete(event);
99
+ this.getKindIndex(event.kind).delete(event);
100
+ for (const tag of getIndexableTags(event)) {
101
+ if (this.tags.has(tag)) {
102
+ this.getTagIndex(tag).delete(event);
103
+ }
104
+ }
105
+ // remove from created_at index
106
+ const i = this.created_at.indexOf(event);
107
+ this.created_at.splice(i, 1);
108
+ this.events.delete(id);
109
+ // remove from replaceable index
110
+ if (isReplaceable(event.kind)) {
111
+ const identifier = event.tags.find((t) => t[0] === "d")?.[1];
112
+ const address = createReplaceableAddress(event.kind, event.pubkey, identifier);
113
+ const array = this.replaceable.get(address);
114
+ if (array && array.includes(event)) {
115
+ const idx = array.indexOf(event);
116
+ array.splice(idx, 1);
117
+ }
118
+ }
119
+ // remove any claims this event has
120
+ this.claims.delete(event);
121
+ return true;
122
+ }
123
+ /** Notify the database that an event has updated */
124
+ update(_event) {
125
+ // Do nothing
126
+ }
127
+ /** A weak map of events that are claimed by other things */
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
+ }
137
+ /** Sets the claim on the event and touches it */
138
+ claim(event, claim) {
139
+ if (!this.claims.has(event)) {
140
+ this.claims.set(event, claim);
141
+ }
142
+ // always touch event
143
+ this.touch(event);
144
+ }
145
+ /** Checks if an event is claimed by anything */
146
+ isClaimed(event) {
147
+ return this.claims.has(event);
148
+ }
149
+ /** Removes a claim from an event */
150
+ removeClaim(event, claim) {
151
+ const current = this.claims.get(event);
152
+ if (current === claim)
153
+ this.claims.delete(event);
154
+ }
155
+ /** Removes all claims on an event */
156
+ clearClaim(event) {
157
+ this.claims.delete(event);
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
+ }
183
+ /** Index helper methods */
184
+ getKindIndex(kind) {
185
+ if (!this.kinds.has(kind))
186
+ this.kinds.set(kind, new Set());
187
+ return this.kinds.get(kind);
188
+ }
189
+ getAuthorsIndex(author) {
190
+ if (!this.authors.has(author))
191
+ this.authors.set(author, new Set());
192
+ return this.authors.get(author);
193
+ }
194
+ getTagIndex(tagAndValue) {
195
+ if (!this.tags.has(tagAndValue)) {
196
+ // build new tag index from existing events
197
+ const events = new Set();
198
+ const ts = Date.now();
199
+ for (const event of this.events.values()) {
200
+ if (getIndexableTags(event).has(tagAndValue)) {
201
+ events.add(event);
202
+ }
203
+ }
204
+ const took = Date.now() - ts;
205
+ if (took > 100)
206
+ this.log(`Built index ${tagAndValue} took ${took}ms`);
207
+ this.tags.set(tagAndValue, events);
208
+ }
209
+ return this.tags.get(tagAndValue);
210
+ }
211
+ /** Iterates over all events by author */
212
+ *iterateAuthors(authors) {
213
+ for (const author of authors) {
214
+ const events = this.authors.get(author);
215
+ if (events) {
216
+ for (const event of events)
217
+ yield event;
218
+ }
219
+ }
220
+ }
221
+ /** Iterates over all events by indexable tag and value */
222
+ *iterateTag(tag, values) {
223
+ for (const value of values) {
224
+ const events = this.getTagIndex(tag + ":" + value);
225
+ if (events) {
226
+ for (const event of events)
227
+ yield event;
228
+ }
229
+ }
230
+ }
231
+ /** Iterates over all events by kind */
232
+ *iterateKinds(kinds) {
233
+ for (const kind of kinds) {
234
+ const events = this.kinds.get(kind);
235
+ if (events) {
236
+ for (const event of events)
237
+ yield event;
238
+ }
239
+ }
240
+ }
241
+ /** Iterates over all events by time */
242
+ *iterateTime(since, until) {
243
+ let untilIndex = 0;
244
+ let sinceIndex = this.created_at.length - 1;
245
+ let start = until
246
+ ? binarySearch(this.created_at, (mid) => {
247
+ return mid.created_at - until;
248
+ })
249
+ : undefined;
250
+ if (start)
251
+ untilIndex = start[0];
252
+ const end = since
253
+ ? binarySearch(this.created_at, (mid) => {
254
+ return mid.created_at - since;
255
+ })
256
+ : undefined;
257
+ if (end)
258
+ sinceIndex = end[0];
259
+ for (let i = untilIndex; i < sinceIndex; i++) {
260
+ yield this.created_at[i];
261
+ }
262
+ }
263
+ /** Iterates over all events by id */
264
+ *iterateIds(ids) {
265
+ for (const id of ids) {
266
+ if (this.events.has(id))
267
+ yield this.events.get(id);
268
+ }
269
+ }
270
+ /** Returns all events that match the filter */
271
+ getEventsForFilter(filter) {
272
+ // search is not supported, return an empty set
273
+ if (filter.search)
274
+ return new Set();
275
+ let first = true;
276
+ let events = new Set();
277
+ const and = (iterable) => {
278
+ const set = iterable instanceof Set ? iterable : new Set(iterable);
279
+ if (first) {
280
+ events = set;
281
+ first = false;
282
+ }
283
+ else {
284
+ for (const event of events) {
285
+ if (!set.has(event))
286
+ events.delete(event);
287
+ }
288
+ }
289
+ return events;
290
+ };
291
+ if (filter.ids)
292
+ and(this.iterateIds(filter.ids));
293
+ let time = null;
294
+ // query for time first if since is set
295
+ if (filter.since !== undefined) {
296
+ time = Array.from(this.iterateTime(filter.since, filter.until));
297
+ and(time);
298
+ }
299
+ for (const t of INDEXABLE_TAGS) {
300
+ const key = `#${t}`;
301
+ const values = filter[key];
302
+ if (values?.length)
303
+ and(this.iterateTag(t, values));
304
+ }
305
+ if (filter.authors)
306
+ and(this.iterateAuthors(filter.authors));
307
+ if (filter.kinds)
308
+ and(this.iterateKinds(filter.kinds));
309
+ // query for time last if only until is set
310
+ if (filter.since === undefined && filter.until !== undefined) {
311
+ time = Array.from(this.iterateTime(filter.since, filter.until));
312
+ and(time);
313
+ }
314
+ // if the filter queried on time and has a limit. truncate the events now
315
+ if (filter.limit && time) {
316
+ const limited = new Set();
317
+ for (const event of time) {
318
+ if (limited.size >= filter.limit)
319
+ break;
320
+ if (events.has(event))
321
+ limited.add(event);
322
+ }
323
+ return limited;
324
+ }
325
+ return events;
326
+ }
327
+ /** Returns all events that match the filters */
328
+ getEventsForFilters(filters) {
329
+ if (filters.length === 0)
330
+ throw new Error("No Filters");
331
+ let events = new Set();
332
+ for (const filter of filters) {
333
+ const filtered = this.getEventsForFilter(filter);
334
+ for (const event of filtered)
335
+ events.add(event);
336
+ }
337
+ return events;
338
+ }
339
+ /** Resets the event set */
340
+ reset() {
341
+ this.events.clear();
342
+ this.kinds.clear();
343
+ this.authors.clear();
344
+ this.tags.clear();
345
+ this.created_at = [];
346
+ this.replaceable.clear();
347
+ this.claims = new WeakMap();
348
+ }
349
+ }
@@ -2,6 +2,7 @@ import { Filter, NostrEvent } from "nostr-tools";
2
2
  import { AddressPointer, EventPointer } from "nostr-tools/nip19";
3
3
  import { Observable, Subject } from "rxjs";
4
4
  import { AddressPointerWithoutD } from "../helpers/pointers.js";
5
+ import { EventMemory } from "./event-memory.js";
5
6
  import { IEventDatabase, IEventStore } from "./interface.js";
6
7
  declare const EventStore_base: {
7
8
  new (...args: any[]): {
@@ -39,6 +40,8 @@ declare const EventStore_base: {
39
40
  /** A wrapper around an event database that handles replaceable events, deletes, and models */
40
41
  export declare class EventStore extends EventStore_base implements IEventStore {
41
42
  database: IEventDatabase;
43
+ /** Optional memory database for ensuring single event instances */
44
+ memory?: EventMemory;
42
45
  /** Enable this to keep old versions of replaceable events */
43
46
  keepOldVersions: boolean;
44
47
  /** Enable this to keep expired events */
@@ -70,6 +73,8 @@ export declare class EventStore extends EventStore_base implements IEventStore {
70
73
  */
71
74
  addressableLoader?: (pointer: AddressPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
72
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;
73
78
  protected deletedIds: Set<string>;
74
79
  protected deletedCoords: Map<string, number>;
75
80
  protected checkDeleted(event: string | NostrEvent): boolean;
@@ -93,12 +98,6 @@ export declare class EventStore extends EventStore_base implements IEventStore {
93
98
  remove(event: string | NostrEvent): boolean;
94
99
  /** Add an event to the store and notifies all subscribes it has updated */
95
100
  update(event: NostrEvent): boolean;
96
- /** Passthrough method for the database.touch */
97
- touch(event: NostrEvent): void;
98
- /** Pass through method for the database.unclaimed */
99
- unclaimed(): Generator<NostrEvent>;
100
- /** Removes any event that is not being used by a subscription */
101
- prune(limit?: number): number;
102
101
  /** Check if the store has an event by id */
103
102
  hasEvent(id: string): boolean;
104
103
  /** Get an event by id from the store */
@@ -110,9 +109,11 @@ export declare class EventStore extends EventStore_base implements IEventStore {
110
109
  /** Returns all versions of a replaceable event */
111
110
  getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
112
111
  /** Get all events matching a filter */
113
- getByFilters(filters: Filter | Filter[]): Set<NostrEvent>;
112
+ getByFilters(filters: Filter | Filter[]): NostrEvent[];
114
113
  /** Returns a timeline of events that match filters */
115
114
  getTimeline(filters: Filter | Filter[]): NostrEvent[];
115
+ /** Passthrough method for the database.touch */
116
+ touch(event: NostrEvent): void | undefined;
116
117
  /** Sets the claim on the event and touches it */
117
118
  claim(event: NostrEvent, claim: any): void;
118
119
  /** Checks if an event is claimed by anything */
@@ -121,6 +122,10 @@ export declare class EventStore extends EventStore_base implements IEventStore {
121
122
  removeClaim(event: NostrEvent, claim: any): void;
122
123
  /** Removes all claims on an event */
123
124
  clearClaim(event: NostrEvent): void;
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;
124
129
  /** Returns an observable that completes when an event is removed */
125
130
  removed(id: string): Observable<never>;
126
131
  /** Creates an observable that emits when event is updated */
@@ -7,13 +7,14 @@ import { getExpirationTimestamp } from "../helpers/expiration.js";
7
7
  import { parseCoordinate } from "../helpers/pointers.js";
8
8
  import { addSeenRelay, getSeenRelays } from "../helpers/relays.js";
9
9
  import { unixNow } from "../helpers/time.js";
10
- // Import common models
11
- import { InMemoryEventDatabase } from "./event-database.js";
10
+ import { EventMemory } from "./event-memory.js";
12
11
  import { EventStoreModelMixin } from "./model-mixin.js";
13
12
  /** A wrapper around an event database that handles replaceable events, deletes, and models */
14
13
  export class EventStore extends EventStoreModelMixin(class {
15
14
  }) {
16
15
  database;
16
+ /** Optional memory database for ensuring single event instances */
17
+ memory;
17
18
  /** Enable this to keep old versions of replaceable events */
18
19
  keepOldVersions = false;
19
20
  /** Enable this to keep expired events */
@@ -44,9 +45,16 @@ export class EventStore extends EventStoreModelMixin(class {
44
45
  * @experimental
45
46
  */
46
47
  addressableLoader;
47
- constructor(database = new InMemoryEventDatabase()) {
48
+ constructor(database = new EventMemory()) {
48
49
  super();
49
- this.database = database;
50
+ if (database) {
51
+ this.database = database;
52
+ this.memory = new EventMemory();
53
+ }
54
+ else {
55
+ // If no database is provided, its the same as having a memory database
56
+ this.database = this.memory = new EventMemory();
57
+ }
50
58
  // when events are added to the database, add the symbol
51
59
  this.insert$.subscribe((event) => {
52
60
  Reflect.set(event, EventStoreSymbol, this);
@@ -56,6 +64,13 @@ export class EventStore extends EventStoreModelMixin(class {
56
64
  Reflect.deleteProperty(event, EventStoreSymbol);
57
65
  });
58
66
  }
67
+ mapToMemory(event) {
68
+ if (event === undefined)
69
+ return undefined;
70
+ if (!this.memory)
71
+ return event;
72
+ return this.memory.add(event);
73
+ }
59
74
  // delete state
60
75
  deletedIds = new Set();
61
76
  deletedCoords = new Map();
@@ -175,22 +190,22 @@ export class EventStore extends EventStoreModelMixin(class {
175
190
  return existing[0];
176
191
  }
177
192
  }
178
- else if (this.database.hasEvent(event.id)) {
179
- // Duplicate event, copy symbols and return existing event
180
- const existing = this.database.getEvent(event.id);
181
- if (existing) {
182
- EventStore.mergeDuplicateEvent(event, existing);
183
- return existing;
184
- }
185
- }
186
193
  // Verify event before inserting into the database
187
194
  if (this.verifyEvent && this.verifyEvent(event) === false)
188
195
  return null;
196
+ // Always add event to memory
197
+ const existing = this.memory?.add(event);
198
+ // If the memory returned a different instance, this is a duplicate event
199
+ if (existing && existing !== event) {
200
+ // Copy cached symbols and return existing event
201
+ EventStore.mergeDuplicateEvent(event, existing);
202
+ // attach relay this event was from
203
+ if (fromRelay)
204
+ addSeenRelay(existing, fromRelay);
205
+ return existing;
206
+ }
189
207
  // Insert event into database
190
- const inserted = this.database.add(event);
191
- // If the event was ignored, return null
192
- if (inserted === null)
193
- return null;
208
+ const inserted = this.mapToMemory(this.database.add(event));
194
209
  // Copy cached data if its a duplicate event
195
210
  if (event !== inserted)
196
211
  EventStore.mergeDuplicateEvent(event, inserted);
@@ -224,6 +239,9 @@ export class EventStore extends EventStoreModelMixin(class {
224
239
  const e = this.database.getEvent(typeof event === "string" ? event : event.id);
225
240
  if (!e)
226
241
  return false;
242
+ // Remove from memory if available
243
+ if (this.memory)
244
+ this.memory.remove(event);
227
245
  const removed = this.database.remove(event);
228
246
  if (removed && e)
229
247
  this.remove$.next(e);
@@ -235,73 +253,83 @@ export class EventStore extends EventStoreModelMixin(class {
235
253
  const e = this.database.add(event);
236
254
  if (!e)
237
255
  return false;
238
- this.database.update(event);
256
+ // Notify the database that the event has updated
257
+ this.database.update?.(event);
239
258
  this.update$.next(event);
240
259
  return true;
241
260
  }
242
- /** Passthrough method for the database.touch */
243
- touch(event) {
244
- this.database.touch(event);
245
- }
246
- /** Pass through method for the database.unclaimed */
247
- unclaimed() {
248
- return this.database.unclaimed();
249
- }
250
- /** Removes any event that is not being used by a subscription */
251
- prune(limit) {
252
- let removed = 0;
253
- const unclaimed = this.database.unclaimed();
254
- for (const event of unclaimed) {
255
- this.remove(event);
256
- removed++;
257
- if (limit && removed >= limit)
258
- break;
259
- }
260
- return removed;
261
- }
262
261
  /** Check if the store has an event by id */
263
262
  hasEvent(id) {
264
- return this.database.hasEvent(id);
263
+ // Check if the event exists in memory first, then in the database
264
+ return this.memory?.hasEvent(id) || this.database.hasEvent(id);
265
265
  }
266
266
  /** Get an event by id from the store */
267
267
  getEvent(id) {
268
- return this.database.getEvent(id);
268
+ // Get the event from memory first, then from the database
269
+ return this.memory?.getEvent(id) ?? this.mapToMemory(this.database.getEvent(id));
269
270
  }
270
271
  /** Check if the store has a replaceable event */
271
272
  hasReplaceable(kind, pubkey, d) {
272
- return this.database.hasReplaceable(kind, pubkey, d);
273
+ // Check if the event exists in memory first, then in the database
274
+ return this.memory?.hasReplaceable(kind, pubkey, d) || this.database.hasReplaceable(kind, pubkey, d);
273
275
  }
274
276
  /** Gets the latest version of a replaceable event */
275
277
  getReplaceable(kind, pubkey, identifier) {
276
- return this.database.getReplaceable(kind, pubkey, identifier);
278
+ // Get the event from memory first, then from the database
279
+ return (this.memory?.getReplaceable(kind, pubkey, identifier) ??
280
+ this.mapToMemory(this.database.getReplaceable(kind, pubkey, identifier)));
277
281
  }
278
282
  /** Returns all versions of a replaceable event */
279
283
  getReplaceableHistory(kind, pubkey, identifier) {
280
- return this.database.getReplaceableHistory(kind, pubkey, identifier);
284
+ // Get the events from memory first, then from the database
285
+ return (this.memory?.getReplaceableHistory(kind, pubkey, identifier) ??
286
+ this.database.getReplaceableHistory(kind, pubkey, identifier)?.map((e) => this.mapToMemory(e) ?? e));
281
287
  }
282
288
  /** Get all events matching a filter */
283
289
  getByFilters(filters) {
284
- return this.database.getByFilters(filters);
290
+ // NOTE: no way to read from memory since memory won't have the full set of events
291
+ const events = this.database.getByFilters(filters);
292
+ // Map events to memory if available for better performance
293
+ if (this.memory)
294
+ return events.map((e) => this.mapToMemory(e));
295
+ else
296
+ return events;
285
297
  }
286
298
  /** Returns a timeline of events that match filters */
287
299
  getTimeline(filters) {
288
- return this.database.getTimeline(filters);
300
+ const events = this.database.getTimeline(filters);
301
+ if (this.memory)
302
+ return events.map((e) => this.mapToMemory(e));
303
+ else
304
+ return events;
305
+ }
306
+ /** Passthrough method for the database.touch */
307
+ touch(event) {
308
+ return this.memory?.touch(event);
289
309
  }
290
310
  /** Sets the claim on the event and touches it */
291
311
  claim(event, claim) {
292
- return this.database.claim(event, claim);
312
+ return this.memory?.claim(event, claim);
293
313
  }
294
314
  /** Checks if an event is claimed by anything */
295
315
  isClaimed(event) {
296
- return this.database.isClaimed(event);
316
+ return this.memory?.isClaimed(event) ?? false;
297
317
  }
298
318
  /** Removes a claim from an event */
299
319
  removeClaim(event, claim) {
300
- return this.database.removeClaim(event, claim);
320
+ return this.memory?.removeClaim(event, claim);
301
321
  }
302
322
  /** Removes all claims on an event */
303
323
  clearClaim(event) {
304
- return this.database.clearClaim(event);
324
+ return this.memory?.clearClaim(event);
325
+ }
326
+ /** Pass through method for the database.unclaimed */
327
+ unclaimed() {
328
+ return this.memory?.unclaimed() || (function* () { })();
329
+ }
330
+ /** Removes any event that is not being used by a subscription */
331
+ prune(limit) {
332
+ return this.memory?.prune(limit) ?? 0;
305
333
  }
306
334
  /** Returns an observable that completes when an event is removed */
307
335
  removed(id) {
@@ -1,4 +1,4 @@
1
1
  export * from "./async-event-store.js";
2
- export * from "./event-database.js";
2
+ export * from "./event-memory.js";
3
3
  export * from "./event-store.js";
4
4
  export * from "./interface.js";
@@ -1,4 +1,4 @@
1
1
  export * from "./async-event-store.js";
2
- export * from "./event-database.js";
2
+ export * from "./event-memory.js";
3
3
  export * from "./event-store.js";
4
4
  export * from "./interface.js";
@@ -18,7 +18,7 @@ export interface IEventStoreRead {
18
18
  /** Get the history of a replaceable event */
19
19
  getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
20
20
  /** Get all events that match the filters */
21
- getByFilters(filters: Filter | Filter[]): Set<NostrEvent>;
21
+ getByFilters(filters: Filter | Filter[]): NostrEvent[];
22
22
  /** Get a timeline of events that match the filters */
23
23
  getTimeline(filters: Filter | Filter[]): NostrEvent[];
24
24
  }
@@ -35,7 +35,7 @@ export interface IAsyncEventStoreRead {
35
35
  /** Get the history of a replaceable event */
36
36
  getReplaceableHistory(kind: number, pubkey: string, identifier?: string): Promise<NostrEvent[] | undefined>;
37
37
  /** Get all events that match the filters */
38
- getByFilters(filters: Filter | Filter[]): Promise<Set<NostrEvent>>;
38
+ getByFilters(filters: Filter | Filter[]): Promise<NostrEvent[]>;
39
39
  /** Get a timeline of events that match the filters */
40
40
  getTimeline(filters: Filter | Filter[]): Promise<NostrEvent[]>;
41
41
  }
@@ -81,21 +81,6 @@ export interface IEventClaims {
81
81
  /** Returns a generator of unclaimed events in order of least used */
82
82
  unclaimed(): Generator<NostrEvent>;
83
83
  }
84
- /** The async claim interface for an event store */
85
- export interface IAsyncEventClaims {
86
- /** Tell the store that this event was used */
87
- touch(event: NostrEvent): Promise<void>;
88
- /** Sets the claim on the event and touches it */
89
- claim(event: NostrEvent, claim: any): Promise<void>;
90
- /** Checks if an event is claimed by anything */
91
- isClaimed(event: NostrEvent): Promise<boolean>;
92
- /** Removes a claim from an event */
93
- removeClaim(event: NostrEvent, claim: any): Promise<void>;
94
- /** Removes all claims on an event */
95
- clearClaim(event: NostrEvent): Promise<void>;
96
- /** Returns a generator of unclaimed events in order of least used */
97
- unclaimed(): AsyncGenerator<NostrEvent>;
98
- }
99
84
  /** An event store that can be subscribed to */
100
85
  export interface IEventSubscriptions {
101
86
  /** Subscribe to an event by id */
@@ -155,14 +140,30 @@ export type Model<T extends unknown, TStore extends IEventStore | IAsyncEventSto
155
140
  export type ModelConstructor<T extends unknown, Args extends Array<any>, TStore extends IEventStore | IAsyncEventStore = IEventStore> = ((...args: Args) => Model<T, TStore>) & {
156
141
  getKey?: (...args: Args) => string;
157
142
  };
158
- /** The base interface for a set of events */
159
- export interface IEventDatabase extends IEventStoreRead, IEventStoreActions, IEventClaims {
143
+ /** The base interface for a database of events */
144
+ export interface IEventDatabase extends IEventStoreRead {
145
+ /** Add an event to the database */
146
+ add(event: NostrEvent): NostrEvent;
147
+ /** Remove an event from the database */
148
+ remove(event: string | NostrEvent): boolean;
149
+ /** Notifies the database that an event has updated */
150
+ update?: (event: NostrEvent) => void;
160
151
  }
161
152
  /** The async base interface for a set of events */
162
- export interface IAsyncEventDatabase extends IAsyncEventStoreRead, IAsyncEventStoreActions, IAsyncEventClaims {
153
+ export interface IAsyncEventDatabase extends IAsyncEventStoreRead {
154
+ /** Add an event to the database */
155
+ add(event: NostrEvent): Promise<NostrEvent>;
156
+ /** Remove an event from the database */
157
+ remove(event: string | NostrEvent): Promise<boolean>;
158
+ /** Notifies the database that an event has updated */
159
+ update?: (event: NostrEvent) => void;
163
160
  }
164
- /** @deprecated use {@link IEventDatabase} instead */
165
- export interface IEventSet extends IEventDatabase {
161
+ /** The base interface for the in-memory database of events */
162
+ export interface IEventMemory extends IEventStoreRead, IEventClaims {
163
+ /** Add an event to the store */
164
+ add(event: NostrEvent): NostrEvent;
165
+ /** Remove an event from the store */
166
+ remove(event: string | NostrEvent): boolean;
166
167
  }
167
168
  /** A set of methods that an event store will use to load single events it does not have */
168
169
  export interface IEventFallbackLoaders {
@@ -174,7 +175,7 @@ export interface IEventFallbackLoaders {
174
175
  addressableLoader?: (pointer: AddressPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
175
176
  }
176
177
  /** The async event store interface */
177
- export interface IAsyncEventStore extends IAsyncEventStoreRead, IEventStoreStreams, IEventSubscriptions, IAsyncEventStoreActions, IEventModelMixin<IAsyncEventStore>, IEventHelpfulSubscriptions, IAsyncEventClaims, IEventFallbackLoaders {
178
+ export interface IAsyncEventStore extends IAsyncEventStoreRead, IEventStoreStreams, IEventSubscriptions, IAsyncEventStoreActions, IEventModelMixin<IAsyncEventStore>, IEventHelpfulSubscriptions, IEventClaims, IEventFallbackLoaders {
178
179
  }
179
180
  /** The sync event store interface */
180
181
  export interface IEventStore extends IEventStoreRead, IEventStoreStreams, IEventSubscriptions, IEventStoreActions, IEventModelMixin<IEventStore>, IEventHelpfulSubscriptions, IEventClaims, IEventFallbackLoaders {
@@ -1,11 +1,11 @@
1
1
  import { NostrEvent, UnsignedEvent } from "nostr-tools";
2
- import { InMemoryEventDatabase } from "../event-store/event-database.js";
2
+ import { EventMemory } from "../event-store/event-memory.js";
3
3
  import { EncryptedContentSigner } from "./encrypted-content.js";
4
4
  /**
5
5
  * An internal event set to keep track of seals and rumors
6
6
  * This is intentionally isolated from the main applications event store so to prevent seals and rumors from being leaked
7
7
  */
8
- export declare const internalGiftWrapEvents: InMemoryEventDatabase;
8
+ export declare const internalGiftWrapEvents: EventMemory;
9
9
  export type Rumor = UnsignedEvent & {
10
10
  id: string;
11
11
  };
@@ -1,12 +1,12 @@
1
1
  import { verifyEvent } from "nostr-tools";
2
- import { InMemoryEventDatabase } from "../event-store/event-database.js";
2
+ import { EventMemory } from "../event-store/event-memory.js";
3
3
  import { getEncryptedContent, isEncryptedContentLocked, lockEncryptedContent, unlockEncryptedContent, } from "./encrypted-content.js";
4
4
  import { notifyEventUpdate } from "./event.js";
5
5
  /**
6
6
  * An internal event set to keep track of seals and rumors
7
7
  * This is intentionally isolated from the main applications event store so to prevent seals and rumors from being leaked
8
8
  */
9
- export const internalGiftWrapEvents = new InMemoryEventDatabase();
9
+ export const internalGiftWrapEvents = new EventMemory();
10
10
  /** Used to store a reference to the seal event on gift wraps (downstream) or the seal event on rumors (upstream[]) */
11
11
  export const SealSymbol = Symbol.for("seal");
12
12
  /** Used to store a reference to the rumor on seals (downstream) */
@@ -1,5 +1,5 @@
1
1
  import { NostrEvent } from "nostr-tools";
2
2
  import { MonoTypeOperatorFunction } from "rxjs";
3
- import { IAsyncEventClaims, IEventClaims } from "../event-store/interface.js";
3
+ import { IEventClaims } from "../event-store/interface.js";
4
4
  /** keep a claim on any event that goes through this observable, claims are removed when the observable completes */
5
- export declare function claimEvents<T extends NostrEvent[] | NostrEvent | undefined>(claims: IEventClaims | IAsyncEventClaims): MonoTypeOperatorFunction<T>;
5
+ export declare function claimEvents<T extends NostrEvent[] | NostrEvent | undefined>(claims: IEventClaims): MonoTypeOperatorFunction<T>;
@@ -1,5 +1,5 @@
1
1
  import { NostrEvent } from "nostr-tools";
2
2
  import { MonoTypeOperatorFunction } from "rxjs";
3
- import { IAsyncEventClaims, IEventClaims } from "../event-store/interface.js";
3
+ import { IEventClaims } from "../event-store/interface.js";
4
4
  /** An operator that claims the latest event with the database */
5
- export declare function claimLatest<T extends NostrEvent | undefined>(claims: IEventClaims | IAsyncEventClaims): MonoTypeOperatorFunction<T>;
5
+ export declare function claimLatest<T extends NostrEvent | undefined>(claims: IEventClaims): MonoTypeOperatorFunction<T>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-core",
3
- "version": "0.0.0-next-20250913205403",
3
+ "version": "0.0.0-next-20250916120818",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",