applesauce-core 3.1.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/event-store/async-event-store.d.ts +136 -0
  2. package/dist/event-store/async-event-store.js +364 -0
  3. package/dist/event-store/{event-set.d.ts → event-memory.d.ts} +17 -25
  4. package/dist/event-store/{event-set.js → event-memory.js} +54 -53
  5. package/dist/event-store/event-store.d.ts +59 -63
  6. package/dist/event-store/event-store.js +126 -190
  7. package/dist/event-store/index.d.ts +2 -1
  8. package/dist/event-store/index.js +2 -1
  9. package/dist/event-store/interface.d.ts +115 -38
  10. package/dist/event-store/model-mixin.d.ts +59 -0
  11. package/dist/event-store/model-mixin.js +147 -0
  12. package/dist/helpers/app-data.d.ts +39 -0
  13. package/dist/helpers/app-data.js +68 -0
  14. package/dist/helpers/bookmarks.d.ts +11 -1
  15. package/dist/helpers/bookmarks.js +29 -4
  16. package/dist/helpers/comment.d.ts +13 -20
  17. package/dist/helpers/comment.js +16 -27
  18. package/dist/helpers/contacts.d.ts +10 -1
  19. package/dist/helpers/contacts.js +30 -3
  20. package/dist/helpers/encrypted-content-cache.js +7 -7
  21. package/dist/helpers/encrypted-content.d.ts +9 -2
  22. package/dist/helpers/encrypted-content.js +12 -9
  23. package/dist/helpers/event-cache.d.ts +3 -1
  24. package/dist/helpers/event-cache.js +3 -1
  25. package/dist/helpers/event-tags.d.ts +6 -0
  26. package/dist/helpers/event-tags.js +4 -0
  27. package/dist/helpers/event.d.ts +8 -1
  28. package/dist/helpers/event.js +6 -0
  29. package/dist/helpers/file-metadata.d.ts +4 -9
  30. package/dist/helpers/file-metadata.js +2 -10
  31. package/dist/helpers/filter.d.ts +4 -3
  32. package/dist/helpers/filter.js +3 -3
  33. package/dist/helpers/gift-wraps.d.ts +35 -14
  34. package/dist/helpers/gift-wraps.js +59 -50
  35. package/dist/helpers/groups.d.ts +2 -5
  36. package/dist/helpers/groups.js +2 -5
  37. package/dist/helpers/hidden-content.d.ts +14 -5
  38. package/dist/helpers/hidden-content.js +19 -8
  39. package/dist/helpers/hidden-tags.d.ts +16 -7
  40. package/dist/helpers/hidden-tags.js +47 -26
  41. package/dist/helpers/index.d.ts +1 -0
  42. package/dist/helpers/index.js +1 -0
  43. package/dist/helpers/legacy-messages.d.ts +17 -13
  44. package/dist/helpers/legacy-messages.js +21 -19
  45. package/dist/helpers/lists.js +2 -1
  46. package/dist/helpers/lnurl.d.ts +4 -0
  47. package/dist/helpers/lnurl.js +7 -3
  48. package/dist/helpers/mailboxes.d.ts +2 -6
  49. package/dist/helpers/mailboxes.js +26 -20
  50. package/dist/helpers/mutes.d.ts +11 -1
  51. package/dist/helpers/mutes.js +30 -5
  52. package/dist/helpers/picture-post.d.ts +2 -1
  53. package/dist/helpers/pointers.d.ts +3 -1
  54. package/dist/helpers/pointers.js +4 -1
  55. package/dist/helpers/profile.d.ts +7 -3
  56. package/dist/helpers/profile.js +7 -8
  57. package/dist/helpers/relay-selection.d.ts +17 -0
  58. package/dist/helpers/relay-selection.js +102 -0
  59. package/dist/helpers/relays.d.ts +3 -1
  60. package/dist/helpers/relays.js +18 -2
  61. package/dist/helpers/url.js +3 -3
  62. package/dist/helpers/wrapped-messages.d.ts +5 -3
  63. package/dist/helpers/wrapped-messages.js +5 -3
  64. package/dist/helpers/zap.d.ts +18 -14
  65. package/dist/helpers/zap.js +26 -28
  66. package/dist/models/common.d.ts +4 -4
  67. package/dist/models/common.js +79 -40
  68. package/dist/models/gift-wrap.d.ts +1 -1
  69. package/dist/models/gift-wrap.js +4 -4
  70. package/dist/models/index.d.ts +1 -0
  71. package/dist/models/index.js +1 -0
  72. package/dist/models/legacy-messages.d.ts +2 -2
  73. package/dist/models/legacy-messages.js +13 -10
  74. package/dist/models/outbox.d.ts +13 -0
  75. package/dist/models/outbox.js +18 -0
  76. package/dist/models/profile.d.ts +1 -1
  77. package/dist/models/zaps.d.ts +5 -4
  78. package/dist/models/zaps.js +2 -2
  79. package/dist/observable/index.d.ts +4 -3
  80. package/dist/observable/index.js +5 -4
  81. package/dist/observable/map-events-to-store.d.ts +5 -3
  82. package/dist/observable/map-events-to-store.js +14 -3
  83. package/dist/observable/map-events-to-timeline.js +12 -0
  84. package/dist/observable/relay-selection.d.ts +9 -0
  85. package/dist/observable/relay-selection.js +43 -0
  86. package/package.json +5 -3
  87. package/dist/observable/map-events-timeline.js +0 -9
  88. /package/dist/observable/{map-events-timeline.d.ts → map-events-to-timeline.d.ts} +0 -0
