applesauce-core 3.0.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/event-store/async-event-store.d.ts +134 -0
  2. package/dist/event-store/async-event-store.js +349 -0
  3. package/dist/event-store/{event-set.d.ts → event-memory.d.ts} +15 -25
  4. package/dist/event-store/{event-set.js → event-memory.js} +43 -53
  5. package/dist/event-store/event-store.d.ts +57 -63
  6. package/dist/event-store/event-store.js +111 -190
  7. package/dist/event-store/index.d.ts +2 -1
  8. package/dist/event-store/index.js +2 -1
  9. package/dist/event-store/interface.d.ts +111 -38
  10. package/dist/event-store/model-mixin.d.ts +59 -0
  11. package/dist/event-store/model-mixin.js +147 -0
  12. package/dist/helpers/app-data.d.ts +39 -0
  13. package/dist/helpers/app-data.js +68 -0
  14. package/dist/helpers/bookmarks.d.ts +11 -1
  15. package/dist/helpers/bookmarks.js +29 -4
  16. package/dist/helpers/comment.d.ts +13 -20
  17. package/dist/helpers/comment.js +16 -27
  18. package/dist/helpers/contacts.d.ts +10 -1
  19. package/dist/helpers/contacts.js +30 -3
  20. package/dist/helpers/encrypted-content-cache.js +7 -7
  21. package/dist/helpers/encrypted-content.d.ts +9 -2
  22. package/dist/helpers/encrypted-content.js +12 -9
  23. package/dist/helpers/event-cache.d.ts +3 -1
  24. package/dist/helpers/event-cache.js +3 -1
  25. package/dist/helpers/event-tags.d.ts +6 -0
  26. package/dist/helpers/event-tags.js +4 -0
  27. package/dist/helpers/event.d.ts +8 -1
  28. package/dist/helpers/event.js +6 -0
  29. package/dist/helpers/file-metadata.d.ts +4 -9
  30. package/dist/helpers/file-metadata.js +2 -10
  31. package/dist/helpers/filter.d.ts +4 -3
  32. package/dist/helpers/filter.js +3 -3
  33. package/dist/helpers/gift-wraps.d.ts +35 -14
  34. package/dist/helpers/gift-wraps.js +59 -50
  35. package/dist/helpers/groups.d.ts +2 -5
  36. package/dist/helpers/groups.js +5 -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 +19 -8
  44. package/dist/helpers/legacy-messages.js +25 -14
  45. package/dist/helpers/lists.js +2 -1
  46. package/dist/helpers/lnurl.d.ts +4 -0
  47. package/dist/helpers/lnurl.js +7 -3
  48. package/dist/helpers/mailboxes.d.ts +2 -6
  49. package/dist/helpers/mailboxes.js +26 -20
  50. package/dist/helpers/mutes.d.ts +11 -1
  51. package/dist/helpers/mutes.js +30 -5
  52. package/dist/helpers/picture-post.d.ts +2 -1
  53. package/dist/helpers/pointers.d.ts +1 -1
  54. package/dist/helpers/pointers.js +2 -1
  55. package/dist/helpers/profile.d.ts +7 -3
  56. package/dist/helpers/profile.js +7 -8
  57. package/dist/helpers/relay-selection.d.ts +13 -0
  58. package/dist/helpers/relay-selection.js +84 -0
  59. package/dist/helpers/relays.d.ts +3 -1
  60. package/dist/helpers/relays.js +18 -2
  61. package/dist/helpers/url.js +3 -3
  62. package/dist/helpers/wrapped-messages.d.ts +10 -1
  63. package/dist/helpers/wrapped-messages.js +15 -2
  64. package/dist/helpers/zap.d.ts +16 -14
  65. package/dist/helpers/zap.js +26 -28
  66. package/dist/models/common.d.ts +4 -4
  67. package/dist/models/common.js +79 -40
  68. package/dist/models/gift-wrap.d.ts +1 -1
  69. package/dist/models/gift-wrap.js +4 -4
  70. package/dist/models/index.d.ts +1 -0
  71. package/dist/models/index.js +1 -0
  72. package/dist/models/legacy-messages.d.ts +2 -2
  73. package/dist/models/legacy-messages.js +13 -10
  74. package/dist/models/outbox.d.ts +13 -0
  75. package/dist/models/outbox.js +18 -0
  76. package/dist/models/profile.d.ts +1 -1
  77. package/dist/models/zaps.d.ts +5 -4
  78. package/dist/models/zaps.js +2 -2
  79. package/dist/observable/index.d.ts +4 -3
  80. package/dist/observable/index.js +5 -4
  81. package/dist/observable/map-events-to-store.d.ts +5 -3
  82. package/dist/observable/map-events-to-store.js +14 -3
  83. package/dist/observable/map-events-to-timeline.js +12 -0
  84. package/dist/observable/relay-selection.d.ts +7 -0
  85. package/dist/observable/relay-selection.js +38 -0
  86. package/package.json +5 -3
  87. package/dist/observable/map-events-timeline.js +0 -9
  88. /package/dist/observable/{map-events-timeline.d.ts → map-events-to-timeline.d.ts} +0 -0
