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
@@ -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;
@@ -4,14 +4,11 @@ import { processTags } from "./tags.js";
4
4
  import { normalizeURL } from "./url.js";
5
5
  export const GROUPS_LIST_KIND = 10009;
6
6
  export const GROUP_MESSAGE_KIND = 9;
7
- /**
8
- * decodes a group identifier into a group pointer object
9
- * @throws
10
- */
7
+ /** decodes a group identifier into a group pointer object */
11
8
  export function decodeGroupPointer(str) {
12
9
  let [relay, id] = str.split("'");
13
10
  if (!relay)
14
- throw new Error("Group pointer missing relay");
11
+ return null;
15
12
  // Prepend wss:// if missing
16
13
  if (!relay.match(/^wss?:/))
17
14
  relay = `wss://${relay}`;
@@ -1,8 +1,13 @@
1
- import { EncryptedContentSigner, EncryptionMethod, getEncryptedContentEncryptionMethods } from "./encrypted-content.js";
1
+ import { EncryptedContentSigner, EncryptionMethod, getEncryptedContentEncryptionMethods, UnlockedEncryptedContent } from "./encrypted-content.js";
2
+ /** Symbol for caching hidden content. Alias for {@link EncryptedContentSymbol} */
2
3
  export declare const HiddenContentSymbol: symbol;
4
+ /** Alias for {@link EncryptedContentSigner} */
3
5
  export interface HiddenContentSigner extends EncryptedContentSigner {
4
6
  }
7
+ /** Alias for {@link getEncryptedContentEncryptionMethods} */
5
8
  export declare const getHiddenContentEncryptionMethods: typeof getEncryptedContentEncryptionMethods;
9
+ /** Type for events with unlocked hidden content. alias for {@link UnlockedEncryptedContent} */
10
+ export type UnlockedHiddenContent = UnlockedEncryptedContent;
6
11
  /** Various event kinds that can have hidden content */
7
12
  export declare const HiddenContentKinds: Set<number>;
8
13
  /** Sets the encryption method for hidden content on a kind */
@@ -14,12 +19,16 @@ export declare function hasHiddenContent<T extends {
14
19
  kind: number;
15
20
  content: string;
16
21
  }>(event: T): boolean;
17
- /** Checks if the hidden content is locked */
18
- export declare function isHiddenContentLocked<T extends object>(event: T): boolean;
22
+ /** Checks if the hidden content is unlocked and casts it to the {@link UnlockedEncryptedContent} type */
23
+ export declare function isHiddenContentUnlocked<T extends {
24
+ kind: number;
25
+ }>(event: T): event is T & UnlockedEncryptedContent;
19
26
  /** Returns the hidden content for an event if they are unlocked */
20
27
  export declare function getHiddenContent<T extends {
21
28
  kind: number;
22
- content: string;
29
+ } & UnlockedHiddenContent>(event: T): string;
30
+ export declare function getHiddenContent<T extends {
31
+ kind: number;
23
32
  }>(event: T): string | undefined;
24
33
  /**
25
34
  * Unlocks the hidden content in the event
@@ -34,7 +43,7 @@ export declare function unlockHiddenContent<T extends {
34
43
  }>(event: T, signer: EncryptedContentSigner, override?: EncryptionMethod): Promise<string>;
35
44
  /**
36
45
  * Sets the hidden content on an event and updates it if its part of an event store
37
- * @throws
46
+ * @throws If the event kind does not support hidden content
38
47
  */
39
48
  export declare function setHiddenContentCache<T extends {
40
49
  kind: number;
@@ -1,7 +1,8 @@
1
1
  import { kinds } from "nostr-tools";
2
- import { canHaveEncryptedContent, EncryptedContentSymbol, getEncryptedContent, getEncryptedContentEncryptionMethods, hasEncryptedContent, isEncryptedContentLocked, lockEncryptedContent, setEncryptedContentCache, setEncryptedContentEncryptionMethod, } from "./encrypted-content.js";
3
- // reexport from encrypted-content
2
+ import { canHaveEncryptedContent, EncryptedContentSymbol, getEncryptedContent, getEncryptedContentEncryptionMethods, hasEncryptedContent, isEncryptedContentUnlocked, lockEncryptedContent, setEncryptedContentCache, setEncryptedContentEncryptionMethod, } from "./encrypted-content.js";
3
+ /** Symbol for caching hidden content. Alias for {@link EncryptedContentSymbol} */
4
4
  export const HiddenContentSymbol = EncryptedContentSymbol;
5
+ /** Alias for {@link getEncryptedContentEncryptionMethods} */
5
6
  export const getHiddenContentEncryptionMethods = getEncryptedContentEncryptionMethods;
6
7
  /** Various event kinds that can have hidden content */
7
8
  export const HiddenContentKinds = new Set([setEncryptedContentEncryptionMethod(kinds.DraftLong, "nip04")]);
@@ -18,14 +19,17 @@ export function canHaveHiddenContent(kind) {
18
19
  export function hasHiddenContent(event) {
19
20
  return canHaveHiddenContent(event.kind) && hasEncryptedContent(event);
20
21
  }
21
- /** Checks if the hidden content is locked */
22
- export function isHiddenContentLocked(event) {
23
- return isEncryptedContentLocked(event);
22
+ /** Checks if the hidden content is unlocked and casts it to the {@link UnlockedEncryptedContent} type */
23
+ export function isHiddenContentUnlocked(event) {
24
+ if (!canHaveHiddenContent(event.kind))
25
+ return false;
26
+ return isEncryptedContentUnlocked(event) && Reflect.has(event, HiddenContentSymbol) === true;
24
27
  }
25
- /** Returns the hidden content for an event if they are unlocked */
26
28
  export function getHiddenContent(event) {
27
- if (!canHaveHiddenContent(event.kind) || isHiddenContentLocked(event))
29
+ if (!canHaveHiddenContent(event.kind))
28
30
  return undefined;
31
+ if (isHiddenContentUnlocked(event))
32
+ return event[EncryptedContentSymbol];
29
33
  return getEncryptedContent(event);
30
34
  }
31
35
  /**
@@ -37,14 +41,21 @@ export function getHiddenContent(event) {
37
41
  export async function unlockHiddenContent(event, signer, override) {
38
42
  if (!canHaveHiddenContent(event.kind))
39
43
  throw new Error("Event kind does not support hidden content");
44
+ // If the encrypted content is already unlocked, return the cached value
45
+ if (isEncryptedContentUnlocked(event))
46
+ return event[EncryptedContentSymbol];
47
+ // Get the encryption method from the signer
40
48
  const encryption = getEncryptedContentEncryptionMethods(event.kind, signer, override);
49
+ // Decrypt the content using the events pubkey
41
50
  const plaintext = await encryption.decrypt(event.pubkey, event.content);
51
+ // Set the cached value
42
52
  setHiddenContentCache(event, plaintext);
53
+ // Return the decrypted content
43
54
  return plaintext;
44
55
  }
45
56
  /**
46
57
  * Sets the hidden content on an event and updates it if its part of an event store
47
- * @throws
58
+ * @throws If the event kind does not support hidden content
48
59
  */
49
60
  export function setHiddenContentCache(event, plaintext) {
50
61
  if (!canHaveHiddenContent(event.kind))
@@ -1,6 +1,11 @@
1
- import { HiddenContentSigner } from "./hidden-content.js";
2
1
  import { EncryptionMethod } from "./encrypted-content.js";
2
+ import { HiddenContentSigner, UnlockedHiddenContent } from "./hidden-content.js";
3
+ /** Symbol for caching hidden tags. */
3
4
  export declare const HiddenTagsSymbol: unique symbol;
5
+ /** Type for events with unlocked hidden tags */
6
+ export type UnlockedHiddenTags = UnlockedHiddenContent & {
7
+ [HiddenTagsSymbol]: string[][];
8
+ };
4
9
  /** Various event kinds that can have hidden tags */
5
10
  export declare const HiddenTagsKinds: Set<number>;
6
11
  /** Checks if an event can have hidden tags */
@@ -12,15 +17,19 @@ export declare function hasHiddenTags<T extends {
12
17
  kind: number;
13
18
  content: string;
14
19
  }>(event: T): boolean;
20
+ /** Returns either nip04 or nip44 encryption method depending on list kind */
21
+ export declare function getHiddenTagsEncryptionMethods(kind: number, signer: HiddenContentSigner): import("./encrypted-content.js").EncryptionMethods;
22
+ /** Checks if the hidden tags are locked and casts it to the {@link UnlockedHiddenTags} type */
23
+ export declare function isHiddenTagsUnlocked<T extends {
24
+ kind: number;
25
+ }>(event: T): event is T & UnlockedHiddenTags;
15
26
  /** Returns the hidden tags for an event if they are unlocked */
16
27
  export declare function getHiddenTags<T extends {
17
28
  kind: number;
18
- content: string;
29
+ } & UnlockedHiddenTags>(event: T): string[][];
30
+ export declare function getHiddenTags<T extends {
31
+ kind: number;
19
32
  }>(event: T): string[][] | undefined;
20
- /** Checks if the hidden tags are locked */
21
- export declare function isHiddenTagsLocked<T extends object>(event: T): boolean;
22
- /** Returns either nip04 or nip44 encryption method depending on list kind */
23
- export declare function getHiddenTagsEncryptionMethods(kind: number, signer: HiddenContentSigner): import("./encrypted-content.js").EncryptionMethods;
24
33
  /**
25
34
  * Decrypts the private list
26
35
  * @param event The list event to decrypt
@@ -35,7 +44,7 @@ export declare function unlockHiddenTags<T extends {
35
44
  }>(event: T, signer: HiddenContentSigner, override?: EncryptionMethod): Promise<string[][]>;
36
45
  /**
37
46
  * Sets the hidden tags on an event and updates it if its part of an event store
38
- * @throws
47
+ * @throws If the event kind does not support hidden tags
39
48
  */
40
49
  export declare function setHiddenTagsCache<T extends {
41
50
  kind: number;
@@ -1,7 +1,7 @@
1
1
  import { kinds } from "nostr-tools";
2
- import { getOrComputeCachedValue } from "./cache.js";
3
- import { canHaveHiddenContent, getHiddenContent, getHiddenContentEncryptionMethods, hasHiddenContent, isHiddenContentLocked, lockHiddenContent, setHiddenContentCache, setHiddenContentEncryptionMethod, unlockHiddenContent, } from "./hidden-content.js";
4
2
  import { GROUPS_LIST_KIND } from "./groups.js";
3
+ import { canHaveHiddenContent, getHiddenContent, getHiddenContentEncryptionMethods, hasHiddenContent, isHiddenContentUnlocked, lockHiddenContent, setHiddenContentCache, setHiddenContentEncryptionMethod, unlockHiddenContent, } from "./hidden-content.js";
4
+ /** Symbol for caching hidden tags. */
5
5
  export const HiddenTagsSymbol = Symbol.for("hidden-tags");
6
6
  /** Various event kinds that can have hidden tags */
7
7
  export const HiddenTagsKinds = new Set([
@@ -33,27 +33,38 @@ export function setHiddenTagsEncryptionMethod(kind, method) {
33
33
  export function hasHiddenTags(event) {
34
34
  return canHaveHiddenTags(event.kind) && hasHiddenContent(event);
35
35
  }
36
- /** Returns the hidden tags for an event if they are unlocked */
37
- export function getHiddenTags(event) {
38
- if (!canHaveHiddenTags(event.kind) || isHiddenTagsLocked(event))
39
- return undefined;
40
- return getOrComputeCachedValue(event, HiddenTagsSymbol, () => {
41
- const plaintext = getHiddenContent(event);
42
- const parsed = JSON.parse(plaintext);
43
- if (!Array.isArray(parsed))
44
- throw new Error("Content is not an array of tags");
45
- // Convert array to tags array string[][]
46
- return parsed.filter((t) => Array.isArray(t)).map((t) => t.map((v) => String(v)));
47
- });
48
- }
49
- /** Checks if the hidden tags are locked */
50
- export function isHiddenTagsLocked(event) {
51
- return isHiddenContentLocked(event);
52
- }
53
36
  /** Returns either nip04 or nip44 encryption method depending on list kind */
54
37
  export function getHiddenTagsEncryptionMethods(kind, signer) {
55
38
  return getHiddenContentEncryptionMethods(kind, signer);
56
39
  }
40
+ /** Checks if the hidden tags are locked and casts it to the {@link UnlockedHiddenTags} type */
41
+ export function isHiddenTagsUnlocked(event) {
42
+ if (!canHaveHiddenTags(event.kind))
43
+ return false;
44
+ return isHiddenContentUnlocked(event) && Reflect.has(event, `HiddenTagsSymbol`);
45
+ }
46
+ export function getHiddenTags(event) {
47
+ if (!canHaveHiddenTags(event.kind))
48
+ return undefined;
49
+ // If the hidden tags are already unlocked, return the cached value
50
+ if (isHiddenTagsUnlocked(event))
51
+ return event[HiddenTagsSymbol];
52
+ // unlock hidden content is needed
53
+ const content = getHiddenContent(event);
54
+ // Return undefined if the hidden content is not unlocked
55
+ if (content === undefined)
56
+ return undefined;
57
+ // Parse the hidden content as an array of tags
58
+ const parsed = JSON.parse(content);
59
+ // Throw error if content is not an array of tags
60
+ if (!Array.isArray(parsed))
61
+ throw new Error("Content is not an array of tags");
62
+ // Convert array to tags array string[][]
63
+ const tags = parsed.filter((t) => Array.isArray(t)).map((t) => t.map((v) => String(v)));
64
+ // Set the cached value
65
+ Reflect.set(event, HiddenTagsSymbol, tags);
66
+ return tags;
67
+ }
57
68
  /**
58
69
  * Decrypts the private list
59
70
  * @param event The list event to decrypt
@@ -64,20 +75,30 @@ export function getHiddenTagsEncryptionMethods(kind, signer) {
64
75
  export async function unlockHiddenTags(event, signer, override) {
65
76
  if (!canHaveHiddenTags(event.kind))
66
77
  throw new Error("Event kind does not support hidden tags");
67
- // unlock hidden content is needed
68
- if (isHiddenContentLocked(event))
69
- await unlockHiddenContent(event, signer, override);
70
- return getHiddenTags(event);
78
+ // Return the cached value if the hidden tags are already unlocked
79
+ if (isHiddenTagsUnlocked(event))
80
+ return event[HiddenTagsSymbol];
81
+ // Unlock hidden content
82
+ await unlockHiddenContent(event, signer, override);
83
+ // Parse the hidden tags
84
+ const tags = getHiddenTags(event);
85
+ if (tags === undefined)
86
+ throw new Error("Failed to unlock hidden tags");
87
+ // Set cache an notify event store
88
+ setHiddenTagsCache(event, tags);
89
+ return tags;
71
90
  }
72
91
  /**
73
92
  * Sets the hidden tags on an event and updates it if its part of an event store
74
- * @throws
93
+ * @throws If the event kind does not support hidden tags
75
94
  */
76
95
  export function setHiddenTagsCache(event, tags) {
77
96
  if (!canHaveHiddenTags(event.kind))
78
97
  throw new Error("Event kind does not support hidden tags");
79
- const plaintext = JSON.stringify(tags);
80
- setHiddenContentCache(event, plaintext);
98
+ // Set the cached value
99
+ Reflect.set(event, HiddenTagsSymbol, tags);
100
+ // Set the cached content
101
+ setHiddenContentCache(event, JSON.stringify(tags));
81
102
  }
82
103
  /** Clears the cached hidden tags on an event */
83
104
  export function lockHiddenTags(event) {
@@ -43,6 +43,7 @@ export * from "./pointers.js";
43
43
  export * from "./poll.js";
44
44
  export * from "./profile.js";
45
45
  export * from "./reactions.js";
46
+ export * from "./relay-selection.js";
46
47
  export * from "./relays.js";
47
48
  export * from "./reports.js";
48
49
  export * from "./share.js";
@@ -43,6 +43,7 @@ export * from "./pointers.js";
43
43
  export * from "./poll.js";
44
44
  export * from "./profile.js";
45
45
  export * from "./reactions.js";
46
+ export * from "./relay-selection.js";
46
47
  export * from "./relays.js";
47
48
  export * from "./reports.js";
48
49
  export * from "./share.js";
@@ -1,21 +1,25 @@
1
- import { NostrEvent } from "nostr-tools";
2
- import { EncryptedContentSigner } from "./encrypted-content.js";
1
+ import { kinds, NostrEvent } from "nostr-tools";
2
+ import { EncryptedContentSigner, UnlockedEncryptedContent } from "./encrypted-content.js";
3
+ import { KnownEvent } from "./index.js";
4
+ /** Type for valid legacy direct messages */
5
+ export type LegacyMessage = KnownEvent<kinds.EncryptedDirectMessage>;
6
+ /** Type for a legacy direct message with unlocked encrypted content */
7
+ export type UnlockedLegacyMessage = LegacyMessage & UnlockedEncryptedContent;
3
8
  /** Checks if a legacy direct message content is encrypted */
4
- export declare function isLegacyMessageLocked(event: NostrEvent): boolean;
5
- /**
6
- * Returns the corraspondant of a legacy direct message
7
- * @throws if no corraspondant is found
8
- */
9
- export declare function getLegacyMessageCorraspondant(message: NostrEvent, self: string): string;
10
- /**
11
- * Returns the receiver of a legacy direct message
12
- * @throws if no receiver is found
13
- */
14
- export declare const getLegacyMessageReceiver: typeof getLegacyMessageCorraspondant;
9
+ export declare function isLegacyMessageUnlocked<T extends NostrEvent>(event: T): event is T & UnlockedEncryptedContent;
10
+ /** Returns the correspondent of a legacy direct message */
11
+ export declare function getLegacyMessageCorrespondent<T extends LegacyMessage>(message: T, self: string): string;
12
+ export declare function getLegacyMessageCorrespondent<T extends NostrEvent>(message: T, self: string): string | undefined;
13
+ /** Returns the receiver of a legacy direct me */
14
+ export declare const getLegacyMessageReceiver: typeof getLegacyMessageCorrespondent;
15
+ /** @deprecated use {@link getLegacyMessageCorrespondent} instead */
16
+ export declare const getLegacyMessageCorraspondant: typeof getLegacyMessageCorrespondent;
15
17
  /** Returns the sender of a legacy direct message */
16
18
  export declare function getLegacyMessageSender(message: NostrEvent): string;
17
19
  /** Returns the parent message id of a legacy message */
18
20
  export declare function getLegacyMessageParent(message: NostrEvent): string | undefined;
21
+ /** Checks if a legacy message is valid */
22
+ export declare function isValidLegacyMessage(event: any): event is LegacyMessage;
19
23
  /**
20
24
  * Returns the decrypted content of a direct message
21
25
  * @param message - The message to decrypt
@@ -1,24 +1,17 @@
1
- import { getEncryptedContent, isEncryptedContentLocked, lockEncryptedContent, unlockEncryptedContent, } from "./encrypted-content.js";
1
+ import { kinds } from "nostr-tools";
2
+ import { getEncryptedContent, isEncryptedContentUnlocked, lockEncryptedContent, unlockEncryptedContent, } from "./encrypted-content.js";
2
3
  import { getTagValue } from "./index.js";
3
4
  /** Checks if a legacy direct message content is encrypted */
4
- export function isLegacyMessageLocked(event) {
5
- return isEncryptedContentLocked(event);
5
+ export function isLegacyMessageUnlocked(event) {
6
+ return isEncryptedContentUnlocked(event);
6
7
  }
7
- /**
8
- * Returns the corraspondant of a legacy direct message
9
- * @throws if no corraspondant is found
10
- */
11
- export function getLegacyMessageCorraspondant(message, self) {
12
- const corraspondant = message.pubkey === self ? getTagValue(message, "p") : message.pubkey;
13
- if (!corraspondant)
14
- throw new Error("No corraspondant found");
15
- return corraspondant;
8
+ export function getLegacyMessageCorrespondent(message, self) {
9
+ return message.pubkey === self ? getTagValue(message, "p") : message.pubkey;
16
10
  }
17
- /**
18
- * Returns the receiver of a legacy direct message
19
- * @throws if no receiver is found
20
- */
21
- export const getLegacyMessageReceiver = getLegacyMessageCorraspondant;
11
+ /** Returns the receiver of a legacy direct me */
12
+ export const getLegacyMessageReceiver = getLegacyMessageCorrespondent;
13
+ /** @deprecated use {@link getLegacyMessageCorrespondent} instead */
14
+ export const getLegacyMessageCorraspondant = getLegacyMessageCorrespondent;
22
15
  /** Returns the sender of a legacy direct message */
23
16
  export function getLegacyMessageSender(message) {
24
17
  return message.pubkey;
@@ -27,6 +20,12 @@ export function getLegacyMessageSender(message) {
27
20
  export function getLegacyMessageParent(message) {
28
21
  return getTagValue(message, "e");
29
22
  }
23
+ /** Checks if a legacy message is valid */
24
+ export function isValidLegacyMessage(event) {
25
+ return (event.kind === kinds.EncryptedDirectMessage &&
26
+ getLegacyMessageCorrespondent(event, event.pubkey) !== undefined &&
27
+ event.content.length > 0);
28
+ }
30
29
  /**
31
30
  * Returns the decrypted content of a direct message
32
31
  * @param message - The message to decrypt
@@ -38,9 +37,12 @@ export async function unlockLegacyMessage(message, self, signer) {
38
37
  const cached = getEncryptedContent(message);
39
38
  if (cached)
40
39
  return cached;
41
- const corraspondant = getLegacyMessageCorraspondant(message, self);
40
+ // Get the correspondent
41
+ const correspondent = getLegacyMessageCorrespondent(message, self);
42
+ if (!correspondent)
43
+ throw new Error("No correspondent found");
42
44
  // Unlock the encrypted content
43
- return await unlockEncryptedContent(message, corraspondant, signer);
45
+ return await unlockEncryptedContent(message, correspondent, signer);
44
46
  }
45
47
  /** Clears the cached plaintext of a direct message */
46
48
  export async function lockLegacyMessage(message) {
@@ -97,7 +97,8 @@ export function isValidList(event) {
97
97
  if (isAddressableKind(event.kind)) {
98
98
  // event is a set
99
99
  // ensure the set has an identifier
100
- getReplaceableIdentifier(event);
100
+ if (!getReplaceableIdentifier(event))
101
+ return false;
101
102
  return true;
102
103
  }
103
104
  else if (isReplaceableKind(event.kind) && event.kind >= 10000 && event.kind < 20000) {
@@ -1,4 +1,8 @@
1
+ /** Parses a lightning address (lud16) into a LNURLp */
1
2
  export declare function parseLightningAddress(address: string): URL | undefined;
3
+ /** Parses a LNURLp into a URL */
2
4
  export declare function decodeLNURL(lnurl: string): URL | undefined;
5
+ /** Parses a lightning address or LNURLp into a URL */
3
6
  export declare function parseLNURLOrAddress(addressOrLNURL: string): URL | undefined;
7
+ /** Requests a bolt11 invoice from a LNURLp callback URL */
4
8
  export declare function getInvoice(callback: URL): Promise<string>;
@@ -1,12 +1,14 @@
1
1
  import { bech32 } from "@scure/base";
2
2
  import { parseBolt11 } from "./bolt11.js";
3
3
  const decoder = new TextDecoder();
4
+ /** Parses a lightning address (lud16) into a LNURLp */
4
5
  export function parseLightningAddress(address) {
5
6
  let [name, domain] = address.split("@");
6
7
  if (!name || !domain)
7
8
  return;
8
9
  return new URL(`https://${domain}/.well-known/lnurlp/${name}`);
9
10
  }
11
+ /** Parses a LNURLp into a URL */
10
12
  export function decodeLNURL(lnurl) {
11
13
  try {
12
14
  const { words, prefix } = bech32.decode(lnurl);
@@ -18,12 +20,14 @@ export function decodeLNURL(lnurl) {
18
20
  catch (error) { }
19
21
  return undefined;
20
22
  }
23
+ /** Parses a lightning address or LNURLp into a URL */
21
24
  export function parseLNURLOrAddress(addressOrLNURL) {
22
- if (addressOrLNURL.includes("@")) {
25
+ if (addressOrLNURL.includes("@"))
23
26
  return parseLightningAddress(addressOrLNURL);
24
- }
25
- return decodeLNURL(addressOrLNURL);
27
+ else
28
+ return decodeLNURL(addressOrLNURL);
26
29
  }
30
+ /** Requests a bolt11 invoice from a LNURLp callback URL */
27
31
  export async function getInvoice(callback) {
28
32
  const { pr: payRequest } = await fetch(callback).then((res) => res.json());
29
33
  const amount = callback.searchParams.get("amount");