applesauce-core 1.2.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/README.md +7 -13
  2. package/dist/event-store/{database.d.ts → event-set.d.ts} +36 -21
  3. package/dist/event-store/{database.js → event-set.js} +98 -67
  4. package/dist/event-store/event-store.d.ts +64 -25
  5. package/dist/event-store/event-store.js +164 -207
  6. package/dist/event-store/index.d.ts +1 -1
  7. package/dist/event-store/index.js +1 -1
  8. package/dist/event-store/interface.d.ts +71 -13
  9. package/dist/helpers/app-handlers.d.ts +23 -0
  10. package/dist/helpers/app-handlers.js +68 -0
  11. package/dist/helpers/article.d.ts +9 -0
  12. package/dist/helpers/article.js +21 -0
  13. package/dist/helpers/bolt11.d.ts +1 -0
  14. package/dist/helpers/bolt11.js +2 -0
  15. package/dist/helpers/bookmarks.js +1 -2
  16. package/dist/helpers/emoji.d.ts +10 -2
  17. package/dist/helpers/emoji.js +21 -3
  18. package/dist/helpers/encrypted-content-cache.d.ts +15 -0
  19. package/dist/helpers/encrypted-content-cache.js +125 -0
  20. package/dist/helpers/encrypted-content.d.ts +48 -0
  21. package/dist/helpers/encrypted-content.js +65 -0
  22. package/dist/helpers/encryption.d.ts +5 -0
  23. package/dist/helpers/encryption.js +10 -0
  24. package/dist/helpers/event.d.ts +5 -8
  25. package/dist/helpers/event.js +25 -11
  26. package/dist/helpers/expiration.d.ts +6 -0
  27. package/dist/helpers/expiration.js +16 -0
  28. package/dist/helpers/filter.d.ts +1 -3
  29. package/dist/helpers/filter.js +1 -3
  30. package/dist/helpers/gift-wraps.d.ts +17 -5
  31. package/dist/helpers/gift-wraps.js +65 -27
  32. package/dist/helpers/groups.d.ts +5 -0
  33. package/dist/helpers/groups.js +12 -2
  34. package/dist/helpers/hidden-content.d.ts +27 -32
  35. package/dist/helpers/hidden-content.js +35 -65
  36. package/dist/helpers/hidden-tags.d.ts +23 -4
  37. package/dist/helpers/hidden-tags.js +39 -4
  38. package/dist/helpers/index.d.ts +11 -1
  39. package/dist/helpers/index.js +11 -1
  40. package/dist/helpers/legacy-messages.d.ts +21 -0
  41. package/dist/helpers/legacy-messages.js +39 -0
  42. package/dist/helpers/lists.d.ts +3 -1
  43. package/dist/helpers/lists.js +9 -3
  44. package/dist/helpers/messages.d.ts +11 -0
  45. package/dist/helpers/messages.js +19 -0
  46. package/dist/helpers/mutes.js +1 -1
  47. package/dist/helpers/pointers.d.ts +33 -9
  48. package/dist/helpers/pointers.js +80 -44
  49. package/dist/helpers/profile.d.ts +10 -2
  50. package/dist/helpers/profile.js +33 -4
  51. package/dist/helpers/reactions.d.ts +8 -0
  52. package/dist/helpers/reactions.js +56 -0
  53. package/dist/helpers/reports.d.ts +28 -0
  54. package/dist/helpers/reports.js +38 -0
  55. package/dist/helpers/share.d.ts +10 -1
  56. package/dist/helpers/share.js +22 -8
  57. package/dist/helpers/url.d.ts +4 -0
  58. package/dist/helpers/url.js +20 -0
  59. package/dist/helpers/user-status.js +2 -1
  60. package/dist/helpers/wrapped-messages.d.ts +23 -0
  61. package/dist/helpers/wrapped-messages.js +38 -0
  62. package/dist/helpers/zap.d.ts +8 -5
  63. package/dist/helpers/zap.js +11 -6
  64. package/dist/index.d.ts +2 -2
  65. package/dist/index.js +2 -2
  66. package/dist/models/blossom.d.ts +3 -0
  67. package/dist/models/blossom.js +8 -0
  68. package/dist/models/bookmarks.d.ts +8 -0
  69. package/dist/{queries → models}/bookmarks.js +9 -9
  70. package/dist/models/channels.d.ts +11 -0
  71. package/dist/{queries → models}/channels.js +9 -9
  72. package/dist/models/comments.d.ts +4 -0
  73. package/dist/models/comments.js +11 -0
  74. package/dist/models/common.d.ts +16 -0
  75. package/dist/models/common.js +176 -0
  76. package/dist/models/contacts.d.ts +8 -0
  77. package/dist/{queries → models}/contacts.js +10 -10
  78. package/dist/models/encrypted-content.d.ts +4 -0
  79. package/dist/models/encrypted-content.js +11 -0
  80. package/dist/models/gift-wrap.d.ts +7 -0
  81. package/dist/models/gift-wrap.js +20 -0
  82. package/dist/{queries → models}/index.d.ts +6 -2
  83. package/dist/{queries → models}/index.js +6 -2
  84. package/dist/models/legacy-messages.d.ts +8 -0
  85. package/dist/models/legacy-messages.js +29 -0
  86. package/dist/models/mailboxes.d.ts +6 -0
  87. package/dist/{queries → models}/mailboxes.js +2 -2
  88. package/dist/models/mutes.d.ts +8 -0
  89. package/dist/{queries → models}/mutes.js +9 -9
  90. package/dist/models/pins.d.ts +4 -0
  91. package/dist/{queries → models}/pins.js +3 -3
  92. package/dist/models/profile.d.ts +4 -0
  93. package/dist/models/profile.js +14 -0
  94. package/dist/models/reactions.d.ts +4 -0
  95. package/dist/{queries → models}/reactions.js +2 -2
  96. package/dist/models/relays.d.ts +27 -0
  97. package/dist/{queries → models}/relays.js +13 -13
  98. package/dist/{queries → models}/thread.d.ts +6 -5
  99. package/dist/{queries → models}/thread.js +4 -3
  100. package/dist/models/user-status.d.ts +11 -0
  101. package/dist/{queries → models}/user-status.js +5 -5
  102. package/dist/models/wrapped-messages.d.ts +25 -0
  103. package/dist/models/wrapped-messages.js +61 -0
  104. package/dist/models/zaps.d.ts +9 -0
  105. package/dist/{queries → models}/zaps.js +11 -3
  106. package/dist/observable/claim-events.d.ts +3 -3
  107. package/dist/observable/claim-events.js +4 -4
  108. package/dist/observable/claim-latest.d.ts +3 -3
  109. package/dist/observable/claim-latest.js +4 -4
  110. package/dist/observable/index.d.ts +3 -1
  111. package/dist/observable/index.js +3 -1
  112. package/dist/observable/map-events-timeline.d.ts +7 -0
  113. package/dist/observable/map-events-timeline.js +9 -0
  114. package/dist/observable/map-events-to-store.d.ts +5 -0
  115. package/dist/observable/map-events-to-store.js +12 -0
  116. package/dist/observable/simple-timeout.d.ts +1 -0
  117. package/dist/observable/simple-timeout.js +1 -0
  118. package/dist/observable/watch-event-updates.d.ts +7 -0
  119. package/dist/observable/watch-event-updates.js +25 -0
  120. package/package.json +11 -16
  121. package/dist/__tests__/exports.test.d.ts +0 -1
  122. package/dist/__tests__/exports.test.js +0 -17
  123. package/dist/__tests__/fixtures.d.ts +0 -8
  124. package/dist/__tests__/fixtures.js +0 -20
  125. package/dist/event-store/__tests__/event-store.test.d.ts +0 -1
  126. package/dist/event-store/__tests__/event-store.test.js +0 -354
  127. package/dist/helpers/__tests__/blossom.test.d.ts +0 -1
  128. package/dist/helpers/__tests__/blossom.test.js +0 -13
  129. package/dist/helpers/__tests__/bookmarks.test.d.ts +0 -1
  130. package/dist/helpers/__tests__/bookmarks.test.js +0 -88
  131. package/dist/helpers/__tests__/comment.test.d.ts +0 -1
  132. package/dist/helpers/__tests__/comment.test.js +0 -249
  133. package/dist/helpers/__tests__/contacts.test.d.ts +0 -1
  134. package/dist/helpers/__tests__/contacts.test.js +0 -34
  135. package/dist/helpers/__tests__/emoji.test.d.ts +0 -1
  136. package/dist/helpers/__tests__/emoji.test.js +0 -15
  137. package/dist/helpers/__tests__/event.test.d.ts +0 -1
  138. package/dist/helpers/__tests__/event.test.js +0 -36
  139. package/dist/helpers/__tests__/events.test.d.ts +0 -1
  140. package/dist/helpers/__tests__/events.test.js +0 -32
  141. package/dist/helpers/__tests__/exports.test.d.ts +0 -1
  142. package/dist/helpers/__tests__/exports.test.js +0 -220
  143. package/dist/helpers/__tests__/file-metadata.test.d.ts +0 -1
  144. package/dist/helpers/__tests__/file-metadata.test.js +0 -103
  145. package/dist/helpers/__tests__/hidden-tags.test.d.ts +0 -1
  146. package/dist/helpers/__tests__/hidden-tags.test.js +0 -29
  147. package/dist/helpers/__tests__/mailboxes.test.d.ts +0 -1
  148. package/dist/helpers/__tests__/mailboxes.test.js +0 -81
  149. package/dist/helpers/__tests__/mutes.test.d.ts +0 -1
  150. package/dist/helpers/__tests__/mutes.test.js +0 -55
  151. package/dist/helpers/__tests__/nip-19.test.d.ts +0 -1
  152. package/dist/helpers/__tests__/nip-19.test.js +0 -42
  153. package/dist/helpers/__tests__/relays.test.d.ts +0 -1
  154. package/dist/helpers/__tests__/relays.test.js +0 -21
  155. package/dist/helpers/__tests__/tags.test.d.ts +0 -1
  156. package/dist/helpers/__tests__/tags.test.js +0 -24
  157. package/dist/helpers/__tests__/threading.test.d.ts +0 -1
  158. package/dist/helpers/__tests__/threading.test.js +0 -41
  159. package/dist/helpers/direct-messages.d.ts +0 -4
  160. package/dist/helpers/direct-messages.js +0 -5
  161. package/dist/helpers/nip-19.d.ts +0 -18
  162. package/dist/helpers/nip-19.js +0 -56
  163. package/dist/observable/__tests__/claim-events.test.d.ts +0 -1
  164. package/dist/observable/__tests__/claim-events.test.js +0 -23
  165. package/dist/observable/__tests__/claim-latest.test.d.ts +0 -1
  166. package/dist/observable/__tests__/claim-latest.test.js +0 -37
  167. package/dist/observable/__tests__/exports.test.d.ts +0 -1
  168. package/dist/observable/__tests__/exports.test.js +0 -18
  169. package/dist/observable/__tests__/listen-latest-updates.test.d.ts +0 -1
  170. package/dist/observable/__tests__/listen-latest-updates.test.js +0 -55
  171. package/dist/observable/__tests__/simple-timeout.test.d.ts +0 -1
  172. package/dist/observable/__tests__/simple-timeout.test.js +0 -34
  173. package/dist/observable/listen-latest-updates.d.ts +0 -5
  174. package/dist/observable/listen-latest-updates.js +0 -12
  175. package/dist/promise/__tests__/exports.test.d.ts +0 -1
  176. package/dist/promise/__tests__/exports.test.js +0 -11
  177. package/dist/queries/__tests__/exports.test.d.ts +0 -1
  178. package/dist/queries/__tests__/exports.test.js +0 -41
  179. package/dist/queries/blossom.d.ts +0 -2
  180. package/dist/queries/blossom.js +0 -5
  181. package/dist/queries/bookmarks.d.ts +0 -8
  182. package/dist/queries/channels.d.ts +0 -11
  183. package/dist/queries/comments.d.ts +0 -4
  184. package/dist/queries/comments.js +0 -11
  185. package/dist/queries/contacts.d.ts +0 -8
  186. package/dist/queries/mailboxes.d.ts +0 -6
  187. package/dist/queries/mutes.d.ts +0 -8
  188. package/dist/queries/pins.d.ts +0 -4
  189. package/dist/queries/profile.d.ts +0 -4
  190. package/dist/queries/profile.js +0 -7
  191. package/dist/queries/reactions.d.ts +0 -4
  192. package/dist/queries/relays.d.ts +0 -27
  193. package/dist/queries/simple.d.ts +0 -16
  194. package/dist/queries/simple.js +0 -21
  195. package/dist/queries/user-status.d.ts +0 -11
  196. package/dist/queries/zaps.d.ts +0 -5
  197. package/dist/query-store/__tests__/exports.test.d.ts +0 -1
  198. package/dist/query-store/__tests__/exports.test.js +0 -12
  199. package/dist/query-store/__tests__/query-store.test.d.ts +0 -1
  200. package/dist/query-store/__tests__/query-store.test.js +0 -63
  201. package/dist/query-store/index.d.ts +0 -1
  202. package/dist/query-store/index.js +0 -1
  203. package/dist/query-store/query-store.d.ts +0 -54
  204. package/dist/query-store/query-store.js +0 -102
