applesauce-common 0.0.0-next-20251203172109

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 (225) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/blueprints/app-data.d.ts +5 -0
  4. package/dist/blueprints/app-data.js +10 -0
  5. package/dist/blueprints/calendar.d.ts +5 -0
  6. package/dist/blueprints/calendar.js +7 -0
  7. package/dist/blueprints/channels.d.ts +6 -0
  8. package/dist/blueprints/channels.js +13 -0
  9. package/dist/blueprints/comment.d.ts +12 -0
  10. package/dist/blueprints/comment.js +15 -0
  11. package/dist/blueprints/delete.d.ts +9 -0
  12. package/dist/blueprints/delete.js +14 -0
  13. package/dist/blueprints/file-metadata.d.ts +8 -0
  14. package/dist/blueprints/file-metadata.js +11 -0
  15. package/dist/blueprints/follow-set.d.ts +11 -0
  16. package/dist/blueprints/follow-set.js +21 -0
  17. package/dist/blueprints/gift-wrap.d.ts +5 -0
  18. package/dist/blueprints/gift-wrap.js +6 -0
  19. package/dist/blueprints/group.d.ts +9 -0
  20. package/dist/blueprints/group.js +17 -0
  21. package/dist/blueprints/highlight.d.ts +20 -0
  22. package/dist/blueprints/highlight.js +12 -0
  23. package/dist/blueprints/index.d.ts +23 -0
  24. package/dist/blueprints/index.js +24 -0
  25. package/dist/blueprints/legacy-message.d.ts +7 -0
  26. package/dist/blueprints/legacy-message.js +29 -0
  27. package/dist/blueprints/live-stream.d.ts +6 -0
  28. package/dist/blueprints/live-stream.js +9 -0
  29. package/dist/blueprints/note.d.ts +16 -0
  30. package/dist/blueprints/note.js +30 -0
  31. package/dist/blueprints/picture-post.d.ts +12 -0
  32. package/dist/blueprints/picture-post.js +17 -0
  33. package/dist/blueprints/poll.d.ts +41 -0
  34. package/dist/blueprints/poll.js +33 -0
  35. package/dist/blueprints/profile.d.ts +3 -0
  36. package/dist/blueprints/profile.js +7 -0
  37. package/dist/blueprints/reaction.d.ts +10 -0
  38. package/dist/blueprints/reaction.js +14 -0
  39. package/dist/blueprints/share.d.ts +12 -0
  40. package/dist/blueprints/share.js +25 -0
  41. package/dist/blueprints/stream.d.ts +6 -0
  42. package/dist/blueprints/stream.js +7 -0
  43. package/dist/blueprints/torrent.d.ts +23 -0
  44. package/dist/blueprints/torrent.js +25 -0
  45. package/dist/blueprints/wrapped-message.d.ts +20 -0
  46. package/dist/blueprints/wrapped-message.js +64 -0
  47. package/dist/helpers/app-data.d.ts +39 -0
  48. package/dist/helpers/app-data.js +68 -0
  49. package/dist/helpers/app-handler.d.ts +22 -0
  50. package/dist/helpers/app-handler.js +67 -0
  51. package/dist/helpers/article.d.ts +14 -0
  52. package/dist/helpers/article.js +24 -0
  53. package/dist/helpers/blossom.d.ts +11 -0
  54. package/dist/helpers/blossom.js +40 -0
  55. package/dist/helpers/bolt11.d.ts +10 -0
  56. package/dist/helpers/bolt11.js +17 -0
  57. package/dist/helpers/bookmark.d.ts +30 -0
  58. package/dist/helpers/bookmark.js +96 -0
  59. package/dist/helpers/calendar-event.d.ts +39 -0
  60. package/dist/helpers/calendar-event.js +121 -0
  61. package/dist/helpers/calendar-rsvp.d.ts +15 -0
  62. package/dist/helpers/calendar-rsvp.js +38 -0
  63. package/dist/helpers/calendar.d.ts +6 -0
  64. package/dist/helpers/calendar.js +11 -0
  65. package/dist/helpers/channels.d.ts +13 -0
  66. package/dist/helpers/channels.js +27 -0
  67. package/dist/helpers/comment.d.ts +47 -0
  68. package/dist/helpers/comment.js +185 -0
  69. package/dist/helpers/content.d.ts +3 -0
  70. package/dist/helpers/content.js +8 -0
  71. package/dist/helpers/emoji.d.ts +21 -0
  72. package/dist/helpers/emoji.js +34 -0
  73. package/dist/helpers/encrypted-content-cache.d.ts +22 -0
  74. package/dist/helpers/encrypted-content-cache.js +138 -0
  75. package/dist/helpers/file-metadata.d.ts +55 -0
  76. package/dist/helpers/file-metadata.js +130 -0
  77. package/dist/helpers/gift-wrap.d.ts +66 -0
  78. package/dist/helpers/gift-wrap.js +204 -0
  79. package/dist/helpers/groups-helper.d.ts +6 -0
  80. package/dist/helpers/groups-helper.js +9 -0
  81. package/dist/helpers/groups.d.ts +26 -0
  82. package/dist/helpers/groups.js +49 -0
  83. package/dist/helpers/hashtag.d.ts +2 -0
  84. package/dist/helpers/hashtag.js +7 -0
  85. package/dist/helpers/highlight.d.ts +45 -0
  86. package/dist/helpers/highlight.js +76 -0
  87. package/dist/helpers/index.d.ts +37 -0
  88. package/dist/helpers/index.js +37 -0
  89. package/dist/helpers/legacy-messages.d.ts +31 -0
  90. package/dist/helpers/legacy-messages.js +49 -0
  91. package/dist/helpers/lists.d.ts +58 -0
  92. package/dist/helpers/lists.js +110 -0
  93. package/dist/helpers/lnurl.d.ts +8 -0
  94. package/dist/helpers/lnurl.js +44 -0
  95. package/dist/helpers/mailboxes.d.ts +7 -0
  96. package/dist/helpers/mailboxes.js +49 -0
  97. package/dist/helpers/messages.d.ts +31 -0
  98. package/dist/helpers/messages.js +57 -0
  99. package/dist/helpers/mute.d.ts +33 -0
  100. package/dist/helpers/mute.js +111 -0
  101. package/dist/helpers/picture-post.d.ts +5 -0
  102. package/dist/helpers/picture-post.js +6 -0
  103. package/dist/helpers/poll.d.ts +46 -0
  104. package/dist/helpers/poll.js +78 -0
  105. package/dist/helpers/reaction.d.ts +8 -0
  106. package/dist/helpers/reaction.js +56 -0
  107. package/dist/helpers/relay-discovery.d.ts +87 -0
  108. package/dist/helpers/relay-discovery.js +126 -0
  109. package/dist/helpers/reports.d.ts +28 -0
  110. package/dist/helpers/reports.js +38 -0
  111. package/dist/helpers/share.d.ts +19 -0
  112. package/dist/helpers/share.js +58 -0
  113. package/dist/helpers/stream-chat.d.ts +4 -0
  114. package/dist/helpers/stream-chat.js +9 -0
  115. package/dist/helpers/stream.d.ts +31 -0
  116. package/dist/helpers/stream.js +81 -0
  117. package/dist/helpers/threading.d.ts +55 -0
  118. package/dist/helpers/threading.js +94 -0
  119. package/dist/helpers/torrent.d.ts +55 -0
  120. package/dist/helpers/torrent.js +270 -0
  121. package/dist/helpers/user-status.d.ts +18 -0
  122. package/dist/helpers/user-status.js +22 -0
  123. package/dist/helpers/wrapped-messages.d.ts +14 -0
  124. package/dist/helpers/wrapped-messages.js +23 -0
  125. package/dist/helpers/zap.d.ts +46 -0
  126. package/dist/helpers/zap.js +125 -0
  127. package/dist/index.d.ts +5 -0
  128. package/dist/index.js +6 -0
  129. package/dist/models/blossom.d.ts +11 -0
  130. package/dist/models/blossom.js +18 -0
  131. package/dist/models/bookmarks.d.ts +8 -0
  132. package/dist/models/bookmarks.js +24 -0
  133. package/dist/models/calendar.d.ts +6 -0
  134. package/dist/models/calendar.js +15 -0
  135. package/dist/models/channels.d.ts +11 -0
  136. package/dist/models/channels.js +61 -0
  137. package/dist/models/comments.d.ts +11 -0
  138. package/dist/models/comments.js +17 -0
  139. package/dist/models/gift-wrap.d.ts +7 -0
  140. package/dist/models/gift-wrap.js +20 -0
  141. package/dist/models/index.d.ts +15 -0
  142. package/dist/models/index.js +16 -0
  143. package/dist/models/legacy-messages.d.ts +14 -0
  144. package/dist/models/legacy-messages.js +64 -0
  145. package/dist/models/mutes.d.ts +16 -0
  146. package/dist/models/mutes.js +34 -0
  147. package/dist/models/pins.d.ts +4 -0
  148. package/dist/models/pins.js +10 -0
  149. package/dist/models/reactions.d.ts +11 -0
  150. package/dist/models/reactions.js +21 -0
  151. package/dist/models/thread.d.ts +33 -0
  152. package/dist/models/thread.js +93 -0
  153. package/dist/models/user-status.d.ts +11 -0
  154. package/dist/models/user-status.js +32 -0
  155. package/dist/models/wrapped-messages.d.ts +31 -0
  156. package/dist/models/wrapped-messages.js +76 -0
  157. package/dist/models/zaps.d.ts +9 -0
  158. package/dist/models/zaps.js +26 -0
  159. package/dist/operations/app-data.d.ts +6 -0
  160. package/dist/operations/app-data.js +21 -0
  161. package/dist/operations/blossom.d.ts +5 -0
  162. package/dist/operations/blossom.js +13 -0
  163. package/dist/operations/calendar-event.d.ts +34 -0
  164. package/dist/operations/calendar-event.js +72 -0
  165. package/dist/operations/calendar-rsvp.d.ts +10 -0
  166. package/dist/operations/calendar-rsvp.js +35 -0
  167. package/dist/operations/calendar.d.ts +9 -0
  168. package/dist/operations/calendar.js +15 -0
  169. package/dist/operations/channel.d.ts +4 -0
  170. package/dist/operations/channel.js +10 -0
  171. package/dist/operations/client.d.ts +4 -0
  172. package/dist/operations/client.js +23 -0
  173. package/dist/operations/comment.d.ts +4 -0
  174. package/dist/operations/comment.js +11 -0
  175. package/dist/operations/file-metadata.d.ts +4 -0
  176. package/dist/operations/file-metadata.js +21 -0
  177. package/dist/operations/geohash.d.ts +5 -0
  178. package/dist/operations/geohash.js +17 -0
  179. package/dist/operations/gift-wrap.d.ts +13 -0
  180. package/dist/operations/gift-wrap.js +93 -0
  181. package/dist/operations/group.d.ts +11 -0
  182. package/dist/operations/group.js +34 -0
  183. package/dist/operations/hashtags.d.ts +7 -0
  184. package/dist/operations/hashtags.js +17 -0
  185. package/dist/operations/highlight.d.ts +18 -0
  186. package/dist/operations/highlight.js +47 -0
  187. package/dist/operations/index.d.ts +28 -0
  188. package/dist/operations/index.js +28 -0
  189. package/dist/operations/legacy-message.d.ts +6 -0
  190. package/dist/operations/legacy-message.js +13 -0
  191. package/dist/operations/list.d.ts +7 -0
  192. package/dist/operations/list.js +14 -0
  193. package/dist/operations/live-stream.d.ts +4 -0
  194. package/dist/operations/live-stream.js +11 -0
  195. package/dist/operations/media-attachment.d.ts +4 -0
  196. package/dist/operations/media-attachment.js +12 -0
  197. package/dist/operations/note.d.ts +9 -0
  198. package/dist/operations/note.js +42 -0
  199. package/dist/operations/picture-post.d.ts +4 -0
  200. package/dist/operations/picture-post.js +14 -0
  201. package/dist/operations/poll-response.d.ts +9 -0
  202. package/dist/operations/poll-response.js +20 -0
  203. package/dist/operations/poll.d.ts +19 -0
  204. package/dist/operations/poll.js +42 -0
  205. package/dist/operations/reaction.d.ts +7 -0
  206. package/dist/operations/reaction.js +39 -0
  207. package/dist/operations/share.d.ts +8 -0
  208. package/dist/operations/share.js +34 -0
  209. package/dist/operations/stream-chat.d.ts +7 -0
  210. package/dist/operations/stream-chat.js +27 -0
  211. package/dist/operations/stream.d.ts +41 -0
  212. package/dist/operations/stream.js +83 -0
  213. package/dist/operations/tag/bookmarks.d.ts +6 -0
  214. package/dist/operations/tag/bookmarks.js +20 -0
  215. package/dist/operations/tag/index.d.ts +1 -0
  216. package/dist/operations/tag/index.js +1 -0
  217. package/dist/operations/torrent.d.ts +33 -0
  218. package/dist/operations/torrent.js +66 -0
  219. package/dist/operations/wrapped-message.d.ts +12 -0
  220. package/dist/operations/wrapped-message.js +28 -0
  221. package/dist/operations/zap-split.d.ts +10 -0
  222. package/dist/operations/zap-split.js +20 -0
  223. package/dist/register.d.ts +11 -0
  224. package/dist/register.js +13 -0
  225. package/package.json +91 -0
