applesauce-core 0.10.0 → 0.12.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 (139) hide show
  1. package/dist/__tests__/fixtures.d.ts +8 -0
  2. package/dist/__tests__/fixtures.js +20 -0
  3. package/dist/event-store/__tests__/event-store.test.js +272 -0
  4. package/dist/event-store/database.d.ts +7 -5
  5. package/dist/event-store/database.js +14 -8
  6. package/dist/event-store/event-store.d.ts +40 -20
  7. package/dist/event-store/event-store.js +269 -314
  8. package/dist/event-store/index.d.ts +1 -0
  9. package/dist/event-store/index.js +1 -0
  10. package/dist/event-store/interface.d.ts +27 -0
  11. package/dist/helpers/__tests__/blossom.test.js +13 -0
  12. package/dist/helpers/__tests__/comment.test.d.ts +1 -0
  13. package/dist/helpers/__tests__/comment.test.js +235 -0
  14. package/dist/helpers/__tests__/emoji.test.d.ts +1 -0
  15. package/dist/helpers/__tests__/emoji.test.js +15 -0
  16. package/dist/helpers/__tests__/event.test.d.ts +1 -0
  17. package/dist/helpers/__tests__/event.test.js +36 -0
  18. package/dist/helpers/__tests__/file-metadata.test.d.ts +1 -0
  19. package/dist/helpers/__tests__/file-metadata.test.js +103 -0
  20. package/dist/helpers/__tests__/hidden-tags.test.d.ts +1 -0
  21. package/dist/helpers/{hidden-tags.test.js → __tests__/hidden-tags.test.js} +2 -1
  22. package/dist/helpers/__tests__/mailboxes.test.d.ts +1 -0
  23. package/dist/helpers/{mailboxes.test.js → __tests__/mailboxes.test.js} +1 -1
  24. package/dist/helpers/__tests__/nip-19.test.d.ts +1 -0
  25. package/dist/helpers/__tests__/nip-19.test.js +42 -0
  26. package/dist/helpers/__tests__/relays.test.d.ts +1 -0
  27. package/dist/helpers/__tests__/relays.test.js +21 -0
  28. package/dist/helpers/__tests__/tags.test.d.ts +1 -0
  29. package/dist/helpers/__tests__/tags.test.js +24 -0
  30. package/dist/helpers/__tests__/threading.test.d.ts +1 -0
  31. package/dist/helpers/{threading.test.js → __tests__/threading.test.js} +1 -1
  32. package/dist/helpers/blossom.d.ts +9 -0
  33. package/dist/helpers/blossom.js +22 -0
  34. package/dist/helpers/bookmarks.d.ts +15 -0
  35. package/dist/helpers/bookmarks.js +27 -0
  36. package/dist/helpers/cache.d.ts +3 -4
  37. package/dist/helpers/cache.js +1 -1
  38. package/dist/helpers/channels.d.ts +10 -0
  39. package/dist/helpers/channels.js +27 -0
  40. package/dist/helpers/comment.d.ts +3 -4
  41. package/dist/helpers/comment.js +20 -16
  42. package/dist/helpers/contacts.d.ts +3 -0
  43. package/dist/helpers/contacts.js +25 -0
  44. package/dist/helpers/direct-messages.d.ts +4 -0
  45. package/dist/helpers/direct-messages.js +5 -0
  46. package/dist/helpers/dns-identity.d.ts +7 -0
  47. package/dist/helpers/dns-identity.js +10 -0
  48. package/dist/helpers/emoji.d.ts +3 -1
  49. package/dist/helpers/emoji.js +2 -2
  50. package/dist/helpers/event.d.ts +15 -1
  51. package/dist/helpers/event.js +34 -11
  52. package/dist/helpers/file-metadata.d.ts +55 -0
  53. package/dist/helpers/file-metadata.js +99 -0
  54. package/dist/helpers/filter.d.ts +4 -0
  55. package/dist/helpers/filter.js +34 -1
  56. package/dist/helpers/gift-wraps.d.ts +12 -0
  57. package/dist/helpers/gift-wraps.js +49 -0
  58. package/dist/helpers/groups.d.ts +24 -0
  59. package/dist/helpers/groups.js +39 -0
  60. package/dist/helpers/hidden-content.d.ts +48 -0
  61. package/dist/helpers/hidden-content.js +88 -0
  62. package/dist/helpers/hidden-tags.d.ts +17 -35
  63. package/dist/helpers/hidden-tags.js +26 -83
  64. package/dist/helpers/index.d.ts +16 -1
  65. package/dist/helpers/index.js +16 -1
  66. package/dist/helpers/lists.d.ts +28 -0
  67. package/dist/helpers/lists.js +65 -0
  68. package/dist/helpers/mailboxes.js +16 -9
  69. package/dist/helpers/mutes.d.ts +15 -0
  70. package/dist/helpers/mutes.js +24 -0
  71. package/dist/helpers/nip-19.d.ts +4 -0
  72. package/dist/helpers/nip-19.js +27 -0
  73. package/dist/helpers/picture-post.d.ts +4 -0
  74. package/dist/helpers/picture-post.js +6 -0
  75. package/dist/helpers/pointers.js +13 -17
  76. package/dist/helpers/profile.d.ts +6 -1
  77. package/dist/helpers/profile.js +4 -0
  78. package/dist/helpers/relays.d.ts +6 -3
  79. package/dist/helpers/relays.js +25 -18
  80. package/dist/helpers/share.d.ts +4 -0
  81. package/dist/helpers/share.js +12 -0
  82. package/dist/helpers/tags.d.ts +17 -0
  83. package/dist/helpers/tags.js +28 -6
  84. package/dist/helpers/threading.js +3 -3
  85. package/dist/helpers/url.d.ts +7 -0
  86. package/dist/helpers/url.js +27 -0
  87. package/dist/helpers/user-status.d.ts +18 -0
  88. package/dist/helpers/user-status.js +21 -0
  89. package/dist/observable/__tests__/claim-events.test.d.ts +1 -0
  90. package/dist/observable/__tests__/claim-events.test.js +23 -0
  91. package/dist/observable/__tests__/claim-latest.test.d.ts +1 -0
  92. package/dist/observable/__tests__/claim-latest.test.js +37 -0
  93. package/dist/observable/__tests__/simple-timeout.test.d.ts +1 -0
  94. package/dist/observable/__tests__/simple-timeout.test.js +34 -0
  95. package/dist/observable/claim-events.d.ts +5 -0
  96. package/dist/observable/claim-events.js +28 -0
  97. package/dist/observable/claim-latest.d.ts +5 -0
  98. package/dist/observable/claim-latest.js +21 -0
  99. package/dist/observable/{get-value.d.ts → get-observable-value.d.ts} +1 -1
  100. package/dist/observable/{get-value.js → get-observable-value.js} +3 -8
  101. package/dist/observable/index.d.ts +2 -1
  102. package/dist/observable/index.js +2 -1
  103. package/dist/observable/share-latest-value.d.ts +2 -4
  104. package/dist/observable/share-latest-value.js +19 -16
  105. package/dist/observable/simple-timeout.d.ts +4 -0
  106. package/dist/observable/simple-timeout.js +6 -0
  107. package/dist/observable/with-immediate-value.d.ts +3 -0
  108. package/dist/observable/with-immediate-value.js +19 -0
  109. package/dist/queries/blossom.d.ts +2 -0
  110. package/dist/queries/blossom.js +10 -0
  111. package/dist/queries/bookmarks.d.ts +8 -0
  112. package/dist/queries/bookmarks.js +23 -0
  113. package/dist/queries/channels.d.ts +11 -0
  114. package/dist/queries/channels.js +73 -0
  115. package/dist/queries/contacts.d.ts +3 -0
  116. package/dist/queries/contacts.js +12 -0
  117. package/dist/queries/index.d.ts +6 -0
  118. package/dist/queries/index.js +6 -0
  119. package/dist/queries/mutes.d.ts +8 -0
  120. package/dist/queries/mutes.js +23 -0
  121. package/dist/queries/pins.d.ts +3 -0
  122. package/dist/queries/pins.js +12 -0
  123. package/dist/queries/simple.d.ts +3 -3
  124. package/dist/queries/simple.js +3 -3
  125. package/dist/queries/thread.js +1 -1
  126. package/dist/queries/user-status.d.ts +11 -0
  127. package/dist/queries/user-status.js +39 -0
  128. package/dist/query-store/__tests__/query-store.test.d.ts +1 -0
  129. package/dist/query-store/__tests__/query-store.test.js +63 -0
  130. package/dist/query-store/index.d.ts +1 -57
  131. package/dist/query-store/index.js +1 -66
  132. package/dist/query-store/query-store.d.ts +53 -0
  133. package/dist/query-store/query-store.js +97 -0
  134. package/package.json +20 -8
  135. package/dist/helpers/media-attachment.d.ts +0 -33
  136. package/dist/helpers/media-attachment.js +0 -60
  137. /package/dist/{helpers/hidden-tags.test.d.ts → event-store/__tests__/event-store.test.d.ts} +0 -0
  138. /package/dist/{helpers/mailboxes.test.d.ts → event-store/interface.js} +0 -0
  139. /package/dist/helpers/{threading.test.d.ts → __tests__/blossom.test.d.ts} +0 -0