@@ -1,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;
@@ -1,12 +1,12 @@
1
- import { verifyEvent } from "nostr-tools";
2
- import { EventSet } from "../event-store/event-set.js";
3
- import { getEncryptedContent, isEncryptedContentLocked, lockEncryptedContent, unlockEncryptedContent, } from "./encrypted-content.js";
1
+ import { kinds, verifyEvent } from "nostr-tools";
2
+ import { EventMemory } from "../event-store/event-memory.js";
3
+ import { getEncryptedContent, isEncryptedContentUnlocked, lockEncryptedContent, unlockEncryptedContent, } from "./encrypted-content.js";
4
4
  import { notifyEventUpdate } from "./event.js";
5
5
  /**
6
6
  * An internal event set to keep track of seals and rumors
7
- * 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
8
8
  */
9
- export const internalGiftWrapEvents = new EventSet();
9
+ export const internalGiftWrapEvents = new EventMemory();
10
10
  /** Used to store a reference to the seal event on gift wraps (downstream) or the seal event on rumors (upstream[]) */
11
11
  export const SealSymbol = Symbol.for("seal");
12
12
  /** Used to store a reference to the rumor on seals (downstream) */
@@ -40,7 +40,6 @@ export function isRumor(event) {
40
40
  typeof event.created_at === "number" &&
41
41
  event.created_at > 0);
42
42
  }
43
- /** Returns all the parent gift wraps for a seal event */
44
43
  export function getSealGiftWrap(seal) {
45
44
  return Reflect.get(seal, GiftWrapSymbol);
46
45
  }
@@ -64,21 +63,37 @@ export function getRumorGiftWraps(rumor) {
64
63
  }
65
64
  return Array.from(giftWraps);
66
65
  }
