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,270 @@
1
+ import { getOrComputeCachedValue } from "applesauce-core/helpers/cache";
2
+ import { getTagValue } from "applesauce-core/helpers/event";
3
+ export const TORRENT_KIND = 2003;
4
+ const TorrentInfoHashSymbol = Symbol.for("torrent-info-hash");
5
+ const TorrentTitleSymbol = Symbol.for("torrent-title");
6
+ const TorrentFilesSymbol = Symbol.for("torrent-files");
7
+ const TorrentTrackersSymbol = Symbol.for("torrent-trackers");
8
+ const TorrentCategorySymbol = Symbol.for("torrent-category");
9
+ const TorrentSearchTagsSymbol = Symbol.for("torrent-search-tags");
10
+ const TorrentCategoryPathSymbol = Symbol.for("torrent-category-path");
11
+ const TorrentExternalIdentifiersSymbol = Symbol.for("torrent-external-identifiers");
12
+ const TorrentMagnetLinkSymbol = Symbol.for("torrent-magnet-link");
13
+ export function getTorrentInfoHash(torrent) {
14
+ if (torrent.kind !== TORRENT_KIND)
15
+ return undefined;
16
+ return getOrComputeCachedValue(torrent, TorrentInfoHashSymbol, () => {
17
+ return getTagValue(torrent, "x");
18
+ });
19
+ }
20
+ /** Returns the torrent title from the `title` tag */
21
+ export function getTorrentTitle(torrent) {
22
+ if (torrent.kind !== TORRENT_KIND)
23
+ return undefined;
24
+ return getOrComputeCachedValue(torrent, TorrentTitleSymbol, () => {
25
+ return getTagValue(torrent, "title");
26
+ });
27
+ }
28
+ /** Returns all file entries from `file` tags */
29
+ export function getTorrentFiles(torrent) {
30
+ if (torrent.kind !== TORRENT_KIND)
31
+ return [];
32
+ return getOrComputeCachedValue(torrent, TorrentFilesSymbol, () => {
33
+ const files = [];
34
+ for (const tag of torrent.tags) {
35
+ if (tag[0] === "file" && tag[1]) {
36
+ const file = {
37
+ name: tag[1],
38
+ };
39
+ // Optional size in bytes (tag[2])
40
+ if (tag[2]) {
41
+ const size = parseInt(tag[2], 10);
42
+ if (!Number.isNaN(size)) {
43
+ file.size = size;
44
+ }
45
+ }
46
+ files.push(file);
47
+ }
48
+ }
49
+ return files;
50
+ });
51
+ }
52
+ /** Returns all tracker URLs from `tracker` tags */
53
+ export function getTorrentTrackers(torrent) {
54
+ if (torrent.kind !== TORRENT_KIND)
55
+ return [];
56
+ return getOrComputeCachedValue(torrent, TorrentTrackersSymbol, () => {
57
+ const trackers = [];
58
+ for (const tag of torrent.tags) {
59
+ if (tag[0] === "tracker" && tag[1])
60
+ trackers.push(tag[1]);
61
+ }
62
+ return trackers;
63
+ });
64
+ }
65
+ /** Returns the newznab category ID from the `i` tag with `newznab:` prefix */
66
+ export function getTorrentCategory(torrent) {
67
+ if (torrent.kind !== TORRENT_KIND)
68
+ return undefined;
69
+ return getOrComputeCachedValue(torrent, TorrentCategorySymbol, () => {
70
+ for (const tag of torrent.tags) {
71
+ if (tag[0] === "i" && tag[1]?.startsWith("newznab:")) {
72
+ const categoryId = parseInt(tag[1].slice(8), 10); // Return the ID after "newznab:"
73
+ if (!Number.isNaN(categoryId)) {
74
+ return categoryId;
75
+ }
76
+ }
77
+ }
78
+ return undefined;
79
+ });
80
+ }
81
+ /** Returns all search tags (for searchability) from `t` tags */
82
+ export function getTorrentSearchTags(torrent) {
83
+ if (torrent.kind !== TORRENT_KIND)
84
+ return [];
85
+ return getOrComputeCachedValue(torrent, TorrentSearchTagsSymbol, () => {
86
+ const tags = [];
87
+ for (const tag of torrent.tags) {
88
+ if (tag[0] === "t" && tag[1])
89
+ tags.push(tag[1]);
90
+ }
91
+ return tags;
92
+ });
93
+ }
94
+ /** Returns the category path from the `tcat` identifier in `i` tags (e.g., "video,movie,4k") */
95
+ export function getTorrentCategoryPath(torrent) {
96
+ if (torrent.kind !== TORRENT_KIND)
97
+ return undefined;
98
+ return getOrComputeCachedValue(torrent, TorrentCategoryPathSymbol, () => {
99
+ for (const tag of torrent.tags) {
100
+ if (tag[0] === "i" && tag[1]?.startsWith("tcat:")) {
101
+ return tag[1].slice(5); // Return the path after "tcat:"
102
+ }
103
+ }
104
+ return undefined;
105
+ });
106
+ }
107
+ /**
108
+ * Parses an external identifier from an `i` tag value
109
+ * Supports prefixes: tmdb, ttvdb, imdb, mal, anilist
110
+ * Supports second-level prefixes: tmdb:movie, ttvdb:movie, mal:anime, mal:manga
111
+ * Note: tcat is excluded as it's handled separately via getTorrentCategoryPath
112
+ * Note: newznab is excluded as it's handled separately via getTorrentCategory
113
+ */
114
+ function parseTorrentExternalIdentifier(value) {
115
+ // Skip tcat - it's handled separately as a category path
116
+ if (value.startsWith("tcat:"))
117
+ return null;
118
+ // Skip newznab - it's handled separately as categories
119
+ if (value.startsWith("newznab:"))
120
+ return null;
121
+ // tmdb:movie:693134 or tmdb:693134
122
+ if (value.startsWith("tmdb:")) {
123
+ const rest = value.slice(5);
124
+ const parts = rest.split(":");
125
+ if (parts.length === 2) {
126
+ return {
127
+ prefix: "tmdb",
128
+ identifier: value,
129
+ mediaType: parts[0],
130
+ id: parts[1],
131
+ };
132
+ }
133
+ else {
134
+ return {
135
+ prefix: "tmdb",
136
+ identifier: value,
137
+ id: rest,
138
+ };
139
+ }
140
+ }
141
+ // ttvdb:movie:290272 or ttvdb:290272
142
+ if (value.startsWith("ttvdb:")) {
143
+ const rest = value.slice(6);
144
+ const parts = rest.split(":");
145
+ if (parts.length === 2) {
146
+ return {
147
+ prefix: "ttvdb",
148
+ identifier: value,
149
+ mediaType: parts[0],
150
+ id: parts[1],
151
+ };
152
+ }
153
+ else {
154
+ return {
155
+ prefix: "ttvdb",
156
+ identifier: value,
157
+ id: rest,
158
+ };
159
+ }
160
+ }
161
+ // imdb:tt15239678
162
+ if (value.startsWith("imdb:")) {
163
+ return {
164
+ prefix: "imdb",
165
+ identifier: value,
166
+ id: value.slice(5),
167
+ };
168
+ }
169
+ // mal:anime:9253 or mal:manga:17517 or mal:9253
170
+ if (value.startsWith("mal:")) {
171
+ const rest = value.slice(4);
172
+ const parts = rest.split(":");
173
+ if (parts.length === 2) {
174
+ return {
175
+ prefix: "mal",
176
+ identifier: value,
177
+ mediaType: parts[0],
178
+ id: parts[1],
179
+ };
180
+ }
181
+ else {
182
+ return {
183
+ prefix: "mal",
184
+ identifier: value,
185
+ id: rest,
186
+ };
187
+ }
188
+ }
189
+ // anilist:12345
190
+ if (value.startsWith("anilist:")) {
191
+ return {
192
+ prefix: "anilist",
193
+ identifier: value,
194
+ id: value.slice(8),
195
+ };
196
+ }
197
+ return null;
198
+ }
199
+ /** Returns all external identifiers from `i` tags (excluding tcat and newznab, which are handled separately) */
200
+ export function getTorrentExternalIdentifiers(torrent) {
201
+ if (torrent.kind !== TORRENT_KIND)
202
+ return [];
203
+ return getOrComputeCachedValue(torrent, TorrentExternalIdentifiersSymbol, () => {
204
+ const identifiers = [];
205
+ for (const tag of torrent.tags) {
206
+ if (tag[0] === "i" && tag[1]) {
207
+ const parsed = parseTorrentExternalIdentifier(tag[1]);
208
+ if (parsed) {
209
+ identifiers.push(parsed);
210
+ }
211
+ }
212
+ }
213
+ return identifiers;
214
+ });
215
+ }
216
+ /**
217
+ * Creates a TorrentExternalIdentifier object from prefix, id, and optional mediaType
218
+ * Automatically constructs the full identifier string
219
+ */
220
+ export function createTorrentExternalIdentifier(prefix, id, mediaType) {
221
+ let identifier;
222
+ // Build identifier string based on prefix and whether mediaType is provided
223
+ if (mediaType) {
224
+ // For prefixes that support mediaType: tmdb, ttvdb, mal
225
+ identifier = `${prefix}:${mediaType}:${id}`;
226
+ }
227
+ else {
228
+ // Simple format: prefix:id
229
+ identifier = `${prefix}:${id}`;
230
+ }
231
+ return {
232
+ prefix,
233
+ identifier,
234
+ mediaType,
235
+ id,
236
+ };
237
+ }
238
+ /**
239
+ * Builds a magnet link from an info hash, optional trackers, and optional name
240
+ * Format: magnet:?xt=urn:btih:${infoHash}${trackers ? '&tr=' + trackers.join('&tr=') : ''}${name ? '&dn=' + encodeURIComponent(name) : ''}
241
+ */
242
+ export function buildTorrentMagnetLink(infoHash, trackers, name) {
243
+ const parts = [`magnet:?xt=urn:btih:${infoHash}`];
244
+ if (trackers && trackers.length > 0) {
245
+ for (const tracker of trackers) {
246
+ parts.push(`&tr=${encodeURIComponent(tracker)}`);
247
+ }
248
+ }
249
+ if (name) {
250
+ parts.push(`&dn=${encodeURIComponent(name)}`);
251
+ }
252
+ return parts.join("");
253
+ }
254
+ /** Returns the magnet link for a torrent, building it from the event if needed */
255
+ export function getTorrentMagnetLink(torrent) {
256
+ if (torrent.kind !== TORRENT_KIND)
257
+ return undefined;
258
+ return getOrComputeCachedValue(torrent, TorrentMagnetLinkSymbol, () => {
259
+ const infoHash = getTorrentInfoHash(torrent);
260
+ if (!infoHash)
261
+ return undefined;
262
+ const trackers = getTorrentTrackers(torrent);
263
+ const title = getTorrentTitle(torrent);
264
+ return buildTorrentMagnetLink(infoHash, trackers.length > 0 ? trackers : undefined, title);
265
+ });
266
+ }
267
+ /** Validates that an event is a valid torrent event (kind 2003 with required `x` tag) */
268
+ export function isValidTorrent(torrent) {
269
+ return torrent.kind === TORRENT_KIND && getTorrentInfoHash(torrent) !== undefined;
270
+ }
@@ -0,0 +1,18 @@
1
+ import { NostrEvent } from "applesauce-core/helpers/event";
2
+ import { AddressPointer, EventPointer, ProfilePointer } from "applesauce-core/helpers/pointers";
3
+ export declare const UserStatusPointerSymbol: unique symbol;
4
+ export type UserStatusPointer = {
5
+ type: "nevent";
6
+ data: EventPointer;
7
+ } | {
8
+ type: "nprofile";
9
+ data: ProfilePointer;
10
+ } | {
11
+ type: "naddr";
12
+ data: AddressPointer;
13
+ } | {
14
+ type: "url";
15
+ data: string;
16
+ };
17
+ /** Gets the {@link UserStatusPointer} for a status event */
18
+ export declare function getUserStatusPointer(status: NostrEvent): UserStatusPointer | null;
@@ -0,0 +1,22 @@
1
+ import { getOrComputeCachedValue } from "applesauce-core/helpers/cache";
2
+ import { getAddressPointerFromATag, getEventPointerFromETag, getProfilePointerFromPTag, } from "applesauce-core/helpers/pointers";
3
+ export const UserStatusPointerSymbol = Symbol.for("user-status-pointer");
4
+ function getStatusPointer(status) {
5
+ const pTag = status.tags.find((t) => t[0] === "p" && t[1]);
6
+ if (pTag)
7
+ return { type: "nprofile", data: getProfilePointerFromPTag(pTag) };
8
+ const eTag = status.tags.find((t) => t[0] === "e" && t[1]);
9
+ if (eTag)
10
+ return { type: "nevent", data: getEventPointerFromETag(eTag) };
11
+ const aTag = status.tags.find((t) => t[0] === "a" && t[1]);
12
+ if (aTag)
13
+ return { type: "naddr", data: getAddressPointerFromATag(aTag) };
14
+ const rTag = status.tags.find((t) => t[0] === "r" && t[1]);
15
+ if (rTag)
16
+ return { type: "url", data: rTag[1] };
17
+ return null;
18
+ }
19
+ /** Gets the {@link UserStatusPointer} for a status event */
20
+ export function getUserStatusPointer(status) {
21
+ return getOrComputeCachedValue(status, UserStatusPointerSymbol, () => getStatusPointer(status));
22
+ }
@@ -0,0 +1,14 @@
1
+ import { Rumor } from "./gift-wrap.js";
2
+ /** Returns the subject of a wrapped direct message */
3
+ export declare function getWrappedMessageSubject(message: Rumor): string | undefined;
4
+ /** Returns the parent id of a wrapped direct message */
5
+ export declare function getWrappedMessageParent(message: Rumor): string | undefined;
6
+ /** Returns the sender of a wrapped direct message */
7
+ export declare function getWrappedMessageSender(message: Rumor): string;
8
+ /** @deprecated use {@link getWrappedMessageSender} instead */
9
+ export declare const getWrappedMesssageSender: typeof getWrappedMessageSender;
10
+ /**
11
+ * Returns the first participant in a conversation that is not the sender
12
+ * @see getConversationParticipants
13
+ */
14
+ export declare function getWrappedMessageReceiver(message: Rumor): string;
@@ -0,0 +1,23 @@
1
+ import { getTagValue } from "applesauce-core/helpers/event";
2
+ import { getConversationParticipants } from "./messages.js";
3
+ /** Returns the subject of a wrapped direct message */
4
+ export function getWrappedMessageSubject(message) {
5
+ return getTagValue(message, "subject");
6
+ }
7
+ /** Returns the parent id of a wrapped direct message */
8
+ export function getWrappedMessageParent(message) {
9
+ return getTagValue(message, "e");
10
+ }
11
+ /** Returns the sender of a wrapped direct message */
12
+ export function getWrappedMessageSender(message) {
13
+ return message.pubkey;
14
+ }
15
+ /** @deprecated use {@link getWrappedMessageSender} instead */
16
+ export const getWrappedMesssageSender = getWrappedMessageSender;
17
+ /**
18
+ * Returns the first participant in a conversation that is not the sender
19
+ * @see getConversationParticipants
20
+ */
21
+ export function getWrappedMessageReceiver(message) {
22
+ return getConversationParticipants(message).filter((p) => p !== message.pubkey)[0];
23
+ }
@@ -0,0 +1,46 @@
1
+ import { KnownEvent } from "applesauce-core/helpers/event";
2
+ import { kinds, NostrEvent } from "applesauce-core/helpers/event";
3
+ import { AddressPointer, EventPointer } from "applesauce-core/helpers/pointers";
4
+ import { ParsedInvoice } from "./bolt11.js";
5
+ /** Type for validated zap event */
6
+ export type ZapEvent = KnownEvent<kinds.Zap>;
7
+ export declare const ZapRequestSymbol: unique symbol;
8
+ export declare const ZapSenderSymbol: unique symbol;
9
+ export declare const ZapReceiverSymbol: unique symbol;
10
+ export declare const ZapInvoiceSymbol: unique symbol;
11
+ export declare const ZapEventPointerSymbol: unique symbol;
12
+ export declare const ZapAddressPointerSymbol: unique symbol;
13
+ /** Returns the senders pubkey */
14
+ export declare function getZapSender(zap: ZapEvent): string;
15
+ export declare function getZapSender(zap: NostrEvent): string | undefined;
16
+ /** Gets the receivers pubkey */
17
+ export declare function getZapRecipient(zap: ZapEvent): string;
18
+ export declare function getZapRecipient(zap: NostrEvent): string | undefined;
19
+ /** Returns the parsed bolt11 invoice */
20
+ export declare function getZapPayment(zap: ZapEvent): ParsedInvoice;
21
+ export declare function getZapPayment(zap: NostrEvent): ParsedInvoice | undefined;
22
+ /** Returns the zap event amount in msats */
23
+ export declare function getZapAmount(zap: ZapEvent): number;
24
+ export declare function getZapAmount(zap: NostrEvent): number | undefined;
25
+ /** Gets the AddressPointer that was zapped */
26
+ export declare function getZapAddressPointer(zap: NostrEvent): AddressPointer | null;
27
+ /** Gets the EventPointer that was zapped */
28
+ export declare function getZapEventPointer(zap: NostrEvent): EventPointer | null;
29
+ /** Gets the preimage for the bolt11 invoice */
30
+ export declare function getZapPreimage(zap: NostrEvent): string | undefined;
31
+ /** Returns the zap request event inside the zap receipt */
32
+ export declare function getZapRequest(zap: ZapEvent): NostrEvent;
33
+ export declare function getZapRequest(zap: NostrEvent): NostrEvent | undefined;
34
+ /**
35
+ * Checks if a zap event is valid (not missing fields)
36
+ * DOES NOT validate LNURL address
37
+ */
38
+ export declare function isValidZap(zap?: NostrEvent): zap is ZapEvent;
39
+ export type ZapSplit = {
40
+ pubkey: string;
41
+ percent: number;
42
+ weight: number;
43
+ relay?: string;
44
+ };
45
+ /** Returns the zap splits for an event */
46
+ export declare function getZapSplits(event: NostrEvent): ZapSplit[] | undefined;
@@ -0,0 +1,125 @@
1
+ import { getOrComputeCachedValue } from "applesauce-core/helpers/cache";
2
+ import { isEvent, verifyWrappedEvent } from "applesauce-core/helpers/event";
3
+ import { getTagValue } from "applesauce-core/helpers/event";
4
+ import { getAddressPointerFromATag, getEventPointerFromETag } from "applesauce-core/helpers/pointers";
5
+ import { isATag, isETag } from "applesauce-core/helpers/tags";
6
+ import { kinds } from "applesauce-core/helpers/event";
7
+ import { parseBolt11 } from "./bolt11.js";
8
+ export const ZapRequestSymbol = Symbol.for("zap-request");
9
+ export const ZapSenderSymbol = Symbol.for("zap-sender");
10
+ export const ZapReceiverSymbol = Symbol.for("zap-receiver");
11
+ export const ZapInvoiceSymbol = Symbol.for("zap-bolt11");
12
+ export const ZapEventPointerSymbol = Symbol.for("zap-event-pointer");
13
+ export const ZapAddressPointerSymbol = Symbol.for("zap-address-pointer");
14
+ export function getZapSender(zap) {
15
+ return getOrComputeCachedValue(zap, ZapSenderSymbol, () => {
16
+ return getTagValue(zap, "P") || getZapRequest(zap)?.pubkey;
17
+ });
18
+ }
19
+ export function getZapRecipient(zap) {
20
+ return getOrComputeCachedValue(zap, ZapReceiverSymbol, () => {
21
+ return getTagValue(zap, "p");
22
+ });
23
+ }
24
+ export function getZapPayment(zap) {
25
+ return getOrComputeCachedValue(zap, ZapInvoiceSymbol, () => {
26
+ const bolt11 = getTagValue(zap, "bolt11");
27
+ try {
28
+ // Catch errors with parsing the bolt11 invoice
29
+ return bolt11 ? parseBolt11(bolt11) : undefined;
30
+ }
31
+ catch (error) {
32
+ return undefined;
33
+ }
34
+ });
35
+ }
36
+ export function getZapAmount(zap) {
37
+ return getZapPayment(zap)?.amount;
38
+ }
39
+ /** Gets the AddressPointer that was zapped */
40
+ export function getZapAddressPointer(zap) {
41
+ return getOrComputeCachedValue(zap, ZapAddressPointerSymbol, () => {
42
+ const a = zap.tags.find(isATag);
43
+ return a ? getAddressPointerFromATag(a) : null;
44
+ });
45
+ }
46
+ /** Gets the EventPointer that was zapped */
47
+ export function getZapEventPointer(zap) {
48
+ return getOrComputeCachedValue(zap, ZapEventPointerSymbol, () => {
49
+ const e = zap.tags.find(isETag);
50
+ return e ? getEventPointerFromETag(e) : null;
51
+ });
52
+ }
53
+ /** Gets the preimage for the bolt11 invoice */
54
+ export function getZapPreimage(zap) {
55
+ return getTagValue(zap, "preimage");
56
+ }
57
+ export function getZapRequest(zap) {
58
+ return getOrComputeCachedValue(zap, ZapRequestSymbol, () => {
59
+ const description = getTagValue(zap, "description");
60
+ if (!description)
61
+ return;
62
+ // Attempt to parse the zap request
63
+ try {
64
+ // Copied from nostr-tools/nip57 and modified to use the internal verifyZap method and return the zap request event
65
+ let zapRequest;
66
+ try {
67
+ zapRequest = JSON.parse(description);
68
+ }
69
+ catch (err) {
70
+ throw new Error("Invalid zap request JSON.");
71
+ }
72
+ if (!isEvent(zapRequest))
73
+ throw new Error("Zap request is not a valid Nostr event.");
74
+ if (!verifyWrappedEvent(zapRequest))
75
+ throw new Error("Invalid signature on zap request.");
76
+ let p = zapRequest.tags.find(([t, v]) => t === "p" && v);
77
+ if (!p)
78
+ throw new Error("Zap request doesn't have a 'p' tag.");
79
+ if (!p[1].match(/^[a-f0-9]{64}$/))
80
+ throw new Error("Zap request 'p' tag is not valid hex.");
81
+ let e = zapRequest.tags.find(([t, v]) => t === "e" && v);
82
+ if (e && !e[1].match(/^[a-f0-9]{64}$/))
83
+ throw new Error("Zap request 'e' tag is not valid hex.");
84
+ let relays = zapRequest.tags.find(([t, v]) => t === "relays" && v);
85
+ if (!relays)
86
+ throw new Error("Zap request doesn't have a 'relays' tag.");
87
+ return zapRequest;
88
+ }
89
+ catch (error) {
90
+ return undefined;
91
+ }
92
+ });
93
+ }
94
+ /**
95
+ * Checks if a zap event is valid (not missing fields)
96
+ * DOES NOT validate LNURL address
97
+ */
98
+ export function isValidZap(zap) {
99
+ if (!zap)
100
+ return false;
101
+ if (zap.kind !== kinds.Zap)
102
+ return false;
103
+ // Is not a valid zap kind if any of these is undefined
104
+ if (getZapPayment(zap) === undefined)
105
+ return false;
106
+ if (getZapRequest(zap) === undefined)
107
+ return false;
108
+ if (getZapRecipient(zap) === undefined)
109
+ return false;
110
+ if (getZapSender(zap) === undefined)
111
+ return false;
112
+ return true;
113
+ }
114
+ /** Returns the zap splits for an event */
115
+ export function getZapSplits(event) {
116
+ const tags = event.tags.filter((t) => t[0] === "zap" && t[1] && t[3]);
117
+ if (tags.length > 0) {
118
+ const targets = tags
119
+ .map((t) => ({ pubkey: t[1], relay: t[2], weight: parseFloat(t[3]) }))
120
+ .filter((p) => Number.isFinite(p.weight));
121
+ const total = targets.reduce((v, p) => v + p.weight, 0);
122
+ return targets.map((p) => ({ ...p, percent: p.weight / total }));
123
+ }
124
+ return undefined;
125
+ }
@@ -0,0 +1,5 @@
1
+ export * as Helpers from "./helpers/index.js";
2
+ export * as Models from "./models/index.js";
3
+ export * as Operations from "./operations/index.js";
4
+ export * as Blueprints from "./blueprints/index.js";
5
+ import "./register.js";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * as Helpers from "./helpers/index.js";
2
+ export * as Models from "./models/index.js";
3
+ export * as Operations from "./operations/index.js";
4
+ export * as Blueprints from "./blueprints/index.js";
5
+ // Register the common models and blueprints with the event store and event factory
6
+ import "./register.js";
@@ -0,0 +1,11 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { ProfilePointer } from "applesauce-core/helpers/pointers";
3
+ import { type Observable } from "rxjs";
4
+ /** A model that returns a users blossom servers */
5
+ export declare function UserBlossomServersModel(user: string | ProfilePointer): Model<URL[]>;
6
+ declare module "applesauce-core/event-store" {
7
+ interface EventModels {
8
+ /** Subscribe to a users blossom servers */
9
+ blossomServers(user: string | ProfilePointer): Observable<URL[]>;
10
+ }
11
+ }
@@ -0,0 +1,18 @@
1
+ import { map } from "rxjs/operators";
2
+ import { BLOSSOM_SERVER_LIST_KIND, getBlossomServersFromList } from "../helpers/blossom.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 a users blossom servers */
6
+ export function UserBlossomServersModel(user) {
7
+ if (typeof user === "string")
8
+ user = { pubkey: user };
9
+ return (store) => store
10
+ .replaceable({ kind: BLOSSOM_SERVER_LIST_KIND, pubkey: user.pubkey, relays: user.relays })
11
+ .pipe(map((event) => (event ? getBlossomServersFromList(event) : [])));
12
+ }
13
+ // Register this model with EventModels
14
+ EventModels.prototype.blossomServers = function (user) {
15
+ if (typeof user === "string")
16
+ user = { pubkey: user };
17
+ return this.model(UserBlossomServersModel, user);
18
+ };
@@ -0,0 +1,8 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { Bookmarks } from "../helpers/bookmark.js";
3
+ /** A model that returns all the bookmarks of a user */
4
+ export declare function UserBookmarkModel(pubkey: string): Model<Bookmarks | undefined>;
5
+ /** A model that returns all the public bookmarks of a user */
6
+ export declare function UserPublicBookmarkModel(pubkey: string): Model<Bookmarks | undefined>;
7
+ /** A model that returns all the hidden bookmarks of a user */
8
+ export declare function UserHiddenBookmarkModel(pubkey: string): Model<Bookmarks | null | undefined>;
@@ -0,0 +1,24 @@
1
+ import { kinds } from "applesauce-core/helpers/event";
2
+ import { watchEventUpdates } from "applesauce-core/observable";
3
+ import { map } from "rxjs/operators";
4
+ import { getBookmarks, getHiddenBookmarks, getPublicBookmarks } from "../helpers/bookmark.js";
5
+ /** A model that returns all the bookmarks of a user */
6
+ export function UserBookmarkModel(pubkey) {
7
+ return (events) => events.replaceable(kinds.Mutelist, pubkey).pipe(
8
+ // listen for event updates (hidden tags unlocked)
9
+ watchEventUpdates(events),
10
+ // Get all bookmarks
11
+ map((event) => event && getBookmarks(event)));
12
+ }
13
+ /** A model that returns all the public bookmarks of a user */
14
+ export function UserPublicBookmarkModel(pubkey) {
15
+ return (events) => events.replaceable(kinds.Mutelist, pubkey).pipe(map((event) => event && getPublicBookmarks(event)));
16
+ }
17
+ /** A model that returns all the hidden bookmarks of a user */
18
+ export function UserHiddenBookmarkModel(pubkey) {
19
+ return (events) => events.replaceable(kinds.Mutelist, pubkey).pipe(
20
+ // listen for event updates (hidden tags unlocked)
21
+ watchEventUpdates(events),
22
+ // Get hidden bookmarks
23
+ map((event) => event && (getHiddenBookmarks(event) ?? null)));
24
+ }
@@ -0,0 +1,6 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
+ /** A model that gets all the events for a calendar */
4
+ export declare function CalendarEventsModel(calendar: NostrEvent): Model<NostrEvent[]>;
5
+ /** A model that gets all the RSVPs for a calendar event */
6
+ export declare function CalendarEventRSVPsModel(event: NostrEvent): Model<NostrEvent[]>;
@@ -0,0 +1,15 @@
1
+ import { getReplaceableAddress } from "applesauce-core/helpers/event";
2
+ import { defined } from "applesauce-core/observable";
3
+ import { combineLatest } from "rxjs";
4
+ import { CALENDAR_EVENT_RSVP_KIND } from "../helpers/calendar-rsvp.js";
5
+ import { getCalendarAddressPointers } from "../helpers/calendar.js";
6
+ /** A model that gets all the events for a calendar */
7
+ export function CalendarEventsModel(calendar) {
8
+ return (events) => combineLatest(getCalendarAddressPointers(calendar).map((p) => events.replaceable(p.kind, p.pubkey, p.identifier).pipe(defined())));
9
+ }
10
+ /** A model that gets all the RSVPs for a calendar event */
11
+ export function CalendarEventRSVPsModel(event) {
12
+ return (events) => {
13
+ return events.timeline({ kinds: [CALENDAR_EVENT_RSVP_KIND], "#a": [getReplaceableAddress(event)] });
14
+ };
15
+ }
@@ -0,0 +1,11 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
+ import { ChannelMetadataContent } from "../helpers/channels.js";
4
+ /** A model that returns a map of hidden messages Map<id, reason> */
5
+ export declare function ChannelHiddenModel(channel: NostrEvent, authors?: string[]): Model<Map<string, string>>;
6
+ /** A model that returns all messages in a channel */
7
+ export declare function ChannelMessagesModel(channel: NostrEvent): Model<NostrEvent[]>;
8
+ /** A model that returns the latest parsed metadata */
9
+ export declare function ChannelMetadataModel(channel: NostrEvent): Model<ChannelMetadataContent | undefined>;
10
+ /** A model that returns a map of muted users Map<pubkey, reason> */
11
+ export declare function ChannelMutedModel(channel: NostrEvent, authors?: string[]): Model<Map<string, string>>;