@@ -0,0 +1,8 @@
1
+ import type { NostrEvent } from "nostr-tools";
2
+ export declare class FakeUser {
3
+ key: Uint8Array<ArrayBufferLike>;
4
+ pubkey: string;
5
+ event(data?: Partial<NostrEvent>): NostrEvent;
6
+ note(content?: string, extra?: Partial<NostrEvent>): import("nostr-tools").Event;
7
+ profile(profile: any, extra?: Partial<NostrEvent>): import("nostr-tools").Event;
8
+ }
@@ -0,0 +1,20 @@
1
+ import { finalizeEvent, generateSecretKey, getPublicKey, kinds } from "nostr-tools";
2
+ import { unixNow } from "../helpers/time.js";
3
+ export class FakeUser {
4
+ key = generateSecretKey();
5
+ pubkey = getPublicKey(this.key);
6
+ event(data) {
7
+ return finalizeEvent({
8
+ kind: data?.kind ?? kinds.ShortTextNote,
9
+ content: data?.content || "",
10
+ created_at: data?.created_at ?? unixNow(),
11
+ tags: data?.tags || [],
12
+ }, this.key);
13
+ }
14
+ note(content = "Hello World", extra) {
15
+ return this.event({ kind: kinds.ShortTextNote, content, ...extra });
16
+ }
17
+ profile(profile, extra) {
18
+ return this.event({ kind: kinds.Metadata, content: JSON.stringify({ ...profile }), ...extra });
19
+ }
20
+ }
@@ -0,0 +1,272 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { kinds } from "nostr-tools";
3
+ import { subscribeSpyTo } from "@hirez_io/observer-spy";
4
+ import { EventStore } from "../event-store.js";
5
+ import { addSeenRelay, getSeenRelays } from "../../helpers/relays.js";
6
+ import { getEventUID } from "../../helpers/event.js";
7
+ import { FakeUser } from "../../__tests__/fixtures.js";
8
+ let eventStore;
9
+ beforeEach(() => {
10
+ eventStore = new EventStore();
11
+ });
12
+ const user = new FakeUser();
13
+ const profile = user.profile({ name: "fake user" });
14
+ const note = user.note();
15
+ describe("add", () => {
16
+ it("should return original event in case of duplicates", () => {
17
+ const a = { ...profile };
18
+ expect(eventStore.add(a)).toBe(a);
19
+ const b = { ...profile };
20
+ expect(eventStore.add(b)).toBe(a);
21
+ const c = { ...profile };
22
+ expect(eventStore.add(c)).toBe(a);
23
+ });
24
+ it("should merge seen relays on duplicate events", () => {
25
+ const a = { ...profile };
26
+ addSeenRelay(a, "wss://relay.a.com");
27
+ eventStore.add(a);
28
+ const b = { ...profile };
29
+ addSeenRelay(b, "wss://relay.b.com");
30
+ eventStore.add(b);
31
+ expect(eventStore.getEvent(profile.id)).toBeDefined();
32
+ expect([...getSeenRelays(eventStore.getEvent(profile.id))]).toEqual(expect.arrayContaining(["wss://relay.a.com", "wss://relay.b.com"]));
33
+ });
34
+ it("should ignore deleted events", () => {
35
+ const deleteEvent = {
36
+ id: "delete event id",
37
+ kind: kinds.EventDeletion,
38
+ created_at: profile.created_at + 100,
39
+ pubkey: user.pubkey,
40
+ tags: [["e", profile.id]],
41
+ sig: "this should be ignored for the test",
42
+ content: "test",
43
+ };
44
+ // add delete event first
45
+ eventStore.add(deleteEvent);
46
+ // now event should be ignored
47
+ eventStore.add(profile);
48
+ expect(eventStore.getEvent(profile.id)).toBeUndefined();
49
+ });
50
+ });
51
+ describe("verifyEvent", () => {
52
+ it("should be called for all events added", () => {
53
+ const verifyEvent = vi.fn().mockReturnValue(true);
54
+ eventStore.verifyEvent = verifyEvent;
55
+ eventStore.add(profile);
56
+ expect(verifyEvent).toHaveBeenCalledWith(profile);
57
+ });
58
+ it("should not be called for duplicate events", () => {
59
+ const verifyEvent = vi.fn().mockReturnValue(true);
60
+ eventStore.verifyEvent = verifyEvent;
61
+ const a = { ...profile };
62
+ eventStore.add(a);
63
+ expect(verifyEvent).toHaveBeenCalledWith(a);
64
+ const b = { ...profile };
65
+ eventStore.add(b);
66
+ expect(verifyEvent).toHaveBeenCalledTimes(1);
67
+ const c = { ...profile };
68
+ eventStore.add(c);
69
+ expect(verifyEvent).toHaveBeenCalledTimes(1);
70
+ });
71
+ });
72
+ describe("removed", () => {
73
+ it("should complete when event is removed", () => {
74
+ eventStore.add(profile);
75
+ const spy = subscribeSpyTo(eventStore.removed(profile.id));
76
+ eventStore.remove(profile);
77
+ expect(spy.getValues()).toEqual([]);
78
+ expect(spy.receivedComplete()).toBe(true);
79
+ });
80
+ });
81
+ describe("event", () => {
82
+ it("should emit existing event", () => {
83
+ eventStore.add(profile);
84
+ const spy = subscribeSpyTo(eventStore.event(profile.id));
85
+ expect(spy.getValues()).toEqual([profile]);
86
+ });
87
+ it("should emit then event when its added", () => {
88
+ const spy = subscribeSpyTo(eventStore.event(profile.id));
89
+ expect(spy.getValues()).toEqual([]);
90
+ eventStore.add(profile);
91
+ expect(spy.getValues()).toEqual([profile]);
92
+ });
93
+ it("should emit undefined when event is removed", () => {
94
+ eventStore.add(profile);
95
+ const spy = subscribeSpyTo(eventStore.event(profile.id));
96
+ expect(spy.getValues()).toEqual([profile]);
97
+ eventStore.remove(profile);
98
+ expect(spy.getValues()).toEqual([profile, undefined]);
99
+ });
100
+ it("should emit new value if event is re-added", () => {
101
+ eventStore.add(profile);
102
+ const spy = subscribeSpyTo(eventStore.event(profile.id));
103
+ eventStore.remove(profile);
104
+ eventStore.add(profile);
105
+ expect(spy.getValues()).toEqual([profile, undefined, profile]);
106
+ });
107
+ it("should not complete when event is removed", () => {
108
+ eventStore.add(profile);
109
+ const spy = subscribeSpyTo(eventStore.event(profile.id));
110
+ eventStore.remove(profile);
111
+ expect(spy.receivedComplete()).toBe(false);
112
+ });
113
+ it("should not emit any values if there are no events", () => {
114
+ const spy = subscribeSpyTo(eventStore.event(profile.id));
115
+ expect(spy.receivedNext()).toBe(false);
116
+ });
117
+ });
118
+ describe("events", () => {
119
+ it("should emit existing events", () => {
120
+ eventStore.add(profile);
121
+ const spy = subscribeSpyTo(eventStore.events([profile.id]));
122
+ expect(spy.getValues()).toEqual([{ [profile.id]: profile }]);
123
+ });
124
+ it("should remove events when they are removed", () => {
125
+ eventStore.add(profile);
126
+ const spy = subscribeSpyTo(eventStore.events([profile.id]));
127
+ expect(spy.getValues()).toEqual([{ [profile.id]: profile }]);
128
+ eventStore.remove(profile);
129
+ expect(spy.getValues()).toEqual([{ [profile.id]: profile }, {}]);
130
+ });
131
+ it("should add events back if then are re-added", () => {
132
+ eventStore.add(profile);
133
+ const spy = subscribeSpyTo(eventStore.events([profile.id]));
134
+ eventStore.remove(profile);
135
+ eventStore.add(profile);
136
+ expect(spy.getValues()).toEqual([{ [profile.id]: profile }, {}, { [profile.id]: profile }]);
137
+ });
138
+ it("should not emit any values if there are no events", () => {
139
+ const spy = subscribeSpyTo(eventStore.events([profile.id]));
140
+ expect(spy.receivedNext()).toBe(false);
141
+ });
142
+ });
143
+ describe("replaceable", () => {
144
+ it("should not emit till there is an event", () => {
145
+ const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
146
+ expect(spy.receivedNext()).toBe(false);
147
+ });
148
+ it("should emit existing events", () => {
149
+ eventStore.add(profile);
150
+ const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
151
+ expect(spy.getValues()).toEqual([profile]);
152
+ });
153
+ it("should emit undefined when event is removed", () => {
154
+ eventStore.add(profile);
155
+ const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
156
+ eventStore.remove(profile);
157
+ expect(spy.getValues()).toEqual([profile, undefined]);
158
+ });
159
+ it("should not complete when event is removed", () => {
160
+ eventStore.add(profile);
161
+ const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
162
+ eventStore.remove(profile);
163
+ expect(spy.receivedComplete()).toBe(false);
164
+ });
165
+ it("should emit event when re-added", () => {
166
+ eventStore.add(profile);
167
+ const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
168
+ eventStore.remove(profile);
169
+ eventStore.add(profile);
170
+ expect(spy.getValues()).toEqual([profile, undefined, profile]);
171
+ });
172
+ it("should claim event", () => {
173
+ eventStore.add(profile);
174
+ eventStore.replaceable(0, user.pubkey).subscribe();
175
+ expect(eventStore.database.isClaimed(profile)).toBe(true);
176
+ });
177
+ it("should remove claim when event is removed", () => {
178
+ eventStore.add(profile);
179
+ eventStore.replaceable(0, user.pubkey).subscribe();
180
+ eventStore.remove(profile);
181
+ expect(eventStore.database.isClaimed(profile)).toBe(false);
182
+ });
183
+ it("should ignore older events added later", () => {
184
+ eventStore.add(profile);
185
+ const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
186
+ eventStore.add(user.profile({ name: "old name" }, { created_at: profile.created_at - 500 }));
187
+ eventStore.add(user.profile({ name: "really old name" }, { created_at: profile.created_at - 1000 }));
188
+ expect(spy.getValues()).toEqual([profile]);
189
+ });
190
+ });
191
+ describe("timeline", () => {
192
+ it("should emit an empty array if there are not events", () => {
193
+ const spy = subscribeSpyTo(eventStore.timeline({ kinds: [1] }));
194
+ expect(spy.getValues()).toEqual([[]]);
195
+ });
196
+ it("should emit existing events", () => {
197
+ eventStore.add(profile);
198
+ const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0] }));
199
+ expect(spy.getValues()).toEqual([[profile]]);
200
+ });
201
+ it("should emit new events", () => {
202
+ const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0, 1] }));
203
+ eventStore.add(profile);
204
+ eventStore.add(note);
205
+ expect(spy.getValues()).toEqual([[], [profile], [note, profile]]);
206
+ });
207
+ it("should remove event when its removed", () => {
208
+ eventStore.add(profile);
209
+ const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0] }));
210
+ eventStore.remove(profile);
211
+ expect(spy.getValues()).toEqual([[profile], []]);
212
+ });
213
+ it("should not emit when other events are removed", () => {
214
+ eventStore.add(profile);
215
+ const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0] }));
216
+ eventStore.add(note);
217
+ eventStore.remove(note);
218
+ expect(spy.getValues()).toEqual([[profile]]);
219
+ });
220
+ it("should ignore older events added later", () => {
221
+ eventStore.add(profile);
222
+ const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0] }));
223
+ eventStore.add(user.profile({ name: "old-name" }, { created_at: profile.created_at - 1000 }));
224
+ expect(spy.getValues()).toEqual([[profile]]);
225
+ });
226
+ it("should return new array for every value", () => {
227
+ const first = user.note("first note");
228
+ const second = user.note("second note");
229
+ const third = user.note("third note");
230
+ eventStore.add(first);
231
+ const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0] }));
232
+ eventStore.add(second);
233
+ eventStore.add(third);
234
+ const hasDuplicates = (arr) => {
235
+ return new Set(arr).size !== arr.length;
236
+ };
237
+ expect(hasDuplicates(spy.getValues())).toBe(false);
238
+ });
239
+ });
240
+ describe("replaceableSet", () => {
241
+ it("should not emit if there are not events", () => {
242
+ const spy = subscribeSpyTo(eventStore.replaceableSet([{ kind: 0, pubkey: user.pubkey }]));
243
+ expect(spy.receivedNext()).toBe(false);
244
+ });
245
+ it("should emit existing events", () => {
246
+ eventStore.add(profile);
247
+ const spy = subscribeSpyTo(eventStore.replaceableSet([{ kind: 0, pubkey: user.pubkey }]));
248
+ expect(spy.getValues()).toEqual([{ [getEventUID(profile)]: profile }]);
249
+ });
250
+ it("should remove event when removed", () => {
251
+ eventStore.add(profile);
252
+ const spy = subscribeSpyTo(eventStore.replaceableSet([{ kind: 0, pubkey: user.pubkey }]));
253
+ eventStore.remove(profile);
254
+ expect(spy.getValues()).toEqual([{ [getEventUID(profile)]: profile }, {}]);
255
+ });
256
+ it("should replace older events", () => {
257
+ const event2 = { ...profile, created_at: profile.created_at + 100, id: "newer-event" };
258
+ const uid = getEventUID(profile);
259
+ eventStore.add(profile);
260
+ const spy = subscribeSpyTo(eventStore.replaceableSet([{ kind: 0, pubkey: user.pubkey }]));
261
+ eventStore.add(event2);
262
+ expect(spy.getValues()).toEqual([{ [uid]: profile }, { [uid]: event2 }]);
263
+ });
264
+ it("should ignore old events added later", () => {
265
+ const old = user.profile({ name: "old-name" }, { created_at: profile.created_at - 1000 });
266
+ const uid = getEventUID(profile);
267
+ eventStore.add(profile);
268
+ const spy = subscribeSpyTo(eventStore.replaceableSet([{ kind: 0, pubkey: user.pubkey }]));
269
+ eventStore.add(old);
270
+ expect(spy.getValues()).toEqual([{ [uid]: profile }]);
271
+ });
272
+ });
@@ -19,8 +19,10 @@ export declare class Database {
19
19
  inserted: Subject<import("nostr-tools").Event>;
20
20
  /** A stream of events that have been updated */
21
21
  updated: Subject<import("nostr-tools").Event>;
22
- /** A stream of events removed of the database */
23
- deleted: Subject<import("nostr-tools").Event>;
22
+ /** A stream of events removed from the database */
23
+ removed: Subject<import("nostr-tools").Event>;
24
+ /** A method thats called before a new event is inserted */
25
+ onBeforeInsert?: (event: NostrEvent) => void;
24
26
  get size(): number;
25
27
  protected claims: WeakMap<import("nostr-tools").Event, any>;
26
28
  /** Index helper methods */
@@ -41,8 +43,8 @@ export declare class Database {
41
43
  addEvent(event: NostrEvent): NostrEvent;
42
44
  /** Inserts and event into the database and notifies all subscriptions that the event has updated */
43
45
  updateEvent(event: NostrEvent): NostrEvent;
44
- /** Deletes an event from the database and notifies all subscriptions */
45
- deleteEvent(eventOrId: string | NostrEvent): boolean;
46
+ /** Removes an event from the database and notifies all subscriptions */
47
+ removeEvent(eventOrId: string | NostrEvent): boolean;
46
48
  /** Sets the claim on the event and touches it */
47
49
  claimEvent(event: NostrEvent, claim: any): void;
48
50
  /** Checks if an event is claimed by anything */
@@ -58,7 +60,7 @@ export declare class Database {
58
60
  iterateIds(ids: Iterable<string>): Generator<NostrEvent>;
59
61
  /** Returns all events that match the filter */
60
62
  getEventsForFilter(filter: Filter): Set<NostrEvent>;
61
- getForFilters(filters: Filter[]): Set<NostrEvent>;
63
+ getEventsForFilters(filters: Filter[]): Set<NostrEvent>;
62
64
  /** Remove the oldest events that are not claimed */
63
65
  prune(limit?: number): number;
64
66
  }
@@ -22,8 +22,10 @@ export class Database {
22
22
  inserted = new Subject();
23
23
  /** A stream of events that have been updated */
24
24
  updated = new Subject();
25
- /** A stream of events removed of the database */
26
- deleted = new Subject();
25
+ /** A stream of events removed from the database */
26
+ removed = new Subject();
27
+ /** A method thats called before a new event is inserted */
28
+ onBeforeInsert;
27
29
  get size() {
28
30
  return this.events.size;
29
31
  }
@@ -82,11 +84,12 @@ export class Database {
82
84
  const id = event.id;
83
85
  const current = this.events.get(id);
84
86
  if (current) {
85
- // if this is a duplicate event, transfer some import symbols
87
+ // if this is a duplicate event, transfer some important symbols
86
88
  if (event[FromCacheSymbol])
87
89
  current[FromCacheSymbol] = event[FromCacheSymbol];
88
90
  return current;
89
91
  }
92
+ this.onBeforeInsert?.(event);
90
93
  this.events.set(id, event);
91
94
  this.getKindIndex(event.kind).add(event);
92
95
  this.getAuthorsIndex(event.pubkey).add(event);
@@ -116,8 +119,8 @@ export class Database {
116
119
  this.updated.next(inserted);
117
120
  return inserted;
118
121
  }
119
- /** Deletes an event from the database and notifies all subscriptions */
120
- deleteEvent(eventOrId) {
122
+ /** Removes an event from the database and notifies all subscriptions */
123
+ removeEvent(eventOrId) {
121
124
  let event = typeof eventOrId === "string" ? this.events.get(eventOrId) : eventOrId;
122
125
  if (!event)
123
126
  throw new Error("Missing event");
@@ -145,7 +148,10 @@ export class Database {
145
148
  array.splice(idx, 1);
146
149
  }
147
150
  }
148
- this.deleted.next(event);
151
+ // remove any claims this event has
152
+ this.claims.delete(event);
153
+ // notify subscribers this event was removed
154
+ this.removed.next(event);
149
155
  return true;
150
156
  }
151
157
  /** Sets the claim on the event and touches it */
@@ -281,7 +287,7 @@ export class Database {
281
287
  }
282
288
  return events;
283
289
  }
284
- getForFilters(filters) {
290
+ getEventsForFilters(filters) {
285
291
  if (filters.length === 0)
286
292
  throw new Error("No Filters");
287
293
  let events = new Set();
@@ -299,7 +305,7 @@ export class Database {
299
305
  while (cursor) {
300
306
  const event = cursor.value;
301
307
  if (!this.isClaimed(event)) {
302
- this.deleteEvent(event);
308
+ this.removeEvent(event);
303
309
  removed++;
304
310
  if (removed >= limit)
305
311
  break;
@@ -1,49 +1,69 @@
1
1
  import { Filter, NostrEvent } from "nostr-tools";
2
2
  import { Observable } from "rxjs";
3
3
  import { Database } from "./database.js";
4
- export declare class EventStore {
4
+ import { IEventStore } from "./interface.js";
5
+ export declare const EventStoreSymbol: unique symbol;
6
+ export declare class EventStore implements IEventStore {
5
7
  database: Database;
6
8
  /** Enable this to keep old versions of replaceable events */
7
9
  keepOldVersions: boolean;
10
+ /** A method used to verify new events before added them */
11
+ verifyEvent?: (event: NostrEvent) => boolean;
12
+ /** A stream of events that have been updated */
13
+ updates: Observable<NostrEvent>;
8
14
  constructor();
9
- /** Adds an event to the database and update subscriptions */
10
- add(event: NostrEvent, fromRelay?: string): NostrEvent;
11
- /** Removes an event from the database and updates subscriptions */
12
- remove(event: string | NostrEvent): boolean;
13
15
  protected deletedIds: Set<string>;
14
16
  protected deletedCoords: Map<string, number>;
17
+ protected checkDeleted(event: string | NostrEvent): boolean;
15
18
  protected handleDeleteEvent(deleteEvent: NostrEvent): void;
16
- protected checkDeleted(event: NostrEvent): boolean;
19
+ /** Copies important metadata from and identical event to another */
20
+ static mergeDuplicateEvent(source: NostrEvent, dest: NostrEvent): void;
21
+ /**
22
+ * Adds an event to the database and update subscriptions
23
+ * @throws
24
+ */
25
+ add(event: NostrEvent, fromRelay?: string): NostrEvent;
26
+ /** Removes an event from the database and updates subscriptions */
27
+ remove(event: string | NostrEvent): boolean;
17
28
  /** Removes any event that is not being used by a subscription */
18
29
  prune(max?: number): number;
19
30
  /** Add an event to the store and notifies all subscribes it has updated */
20
31
  update(event: NostrEvent): NostrEvent;
21
- getAll(filters: Filter[]): Set<NostrEvent>;
22
- hasEvent(uid: string): boolean;
23
- getEvent(uid: string): NostrEvent | undefined;
32
+ /** Get all events matching a filter */
33
+ getAll(filters: Filter | Filter[]): Set<NostrEvent>;
34
+ /** Check if the store has an event */
35
+ hasEvent(id: string): boolean;
36
+ getEvent(id: string): NostrEvent | undefined;
37
+ /** Check if the store has a replaceable event */
24
38
  hasReplaceable(kind: number, pubkey: string, d?: string): boolean;
25
39
  /** Gets the latest version of a replaceable event */
26
40
  getReplaceable(kind: number, pubkey: string, d?: string): NostrEvent | undefined;
27
41
  /** Returns all versions of a replaceable event */
28
42
  getReplaceableHistory(kind: number, pubkey: string, d?: string): NostrEvent[] | undefined;
29
- /** Creates an observable that updates a single event */
43
+ /** Returns a timeline of events that match filters */
44
+ getTimeline(filters: Filter | Filter[]): NostrEvent[];
45
+ /**
46
+ * Creates an observable that streams all events that match the filter and remains open
47
+ * @param filters
48
+ * @param [onlyNew=false] Only subscribe to new events
49
+ */
50
+ filters(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent>;
51
+ /** Returns an observable that completes when an event is removed */
52
+ removed(id: string): Observable<never>;
53
+ /** Creates an observable that emits when event is updated */
54
+ updated(event: string | NostrEvent): Observable<NostrEvent>;
55
+ /** Creates an observable that subscribes to a single event */
30
56
  event(id: string): Observable<NostrEvent | undefined>;
31
57
  /** Creates an observable that subscribes to multiple events */
32
- events(ids: string[]): Observable<Map<string, NostrEvent>>;
33
- /** Creates an observable with the latest version of a replaceable event */
58
+ events(ids: string[]): Observable<Record<string, NostrEvent>>;
59
+ /** Creates an observable that subscribes to the latest version of a replaceable event */
34
60
  replaceable(kind: number, pubkey: string, d?: string): Observable<NostrEvent | undefined>;
35
- /** Creates an observable with the latest versions of replaceable events */
61
+ /** Creates an observable that subscribes to the latest version of an array of replaceable events*/
36
62
  replaceableSet(pointers: {
37
63
  kind: number;
38
64
  pubkey: string;
39
65
  identifier?: string;
40
- }[]): Observable<Map<string, NostrEvent>>;
41
- /**
42
- * Creates an observable that streams all events that match the filter
43
- * @param filters
44
- * @param [onlyNew=false] Only subscribe to new events
45
- */
46
- stream(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent>;
66
+ }[]): Observable<Record<string, NostrEvent>>;
47
67
  /** Creates an observable that updates with an array of sorted events */
48
68
  timeline(filters: Filter | Filter[], keepOldVersions?: boolean): Observable<NostrEvent[]>;
49
69
  }