67
- /** Checks if a seal event is locked */
68
- export function isSealLocked(seal) {
69
- return isEncryptedContentLocked(seal);
66
+ /** Checks if a seal event is locked and casts it to the {@link UnlockedSeal} type */
67
+ export function isSealUnlocked(seal) {
68
+ return isEncryptedContentUnlocked(seal) === true && Reflect.has(seal, RumorSymbol) === true;
69
+ }
70
+ /** Returns if a gift-wrap event or gift-wrap seal is locked */
71
+ export function isGiftWrapUnlocked(gift) {
72
+ if (isEncryptedContentUnlocked(gift) === false)
73
+ return false;
74
+ // Get the seal event
75
+ const seal = getGiftWrapSeal(gift);
76
+ if (!seal)
77
+ return false;
78
+ // If seal is locked, return false
79
+ if (!isSealUnlocked(seal))
80
+ return false;
81
+ return true;
70
82
  }
71
- /** Gets the rumor from a seal event */
72
83
  export function getSealRumor(seal) {
73
- // Returned cached rumor if it exists (downstream)
74
- const cached = Reflect.get(seal, RumorSymbol);
75
- if (cached)
76
- return cached;
84
+ // Non seal events cant have rumors
85
+ if (seal.kind !== kinds.Seal)
86
+ return undefined;
87
+ // If unlocked return the rumor
88
+ if (isSealUnlocked(seal))
89
+ return seal[RumorSymbol];
77
90
  // Get the encrypted content plaintext
78
- const plaintext = getEncryptedContent(seal);
79
- if (!plaintext)
91
+ const content = getEncryptedContent(seal);
92
+ // Return undefined if the content is not found
93
+ if (!content)
80
94
  return undefined;
81
- let rumor = JSON.parse(plaintext);
95
+ // Parse the content as a rumor event
96
+ let rumor = JSON.parse(content);
82
97
  // Check if the rumor event already exists in the internal event set
83
98
  const existing = internalGiftWrapEvents.getEvent(rumor.id);
84
99
  if (existing)
@@ -87,23 +102,26 @@ export function getSealRumor(seal) {
87
102
  else
88
103
  // Add to the internal event set
89
104
  internalGiftWrapEvents.add(rumor);
105
+ // Throw an error if the seal and rumor authors do not match
106
+ if (rumor.pubkey !== seal.pubkey)
107
+ throw new Error("Seal author does not match rumor author");
90
108
  // Save a reference to the parent seal event
91
109
  addParentSealReference(rumor, seal);
92
- // Save a reference to the rumor on the seal (downstream)
110
+ // Cache the rumor event
93
111
  Reflect.set(seal, RumorSymbol, rumor);
94
112
  return rumor;
95
113
  }
96
- /** Returns the seal event in a gift-wrap event */
97
114
  export function getGiftWrapSeal(gift) {
98
115
  // Returned cached seal if it exists (downstream)
99
- const cached = Reflect.get(gift, SealSymbol);
100
- if (cached)
101
- return cached;
102
- // Get the encrypted content plaintext
103
- const plaintext = getEncryptedContent(gift);
104
- if (!plaintext)
116
+ if (Reflect.has(gift, SealSymbol))
117
+ return Reflect.get(gift, SealSymbol);
118
+ // Get the encrypted content
119
+ const content = getEncryptedContent(gift);
120
+ // Return undefined if the content is not found
121
+ if (!content)
105
122
  return undefined;
106
- let seal = JSON.parse(plaintext);
123
+ // Parse seal as nostr event
124
+ let seal = JSON.parse(content);
107
125
  // Check if the seal event already exists in the internal event set
108
126
  const existing = internalGiftWrapEvents.getEvent(seal.id);
109
127
  if (existing) {
@@ -122,39 +140,27 @@ export function getGiftWrapSeal(gift) {
122
140
  Reflect.set(gift, SealSymbol, seal);
123
141
  return seal;
124
142
  }
125
- /** Returns the unsigned rumor in the gift-wrap */
126
143
  export function getGiftWrapRumor(gift) {
127
144
  const seal = getGiftWrapSeal(gift);
128
145
  if (!seal)
129
146
  return undefined;
130
147
  return getSealRumor(seal);
131
148
  }
132
- /** Returns if a gift-wrap event or gift-wrap seal is locked */
133
- export function isGiftWrapLocked(gift) {
134
- if (isEncryptedContentLocked(gift))
135
- return true;
136
- else {
137
- const seal = getGiftWrapSeal(gift);
138
- if (!seal || isSealLocked(seal))
139
- return true;
140
- else
141
- return false;
142
- }
143
- }
144
149
  /**
145
150
  * Unlocks a seal event and returns the rumor event
146
151
  * @throws {Error} If the author of the rumor event does not match the author of the seal
147
152
  */
148
153
  export async function unlockSeal(seal, signer) {
149
- if (isEncryptedContentLocked(seal))
150
- await unlockEncryptedContent(seal, seal.pubkey, signer);
151
- // Parse the rumor event
154
+ // If already unlocked, return the rumor
155
+ if (isSealUnlocked(seal))
156
+ return seal[RumorSymbol];
157
+ // unlock encrypted content as needed
158
+ await unlockEncryptedContent(seal, seal.pubkey, signer);
152
159
  const rumor = getSealRumor(seal);
153
160
  if (!rumor)
154
161
  throw new Error("Failed to read rumor in gift wrap");
155
- // Check if the seal and rumor authors match
156
- if (rumor.pubkey !== seal.pubkey)
157
- throw new Error("Seal author does not match rumor author");
162
+ // Notify event store
163
+ notifyEventUpdate(seal);
158
164
  return rumor;
159
165
  }
160
166
  /**
@@ -162,11 +168,13 @@ export async function unlockSeal(seal, signer) {
162
168
  * @throws {Error} If the author of the rumor event does not match the author of the seal
163
169
  */
164
170
  export async function unlockGiftWrap(gift, signer) {
165
- // First unlock the gift-wrap event
166
- if (isEncryptedContentLocked(gift))
167
- await unlockEncryptedContent(gift, gift.pubkey, signer);
168
- // Get the seal event
169
- const seal = getGiftWrapSeal(gift);
171
+ // If already unlocked, return the rumor
172
+ if (isGiftWrapUnlocked(gift))
173
+ return getGiftWrapRumor(gift);
174
+ // Unlock the encrypted content
175
+ await unlockEncryptedContent(gift, gift.pubkey, signer);
176
+ // Parse seal as nostr event
177
+ let seal = getGiftWrapSeal(gift);
170
178
  if (!seal)
171
179
  throw new Error("Failed to read seal in gift wrap");
172
180
  // Unlock the seal event
@@ -175,6 +183,7 @@ export async function unlockGiftWrap(gift, signer) {
175
183
  notifyEventUpdate(gift);
176
184
  return rumor;
177
185
  }
186
+ /** Locks a gift-wrap event and seals its seal event */
178
187
  export function lockGiftWrap(gift) {
179
188
  const seal = getGiftWrapSeal(gift);
180
189
  if (seal) {
@@ -10,11 +10,8 @@ export type GroupPointer = {
10
10
  /** The name of the group */
11
11
  name?: string;
12
12
  };
13
- /**
14
- * decodes a group identifier into a group pointer object
15
- * @throws
16
- */
17
- export declare function decodeGroupPointer(str: string): GroupPointer;
13
+ /** decodes a group identifier into a group pointer object */
14
+ export declare function decodeGroupPointer(str: string): GroupPointer | null;
18
15
  /** Converts a group pointer into a group identifier */
19
16
  export declare function encodeGroupPointer(pointer: GroupPointer): string;
20
17
  export declare const GroupsPublicSymbol: unique symbol;