@@ -1,4 +1,5 @@
1
1
  import { MonoTypeOperatorFunction } from "rxjs";
2
2
  export declare class TimeoutError extends Error {
3
3
  }
4
+ /** Throws a {@link TimeoutError} if a value is not emitted within the timeout */
4
5
  export declare function simpleTimeout<T extends unknown>(first: number, message?: string): MonoTypeOperatorFunction<T>;
@@ -1,6 +1,7 @@
1
1
  import { throwError, timeout } from "rxjs";
2
2
  export class TimeoutError extends Error {
3
3
  }
4
+ /** Throws a {@link TimeoutError} if a value is not emitted within the timeout */
4
5
  export function simpleTimeout(first, message) {
5
6
  return timeout({ first, with: () => throwError(() => new TimeoutError(message ?? "Timeout")) });
6
7
  }
@@ -0,0 +1,7 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { MonoTypeOperatorFunction } from "rxjs";
3
+ import { IEventStoreStreams } from "../event-store/interface.js";
4
+ /** Watches for any updates to the latest event and remits the event when updated */
5
+ export declare function watchEventUpdates(eventStore: IEventStoreStreams): MonoTypeOperatorFunction<NostrEvent | undefined>;
6
+ /** Watches for any updates to the latest array of events and remits the array of events when updated */
7
+ export declare function watchEventsUpdates(eventStore: IEventStoreStreams): MonoTypeOperatorFunction<NostrEvent[]>;
@@ -0,0 +1,25 @@
1
+ import { filter, map, merge, tap } from "rxjs";
2
+ /** Watches for any updates to the latest event and remits the event when updated */
3
+ export function watchEventUpdates(eventStore) {
4
+ return (source) => {
5
+ let latest;
6
+ return merge(
7
+ // Get the latest event
8
+ source.pipe(tap((value) => (latest = value))),
9
+ // listen for updates
10
+ eventStore.update$.pipe(filter((e) => e.id === latest?.id)));
11
+ };
12
+ }
13
+ /** Watches for any updates to the latest array of events and remits the array of events when updated */
14
+ export function watchEventsUpdates(eventStore) {
15
+ return (source) => {
16
+ let latest = [];
17
+ return merge(
18
+ // Get the latest event
19
+ source.pipe(tap((value) => (latest = value))),
20
+ // listen for updates
21
+ eventStore.update$.pipe(filter((e) => latest.includes(e)),
22
+ // re-emit the array of events
23
+ map(() => latest)));
24
+ };
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-core",
3
- "version": "1.2.0",
3
+ "version": "2.1.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,15 +30,15 @@
30
30
  "require": "./dist/helpers/*.js",
31
31
  "types": "./dist/helpers/*.d.ts"
32
32
  },