@@ -0,0 +1,61 @@
1
+ import { kinds } from "applesauce-core/helpers/event";
2
+ import { safeParse } from "applesauce-core/helpers/json";
3
+ import { map } from "rxjs";
4
+ import { getChannelMetadataContent } from "../helpers/channels.js";
5
+ /** A model that returns a map of hidden messages Map<id, reason> */
6
+ export function ChannelHiddenModel(channel, authors = []) {
7
+ return (events) => {
8
+ const hidden = new Map();
9
+ return events
10
+ .filters([{ kinds: [kinds.ChannelHideMessage], "#e": [channel.id], authors: [channel.pubkey, ...authors] }])
11
+ .pipe(map((event) => {
12
+ const reason = safeParse(event.content)?.reason;
13
+ for (const tag of event.tags) {
14
+ if (tag[0] === "e" && tag[1])
15
+ hidden.set(tag[1], reason ?? "");
16
+ }
17
+ return hidden;
18
+ }));
19
+ };
20
+ }
21
+ /** A model that returns all messages in a channel */
22
+ export function ChannelMessagesModel(channel) {
23
+ return (events) => events.timeline([{ kinds: [kinds.ChannelMessage], "#e": [channel.id] }]);
24
+ }
25
+ /** A model that returns the latest parsed metadata */
26
+ export function ChannelMetadataModel(channel) {
27
+ return (events) => {
28
+ const filters = [
29
+ { ids: [channel.id] },
30
+ { kinds: [kinds.ChannelMetadata], "#e": [channel.id], authors: [channel.pubkey] },
31
+ ];
32
+ let latest = channel;
33
+ return events.filters(filters).pipe(map((event) => {
34
+ try {
35
+ if (event.pubkey === latest.pubkey && event.created_at > latest.created_at) {
36
+ latest = event;
37
+ }
38
+ return getChannelMetadataContent(latest);
39
+ }
40
+ catch (error) {
41
+ return undefined;
42
+ }
43
+ }));
44
+ };
45
+ }
46
+ /** A model that returns a map of muted users Map<pubkey, reason> */
47
+ export function ChannelMutedModel(channel, authors = []) {
48
+ return (events) => {
49
+ const muted = new Map();
50
+ return events
51
+ .filters([{ kinds: [kinds.ChannelMuteUser], "#e": [channel.id], authors: [channel.pubkey, ...authors] }])
52
+ .pipe(map((event) => {
53
+ const reason = safeParse(event.content)?.reason;
54
+ for (const tag of event.tags) {
55
+ if (tag[0] === "p" && tag[1])
56
+ muted.set(tag[1], reason ?? "");
57
+ }
58
+ return muted;
59
+ }));
60
+ };
61
+ }
@@ -0,0 +1,11 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
+ import { type Observable } from "rxjs";
4
+ /** A model that returns all NIP-22 comment replies for the event */
5
+ export declare function CommentsModel(parent: NostrEvent): Model<NostrEvent[]>;
6
+ declare module "applesauce-core/event-store" {
7
+ interface EventModels {
8
+ /** Subscribe to an event's comments */
9
+ comments(event: NostrEvent): Observable<NostrEvent[]>;
10
+ }
11
+ }
@@ -0,0 +1,17 @@
1
+ import { getReplaceableAddress, isAddressableKind } from "applesauce-core/helpers/event";
2
+ import { COMMENT_KIND } from "../helpers/comment.js";
3
+ // Import EventModels as a value (class) to modify its prototype
4
+ import { EventModels } from "applesauce-core/event-store";
5
+ /** A model that returns all NIP-22 comment replies for the event */
6
+ export function CommentsModel(parent) {
7
+ return (events) => {
8
+ const filters = [{ kinds: [COMMENT_KIND], "#e": [parent.id] }];
9
+ if (isAddressableKind(parent.kind))
10
+ filters.push({ kinds: [COMMENT_KIND], "#a": [getReplaceableAddress(parent)] });
11
+ return events.timeline(filters);
12
+ };
13
+ }
14
+ // Register this model with EventModels
15
+ EventModels.prototype.comments = function (event) {
16
+ return this.model(CommentsModel, event);
17
+ };
@@ -0,0 +1,7 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
+ import { Rumor } from "../helpers/gift-wrap.js";
4
+ /** A model that returns all gift wrap events for a pubkey, optionally filtered by locked status */
5
+ export declare function GiftWrapsModel(pubkey: string, unlocked?: boolean): Model<NostrEvent[]>;
6
+ /** A model that returns the rumor event of a gift wrap event when its unlocked */
7
+ export declare function GiftWrapRumorModel(gift: NostrEvent | string): Model<Rumor | undefined>;
@@ -0,0 +1,20 @@
1
+ import { kinds } from "applesauce-core/helpers/event";
2
+ import { watchEventsUpdates, watchEventUpdates } from "applesauce-core/observable";
3
+ import { identity, map, of } from "rxjs";
4
+ import { getGiftWrapRumor, isGiftWrapUnlocked } from "../helpers/gift-wrap.js";
5
+ /** A model that returns all gift wrap events for a pubkey, optionally filtered by locked status */
6
+ export function GiftWrapsModel(pubkey, unlocked) {
7
+ return (store) => store.timeline({ kinds: [kinds.GiftWrap], "#p": [pubkey] }).pipe(
8
+ // Update the timeline when events are updated
9
+ watchEventsUpdates(store),
10
+ // If unlock is specified filter on unlocked status
11
+ unlocked !== undefined ? map((events) => events.filter((e) => isGiftWrapUnlocked(e) === unlocked)) : identity);
12
+ }
13
+ /** A model that returns the rumor event of a gift wrap event when its unlocked */
14
+ export function GiftWrapRumorModel(gift) {
15
+ return (events) => (typeof gift === "string" ? events.event(gift) : of(gift)).pipe(
16
+ // Listen for updates to the event
17
+ watchEventUpdates(events),
18
+ // Get the rumor event
19
+ map((event) => event && getGiftWrapRumor(event)));
20
+ }
@@ -0,0 +1,15 @@
1
+ export * from "./blossom.js";
2
+ export * from "./bookmarks.js";
3
+ export * from "./calendar.js";
4
+ export * from "./channels.js";
5
+ export * from "./comments.js";
6
+ export * from "./legacy-messages.js";
7
+ export * from "./mutes.js";
8
+ export * from "./pins.js";
9
+ export * from "./reactions.js";
10
+ export * from "./thread.js";
11
+ export * from "./user-status.js";
12
+ export * from "./wrapped-messages.js";
13
+ export * from "./zaps.js";
14
+ export * from "./gift-wrap.js";
15
+ import "../register.js";
@@ -0,0 +1,16 @@
1
+ export * from "./blossom.js";
2
+ export * from "./bookmarks.js";
3
+ export * from "./calendar.js";
4
+ export * from "./channels.js";
5
+ export * from "./comments.js";
6
+ export * from "./legacy-messages.js";
7
+ export * from "./mutes.js";
8
+ export * from "./pins.js";
9
+ export * from "./reactions.js";
10
+ export * from "./thread.js";
11
+ export * from "./user-status.js";
12
+ export * from "./wrapped-messages.js";
13
+ export * from "./zaps.js";
14
+ export * from "./gift-wrap.js";
15
+ // Register the common models with the event store
16
+ import "../register.js";
@@ -0,0 +1,14 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
+ /** A model that returns all legacy message groups (1-1) that a pubkey is participating in */
4
+ export declare function LegacyMessagesGroups(self: string): Model<{
5
+ id: string;
6
+ participants: string[];
7
+ lastMessage: NostrEvent;
8
+ }[]>;
9
+ /** Returns all legacy direct messages in a group */
10
+ export declare function LegacyMessagesGroup(self: string, correspondent: string): Model<NostrEvent[]>;
11
+ /** Returns an array of legacy messages that have replies */
12
+ export declare function LegacyMessageThreads(self: string, correspondent: string): Model<NostrEvent[]>;
13
+ /** Returns all the legacy direct messages that are replies to a given message */
14
+ export declare function LegacyMessageReplies(self: string, message: NostrEvent): Model<NostrEvent[]>;
@@ -0,0 +1,64 @@
1
+ import { kinds } from "applesauce-core/helpers/event";
2
+ import { hasNameValueTag } from "applesauce-core/helpers/event";
3
+ import { map } from "rxjs";
4
+ import { getLegacyMessageCorrespondent, getLegacyMessageParent } from "../helpers/legacy-messages.js";
5
+ import { getConversationIdentifierFromMessage, getConversationParticipants } from "../helpers/messages.js";
6
+ /** A model that returns all legacy message groups (1-1) that a pubkey is participating in */
7
+ export function LegacyMessagesGroups(self) {
8
+ return (store) => store.timeline({ kinds: [kinds.EncryptedDirectMessage], "#p": [self] }).pipe(map((messages) => {
9
+ const groups = {};
10
+ for (const message of messages) {
11
+ const id = getConversationIdentifierFromMessage(message);
12
+ if (!groups[id] || groups[id].created_at < message.created_at)
13
+ groups[id] = message;
14
+ }
15
+ return Object.values(groups).map((message) => ({
16
+ id: getConversationIdentifierFromMessage(message),
17
+ participants: getConversationParticipants(message),
18
+ lastMessage: message,
19
+ }));
20
+ }));
21
+ }
22
+ /** Returns all legacy direct messages in a group */
23
+ export function LegacyMessagesGroup(self, correspondent) {
24
+ return (store) => store.timeline([
25
+ {
26
+ kinds: [kinds.EncryptedDirectMessage],
27
+ "#p": [self],
28
+ authors: [correspondent],
29
+ },
30
+ {
31
+ kinds: [kinds.EncryptedDirectMessage],
32
+ "#p": [correspondent],
33
+ authors: [self],
34
+ },
35
+ ]);
36
+ }
37
+ /** Returns an array of legacy messages that have replies */
38
+ export function LegacyMessageThreads(self, correspondent) {
39
+ return (store) => store.model(LegacyMessagesGroup, self, correspondent).pipe(map((messages) => messages.filter((message) =>
40
+ // Only select messages that are not replies
41
+ !getLegacyMessageParent(message) &&
42
+ // Check if message has any replies
43
+ messages.some((m) => hasNameValueTag(m, "e", message.id)))));
44
+ }
45
+ /** Returns all the legacy direct messages that are replies to a given message */
46
+ export function LegacyMessageReplies(self, message) {
47
+ const correspondent = getLegacyMessageCorrespondent(message, self);
48
+ if (!correspondent)
49
+ throw new Error("Legacy message has no correspondent");
50
+ return (store) => store.timeline([
51
+ {
52
+ kinds: [kinds.EncryptedDirectMessage],
53
+ "#p": [self],
54
+ authors: [correspondent],
55
+ "#e": [message.id],
56
+ },
57
+ {
58
+ kinds: [kinds.EncryptedDirectMessage],
59
+ "#p": [correspondent],
60
+ authors: [self],
61
+ "#e": [message.id],
62
+ },
63
+ ]);
64
+ }
@@ -0,0 +1,16 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { ProfilePointer } from "applesauce-core/helpers/pointers";
3
+ import { type Observable } from "rxjs";
4
+ import { Mutes } from "../helpers/mute.js";
5
+ /** A model that returns all a users muted things */
6
+ export declare function MuteModel(user: string | ProfilePointer): Model<Mutes | undefined>;
7
+ /** A model that returns all a users public muted things */
8
+ export declare function PublicMuteModel(pubkey: string): Model<Mutes | undefined>;
9
+ /** A model that returns all a users hidden muted things */
10
+ export declare function HiddenMuteModel(pubkey: string): Model<Mutes | null | undefined>;
11
+ declare module "applesauce-core/event-store" {
12
+ interface EventModels {
13
+ /** Subscribe to a users mutes */
14
+ mutes(user: string | ProfilePointer): Observable<Mutes | undefined>;
15
+ }
16
+ }
@@ -0,0 +1,34 @@
1
+ import { kinds } from "applesauce-core/helpers/event";
2
+ import { watchEventUpdates } from "applesauce-core/observable";
3
+ import { map } from "rxjs/operators";
4
+ import { getHiddenMutedThings, getMutedThings, getPublicMutedThings } from "../helpers/mute.js";
5
+ // Import EventModels as a value (class) to modify its prototype
6
+ import { EventModels } from "applesauce-core/event-store";
7
+ /** A model that returns all a users muted things */
8
+ export function MuteModel(user) {
9
+ if (typeof user === "string")
10
+ user = { pubkey: user };
11
+ return (events) => events.replaceable({ kind: kinds.Mutelist, pubkey: user.pubkey, relays: user.relays }).pipe(
12
+ // listen for event updates (hidden tags unlocked)
13
+ watchEventUpdates(events),
14
+ // Get all muted things
15
+ map((event) => event && getMutedThings(event)));
16
+ }
17
+ /** A model that returns all a users public muted things */
18
+ export function PublicMuteModel(pubkey) {
19
+ return (events) => events.replaceable(kinds.Mutelist, pubkey).pipe(map((event) => event && getPublicMutedThings(event)));
20
+ }
21
+ /** A model that returns all a users hidden muted things */
22
+ export function HiddenMuteModel(pubkey) {
23
+ return (events) => events.replaceable(kinds.Mutelist, pubkey).pipe(
24
+ // listen for event updates (hidden tags unlocked)
25
+ watchEventUpdates(events),
26
+ // Get hidden muted things
27
+ map((event) => event && getHiddenMutedThings(event)));
28
+ }
29
+ // Register this model with EventModels
30
+ EventModels.prototype.mutes = function (user) {
31
+ if (typeof user === "string")
32
+ user = { pubkey: user };
33
+ return this.model(MuteModel, user);
34
+ };
@@ -0,0 +1,4 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { EventPointer } from "applesauce-core/helpers/pointers";
3
+ /** A model that returns all pinned pointers for a user */
4
+ export declare function UserPinnedModel(pubkey: string): Model<EventPointer[] | undefined>;
@@ -0,0 +1,10 @@
1
+ import { kinds } from "applesauce-core/helpers/event";
2
+ import { getEventPointerFromETag } from "applesauce-core/helpers/pointers";
3
+ import { isETag, processTags } from "applesauce-core/helpers/tags";
4
+ import { map } from "rxjs/operators";
5
+ /** A model that returns all pinned pointers for a user */
6
+ export function UserPinnedModel(pubkey) {
7
+ return (events) => events
8
+ .replaceable(kinds.Pinlist, pubkey)
9
+ .pipe(map((event) => event && processTags(event.tags.filter(isETag), getEventPointerFromETag)));
10
+ }
@@ -0,0 +1,11 @@
1
+ import { NostrEvent } from "applesauce-core/helpers/event";
2
+ import { Model } from "applesauce-core/event-store";
3
+ import { type Observable } from "rxjs";
4
+ /** A model that returns all reactions to an event (supports replaceable events) */
5
+ export declare function ReactionsModel(event: NostrEvent): Model<NostrEvent[]>;
6
+ declare module "applesauce-core/event-store" {
7
+ interface EventModels {
8
+ /** Subscribe to an event's reactions */
9
+ reactions(event: NostrEvent): Observable<NostrEvent[]>;
10
+ }
11
+ }
@@ -0,0 +1,21 @@
1
+ import { getEventUID, isReplaceable, kinds } from "applesauce-core/helpers/event";
2
+ // Import EventModels as a value (class) to modify its prototype
3
+ import { EventModels } from "applesauce-core/event-store";
4
+ /** A model that returns all reactions to an event (supports replaceable events) */
5
+ export function ReactionsModel(event) {
6
+ return (events) => events.timeline(isReplaceable(event.kind)
7
+ ? [
8
+ { kinds: [kinds.Reaction], "#e": [event.id] },
9
+ { kinds: [kinds.Reaction], "#a": [getEventUID(event)] },
10
+ ]
11
+ : [
12
+ {
13
+ kinds: [kinds.Reaction],
14
+ "#e": [event.id],
15
+ },
16
+ ]);
17
+ }
18
+ // Register this model with EventModels
19
+ EventModels.prototype.reactions = function (event) {
20
+ return this.model(ReactionsModel, event);
21
+ };
@@ -0,0 +1,33 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
+ import { AddressPointer, EventPointer } from "applesauce-core/helpers/pointers";
4
+ import { type Observable } from "rxjs";
5
+ import { ThreadReferences } from "../helpers/threading.js";
6
+ export type Thread = {
7
+ root?: ThreadItem;
8
+ all: Map<string, ThreadItem>;
9
+ };
10
+ export type ThreadItem = {
11
+ /** underlying nostr event */
12
+ event: NostrEvent;
13
+ refs: ThreadReferences;
14
+ /** the thread root, according to this event */
15
+ root?: ThreadItem;
16
+ /** the parent event this is replying to */
17
+ parent?: ThreadItem;
18
+ /** direct child replies */
19
+ replies: Set<ThreadItem>;
20
+ };
21
+ export type ThreadModelOptions = {
22
+ kinds?: number[];
23
+ };
24
+ /** A model that returns a NIP-10 thread of events */
25
+ export declare function ThreadModel(root: string | AddressPointer | EventPointer, opts?: ThreadModelOptions): Model<Thread>;
26
+ /** A model that gets all legacy and NIP-10, and NIP-22 replies for an event */
27
+ export declare function RepliesModel(event: NostrEvent, overrideKinds?: number[]): Model<NostrEvent[]>;
28
+ declare module "applesauce-core/event-store" {
29
+ interface EventModels {
30
+ /** Subscribe to a thread */
31
+ thread(root: string | EventPointer | AddressPointer): Observable<Thread>;
32
+ }
33
+ }
@@ -0,0 +1,93 @@
1
+ import { createReplaceableAddress, getEventUID, isEvent, kinds } from "applesauce-core/helpers/event";
2
+ import { getTagValue } from "applesauce-core/helpers/event";
3
+ import { getCoordinateFromAddressPointer, isAddressPointer, isEventPointer, } from "applesauce-core/helpers/pointers";
4
+ import { isAddressableKind } from "applesauce-core/helpers/event";
5
+ import { map } from "rxjs/operators";
6
+ import { COMMENT_KIND } from "../helpers/comment.js";
7
+ import { getNip10References, interpretThreadTags } from "../helpers/threading.js";
8
+ // Import EventModels as a value (class) to modify its prototype
9
+ import { EventModels } from "applesauce-core/event-store";
10
+ const defaultOptions = {
11
+ kinds: [kinds.ShortTextNote],
12
+ };
13
+ /** A model that returns a NIP-10 thread of events */
14
+ export function ThreadModel(root, opts) {
15
+ const parentReferences = new Map();
16
+ const items = new Map();
17
+ const { kinds } = { ...defaultOptions, ...opts };
18
+ let rootUID = "";
19
+ const rootFilter = {};
20
+ const replyFilter = { kinds };
21
+ if (isAddressPointer(root)) {
22
+ rootUID = getCoordinateFromAddressPointer(root);
23
+ rootFilter.kinds = [root.kind];
24
+ rootFilter.authors = [root.pubkey];
25
+ rootFilter["#d"] = [root.identifier];
26
+ replyFilter["#a"] = [rootUID];
27
+ }
28
+ else if (typeof root === "string") {
29
+ rootUID = root;
30
+ rootFilter.ids = [root];
31
+ replyFilter["#e"] = [root];
32
+ }
33
+ else {
34
+ rootUID = root.id;
35
+ rootFilter.ids = [root.id];
36
+ replyFilter["#e"] = [root.id];
37
+ }
38
+ return (events) => events.filters([rootFilter, replyFilter]).pipe(map((event) => {
39
+ if (!items.has(getEventUID(event))) {
40
+ const refs = getNip10References(event);
41
+ const replies = parentReferences.get(getEventUID(event)) || new Set();
42
+ const item = { event, refs, replies };
43
+ for (const child of replies) {
44
+ child.parent = item;
45
+ }
46
+ // add item to parent
47
+ if (refs.reply?.e || refs.reply?.a) {
48
+ let uid = refs.reply.e ? refs.reply.e.id : getCoordinateFromAddressPointer(refs.reply.a);
49
+ item.parent = items.get(uid);
50
+ if (item.parent) {
51
+ item.parent.replies.add(item);
52
+ }
53
+ else {
54
+ // parent isn't created yet, store ref for later
55
+ let set = parentReferences.get(uid);
56
+ if (!set) {
57
+ set = new Set();
58
+ parentReferences.set(uid, set);
59
+ }
60
+ set.add(item);
61
+ }
62
+ }
63
+ // add item to map
64
+ items.set(getEventUID(event), item);
65
+ }
66
+ return { root: items.get(rootUID), all: items };
67
+ }));
68
+ }
69
+ /** A model that gets all legacy and NIP-10, and NIP-22 replies for an event */
70
+ export function RepliesModel(event, overrideKinds) {
71
+ return (events) => {
72
+ const kinds = overrideKinds || event.kind === 1 ? [1, COMMENT_KIND] : [COMMENT_KIND];
73
+ const filter = { kinds };
74
+ if (isEvent(parent) || isEventPointer(event))
75
+ filter["#e"] = [event.id];
76
+ const address = isAddressableKind(event.kind)
77
+ ? createReplaceableAddress(event.kind, event.pubkey, getTagValue(event, "d"))
78
+ : undefined;
79
+ if (address) {
80
+ filter["#a"] = [address];
81
+ }
82
+ return events.timeline(filter).pipe(map((events) => {
83
+ return events.filter((e) => {
84
+ const refs = interpretThreadTags(e.tags);
85
+ return refs.reply?.e?.[1] === event.id || refs.reply?.a?.[1] === address;
86
+ });
87
+ }));
88
+ };
89
+ }
90
+ // Register this model with EventModels
91
+ EventModels.prototype.thread = function (root) {
92
+ return this.model(ThreadModel, root);
93
+ };
@@ -0,0 +1,11 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
+ import { UserStatusPointer } from "../helpers/user-status.js";
4
+ export type UserStatus = UserStatusPointer & {
5
+ event: NostrEvent;
6
+ content: string;
7
+ };
8
+ /** A model that returns a parsed {@link UserStatus} for a certain type */
9
+ export declare function UserStatusModel(pubkey: string, type?: string): Model<UserStatus | undefined | null>;
10
+ /** A model that returns a directory of parsed {@link UserStatus} for a pubkey */
11
+ export declare function UserStatusesModel(pubkey: string): Model<Record<string, UserStatus>>;
@@ -0,0 +1,32 @@
1
+ import { getReplaceableIdentifier, kinds } from "applesauce-core/helpers/event";
2
+ import { map } from "rxjs";
3
+ import { getUserStatusPointer } from "../helpers/user-status.js";
4
+ /** A model that returns a parsed {@link UserStatus} for a certain type */
5
+ export function UserStatusModel(pubkey, type = "general") {
6
+ return (events) => events.replaceable(kinds.UserStatuses, pubkey, type).pipe(map((event) => {
7
+ if (!event)
8
+ return undefined;
9
+ const pointer = getUserStatusPointer(event);
10
+ if (!pointer)
11
+ return null;
12
+ return {
13
+ ...pointer,
14
+ event,
15
+ content: event.content,
16
+ };
17
+ }));
18
+ }
19
+ /** A model that returns a directory of parsed {@link UserStatus} for a pubkey */
20
+ export function UserStatusesModel(pubkey) {
21
+ return (events) => events.timeline([{ kinds: [kinds.UserStatuses], authors: [pubkey] }]).pipe(map((events) => {
22
+ return events.reduce((dir, event) => {
23
+ try {
24
+ const d = getReplaceableIdentifier(event);
25
+ return { ...dir, [d]: { event, ...getUserStatusPointer(event), content: event.content } };
26
+ }
27
+ catch (error) {
28
+ return dir;
29
+ }
30
+ }, {});
31
+ }));
32
+ }
@@ -0,0 +1,31 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { Rumor } from "../helpers/gift-wrap.js";
3
+ /**
4
+ * A model that returns all wrapped messages for a pubkey
5
+ * @param self - The pubkey of the user
6
+ */
7
+ export declare function WrappedMessagesModel(self: string): Model<Rumor[]>;
8
+ /** A model that returns all conversations that a pubkey is participating in */
9
+ export declare function WrappedMessagesGroups(self: string): Model<{
10
+ id: string;
11
+ participants: string[];
12
+ lastMessage: Rumor;
13
+ }[]>;
14
+ /**
15
+ * A model that returns all wrapped direct messages in a conversation
16
+ * @param self - The pubkey of the user
17
+ * @param participants - A conversation identifier or a list of participant pubkeys
18
+ */
19
+ export declare function WrappedMessagesGroup(self: string, participants: string | string[]): Model<Rumor[]>;
20
+ /**
21
+ * Returns an array of root wrapped messages that have replies
22
+ * @param self - The pubkey of the user
23
+ * @param participants - A conversation identifier or a list of participant pubkeys
24
+ */
25
+ export declare function WrappedMessageThreads(self: string, participants: string | string[]): Model<Rumor[]>;
26
+ /**
27
+ * A model that returns all the gift wrapped direct messages that are replies to a given message
28
+ * @param self - The pubkey of the user
29
+ * @param message - The message to get the replies for
30
+ */
31
+ export declare function WrappedMessageReplies(self: string, message: Rumor): Model<Rumor[]>;
@@ -0,0 +1,76 @@
1
+ import { watchEventsUpdates } from "applesauce-core/observable";
2
+ import { kinds } from "applesauce-core/helpers/event";
3
+ import { map } from "rxjs";
4
+ import { getGiftWrapRumor } from "../helpers/gift-wrap.js";
5
+ import { createConversationIdentifier, getConversationIdentifierFromMessage, getConversationParticipants, } from "../helpers/messages.js";
6
+ import { getWrappedMessageParent } from "../helpers/wrapped-messages.js";
7
+ /**
8
+ * A model that returns all wrapped messages for a pubkey
9
+ * @param self - The pubkey of the user
10
+ */
11
+ export function WrappedMessagesModel(self) {
12
+ return (store) => store.timeline({ kinds: [kinds.GiftWrap], "#p": [self] }).pipe(
13
+ // Watch for updates to the gift wraps
14
+ watchEventsUpdates(store),
15
+ // Get rumors and filter out locked
16
+ map((rumors) => rumors
17
+ .map((gift) => getGiftWrapRumor(gift))
18
+ .filter((e) => !!e)
19
+ .filter((e) => e.kind === kinds.PrivateDirectMessage)
20
+ .sort((a, b) => b.created_at - a.created_at)));
21
+ }
22
+ /** A model that returns all conversations that a pubkey is participating in */
23
+ export function WrappedMessagesGroups(self) {
24
+ return (store) => store.model(WrappedMessagesModel, self).pipe(map((messages) => {
25
+ const groups = {};
26
+ for (const message of messages) {
27
+ const id = getConversationIdentifierFromMessage(message);
28
+ if (!groups[id] || groups[id].created_at < message.created_at)
29
+ groups[id] = message;
30
+ }
31
+ return Object.values(groups).map((message) => ({
32
+ id: getConversationIdentifierFromMessage(message),
33
+ participants: getConversationParticipants(message),
34
+ lastMessage: message,
35
+ }));
36
+ }));
37
+ }
38
+ /**
39
+ * A model that returns all wrapped direct messages in a conversation
40
+ * @param self - The pubkey of the user
41
+ * @param participants - A conversation identifier or a list of participant pubkeys
42
+ */
43
+ export function WrappedMessagesGroup(self, participants) {
44
+ // Get the conversation identifier include the users pubkey
45
+ const identifier = createConversationIdentifier(self, participants);
46
+ return (store) => store.model(WrappedMessagesModel, self).pipe(
47
+ // Only select direct messages for this conversation
48
+ map((rumors) => rumors.filter((rumor) => rumor.kind === kinds.PrivateDirectMessage &&
49
+ // Only select message for this conversation (the identifier from the message will include "self")
50
+ getConversationIdentifierFromMessage(rumor) === identifier)));
51
+ }
52
+ /**
53
+ * Returns an array of root wrapped messages that have replies
54
+ * @param self - The pubkey of the user
55
+ * @param participants - A conversation identifier or a list of participant pubkeys
56
+ */
57
+ export function WrappedMessageThreads(self, participants) {
58
+ return (store) => store.model(WrappedMessagesGroup, self, participants).pipe(
59
+ // Filter down messages to only include root messages that have replies
60
+ map((rumors) => rumors.filter((rumor) =>
61
+ // Only select root messages
62
+ !getWrappedMessageParent(rumor) &&
63
+ // Check if message has any replies
64
+ rumors.some((r) => getWrappedMessageParent(r) === rumor.id))));
65
+ }
66
+ /**
67
+ * A model that returns all the gift wrapped direct messages that are replies to a given message
68
+ * @param self - The pubkey of the user
69
+ * @param message - The message to get the replies for
70
+ */
71
+ export function WrappedMessageReplies(self, message) {
72
+ const conversation = getConversationIdentifierFromMessage(message);
73
+ return (store) => store.model(WrappedMessagesGroup, self, conversation).pipe(
74
+ // Only select replies to this message
75
+ map((rumors) => rumors.filter((rumor) => getWrappedMessageParent(rumor) === message.id)));
76
+ }
@@ -0,0 +1,9 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { KnownEvent, kinds } from "applesauce-core/helpers/event";
3
+ import { AddressPointer, EventPointer } from "applesauce-core/helpers/pointers";
4
+ /** A model that gets all zap events for an event */
5
+ export declare function EventZapsModel(id: string | EventPointer | AddressPointer): Model<KnownEvent<kinds.Zap>[]>;
6
+ /** A model that returns all zaps sent by a user */
7
+ export declare function SentZapsModel(pubkey: string): Model<KnownEvent<kinds.Zap>[]>;
8
+ /** A model that returns all zaps received by a user */
9
+ export declare function ReceivedZapsModel(pubkey: string): Model<KnownEvent<kinds.Zap>[]>;