applesauce-core 0.9.0 → 0.10.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 (74) hide show
  1. package/README.md +1 -1
  2. package/dist/event-store/database.d.ts +17 -13
  3. package/dist/event-store/database.js +50 -33
  4. package/dist/event-store/event-store.d.ts +37 -14
  5. package/dist/event-store/event-store.js +249 -96
  6. package/dist/helpers/bolt11.d.ts +1 -0
  7. package/dist/helpers/bolt11.js +1 -0
  8. package/dist/helpers/comment.d.ts +48 -0
  9. package/dist/helpers/comment.js +116 -0
  10. package/dist/helpers/content.d.ts +3 -0
  11. package/dist/helpers/content.js +8 -0
  12. package/dist/helpers/delete.d.ts +3 -0
  13. package/dist/helpers/delete.js +7 -0
  14. package/dist/helpers/emoji.d.ts +10 -1
  15. package/dist/helpers/emoji.js +12 -0
  16. package/dist/helpers/event.d.ts +9 -1
  17. package/dist/helpers/event.js +25 -1
  18. package/dist/helpers/external-id.d.ts +29 -0
  19. package/dist/helpers/external-id.js +20 -0
  20. package/dist/helpers/filter.d.ts +0 -2
  21. package/dist/helpers/filter.js +3 -7
  22. package/dist/helpers/hidden-tags.d.ts +48 -0
  23. package/dist/helpers/hidden-tags.js +108 -0
  24. package/dist/helpers/hidden-tags.test.d.ts +1 -0
  25. package/dist/helpers/hidden-tags.test.js +28 -0
  26. package/dist/helpers/index.d.ts +16 -8
  27. package/dist/helpers/index.js +16 -8
  28. package/dist/helpers/json.d.ts +1 -0
  29. package/dist/helpers/json.js +1 -0
  30. package/dist/helpers/lnurl.d.ts +4 -0
  31. package/dist/helpers/lnurl.js +40 -0
  32. package/dist/helpers/mailboxes.test.js +13 -12
  33. package/dist/helpers/media-attachment.d.ts +33 -0
  34. package/dist/helpers/media-attachment.js +60 -0
  35. package/dist/helpers/pointers.d.ts +38 -5
  36. package/dist/helpers/pointers.js +101 -17
  37. package/dist/helpers/profile.js +1 -1
  38. package/dist/helpers/string.d.ts +6 -0
  39. package/dist/helpers/string.js +2 -0
  40. package/dist/helpers/tags.d.ts +6 -0
  41. package/dist/helpers/tags.js +6 -0
  42. package/dist/helpers/threading.d.ts +6 -6
  43. package/dist/helpers/threading.js +30 -9
  44. package/dist/helpers/threading.test.d.ts +1 -0
  45. package/dist/helpers/threading.test.js +41 -0
  46. package/dist/helpers/url.d.ts +4 -1
  47. package/dist/helpers/url.js +4 -3
  48. package/dist/helpers/zap.d.ts +25 -0
  49. package/dist/helpers/zap.js +32 -3
  50. package/dist/observable/{getValue.d.ts → get-value.d.ts} +1 -0
  51. package/dist/observable/{getValue.js → get-value.js} +1 -0
  52. package/dist/observable/index.d.ts +1 -1
  53. package/dist/observable/index.js +1 -1
  54. package/dist/promise/deferred.d.ts +1 -0
  55. package/dist/promise/deferred.js +1 -0
  56. package/dist/queries/comments.d.ts +4 -0
  57. package/dist/queries/comments.js +14 -0
  58. package/dist/queries/index.d.ts +3 -2
  59. package/dist/queries/index.js +3 -2
  60. package/dist/queries/mailboxes.d.ts +1 -0
  61. package/dist/queries/mailboxes.js +1 -0
  62. package/dist/queries/profile.d.ts +1 -0
  63. package/dist/queries/profile.js +1 -0
  64. package/dist/queries/reactions.d.ts +1 -1
  65. package/dist/queries/reactions.js +1 -1
  66. package/dist/queries/simple.d.ts +3 -3
  67. package/dist/queries/simple.js +13 -13
  68. package/dist/queries/thread.d.ts +2 -0
  69. package/dist/queries/thread.js +29 -3
  70. package/dist/queries/zaps.d.ts +1 -0
  71. package/dist/queries/zaps.js +1 -0
  72. package/dist/query-store/index.d.ts +22 -12
  73. package/dist/query-store/index.js +36 -30
  74. package/package.json +10 -18