33
- "./queries": {
34
- "import": "./dist/queries/index.js",
35
- "require": "./dist/queries/index.js",
36
- "types": "./dist/queries/index.d.ts"
33
+ "./models": {
34
+ "import": "./dist/models/index.js",
35
+ "require": "./dist/models/index.js",
36
+ "types": "./dist/models/index.d.ts"
37
37
  },
38
- "./queries/*": {
39
- "import": "./dist/queries/*.js",
40
- "require": "./dist/queries/*.js",
41
- "types": "./dist/queries/*.d.ts"
38
+ "./models/*": {
39
+ "import": "./dist/models/*.js",
40
+ "require": "./dist/models/*.js",
41
+ "types": "./dist/models/*.d.ts"
42
42
  },
43
43
  "./observable": {
44
44
  "import": "./dist/observable/index.js",
@@ -50,11 +50,6 @@
50
50
  "require": "./dist/promise/index.js",
51
51
  "types": "./dist/promise/index.d.ts"
52
52
  },
53
- "./query-store": {
54
- "import": "./dist/query-store/index.js",
55
- "require": "./dist/query-store/index.js",
56
- "types": "./dist/query-store/index.d.ts"
57
- },
58
53
  "./event-store": {
59
54
  "import": "./dist/event-store/index.js",
60
55
  "require": "./dist/event-store/index.js",
@@ -69,7 +64,7 @@
69
64
  "hash-sum": "^2.0.0",
70
65
  "light-bolt11-decoder": "^3.2.0",
71
66
  "nanoid": "^5.0.9",
72
- "nostr-tools": "^2.10.4",
67
+ "nostr-tools": "^2.13",
73
68
  "rxjs": "^7.8.1"
74
69
  },
75
70
  "devDependencies": {
@@ -77,7 +72,7 @@
77
72
  "@types/debug": "^4.1.12",
78
73
  "@types/hash-sum": "^1.0.2",
79
74
  "typescript": "^5.8.3",
80
- "vitest": "^3.1.1"
75
+ "vitest": "^3.2.3"
81
76
  },
82
77
  "funding": {
83
78
  "type": "lightning",
@@ -1 +0,0 @@
1
- export {};
@@ -1,17 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import * as exports from "../index.js";
3
- describe("exports", () => {
4
- it("should export the expected functions", () => {
5
- expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
6
- [
7
- "Database",
8
- "EventStore",
9
- "EventStoreSymbol",
10
- "Helpers",
11
- "Queries",
12
- "QueryStore",
13
- "logger",
14
- ]
15
- `);
16
- });
17
- });
@@ -1,8 +0,0 @@
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
- }
@@ -1,20 +0,0 @@
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
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,354 +0,0 @@
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 old deleted events but not newer ones", () => {
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
- const newProfile = user.profile({ name: "new name" }, { created_at: profile.created_at + 1000 });
49
- eventStore.add(newProfile);
50
- expect(eventStore.getEvent(profile.id)).toBeUndefined();
51
- expect(eventStore.getEvent(newProfile.id)).toBeDefined();
52
- });
53
- it("should remove profile events when delete event is added", () => {
54
- // Add initial replaceable event
55
- eventStore.add(profile);
56
- expect(eventStore.getEvent(profile.id)).toBeDefined();
57
- const newProfile = user.profile({ name: "new name" }, { created_at: profile.created_at + 1000 });
58
- eventStore.add(newProfile);
59
- const deleteEvent = {
60
- id: "delete event id",
61
- kind: kinds.EventDeletion,
62
- created_at: profile.created_at + 100,
63
- pubkey: user.pubkey,
64
- tags: [["a", `${profile.kind}:${profile.pubkey}`]],
65
- sig: "this should be ignored for the test",
66
- content: "test",
67
- };
68
- // Add delete event with coordinate
69
- eventStore.add(deleteEvent);
70
- // Profile should be removed since delete event is newer
71
- expect(eventStore.getEvent(profile.id)).toBeUndefined();
72
- expect(eventStore.getEvent(newProfile.id)).toBeDefined();
73
- expect(eventStore.getReplaceable(profile.kind, profile.pubkey)).toBe(newProfile);
74
- });
75
- it("should remove addressable replaceable events when delete event is added", () => {
76
- // Add initial replaceable event
77
- const event = user.event({ content: "test", kind: 30000, tags: [["d", "test"]] });
78
- eventStore.add(event);
79
- expect(eventStore.getEvent(event.id)).toBeDefined();
80
- const newEvent = user.event({
81
- ...event,
82
- created_at: event.created_at + 500,
83
- });
84
- eventStore.add(newEvent);
85
- const deleteEvent = {
86
- id: "delete event id",
87
- kind: kinds.EventDeletion,
88
- created_at: event.created_at + 100,
89
- pubkey: user.pubkey,
90
- tags: [["a", `${event.kind}:${event.pubkey}:test`]],
91
- sig: "this should be ignored for the test",
92
- content: "test",
93
- };
94
- // Add delete event with coordinate
95
- eventStore.add(deleteEvent);
96
- // Profile should be removed since delete event is newer
97
- expect(eventStore.getEvent(event.id)).toBeUndefined();
98
- expect(eventStore.getEvent(newEvent.id)).toBeDefined();
99
- expect(eventStore.getReplaceable(event.kind, event.pubkey, "test")).toBe(newEvent);
100
- });
101
- });
102
- describe("inserts", () => {
103
- it("should emit newer replaceable events", () => {
104
- const spy = subscribeSpyTo(eventStore.inserts);
105
- eventStore.add(profile);
106
- const newer = user.profile({ name: "new name" }, { created_at: profile.created_at + 100 });
107
- eventStore.add(newer);
108
- expect(spy.getValues()).toEqual([profile, newer]);
109
- });
110
- it("should not emit when older replaceable event is added", () => {
111
- const spy = subscribeSpyTo(eventStore.inserts);
112
- eventStore.add(profile);
113
- eventStore.add(user.profile({ name: "new name" }, { created_at: profile.created_at - 1000 }));
114
- expect(spy.getValues()).toEqual([profile]);
115
- });
116
- });
117
- describe("removes", () => {
118
- it("should emit older replaceable events when the newest replaceable event is added", () => {
119
- const spy = subscribeSpyTo(eventStore.removes);
120
- eventStore.add(profile);
121
- const newer = user.profile({ name: "new name" }, { created_at: profile.created_at + 1000 });
122
- eventStore.add(newer);
123
- expect(spy.getValues()).toEqual([profile]);
124
- });
125
- });
126
- describe("verifyEvent", () => {
127
- it("should be called for all events added", () => {
128
- const verifyEvent = vi.fn().mockReturnValue(true);
129
- eventStore.verifyEvent = verifyEvent;
130
- eventStore.add(profile);
131
- expect(verifyEvent).toHaveBeenCalledWith(profile);
132
- });
133
- it("should not be called for duplicate events", () => {
134
- const verifyEvent = vi.fn().mockReturnValue(true);
135
- eventStore.verifyEvent = verifyEvent;
136
- const a = { ...profile };
137
- eventStore.add(a);
138
- expect(verifyEvent).toHaveBeenCalledWith(a);
139
- const b = { ...profile };
140
- eventStore.add(b);
141
- expect(verifyEvent).toHaveBeenCalledTimes(1);
142
- const c = { ...profile };
143
- eventStore.add(c);
144
- expect(verifyEvent).toHaveBeenCalledTimes(1);
145
- });
146
- });
147
- describe("removed", () => {
148
- it("should complete when event is removed", () => {
149
- eventStore.add(profile);
150
- const spy = subscribeSpyTo(eventStore.removed(profile.id));
151
- eventStore.remove(profile);
152
- expect(spy.getValues()).toEqual([]);
153
- expect(spy.receivedComplete()).toBe(true);
154
- });
155
- });
156
- describe("event", () => {
157
- it("should emit existing event", () => {
158
- eventStore.add(profile);
159
- const spy = subscribeSpyTo(eventStore.event(profile.id));
160
- expect(spy.getValues()).toEqual([profile]);
161
- });
162
- it("should emit then event when its added", () => {
163
- const spy = subscribeSpyTo(eventStore.event(profile.id));
164
- expect(spy.getValues()).toEqual([]);
165
- eventStore.add(profile);
166
- expect(spy.getValues()).toEqual([profile]);
167
- });
168
- it("should emit undefined when event is removed", () => {
169
- eventStore.add(profile);
170
- const spy = subscribeSpyTo(eventStore.event(profile.id));
171
- expect(spy.getValues()).toEqual([profile]);
172
- eventStore.remove(profile);
173
- expect(spy.getValues()).toEqual([profile, undefined]);
174
- });
175
- it("should emit new value if event is re-added", () => {
176
- eventStore.add(profile);
177
- const spy = subscribeSpyTo(eventStore.event(profile.id));
178
- eventStore.remove(profile);
179
- eventStore.add(profile);
180
- expect(spy.getValues()).toEqual([profile, undefined, profile]);
181
- });
182
- it("should not complete when event is removed", () => {
183
- eventStore.add(profile);
184
- const spy = subscribeSpyTo(eventStore.event(profile.id));
185
- eventStore.remove(profile);
186
- expect(spy.receivedComplete()).toBe(false);
187
- });
188
- it("should not emit any values if there are no events", () => {
189
- const spy = subscribeSpyTo(eventStore.event(profile.id));
190
- expect(spy.receivedNext()).toBe(false);
191
- });
192
- });
193
- describe("events", () => {
194
- it("should emit existing events", () => {
195
- eventStore.add(profile);
196
- const spy = subscribeSpyTo(eventStore.events([profile.id]));
197
- expect(spy.getValues()).toEqual([{ [profile.id]: profile }]);
198
- });
199
- it("should remove events when they are removed", () => {
200
- eventStore.add(profile);
201
- const spy = subscribeSpyTo(eventStore.events([profile.id]));
202
- expect(spy.getValues()).toEqual([{ [profile.id]: profile }]);
203
- eventStore.remove(profile);
204
- expect(spy.getValues()).toEqual([{ [profile.id]: profile }, {}]);
205
- });
206
- it("should add events back if then are re-added", () => {
207
- eventStore.add(profile);
208
- const spy = subscribeSpyTo(eventStore.events([profile.id]));
209
- eventStore.remove(profile);
210
- eventStore.add(profile);
211
- expect(spy.getValues()).toEqual([{ [profile.id]: profile }, {}, { [profile.id]: profile }]);
212
- });
213
- it("should not emit any values if there are no events", () => {
214
- const spy = subscribeSpyTo(eventStore.events([profile.id]));
215
- expect(spy.receivedNext()).toBe(false);
216
- });
217
- });
218
- describe("replaceable", () => {
219
- it("should not emit till there is an event", () => {
220
- const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
221
- expect(spy.receivedNext()).toBe(false);
222
- });
223
- it("should emit existing events", () => {
224
- eventStore.add(profile);
225
- const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
226
- expect(spy.getValues()).toEqual([profile]);
227
- });
228
- it("should emit undefined when event is removed", () => {
229
- eventStore.add(profile);
230
- const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
231
- eventStore.remove(profile);
232
- expect(spy.getValues()).toEqual([profile, undefined]);
233
- });
234
- it("should not complete when event is removed", () => {
235
- eventStore.add(profile);
236
- const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
237
- eventStore.remove(profile);
238
- expect(spy.receivedComplete()).toBe(false);
239
- });
240
- it("should emit event when re-added", () => {
241
- eventStore.add(profile);
242
- const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
243
- eventStore.remove(profile);
244
- eventStore.add(profile);
245
- expect(spy.getValues()).toEqual([profile, undefined, profile]);
246
- });
247
- it("should claim event", () => {
248
- eventStore.add(profile);
249
- eventStore.replaceable(0, user.pubkey).subscribe();
250
- expect(eventStore.database.isClaimed(profile)).toBe(true);
251
- });
252
- it("should remove claim when event is removed", () => {
253
- eventStore.add(profile);
254
- eventStore.replaceable(0, user.pubkey).subscribe();
255
- eventStore.remove(profile);
256
- expect(eventStore.database.isClaimed(profile)).toBe(false);
257
- });
258
- it("should ignore older events added later", () => {
259
- eventStore.add(profile);
260
- const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
261
- eventStore.add(user.profile({ name: "old name" }, { created_at: profile.created_at - 500 }));
262
- eventStore.add(user.profile({ name: "really old name" }, { created_at: profile.created_at - 1000 }));
263
- expect(spy.getValues()).toEqual([profile]);
264
- });
265
- it("should emit newer events", () => {
266
- const spy = subscribeSpyTo(eventStore.replaceable(0, user.pubkey));
267
- eventStore.add(profile);
268
- const newProfile = user.profile({ name: "new name" }, { created_at: profile.created_at + 500 });
269
- eventStore.add(newProfile);
270
- expect(spy.getValues()).toEqual([profile, newProfile]);
271
- });
272
- });
273
- describe("timeline", () => {
274
- it("should emit an empty array if there are not events", () => {
275
- const spy = subscribeSpyTo(eventStore.timeline({ kinds: [1] }));
276
- expect(spy.getValues()).toEqual([[]]);
277
- });
278
- it("should emit existing events", () => {
279
- eventStore.add(profile);
280
- const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0] }));
281
- expect(spy.getValues()).toEqual([[profile]]);
282
- });
283
- it("should emit new events", () => {
284
- const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0, 1] }));
285
- eventStore.add(profile);
286
- eventStore.add(note);
287
- expect(spy.getValues()).toEqual([[], [profile], [note, profile]]);
288
- });
289
- it("should remove event when its removed", () => {
290
- eventStore.add(profile);
291
- const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0] }));
292
- eventStore.remove(profile);
293
- expect(spy.getValues()).toEqual([[profile], []]);
294
- });
295
- it("should not emit when other events are removed", () => {
296
- eventStore.add(profile);
297
- const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0] }));
298
- eventStore.add(note);
299
- eventStore.remove(note);
300
- expect(spy.getValues()).toEqual([[profile]]);
301
- });
302
- it("should ignore older events added later", () => {
303
- eventStore.add(profile);
304
- const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0] }));
305
- eventStore.add(user.profile({ name: "old-name" }, { created_at: profile.created_at - 1000 }));
306
- expect(spy.getValues()).toEqual([[profile]]);
307
- });
308
- it("should return new array for every value", () => {
309
- const first = user.note("first note");
310
- const second = user.note("second note");
311
- const third = user.note("third note");
312
- eventStore.add(first);
313
- const spy = subscribeSpyTo(eventStore.timeline({ kinds: [0] }));
314
- eventStore.add(second);
315
- eventStore.add(third);
316
- const hasDuplicates = (arr) => {
317
- return new Set(arr).size !== arr.length;
318
- };
319
- expect(hasDuplicates(spy.getValues())).toBe(false);
320
- });
321
- });
322
- describe("replaceableSet", () => {
323
- it("should not emit if there are not events", () => {
324
- const spy = subscribeSpyTo(eventStore.replaceableSet([{ kind: 0, pubkey: user.pubkey }]));
325
- expect(spy.receivedNext()).toBe(false);
326
- });
327
- it("should emit existing events", () => {
328
- eventStore.add(profile);
329
- const spy = subscribeSpyTo(eventStore.replaceableSet([{ kind: 0, pubkey: user.pubkey }]));
330
- expect(spy.getValues()).toEqual([{ [getEventUID(profile)]: profile }]);
331
- });
332
- it("should remove event when removed", () => {
333
- eventStore.add(profile);
334
- const spy = subscribeSpyTo(eventStore.replaceableSet([{ kind: 0, pubkey: user.pubkey }]));
335
- eventStore.remove(profile);
336
- expect(spy.getValues()).toEqual([{ [getEventUID(profile)]: profile }, {}]);
337
- });
338
- it("should replace older events", () => {
339
- const event2 = { ...profile, created_at: profile.created_at + 100, id: "newer-event" };
340
- const uid = getEventUID(profile);
341
- eventStore.add(profile);
342
- const spy = subscribeSpyTo(eventStore.replaceableSet([{ kind: 0, pubkey: user.pubkey }]));
343
- eventStore.add(event2);
344
- expect(spy.getValues()).toEqual([{ [uid]: profile }, { [uid]: event2 }]);
345
- });
346
- it("should ignore old events added later", () => {
347
- const old = user.profile({ name: "old-name" }, { created_at: profile.created_at - 1000 });
348
- const uid = getEventUID(profile);
349
- eventStore.add(profile);
350
- const spy = subscribeSpyTo(eventStore.replaceableSet([{ kind: 0, pubkey: user.pubkey }]));
351
- eventStore.add(old);
352
- expect(spy.getValues()).toEqual([{ [uid]: profile }]);
353
- });
354
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,13 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { areBlossomServersEqual } from "../blossom.js";
3
- describe("areBlossomServersEqual", () => {
4
- it("should ignore path", () => {
5
- expect(areBlossomServersEqual("https://cdn.server.com/pathname", "https://cdn.server.com")).toBe(true);
6
- });
7
- it("should not ignore protocol", () => {
8
- expect(areBlossomServersEqual("http://cdn.server.com", "https://cdn.server.com")).toBe(false);
9
- });
10
- it("should not ignore port", () => {
11
- expect(areBlossomServersEqual("http://cdn.server.com:4658", "https://cdn.server.com")).toBe(false);
12
- });
13
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,88 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { kinds } from "nostr-tools";
3
- import { mergeBookmarks } from "../bookmarks.js";
4
- describe("mergeBookmarks", () => {
5
- it("should merge bookmarks and handle duplicates", () => {
6
- // Create test data with some duplicates
7
- const eventPointer1 = {
8
- id: "event1",
9
- relays: ["wss://relay1.com/", "wss://relay2.com/"],
10
- author: "author1",
11
- };
12
- const eventPointer2 = {
13
- id: "event1", // Same ID as eventPointer1
14
- relays: ["wss://relay2.com/", "wss://relay3.com/"],
15
- author: "author1",
16
- };
17
- const eventPointer3 = {
18
- id: "event2",
19
- relays: ["wss://relay1.com/"],
20
- author: "author2",
21
- };
22
- const addressPointer1 = {
23
- kind: kinds.LongFormArticle,
24
- pubkey: "pubkey1",
25
- identifier: "article1",
26
- relays: ["wss://relay1.com/", "wss://relay2.com/"],
27
- };
28
- const addressPointer2 = {
29
- kind: kinds.LongFormArticle,
30
- pubkey: "pubkey1",
31
- identifier: "article1", // Same as addressPointer1
32
- relays: ["wss://relay3.com/"],
33
- };
34
- const bookmark1 = {
35
- notes: [eventPointer1],
36
- articles: [addressPointer1],
37
- hashtags: ["tag1", "tag2"],
38
- urls: ["https://example1.com/"],
39
- };
40
- const bookmark2 = {
41
- notes: [eventPointer2, eventPointer3],
42
- articles: [addressPointer2],
43
- hashtags: ["tag2", "tag3"],
44
- urls: ["https://example1.com/", "https://example2.com/"],
45
- };
46
- const result = mergeBookmarks(bookmark1, bookmark2);
47
- // Check that duplicates are properly merged
48
- expect(result.notes).toHaveLength(2); // event1 should be merged, plus event2
49
- expect(result.articles).toHaveLength(1); // article1 should be merged
50
- expect(result.hashtags).toHaveLength(3); // unique tags
51
- expect(result.urls).toHaveLength(2); // unique urls
52
- // Check that relays are merged for duplicate event
53
- const mergedEvent = result.notes.find((note) => note.id === "event1");
54
- expect(mergedEvent?.relays).toHaveLength(3);
55
- expect(mergedEvent?.relays).toContain("wss://relay1.com/");
56
- expect(mergedEvent?.relays).toContain("wss://relay2.com/");
57
- expect(mergedEvent?.relays).toContain("wss://relay3.com/");
58
- // Check that relays are merged for duplicate article
59
- const mergedArticle = result.articles[0];
60
- expect(mergedArticle.relays).toHaveLength(3);
61
- expect(mergedArticle.relays).toContain("wss://relay1.com/");
62
- expect(mergedArticle.relays).toContain("wss://relay2.com/");
63
- expect(mergedArticle.relays).toContain("wss://relay3.com/");
64
- // Check that hashtags are unique
65
- expect(result.hashtags).toContain("tag1");
66
- expect(result.hashtags).toContain("tag2");
67
- expect(result.hashtags).toContain("tag3");
68
- // Check that urls are unique
69
- expect(result.urls).toContain("https://example1.com/");
70
- expect(result.urls).toContain("https://example2.com/");
71
- });
72
- it("should handle undefined bookmarks", () => {
73
- const bookmark = {
74
- notes: [{ id: "event1", relays: ["wss://relay1.com/"] }],
75
- articles: [],
76
- hashtags: ["tag1"],
77
- urls: ["https://example.com/"],
78
- };
79
- const result = mergeBookmarks(bookmark, undefined);
80
- expect(result).toEqual(bookmark);
81
- expect(mergeBookmarks(undefined, undefined)).toEqual({
82
- notes: [],
83
- articles: [],
84
- hashtags: [],
85
- urls: [],
86
- });
87
- });
88
- });
@@ -1 +0,0 @@
1
- export {};