@@ -5,16 +5,14 @@ import { isSafeRelayURL } from "./relays.js";
5
5
  export const COMMENT_KIND = 1111;
6
6
  export const CommentRootPointerSymbol = Symbol.for("comment-root-pointer");
7
7
  export const CommentReplyPointerSymbol = Symbol.for("comment-reply-pointer");
8
- /**
9
- * Gets the EventPointer from an array of tags
10
- * @throws
11
- */
8
+ /** Gets the EventPointer from an array of tags */
12
9
  export function getCommentEventPointer(tags, root = false) {
13
10
  const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
14
11
  const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
15
12
  if (eTag) {
13
+ // Missing kind tag, return null
16
14
  if (!kind)
17
- throw new Error("Missing kind tag");
15
+ return null;
18
16
  // only the root pubkey can be gotten from the tags, since due to quotes and mentions there will be many "p" tags for replies
19
17
  const rootPubkey = root ? tags.find((t) => t[0] === "P")?.[1] : undefined;
20
18
  const pointer = {
@@ -28,17 +26,15 @@ export function getCommentEventPointer(tags, root = false) {
28
26
  }
29
27
  return null;
30
28
  }
31
- /**
32
- * Gets the AddressPointer from an array of tags
33
- * @throws
34
- */
29
+ /** Gets the AddressPointer from an array of tags */
35
30
  export function getCommentAddressPointer(tags, root = false) {
36
31
  const aTag = tags.find((t) => t[0] === (root ? "A" : "a"));
37
32
  const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
38
33
  const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
39
34
  if (aTag) {
35
+ // Missing kind tag, return null
40
36
  if (!kind)
41
- throw new Error("Missing kind tag");
37
+ return null;
42
38
  const addressPointer = getAddressPointerFromATag(aTag);
43
39
  const pointer = {
44
40
  type: "address",
@@ -52,16 +48,10 @@ export function getCommentAddressPointer(tags, root = false) {
52
48
  }
53
49
  return null;
54
50
  }
55
- /**
56
- * Gets the ExternalPointer from an array of tags
57
- * @throws
58
- */
51
+ /** Gets the ExternalPointer from an array of tags */
59
52
  export function getCommentExternalPointer(tags, root = false) {
60
53
  const iTag = tags.find((t) => t[0] === (root ? "I" : "i"));
61
- const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
62
54
  if (iTag) {
63
- if (!kind)
64
- throw new Error("Missing kind tag");
65
55
  return {
66
56
  type: "external",
67
57
  ...getExternalPointerFromTag(iTag),
@@ -69,13 +59,9 @@ export function getCommentExternalPointer(tags, root = false) {
69
59
  }
70
60
  return null;
71
61
  }
72
- /**
73
- * Returns the root pointer for a comment
74
- * @throws
75
- */
76
62
  export function getCommentRootPointer(comment) {
77
63
  if (comment.kind !== COMMENT_KIND)
78
- throw new Error("Event is not a comment");
64
+ return null;
79
65
  return getOrComputeCachedValue(comment, CommentRootPointerSymbol, () => {
80
66
  // check for address pointer first since it can also have E tags
81
67
  const A = getCommentAddressPointer(comment.tags, true);
@@ -90,13 +76,10 @@ export function getCommentRootPointer(comment) {
90
76
  return null;
91
77
  });
92
78
  }
93
- /**
94
- * Returns the reply pointer for a comment
95
- * @throws
96
- */
79
+ /** Returns the reply pointer for a comment */
97
80
  export function getCommentReplyPointer(comment) {
98
81
  if (comment.kind !== COMMENT_KIND)
99
- throw new Error("Event is not a comment");
82
+ return null;
100
83
  return getOrComputeCachedValue(comment, CommentReplyPointerSymbol, () => {
101
84
  // check for address pointer first since it can also have E tags
102
85
  const A = getCommentAddressPointer(comment.tags, false);
@@ -111,15 +94,21 @@ export function getCommentReplyPointer(comment) {
111
94
  return null;
112
95
  });
113
96
  }
97
+ /** Checks if a pointer is a {@link CommentEventPointer} */
114
98
  export function isCommentEventPointer(pointer) {
115
99
  return (Reflect.has(pointer, "id") &&
116
100
  Reflect.has(pointer, "kind") &&
117
101
  !Reflect.has(pointer, "identifier") &&
118
102
  typeof pointer.kind === "number");
119
103
  }
104
+ /** Checks if a pointer is a {@link CommentAddressPointer} */
120
105
  export function isCommentAddressPointer(pointer) {
121
106
  return (Reflect.has(pointer, "identifier") &&
122
107
  Reflect.has(pointer, "pubkey") &&
123
108
  Reflect.has(pointer, "kind") &&
124
109
  typeof pointer.kind === "number");
125
110
  }
111
+ /** Checks if a comment event is valid */
112
+ export function isValidComment(comment) {
113
+ return (comment.kind === COMMENT_KIND && getCommentRootPointer(comment) !== null && getCommentReplyPointer(comment) !== null);
114
+ }
@@ -1,14 +1,23 @@
1
1
  import { NostrEvent } from "nostr-tools";
2
2
  import { ProfilePointer } from "nostr-tools/nip19";
3
+ import { HiddenContentSigner } from "./hidden-content.js";
3
4
  export declare const ContactsRelaysSymbol: unique symbol;
4
5
  export declare const PublicContactsSymbol: unique symbol;
5
6
  export declare const HiddenContactsSymbol: unique symbol;
6
- export declare function getRelaysFromContactsEvent(event: NostrEvent): Map<string, "all" | "inbox" | "outbox"> | null;
7
+ /** Type for contact events with unlocked hidden tags */
8
+ export type UnlockedContacts = {
9
+ [HiddenContactsSymbol]: ProfilePointer[];
10
+ };
11
+ export declare function getRelaysFromContactsEvent(event: NostrEvent): Map<string, "inbox" | "outbox" | "all"> | null;
7
12
  /** Merges any number of contact lists into a single list */
8
13
  export declare function mergeContacts(...pointers: (ProfilePointer | undefined | (ProfilePointer | undefined)[])[]): ProfilePointer[];
9
14
  /** Returns all public and hidden contacts from a contacts list event */
10
15
  export declare function getContacts(event: NostrEvent): ProfilePointer[];
11
16
  /** Returns only the public contacts from a contacts list event */
12
17
  export declare function getPublicContacts(event: NostrEvent): ProfilePointer[];
18
+ /** Checks if the hidden contacts are unlocked */
19
+ export declare function isHiddenContactsUnlocked<T extends NostrEvent>(event: T): event is T & UnlockedContacts;
13
20
  /** Returns only the hidden contacts from a contacts list event */
14
21
  export declare function getHiddenContacts(event: NostrEvent): ProfilePointer[] | undefined;
22
+ /** Unlocks the hidden contacts */
23
+ export declare function unlockHiddenContacts(event: NostrEvent, signer: HiddenContentSigner): Promise<ProfilePointer[]>;
@@ -2,7 +2,8 @@ import { getOrComputeCachedValue } from "./cache.js";
2
2
  import { isSafeRelayURL } from "./relays.js";
3
3
  import { isPTag, processTags } from "./tags.js";
4
4
  import { getProfilePointerFromPTag } from "./pointers.js";
5
- import { getHiddenTags, isHiddenTagsLocked } from "./hidden-tags.js";
5
+ import { getHiddenTags, isHiddenTagsUnlocked, unlockHiddenTags } from "./hidden-tags.js";
6
+ import { notifyEventUpdate } from "./index.js";
6
7
  export const ContactsRelaysSymbol = Symbol.for("contacts-relays");
7
8
  export const PublicContactsSymbol = Symbol.for("public-contacts");
8
9
  export const HiddenContactsSymbol = Symbol.for("hidden-contacts");
@@ -51,9 +52,35 @@ export function getContacts(event) {
51
52
  export function getPublicContacts(event) {
52
53
  return getOrComputeCachedValue(event, PublicContactsSymbol, () => processTags(event.tags, (t) => (isPTag(t) ? t : undefined), getProfilePointerFromPTag));
53
54
  }
55
+ /** Checks if the hidden contacts are unlocked */
56
+ export function isHiddenContactsUnlocked(event) {
57
+ return isHiddenTagsUnlocked(event) && Reflect.has(event, HiddenContactsSymbol);
58
+ }
54
59
  /** Returns only the hidden contacts from a contacts list event */
55
60
  export function getHiddenContacts(event) {
56
- if (isHiddenTagsLocked(event))
61
+ if (isHiddenContactsUnlocked(event))
62
+ return event[HiddenContactsSymbol];
63
+ // Get hidden tags
64
+ const tags = getHiddenTags(event);
65
+ if (!tags)
57
66
  return undefined;
58
- return getOrComputeCachedValue(event, HiddenContactsSymbol, () => processTags(getHiddenTags(event), (t) => (isPTag(t) ? t : undefined), getProfilePointerFromPTag));
67
+ // Parse tags
68
+ const contacts = processTags(tags, (t) => (isPTag(t) ? t : undefined), getProfilePointerFromPTag);
69
+ // Set cache and notify event store
70
+ Reflect.set(event, HiddenContactsSymbol, contacts);
71
+ return contacts;
72
+ }
73
+ /** Unlocks the hidden contacts */
74
+ export async function unlockHiddenContacts(event, signer) {
75
+ if (isHiddenContactsUnlocked(event))
76
+ return event[HiddenContactsSymbol];
77
+ // Unlock hidden tags
78
+ await unlockHiddenTags(event, signer);
79
+ // Get hidden contacts
80
+ const contacts = getHiddenContacts(event);
81
+ if (!contacts)
82
+ throw new Error("Failed to unlock hidden contacts");
83
+ // Set cache and notify event store
84
+ notifyEventUpdate(event);
85
+ return contacts;
59
86
  }
@@ -1,7 +1,7 @@
1
1
  import { kinds } from "nostr-tools";
2
2
  import { catchError, combineLatest, distinct, EMPTY, filter, isObservable, map, merge, mergeMap, of, switchMap, } from "rxjs";
3
3
  import { logger } from "../logger.js";
4
- import { canHaveEncryptedContent, getEncryptedContent, isEncryptedContentLocked, setEncryptedContentCache, } from "./encrypted-content.js";
4
+ import { canHaveEncryptedContent, getEncryptedContent, isEncryptedContentUnlocked, setEncryptedContentCache, } from "./encrypted-content.js";
5
5
  import { notifyEventUpdate } from "./event.js";
6
6
  import { getGiftWrapSeal, getSealGiftWrap, getSealRumor } from "./gift-wraps.js";
7
7
  /** A symbol that is used to mark encrypted content as being from a cache */
@@ -32,7 +32,7 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
32
32
  const restore = eventStore.insert$
33
33
  .pipe(
34
34
  // Look for events that support encrypted content and are locked
35
- filter((e) => canHaveEncryptedContent(e.kind) && isEncryptedContentLocked(e)),
35
+ filter((e) => canHaveEncryptedContent(e.kind) && isEncryptedContentUnlocked(e) === false),
36
36
  // Get the encrypted content from storage
37
37
  mergeMap((event) =>
38
38
  // Wait for storage to be available
@@ -52,11 +52,11 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
52
52
  const restoreSeals = eventStore.update$
53
53
  .pipe(
54
54
  // Look for gift wraps that are unlocked
55
- filter((e) => e.kind === kinds.GiftWrap && !isEncryptedContentLocked(e)),
55
+ filter((e) => e.kind === kinds.GiftWrap && isEncryptedContentUnlocked(e)),
56
56
  // Get the seal event
57
57
  map((gift) => getGiftWrapSeal(gift)),
58
58
  // Look for gift wraps with locked seals
59
- filter((seal) => seal !== undefined && isEncryptedContentLocked(seal)),
59
+ filter((seal) => seal !== undefined && isEncryptedContentUnlocked(seal) === false),
60
60
  // Only attempt to unlock seals once
61
61
  distinct((seal) => seal.id),
62
62
  // Get encrypted content from storage
@@ -84,7 +84,7 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
84
84
  .pipe(
85
85
  // Look for events that support encrypted content and are unlocked and not from the cache
86
86
  filter(([event]) => canHaveEncryptedContent(event.kind) &&
87
- !isEncryptedContentLocked(event) &&
87
+ isEncryptedContentUnlocked(event) &&
88
88
  !isEncryptedContentFromCache(event)),
89
89
  // Only persist the encrypted content once
90
90
  distinct(([event]) => event.id))
@@ -106,13 +106,13 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
106
106
  const persistSeals = combineLatest([merge(eventStore.update$, eventStore.insert$), storage$])
107
107
  .pipe(
108
108
  // Look for gift wraps that are unlocked
109
- filter(([event]) => event.kind === kinds.GiftWrap && !isEncryptedContentLocked(event)),
109
+ filter(([event]) => event.kind === kinds.GiftWrap && isEncryptedContentUnlocked(event)),
110
110
  // Get the seal event
111
111
  map(([gift, storage]) => [getGiftWrapSeal(gift), storage]),
112
112
  // Make sure the seal is defined
113
113
  filter(([seal]) => seal !== undefined),
114
114
  // Make sure seal is unlocked and not from cache
115
- filter(([seal]) => !isEncryptedContentLocked(seal) && !isEncryptedContentFromCache(seal)),
115
+ filter(([seal]) => isEncryptedContentUnlocked(seal) && !isEncryptedContentFromCache(seal)),
116
116
  // Only persist the seal once
117
117
  distinct(([seal]) => seal.id))
118
118
  .subscribe(async ([seal, storage]) => {
@@ -10,7 +10,12 @@ export interface EncryptedContentSigner {
10
10
  decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
11
11
  };
12
12
  }
13
+ /** Encryption method types */
13
14
  export type EncryptionMethod = "nip04" | "nip44";
15
+ /** Type for an event who's encrypted content is unlocked */
16
+ export type UnlockedEncryptedContent = {
17
+ [EncryptedContentSymbol]: string;
18
+ };
14
19
  /** A pair of encryption methods for encrypting and decrypting event content */
15
20
  export interface EncryptionMethods {
16
21
  encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
@@ -37,14 +42,16 @@ export declare function hasEncryptedContent<T extends {
37
42
  content: string;
38
43
  }>(event: T): boolean;
39
44
  /** Returns the encrypted content for an event if it is unlocked */
45
+ export declare function getEncryptedContent<T extends UnlockedEncryptedContent>(event: T): string;
40
46
  export declare function getEncryptedContent<T extends object>(event: T): string | undefined;
41
- /** Checks if the encrypted content is locked */
42
- export declare function isEncryptedContentLocked<T extends object>(event: T): boolean;
47
+ /** Checks if the encrypted content is unlocked and casts it to the {@link UnlockedEncryptedContent} type */
48
+ export declare function isEncryptedContentUnlocked<T extends object>(event: T): event is T & UnlockedEncryptedContent;
43
49
  /**
44
50
  * Unlocks the encrypted content in an event and caches it
45
51
  * @param event The event with content to decrypt
46
52
  * @param pubkey The other pubkey that encrypted the content
47
53
  * @param signer A signer to use to decrypt the content
54
+ * @throws If the event kind does not support encrypted content
48
55
  */
49
56
  export declare function unlockEncryptedContent<T extends {
50
57
  kind: number;
@@ -1,5 +1,5 @@
1
1
  import { kinds } from "nostr-tools";
2
- import { isEvent, notifyEventUpdate } from "./event.js";
2
+ import { notifyEventUpdate } from "./event.js";
3
3
  /** A symbol use to store the encrypted content of an event in memory */
4
4
  export const EncryptedContentSymbol = Symbol.for("encrypted-content");
5
5
  /** Various event kinds that can have encrypted content and which encryption method they use */
@@ -39,37 +39,40 @@ export function canHaveEncryptedContent(kind) {
39
39
  export function hasEncryptedContent(event) {
40
40
  return event.content.length > 0;
41
41
  }
42
- /** Returns the encrypted content for an event if it is unlocked */
43
42
  export function getEncryptedContent(event) {
44
43
  return Reflect.get(event, EncryptedContentSymbol);
45
44
  }
46
- /** Checks if the encrypted content is locked */
47
- export function isEncryptedContentLocked(event) {
48
- return Reflect.has(event, EncryptedContentSymbol) === false;
45
+ /** Checks if the encrypted content is unlocked and casts it to the {@link UnlockedEncryptedContent} type */
46
+ export function isEncryptedContentUnlocked(event) {
47
+ return Reflect.has(event, EncryptedContentSymbol) === true;
49
48
  }
50
49
  /**
51
50
  * Unlocks the encrypted content in an event and caches it
52
51
  * @param event The event with content to decrypt
53
52
  * @param pubkey The other pubkey that encrypted the content
54
53
  * @param signer A signer to use to decrypt the content
54
+ * @throws If the event kind does not support encrypted content
55
55
  */
56
56
  export async function unlockEncryptedContent(event, pubkey, signer) {
57
+ if (!canHaveEncryptedContent(event.kind))
58
+ throw new Error("Event kind does not support encrypted content");
59
+ // Get the encryption methods from the signer
57
60
  const encryption = getEncryptedContentEncryptionMethods(event.kind, signer);
58
61
  const plaintext = await encryption.decrypt(pubkey, event.content);
62
+ // Set the cached value and trigger update
59
63
  setEncryptedContentCache(event, plaintext);
64
+ // Return the decrypted content
60
65
  return plaintext;
61
66
  }
62
67
  /** Sets the encrypted content on an event and updates it if its part of an event store */
63
68
  export function setEncryptedContentCache(event, plaintext) {
64
69
  Reflect.set(event, EncryptedContentSymbol, plaintext);
65
70
  // if the event has been added to an event store, notify it
66
- if (isEvent(event))
67
- notifyEventUpdate(event);
71
+ notifyEventUpdate(event);
68
72
  }
69
73
  /** Removes the encrypted content cache on an event */
70
74
  export function lockEncryptedContent(event) {
71
75
  Reflect.deleteProperty(event, EncryptedContentSymbol);
72
76
  // if the event has been added to an event store, notify it
73
- if (isEvent(event))
74
- notifyEventUpdate(event);
77
+ notifyEventUpdate(event);
75
78
  }
@@ -9,7 +9,9 @@ import { IEventStoreStreams } from "../event-store/interface.js";
9
9
  * @param opts.maxBatchSize - The maximum number of events to write in a batch
10
10
  * @returns A function to stop the process
11
11
  */
12
- export declare function presistEventsToCache(eventStore: IEventStoreStreams, write: (events: NostrEvent[]) => Promise<void>, opts?: {
12
+ export declare function persistEventsToCache(eventStore: IEventStoreStreams, write: (events: NostrEvent[]) => Promise<void>, opts?: {
13
13
  maxBatchSize?: number;
14
14
  batchTime?: number;
15
15
  }): () => void;
16
+ /** @deprecated Use persistEventsToCache instead */
17
+ export declare const presistEventsToCache: typeof persistEventsToCache;
@@ -11,7 +11,7 @@ const log = logger.extend("event-cache");
11
11
  * @param opts.maxBatchSize - The maximum number of events to write in a batch
12
12
  * @returns A function to stop the process
13
13
  */
14
- export function presistEventsToCache(eventStore, write, opts) {
14
+ export function persistEventsToCache(eventStore, write, opts) {
15
15
  const time = opts?.batchTime ?? 5_000;
16
16
  // Save all new events to the cache
17
17
  const sub = eventStore.insert$
@@ -30,3 +30,5 @@ export function presistEventsToCache(eventStore, write, opts) {
30
30
  });
31
31
  return () => sub.unsubscribe();
32
32
  }
33
+ /** @deprecated Use persistEventsToCache instead */
34
+ export const presistEventsToCache = persistEventsToCache;
@@ -10,5 +10,11 @@ export declare function getTagValue<T extends {
10
10
  tags: string[][];
11
11
  content: string;
12
12
  }>(event: T, name: string): string | undefined;
13
+ /** Checks if an event has a public name / value tag*/
14
+ export declare function hasNameValueTag<T extends {
15
+ kind: number;
16
+ tags: string[][];
17
+ content: string;
18
+ }>(event: T, name: string, value: string): boolean;
13
19
  /** Returns a Set of tag names and values that are indexable */
14
20
  export declare function getIndexableTags(event: NostrEvent): Set<string>;
@@ -13,6 +13,10 @@ export function getTagValue(event, name) {
13
13
  return hiddenValue;
14
14
  return event.tags.find((t) => t[0] === name)?.[1];
15
15
  }
16
+ /** Checks if an event has a public name / value tag*/
17
+ export function hasNameValueTag(event, name, value) {
18
+ return event.tags.some((t) => t[0] === name && t[1] === value);
19
+ }
16
20
  /** Returns a Set of tag names and values that are indexable */
17
21
  export function getIndexableTags(event) {
18
22
  let indexable = Reflect.get(event, EventIndexableTagsSymbol);
@@ -1,5 +1,12 @@
1
1
  import { NostrEvent, VerifiedEvent } from "nostr-tools";
2
2
  import { IEventStore } from "../event-store/interface.js";
3
+ export { NostrEvent, EventTemplate, UnsignedEvent, verifiedSymbol, verifyEvent, VerifiedEvent } from "nostr-tools/pure";
4
+ export { bytesToHex, hexToBytes, insertEventIntoAscendingList, insertEventIntoDescendingList, binarySearch, } from "nostr-tools/utils";
5
+ export * as kinds from "nostr-tools/kinds";
6
+ /** An event with a known kind. this is used to know if events have been validated */
7
+ export type KnownEvent<K extends number> = Omit<NostrEvent, "kind"> & {
8
+ kind: K;
9
+ };
3
10
  /** A symbol on an event that marks which event store its part of */
4
11
  export declare const EventStoreSymbol: unique symbol;
5
12
  export declare const EventUIDSymbol: unique symbol;
@@ -38,7 +45,7 @@ export declare function isFromCache(event: NostrEvent): boolean;
38
45
  /** Returns the EventStore of an event if its been added to one */
39
46
  export declare function getParentEventStore<T extends object>(event: T): IEventStore | undefined;
40
47
  /** Notifies the events parent store that an event has been updated */
41
- export declare function notifyEventUpdate(event: NostrEvent): void;
48
+ export declare function notifyEventUpdate(event: any): void;
42
49
  /** Returns the replaceable identifier for a replaceable event */
43
50
  export declare function getReplaceableIdentifier(event: NostrEvent): string;
44
51
  /** Checks if an event is a NIP-70 protected event */
@@ -1,6 +1,10 @@
1
1
  import { verifiedSymbol } from "nostr-tools";
2
2
  import { isAddressableKind, isReplaceableKind } from "nostr-tools/kinds";
3
3
  import { getOrComputeCachedValue } from "./cache.js";
4
+ // Re-export types from nostr-tools
5
+ export { verifiedSymbol, verifyEvent } from "nostr-tools/pure";
6
+ export { bytesToHex, hexToBytes, insertEventIntoAscendingList, insertEventIntoDescendingList, binarySearch, } from "nostr-tools/utils";
7
+ export * as kinds from "nostr-tools/kinds";
4
8
  /** A symbol on an event that marks which event store its part of */
5
9
  export const EventStoreSymbol = Symbol.for("event-store");
6
10
  export const EventUIDSymbol = Symbol.for("event-uid");
@@ -81,6 +85,8 @@ export function getParentEventStore(event) {
81
85
  }
82
86
  /** Notifies the events parent store that an event has been updated */
83
87
  export function notifyEventUpdate(event) {
88
+ if (!isEvent(event))
89
+ return;
84
90
  const eventStore = getParentEventStore(event);
85
91
  if (eventStore)
86
92
  eventStore.update(event);
@@ -1,7 +1,7 @@
1
1
  import { NostrEvent } from "nostr-tools";
2
2
  export type FileMetadata = {
3
3
  /** URL of the file */
4
- url: string;
4
+ url?: string;
5
5
  /** MIME type */
6
6
  type?: string;
7
7
  /** sha256 hash of the file */
@@ -32,24 +32,19 @@ export type FileMetadata = {
32
32
  /** fallback URLs */
33
33
  fallback?: string[];
34
34
  };
35
+ /** Alias for {@link FileMetadata} */
35
36
  export type MediaAttachment = FileMetadata;
36
37
  /**
37
38
  * Parses file metadata tags into {@link FileMetadata}
38
39
  * @throws
39
40
  */
40
41
  export declare function parseFileMetadataTags(tags: string[][]): FileMetadata;
41
- /**
42
- * Parses a imeta tag into a {@link FileMetadata}
43
- * @throws
44
- */
42
+ /** Parses a imeta tag into a {@link FileMetadata} */
45
43
  export declare function getFileMetadataFromImetaTag(tag: string[]): FileMetadata;
46
44
  export declare const MediaAttachmentsSymbol: unique symbol;
47
45
  /** Gets all the media attachments on an event */
48
46
  export declare function getMediaAttachments(event: NostrEvent): FileMetadata[];
49
- /**
50
- * Gets {@link FileMetadata} for a NIP-94 kind 1063 event
51
- * @throws
52
- */
47
+ /** Gets {@link FileMetadata} for a NIP-94 kind 1063 event */
53
48
  export declare function getFileMetadata(file: NostrEvent): FileMetadata;
54
49
  /** Returns the last 64 length hex string in a URL */
55
50
  export declare function getSha256FromURL(url: string | URL): string | undefined;
@@ -16,8 +16,6 @@ export function parseFileMetadataTags(tags) {
16
16
  break;
17
17
  }
18
18
  }
19
- if (!fields.url)
20
- throw new Error("Missing required url in file metadata");
21
19
  const metadata = { url: fields.url, fallback };
22
20
  // parse size
23
21
  if (fields.size)
@@ -47,10 +45,7 @@ export function parseFileMetadataTags(tags) {
47
45
  metadata.blurhash = fields.blurhash;
48
46
  return metadata;
49
47
  }
50
- /**
51
- * Parses a imeta tag into a {@link FileMetadata}
52
- * @throws
53
- */
48
+ /** Parses a imeta tag into a {@link FileMetadata} */
54
49
  export function getFileMetadataFromImetaTag(tag) {
55
50
  const parts = tag.slice(1);
56
51
  const tags = [];
@@ -81,10 +76,7 @@ export function getMediaAttachments(event) {
81
76
  .filter((a) => !!a);
82
77
  });
83
78
  }
84
- /**
85
- * Gets {@link FileMetadata} for a NIP-94 kind 1063 event
86
- * @throws
87
- */
79
+ /** Gets {@link FileMetadata} for a NIP-94 kind 1063 event */
88
80
  export function getFileMetadata(file) {
89
81
  return parseFileMetadataTags(file.tags);
90
82
  }
@@ -1,12 +1,13 @@
1
1
  import { Filter, NostrEvent } from "nostr-tools";
2
+ export { Filter } from "nostr-tools/filter";
2
3
  /**
3
- * Copied from nostr-tools and modified to use getIndexableTags
4
+ * Copied from nostr-tools and modified to use {@link getIndexableTags}
4
5
  * @see https://github.com/nbd-wtf/nostr-tools/blob/a61cde77eacc9518001f11d7f67f1a50ae05fd80/filter.ts
5
6
  */
6
7
  export declare function matchFilter(filter: Filter, event: NostrEvent): boolean;
7
- /** Copied from nostr-tools */
8
+ /** Copied from nostr-tools and modified to use {@link matchFilter} */
8
9
  export declare function matchFilters(filters: Filter[], event: NostrEvent): boolean;
9
- /** Copied from nostr-tools and modified to support undefined */
10
+ /** Copied from nostr-tools and modified to support undefined values */
10
11
  export declare function mergeFilters(...filters: Filter[]): Filter;
11
12
  /** Check if two filters are equal */
12
13
  export declare function isFilterEqual(a: Filter | Filter[], b: Filter | Filter[]): boolean;
@@ -1,7 +1,7 @@
1
1
  import equal from "fast-deep-equal";
2
2
  import { getIndexableTags } from "./event-tags.js";
3
3
  /**
4
- * Copied from nostr-tools and modified to use getIndexableTags
4
+ * Copied from nostr-tools and modified to use {@link getIndexableTags}
5
5
  * @see https://github.com/nbd-wtf/nostr-tools/blob/a61cde77eacc9518001f11d7f67f1a50ae05fd80/filter.ts
6
6
  */
7
7
  export function matchFilter(filter, event) {
@@ -31,7 +31,7 @@ export function matchFilter(filter, event) {
31
31
  return false;
32
32
  return true;
33
33
  }
34
- /** Copied from nostr-tools */
34
+ /** Copied from nostr-tools and modified to use {@link matchFilter} */
35
35
  export function matchFilters(filters, event) {
36
36
  for (let i = 0; i < filters.length; i++) {
37
37
  if (matchFilter(filters[i], event)) {
@@ -40,7 +40,7 @@ export function matchFilters(filters, event) {
40
40
  }
41
41
  return false;
42
42
  }
43
- /** Copied from nostr-tools and modified to support undefined */
43
+ /** Copied from nostr-tools and modified to support undefined values */
44
44
  export function mergeFilters(...filters) {
45
45
  let result = {};
46
46
  for (let i = 0; i < filters.length; i++) {
@@ -1,11 +1,12 @@
1
- import { NostrEvent, UnsignedEvent } from "nostr-tools";
2
- import { EventSet } from "../event-store/event-set.js";
1
+ import { kinds, NostrEvent, UnsignedEvent } from "nostr-tools";
2
+ import { EventMemory } from "../event-store/event-memory.js";
3
3
  import { EncryptedContentSigner } from "./encrypted-content.js";
4
+ import { KnownEvent } from "./event.js";
4
5
  /**
5
6
  * An internal event set to keep track of seals and rumors
6
- * This is intentially isolated from the main applications event store so to prevent seals and rumors from being leaked
7
+ * This is intentionally isolated from the main applications event store so to prevent seals and rumors from being leaked
7
8
  */
8
- export declare const internalGiftWrapEvents: EventSet;
9
+ export declare const internalGiftWrapEvents: EventMemory;
9
10
  export type Rumor = UnsignedEvent & {
10
11
  id: string;
11
12
  };
@@ -15,24 +16,43 @@ export declare const SealSymbol: unique symbol;
15
16
  export declare const RumorSymbol: unique symbol;
16
17
  /** Used to store a reference to the parent gift wrap event on seals (upstream) */
17
18
  export declare const GiftWrapSymbol: unique symbol;
19
+ /** A gift wrap event that knows its seal event */
20
+ export type UnlockedGiftWrapEvent = KnownEvent<kinds.GiftWrap> & {
21
+ /** Downstream seal event */
22
+ [SealSymbol]: UnlockedSeal;
23
+ };
24
+ /** A seal that knows its parent gift wrap event */
25
+ export type UnlockedSeal = KnownEvent<kinds.Seal> & {
26
+ /** Upstream gift wrap event */
27
+ [SealSymbol]: UnlockedGiftWrapEvent;
28
+ /** Downstream rumor event */
29
+ [RumorSymbol]: Rumor;
30
+ };
18
31
  /** Checks if an event is a rumor (normal event with "id" and no "sig") */
19
32
  export declare function isRumor(event: any): event is Rumor;
20
33
  /** Returns all the parent gift wraps for a seal event */
21
- export declare function getSealGiftWrap(seal: NostrEvent): NostrEvent | undefined;
34
+ export declare function getSealGiftWrap(seal: UnlockedSeal): UnlockedGiftWrapEvent;
35
+ export declare function getSealGiftWrap(seal: NostrEvent): UnlockedGiftWrapEvent | undefined;
22
36
  /** Returns all the parent seals for a rumor event */
23
- export declare function getRumorSeals(rumor: Rumor): NostrEvent[];
37
+ export declare function getRumorSeals(rumor: Rumor): UnlockedSeal[];
24
38
  /** Returns all the parent gift wraps for a rumor event */
25
- export declare function getRumorGiftWraps(rumor: Rumor): NostrEvent[];
26
- /** Checks if a seal event is locked */
27
- export declare function isSealLocked(seal: NostrEvent): boolean;
28
- /** Gets the rumor from a seal event */
39
+ export declare function getRumorGiftWraps(rumor: Rumor): UnlockedGiftWrapEvent[];
40
+ /** Checks if a seal event is locked and casts it to the {@link UnlockedSeal} type */
41
+ export declare function isSealUnlocked(seal: NostrEvent): seal is UnlockedSeal;
42
+ /** Returns if a gift-wrap event or gift-wrap seal is locked */
43
+ export declare function isGiftWrapUnlocked(gift: NostrEvent): gift is UnlockedGiftWrapEvent;
44
+ /**
45
+ * Gets the rumor from a seal event
46
+ * @throws {Error} If the author of the rumor event does not match the author of the seal
47
+ */
48
+ export declare function getSealRumor(seal: UnlockedSeal): Rumor;
29
49
  export declare function getSealRumor(seal: NostrEvent): Rumor | undefined;
30
- /** Returns the seal event in a gift-wrap event */
50
+ /** Returns the seal event in a gift-wrap -> seal (downstream) */
51
+ export declare function getGiftWrapSeal(gift: UnlockedGiftWrapEvent): UnlockedSeal;
31
52
  export declare function getGiftWrapSeal(gift: NostrEvent): NostrEvent | undefined;
32
- /** Returns the unsigned rumor in the gift-wrap */
53
+ /** Returns the unsigned rumor in the gift-wrap -> seal -> rumor (downstream) */
54
+ export declare function getGiftWrapRumor(gift: UnlockedGiftWrapEvent): Rumor;
33
55
  export declare function getGiftWrapRumor(gift: NostrEvent): Rumor | undefined;
34
- /** Returns if a gift-wrap event or gift-wrap seal is locked */
35
- export declare function isGiftWrapLocked(gift: NostrEvent): boolean;
36
56
  /**
37
57
  * Unlocks a seal event and returns the rumor event
38
58
  * @throws {Error} If the author of the rumor event does not match the author of the seal
@@ -43,4 +63,5 @@ export declare function unlockSeal(seal: NostrEvent, signer: EncryptedContentSig
43
63
  * @throws {Error} If the author of the rumor event does not match the author of the seal
44
64
  */
45
65
  export declare function unlockGiftWrap(gift: NostrEvent, signer: EncryptedContentSigner): Promise<Rumor>;
66
+ /** Locks a gift-wrap event and seals its seal event */
46
67
  export declare function lockGiftWrap(gift: NostrEvent): void;