@@ -1,21 +1,89 @@
1
+ import { kinds } from "nostr-tools";
1
2
  import { insertEventIntoDescendingList } from "nostr-tools/utils";
3
+ import { isParameterizedReplaceableKind } from "nostr-tools/kinds";
2
4
  import { Observable } from "rxjs";
3
5
  import { Database } from "./database.js";
4
- import { getEventUID, getReplaceableUID } from "../helpers/event.js";
6
+ import { getEventUID, getReplaceableUID, getTagValue, isReplaceable } from "../helpers/event.js";
5
7
  import { matchFilters } from "../helpers/filter.js";
6
8
  import { addSeenRelay } from "../helpers/relays.js";
9
+ import { getDeleteCoordinates, getDeleteIds } from "../helpers/delete.js";
7
10
  export class EventStore {
8
11
  database;
12
+ /** Enable this to keep old versions of replaceable events */
13
+ keepOldVersions = false;
9
14
  constructor() {
10
15
  this.database = new Database();
11
16
  }
12
- /** Adds an event to the database */
17
+ /** Adds an event to the database and update subscriptions */
13
18
  add(event, fromRelay) {
19
+ if (event.kind === kinds.EventDeletion)
20
+ this.handleDeleteEvent(event);
21
+ // ignore if the event was deleted
22
+ if (this.checkDeleted(event))
23
+ return event;
24
+ // insert event into database
14
25
  const inserted = this.database.addEvent(event);
26
+ // remove all old version of the replaceable event
27
+ if (!this.keepOldVersions && isReplaceable(event.kind)) {
28
+ const current = this.database.getReplaceable(event.kind, event.pubkey, getTagValue(event, "d"));
29
+ if (current) {
30
+ const older = Array.from(current).filter((e) => e.created_at < event.created_at);
31
+ for (const old of older)
32
+ this.database.deleteEvent(old);
33
+ // skip inserting this event because its not the newest
34
+ if (current.length !== older.length)
35
+ return current[0];
36
+ }
37
+ }
38
+ // attach relay this event was from
15
39
  if (fromRelay)
16
40
  addSeenRelay(inserted, fromRelay);
17
41
  return inserted;
18
42
  }
43
+ /** Removes an event from the database and updates subscriptions */
44
+ remove(event) {
45
+ if (typeof event === "string")
46
+ return this.database.deleteEvent(event);
47
+ else if (this.database.hasEvent(event.id)) {
48
+ return this.database.deleteEvent(event.id);
49
+ }
50
+ else
51
+ return false;
52
+ }
53
+ deletedIds = new Set();
54
+ deletedCoords = new Map();
55
+ handleDeleteEvent(deleteEvent) {
56
+ const ids = getDeleteIds(deleteEvent);
57
+ for (const id of ids) {
58
+ this.deletedIds.add(id);
59
+ // remove deleted events in the database
60
+ const event = this.database.getEvent(id);
61
+ if (event)
62
+ this.database.deleteEvent(event);
63
+ }
64
+ const coords = getDeleteCoordinates(deleteEvent);
65
+ for (const coord of coords) {
66
+ this.deletedCoords.set(coord, Math.max(this.deletedCoords.get(coord) ?? 0, deleteEvent.created_at));
67
+ // remove deleted events in the database
68
+ const event = this.database.getEvent(coord);
69
+ if (event && event.created_at < deleteEvent.created_at)
70
+ this.database.deleteEvent(event);
71
+ }
72
+ }
73
+ checkDeleted(event) {
74
+ if (this.deletedIds.has(event.id))
75
+ return true;
76
+ if (isParameterizedReplaceableKind(event.kind)) {
77
+ const deleted = this.deletedCoords.get(getEventUID(event));
78
+ if (deleted)
79
+ return deleted > event.created_at;
80
+ }
81
+ return false;
82
+ }
83
+ /** Removes any event that is not being used by a subscription */
84
+ prune(max) {
85
+ return this.database.prune(max);
86
+ }
19
87
  /** Add an event to the store and notifies all subscribes it has updated */
20
88
  update(event) {
21
89
  return this.database.updateEvent(event);
@@ -32,93 +100,87 @@ export class EventStore {
32
100
  hasReplaceable(kind, pubkey, d) {
33
101
  return this.database.hasReplaceable(kind, pubkey, d);
34
102
  }
103
+ /** Gets the latest version of a replaceable event */
35
104
  getReplaceable(kind, pubkey, d) {
105
+ return this.database.getReplaceable(kind, pubkey, d)?.[0];
106
+ }
107
+ /** Returns all versions of a replaceable event */
108
+ getReplaceableHistory(kind, pubkey, d) {
36
109
  return this.database.getReplaceable(kind, pubkey, d);
37
110
  }
38
111
  /** Creates an observable that updates a single event */
39
- event(uid) {
112
+ event(id) {
40
113
  return new Observable((observer) => {
41
- let current = this.database.getEvent(uid);
114
+ let current = this.database.getEvent(id);
42
115
  if (current) {
43
116
  observer.next(current);
44
117
  this.database.claimEvent(current, observer);
45
118
  }
46
119
  // subscribe to future events
47
120
  const inserted = this.database.inserted.subscribe((event) => {
48
- if (getEventUID(event) === uid && (!current || event.created_at > current.created_at)) {
49
- // remove old claim
50
- if (current)
51
- this.database.removeClaim(current, observer);
121
+ if (event.id === id) {
52
122
  current = event;
53
123
  observer.next(event);
54
- // claim new event
55
- this.database.claimEvent(current, observer);
124
+ this.database.claimEvent(event, observer);
56
125
  }
57
126
  });
58
- // subscribe to updates
127
+ // subscribe to updated events
59
128
  const updated = this.database.updated.subscribe((event) => {
60
- if (event === current)
61
- observer.next(event);
129
+ if (event.id === id)
130
+ observer.next(current);
62
131
  });
63
132
  // subscribe to deleted events
64
133
  const deleted = this.database.deleted.subscribe((event) => {
65
- if (getEventUID(event) === uid && current) {
134
+ if (current?.id === event.id) {
66
135
  this.database.removeClaim(current, observer);
67
136
  current = undefined;
68
137
  observer.next(undefined);
69
138
  }
70
139
  });
71
140
  return () => {
72
- inserted.unsubscribe();
73
141
  deleted.unsubscribe();
74
142
  updated.unsubscribe();
143
+ inserted.unsubscribe();
75
144
  if (current)
76
145
  this.database.removeClaim(current, observer);
77
146
  };
78
147
  });
79
148
  }
80
149
  /** Creates an observable that subscribes to multiple events */
81
- events(uids) {
150
+ events(ids) {
82
151
  return new Observable((observer) => {
83
152
  const events = new Map();
84
- for (const uid of uids) {
85
- const e = this.getEvent(uid);
86
- if (e) {
87
- events.set(uid, e);
88
- this.database.claimEvent(e, observer);
153
+ for (const id of ids) {
154
+ const event = this.getEvent(id);
155
+ if (event) {
156
+ events.set(id, event);
157
+ this.database.claimEvent(event, observer);
89
158
  }
90
159
  }
91
160
  observer.next(events);
92
161
  // subscribe to future events
93
162
  const inserted = this.database.inserted.subscribe((event) => {
94
- const uid = getEventUID(event);
95
- if (uids.includes(uid)) {
96
- const current = events.get(uid);
97
- // remove old claim
98
- if (!current || event.created_at > current.created_at) {
99
- if (current)
100
- this.database.removeClaim(current, observer);
101
- events.set(uid, event);
102
- observer.next(events);
103
- // claim new event
104
- this.database.claimEvent(event, observer);
105
- }
163
+ const id = event.id;
164
+ if (ids.includes(id) && !events.has(id)) {
165
+ events.set(id, event);
166
+ observer.next(events);
167
+ // claim new event
168
+ this.database.claimEvent(event, observer);
106
169
  }
107
170
  });
108
- // subscribe to updates
171
+ // subscribe to updated events
109
172
  const updated = this.database.updated.subscribe((event) => {
110
- const uid = getEventUID(event);
111
- if (uids.includes(uid))
173
+ if (ids.includes(event.id))
112
174
  observer.next(events);
113
175
  });
114
176
  // subscribe to deleted events
115
177
  const deleted = this.database.deleted.subscribe((event) => {
116
- const uid = getEventUID(event);
117
- if (uids.includes(uid)) {
118
- const current = events.get(uid);
178
+ const id = event.id;
179
+ if (ids.includes(id)) {
180
+ const current = events.get(id);
119
181
  if (current) {
120
182
  this.database.removeClaim(current, observer);
121
- events.delete(uid);
183
+ events.delete(id);
122
184
  observer.next(events);
123
185
  }
124
186
  }
@@ -133,92 +195,182 @@ export class EventStore {
133
195
  };
134
196
  });
135
197
  }
136
- /** Creates an observable that updates a single replaceable event */
198
+ /** Creates an observable with the latest version of a replaceable event */
137
199
  replaceable(kind, pubkey, d) {
138
- return this.event(getReplaceableUID(kind, pubkey, d));
200
+ return new Observable((observer) => {
201
+ const uid = getReplaceableUID(kind, pubkey, d);
202
+ // get latest version
203
+ let current = this.database.getReplaceable(kind, pubkey, d)?.[0];
204
+ if (current) {
205
+ observer.next(current);
206
+ this.database.claimEvent(current, observer);
207
+ }
208
+ // subscribe to future events
209
+ const inserted = this.database.inserted.subscribe((event) => {
210
+ if (getEventUID(event) === uid && (!current || event.created_at > current.created_at)) {
211
+ // remove old claim
212
+ if (current)
213
+ this.database.removeClaim(current, observer);
214
+ current = event;
215
+ observer.next(event);
216
+ // claim new event
217
+ this.database.claimEvent(current, observer);
218
+ }
219
+ });
220
+ // subscribe to updated events
221
+ const updated = this.database.updated.subscribe((event) => {
222
+ if (event === current)
223
+ observer.next(event);
224
+ });
225
+ // subscribe to deleted events
226
+ const deleted = this.database.deleted.subscribe((event) => {
227
+ if (getEventUID(event) === uid && event === current) {
228
+ this.database.removeClaim(current, observer);
229
+ current = undefined;
230
+ observer.next(undefined);
231
+ }
232
+ });
233
+ return () => {
234
+ inserted.unsubscribe();
235
+ deleted.unsubscribe();
236
+ updated.unsubscribe();
237
+ if (current)
238
+ this.database.removeClaim(current, observer);
239
+ };
240
+ });
139
241
  }
140
- /** Creates an observable that streams all events that match the filter */
141
- stream(filters) {
242
+ /** Creates an observable with the latest versions of replaceable events */
243
+ replaceableSet(pointers) {
142
244
  return new Observable((observer) => {
143
- let claimed = new Set();
144
- let events = this.database.getForFilters(filters);
145
- for (const event of events) {
146
- observer.next(event);
245
+ const coords = pointers.map((p) => getReplaceableUID(p.kind, p.pubkey, p.identifier));
246
+ const events = new Map();
247
+ const handleEvent = (event) => {
248
+ const uid = getEventUID(event);
249
+ const current = events.get(uid);
250
+ if (current) {
251
+ if (event.created_at > current.created_at) {
252
+ this.database.removeClaim(current, observer);
253
+ }
254
+ else
255
+ return;
256
+ }
257
+ events.set(uid, event);
147
258
  this.database.claimEvent(event, observer);
148
- claimed.add(event);
259
+ };
260
+ // get latest version
261
+ for (const pointer of pointers) {
262
+ const events = this.database.getReplaceable(pointer.kind, pointer.pubkey, pointer.identifier);
263
+ if (events)
264
+ handleEvent(events[0]);
149
265
  }
266
+ observer.next(events);
150
267
  // subscribe to future events
151
- const sub = this.database.inserted.subscribe((event) => {
152
- if (matchFilters(filters, event)) {
153
- observer.next(event);
154
- this.database.claimEvent(event, observer);
155
- claimed.add(event);
268
+ const inserted = this.database.inserted.subscribe((event) => {
269
+ if (isReplaceable(event.kind) && coords.includes(getEventUID(event))) {
270
+ handleEvent(event);
271
+ observer.next(events);
272
+ }
273
+ });
274
+ // subscribe to updated events
275
+ const updated = this.database.updated.subscribe((event) => {
276
+ if (isReplaceable(event.kind) && coords.includes(getEventUID(event))) {
277
+ observer.next(events);
278
+ }
279
+ });
280
+ // subscribe to deleted events
281
+ const deleted = this.database.deleted.subscribe((event) => {
282
+ const uid = getEventUID(event);
283
+ if (events.has(uid)) {
284
+ events.delete(uid);
285
+ this.database.removeClaim(event, observer);
286
+ observer.next(events);
156
287
  }
157
288
  });
158
289
  return () => {
159
- sub.unsubscribe();
160
- // remove all claims
161
- for (const event of claimed)
290
+ inserted.unsubscribe();
291
+ deleted.unsubscribe();
292
+ updated.unsubscribe();
293
+ for (const [_id, event] of events) {
162
294
  this.database.removeClaim(event, observer);
163
- claimed.clear();
295
+ }
164
296
  };
165
297
  });
166
298
  }
299
+ /**
300
+ * Creates an observable that streams all events that match the filter
301
+ * @param filters
302
+ * @param [onlyNew=false] Only subscribe to new events
303
+ */
304
+ stream(filters, onlyNew = false) {
305
+ filters = Array.isArray(filters) ? filters : [filters];
306
+ return new Observable((observer) => {
307
+ if (!onlyNew) {
308
+ let events = this.database.getForFilters(filters);
309
+ for (const event of events)
310
+ observer.next(event);
311
+ }
312
+ // subscribe to future events
313
+ const sub = this.database.inserted.subscribe((event) => {
314
+ if (matchFilters(filters, event))
315
+ observer.next(event);
316
+ });
317
+ return () => sub.unsubscribe();
318
+ });
319
+ }
167
320
  /** Creates an observable that updates with an array of sorted events */
168
- timeline(filters) {
321
+ timeline(filters, keepOldVersions = false) {
322
+ filters = Array.isArray(filters) ? filters : [filters];
169
323
  return new Observable((observer) => {
170
324
  const seen = new Map();
171
325
  const timeline = [];
172
- // build initial timeline
173
- const events = this.database.getForFilters(filters);
174
- for (const event of events) {
326
+ // NOTE: only call this if we know the event is in timeline
327
+ const removeFromTimeline = (event) => {
328
+ timeline.splice(timeline.indexOf(event), 1);
329
+ if (!keepOldVersions && isReplaceable(event.kind))
330
+ seen.delete(getEventUID(event));
331
+ this.database.removeClaim(event, observer);
332
+ };
333
+ // inserts an event into the timeline and handles replaceable events
334
+ const insertIntoTimeline = (event) => {
335
+ // remove old versions
336
+ if (!keepOldVersions && isReplaceable(event.kind)) {
337
+ const uid = getEventUID(event);
338
+ const old = seen.get(uid);
339
+ if (old) {
340
+ if (event.created_at > old.created_at)
341
+ removeFromTimeline(old);
342
+ else
343
+ return;
344
+ }
345
+ seen.set(uid, event);
346
+ }
347
+ // insert into timeline
175
348
  insertEventIntoDescendingList(timeline, event);
176
349
  this.database.claimEvent(event, observer);
177
- seen.set(getEventUID(event), event);
178
- }
350
+ };
351
+ // build initial timeline
352
+ const events = this.database.getForFilters(filters);
353
+ for (const event of events)
354
+ insertIntoTimeline(event);
179
355
  observer.next([...timeline]);
180
356
  // subscribe to future events
181
357
  const inserted = this.database.inserted.subscribe((event) => {
182
358
  if (matchFilters(filters, event)) {
183
- const uid = getEventUID(event);
184
- let current = seen.get(uid);
185
- if (current) {
186
- if (event.created_at > current.created_at) {
187
- // replace event
188
- timeline.splice(timeline.indexOf(current), 1, event);
189
- observer.next([...timeline]);
190
- // update the claim
191
- seen.set(uid, event);
192
- this.database.removeClaim(current, observer);
193
- this.database.claimEvent(event, observer);
194
- }
195
- }
196
- else {
197
- insertEventIntoDescendingList(timeline, event);
198
- observer.next([...timeline]);
199
- // claim new event
200
- this.database.claimEvent(event, observer);
201
- seen.set(getEventUID(event), event);
202
- }
359
+ insertIntoTimeline(event);
360
+ observer.next([...timeline]);
203
361
  }
204
362
  });
205
- // subscribe to updates
363
+ // subscribe to updated events
206
364
  const updated = this.database.updated.subscribe((event) => {
207
- if (seen.has(getEventUID(event))) {
365
+ if (timeline.includes(event)) {
208
366
  observer.next([...timeline]);
209
367
  }
210
368
  });
211
- // subscribe to removed events
369
+ // subscribe to deleted events
212
370
  const deleted = this.database.deleted.subscribe((event) => {
213
- const uid = getEventUID(event);
214
- let current = seen.get(uid);
215
- if (current) {
216
- // remove the event
217
- timeline.splice(timeline.indexOf(current), 1);
371
+ if (timeline.includes(event)) {
372
+ removeFromTimeline(event);
218
373
  observer.next([...timeline]);
219
- // remove the claim
220
- seen.delete(uid);
221
- this.database.removeClaim(current, observer);
222
374
  }
223
375
  });
224
376
  return () => {
@@ -226,9 +378,10 @@ export class EventStore {
226
378
  deleted.unsubscribe();
227
379
  updated.unsubscribe();
228
380
  // remove all claims
229
- for (const [_, event] of seen) {
381
+ for (const event of timeline) {
230
382
  this.database.removeClaim(event, observer);
231
383
  }
384
+ // forget seen replaceable events
232
385
  seen.clear();
233
386
  };
234
387
  });
@@ -5,4 +5,5 @@ export type ParsedInvoice = {
5
5
  timestamp: number;
6
6
  expiry: number;
7
7
  };
8
+ /** Parses a lightning invoice */
8
9
  export declare function parseBolt11(paymentRequest: string): ParsedInvoice;
@@ -1,4 +1,5 @@
1
1
  import { decode } from "light-bolt11-decoder";
2
+ /** Parses a lightning invoice */
2
3
  export function parseBolt11(paymentRequest) {
3
4
  const decoded = decode(paymentRequest);
4
5
  const timestamp = decoded.sections.find((s) => s.name === "timestamp")?.value ?? 0;
@@ -0,0 +1,48 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { ExternalPointer, ExternalIdentifiers } from "./external-id.js";
3
+ export declare const COMMENT_KIND = 1111;
4
+ type CommentEventPointer = {
5
+ id: string;
6
+ kind: number;
7
+ pubkey?: string;
8
+ relay?: string;
9
+ };
10
+ type CommentAddressPointer = {
11
+ id?: string;
12
+ kind: number;
13
+ pubkey: string;
14
+ identifier: string;
15
+ relay?: string;
16
+ };
17
+ type CommentExternalPointer = ExternalPointer<keyof ExternalIdentifiers>;
18
+ export type CommentPointer = CommentEventPointer | CommentAddressPointer | CommentExternalPointer;
19
+ export declare const CommentRootPointerSymbol: unique symbol;
20
+ export declare const CommentReplyPointerSymbol: unique symbol;
21
+ /**
22
+ * Gets the EventPointer from an array of tags
23
+ * @throws
24
+ */
25
+ export declare function getCommentEventPointer(tags: string[][], root?: boolean): CommentEventPointer | null;
26
+ /**
27
+ * Gets the AddressPointer from an array of tags
28
+ * @throws
29
+ */
30
+ export declare function getCommentAddressPointer(tags: string[][], root?: boolean): CommentAddressPointer | null;
31
+ /**
32
+ * Gets the ExternalPointer from an array of tags
33
+ * @throws
34
+ */
35
+ export declare function getCommentExternalPointer(tags: string[][], root?: boolean): CommentExternalPointer | null;
36
+ /**
37
+ * Returns the root pointer for a comment
38
+ * @throws
39
+ */
40
+ export declare function getCommentRootPointer(comment: NostrEvent): CommentPointer | null;
41
+ /**
42
+ * Returns the reply pointer for a comment
43
+ * @throws
44
+ */
45
+ export declare function getCommentReplyPointer(comment: NostrEvent): CommentPointer | null;
46
+ export declare function isCommentEventPointer(pointer: any): pointer is CommentEventPointer;
47
+ export declare function isCommentAddressPointer(pointer: any): pointer is CommentAddressPointer;
48
+ export {};
@@ -0,0 +1,116 @@
1
+ import { getExternalPointerFromTag } from "./external-id.js";
2
+ import { getOrComputeCachedValue } from "./cache.js";
3
+ import { getAddressPointerFromATag } from "./pointers.js";
4
+ import { safeRelayUrl } from "./relays.js";
5
+ export const COMMENT_KIND = 1111;
6
+ export const CommentRootPointerSymbol = Symbol.for("comment-root-pointer");
7
+ export const CommentReplyPointerSymbol = Symbol.for("comment-reply-pointer");
8
+ /**
9
+ * Gets the EventPointer from an array of tags
10
+ * @throws
11
+ */
12
+ export function getCommentEventPointer(tags, root = false) {
13
+ const tag = tags.find((t) => t[0] === (root ? "E" : "e"));
14
+ const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
15
+ if (tag) {
16
+ if (!kind)
17
+ throw new Error("Missing kind tag");
18
+ const pointer = {
19
+ id: tag[1],
20
+ kind: parseInt(kind),
21
+ pubkey: tag[3] || undefined,
22
+ relay: tag[2] && (safeRelayUrl(tag[2]) ?? undefined),
23
+ };
24
+ return pointer;
25
+ }
26
+ return null;
27
+ }
28
+ /**
29
+ * Gets the AddressPointer from an array of tags
30
+ * @throws
31
+ */
32
+ export function getCommentAddressPointer(tags, root = false) {
33
+ const tag = tags.find((t) => t[0] === (root ? "A" : "a"));
34
+ const id = tags.find((t) => t[0] === (root ? "E" : "e"))?.[1];
35
+ const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
36
+ if (tag) {
37
+ if (!kind)
38
+ throw new Error("Missing kind tag");
39
+ const pointer = {
40
+ id,
41
+ ...getAddressPointerFromATag(tag),
42
+ kind: parseInt(kind),
43
+ };
44
+ return pointer;
45
+ }
46
+ return null;
47
+ }
48
+ /**
49
+ * Gets the ExternalPointer from an array of tags
50
+ * @throws
51
+ */
52
+ export function getCommentExternalPointer(tags, root = false) {
53
+ const tag = tags.find((t) => t[0] === (root ? "I" : "i"));
54
+ const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
55
+ if (tag) {
56
+ if (!kind)
57
+ throw new Error("Missing kind tag");
58
+ const pointer = getExternalPointerFromTag(tag);
59
+ return pointer;
60
+ }
61
+ return null;
62
+ }
63
+ /**
64
+ * Returns the root pointer for a comment
65
+ * @throws
66
+ */
67
+ export function getCommentRootPointer(comment) {
68
+ if (comment.kind !== COMMENT_KIND)
69
+ throw new Error("Event is not a comment");
70
+ return getOrComputeCachedValue(comment, CommentRootPointerSymbol, () => {
71
+ // check for address pointer first since it can also have E tags
72
+ const A = getCommentAddressPointer(comment.tags, true);
73
+ if (A)
74
+ return A;
75
+ const E = getCommentEventPointer(comment.tags, true);
76
+ if (E)
77
+ return E;
78
+ const I = getCommentExternalPointer(comment.tags, true);
79
+ if (I)
80
+ return I;
81
+ return null;
82
+ });
83
+ }
84
+ /**
85
+ * Returns the reply pointer for a comment
86
+ * @throws
87
+ */
88
+ export function getCommentReplyPointer(comment) {
89
+ if (comment.kind !== COMMENT_KIND)
90
+ throw new Error("Event is not a comment");
91
+ return getOrComputeCachedValue(comment, CommentReplyPointerSymbol, () => {
92
+ // check for address pointer first since it can also have E tags
93
+ const A = getCommentAddressPointer(comment.tags, false);
94
+ if (A)
95
+ return A;
96
+ const E = getCommentEventPointer(comment.tags, false);
97
+ if (E)
98
+ return E;
99
+ const I = getCommentExternalPointer(comment.tags, false);
100
+ if (I)
101
+ return I;
102
+ return null;
103
+ });
104
+ }
105
+ export function isCommentEventPointer(pointer) {
106
+ return (Reflect.has(pointer, "id") &&
107
+ Reflect.has(pointer, "kind") &&
108
+ !Reflect.has(pointer, "identifier") &&
109
+ typeof pointer.kind === "number");
110
+ }
111
+ export function isCommentAddressPointer(pointer) {
112
+ return (Reflect.has(pointer, "identifier") &&
113
+ Reflect.has(pointer, "pubkey") &&
114
+ Reflect.has(pointer, "kind") &&
115
+ typeof pointer.kind === "number");
116
+ }
@@ -0,0 +1,3 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ /** Returns the NIP-36 content-warning for an event. returns boolean if there is no "reason" */
3
+ export declare function getContentWarning(event: NostrEvent): string | boolean;
@@ -0,0 +1,8 @@
1
+ /** Returns the NIP-36 content-warning for an event. returns boolean if there is no "reason" */
2
+ export function getContentWarning(event) {
3
+ const tag = event.tags.find((t) => t[0] === "content-warning");
4
+ if (tag)
5
+ return tag[1] || true;
6
+ else
7
+ return false;
8
+ }
@@ -0,0 +1,3 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ export declare function getDeleteIds(deleteEvent: NostrEvent): string[];
3
+ export declare function getDeleteCoordinates(deleteEvent: NostrEvent): string[];
@@ -0,0 +1,7 @@
1
+ import { isATag, isETag } from "./tags.js";
2
+ export function getDeleteIds(deleteEvent) {
3
+ return deleteEvent.tags.filter(isETag).map((t) => t[1]);
4
+ }
5
+ export function getDeleteCoordinates(deleteEvent) {
6
+ return deleteEvent.tags.filter(isATag).map((t) => t[1]);
7
+ }