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.
- package/README.md +1 -1
- package/dist/event-store/database.d.ts +17 -13
- package/dist/event-store/database.js +50 -33
- package/dist/event-store/event-store.d.ts +37 -14
- package/dist/event-store/event-store.js +249 -96
- package/dist/helpers/bolt11.d.ts +1 -0
- package/dist/helpers/bolt11.js +1 -0
- package/dist/helpers/comment.d.ts +48 -0
- package/dist/helpers/comment.js +116 -0
- package/dist/helpers/content.d.ts +3 -0
- package/dist/helpers/content.js +8 -0
- package/dist/helpers/delete.d.ts +3 -0
- package/dist/helpers/delete.js +7 -0
- package/dist/helpers/emoji.d.ts +10 -1
- package/dist/helpers/emoji.js +12 -0
- package/dist/helpers/event.d.ts +9 -1
- package/dist/helpers/event.js +25 -1
- package/dist/helpers/external-id.d.ts +29 -0
- package/dist/helpers/external-id.js +20 -0
- package/dist/helpers/filter.d.ts +0 -2
- package/dist/helpers/filter.js +3 -7
- package/dist/helpers/hidden-tags.d.ts +48 -0
- package/dist/helpers/hidden-tags.js +108 -0
- package/dist/helpers/hidden-tags.test.d.ts +1 -0
- package/dist/helpers/hidden-tags.test.js +28 -0
- package/dist/helpers/index.d.ts +16 -8
- package/dist/helpers/index.js +16 -8
- package/dist/helpers/json.d.ts +1 -0
- package/dist/helpers/json.js +1 -0
- package/dist/helpers/lnurl.d.ts +4 -0
- package/dist/helpers/lnurl.js +40 -0
- package/dist/helpers/mailboxes.test.js +13 -12
- package/dist/helpers/media-attachment.d.ts +33 -0
- package/dist/helpers/media-attachment.js +60 -0
- package/dist/helpers/pointers.d.ts +38 -5
- package/dist/helpers/pointers.js +101 -17
- package/dist/helpers/profile.js +1 -1
- package/dist/helpers/string.d.ts +6 -0
- package/dist/helpers/string.js +2 -0
- package/dist/helpers/tags.d.ts +6 -0
- package/dist/helpers/tags.js +6 -0
- package/dist/helpers/threading.d.ts +6 -6
- package/dist/helpers/threading.js +30 -9
- package/dist/helpers/threading.test.d.ts +1 -0
- package/dist/helpers/threading.test.js +41 -0
- package/dist/helpers/url.d.ts +4 -1
- package/dist/helpers/url.js +4 -3
- package/dist/helpers/zap.d.ts +25 -0
- package/dist/helpers/zap.js +32 -3
- package/dist/observable/{getValue.d.ts → get-value.d.ts} +1 -0
- package/dist/observable/{getValue.js → get-value.js} +1 -0
- package/dist/observable/index.d.ts +1 -1
- package/dist/observable/index.js +1 -1
- package/dist/promise/deferred.d.ts +1 -0
- package/dist/promise/deferred.js +1 -0
- package/dist/queries/comments.d.ts +4 -0
- package/dist/queries/comments.js +14 -0
- package/dist/queries/index.d.ts +3 -2
- package/dist/queries/index.js +3 -2
- package/dist/queries/mailboxes.d.ts +1 -0
- package/dist/queries/mailboxes.js +1 -0
- package/dist/queries/profile.d.ts +1 -0
- package/dist/queries/profile.js +1 -0
- package/dist/queries/reactions.d.ts +1 -1
- package/dist/queries/reactions.js +1 -1
- package/dist/queries/simple.d.ts +3 -3
- package/dist/queries/simple.js +13 -13
- package/dist/queries/thread.d.ts +2 -0
- package/dist/queries/thread.js +29 -3
- package/dist/queries/zaps.d.ts +1 -0
- package/dist/queries/zaps.js +1 -0
- package/dist/query-store/index.d.ts +22 -12
- package/dist/query-store/index.js +36 -30
- 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(
|
|
112
|
+
event(id) {
|
|
40
113
|
return new Observable((observer) => {
|
|
41
|
-
let current = this.database.getEvent(
|
|
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 (
|
|
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
|
-
|
|
55
|
-
this.database.claimEvent(current, observer);
|
|
124
|
+
this.database.claimEvent(event, observer);
|
|
56
125
|
}
|
|
57
126
|
});
|
|
58
|
-
// subscribe to
|
|
127
|
+
// subscribe to updated events
|
|
59
128
|
const updated = this.database.updated.subscribe((event) => {
|
|
60
|
-
if (event ===
|
|
61
|
-
observer.next(
|
|
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 (
|
|
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(
|
|
150
|
+
events(ids) {
|
|
82
151
|
return new Observable((observer) => {
|
|
83
152
|
const events = new Map();
|
|
84
|
-
for (const
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
events.set(
|
|
88
|
-
this.database.claimEvent(
|
|
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
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
171
|
+
// subscribe to updated events
|
|
109
172
|
const updated = this.database.updated.subscribe((event) => {
|
|
110
|
-
|
|
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
|
|
117
|
-
if (
|
|
118
|
-
const current = events.get(
|
|
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(
|
|
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
|
|
198
|
+
/** Creates an observable with the latest version of a replaceable event */
|
|
137
199
|
replaceable(kind, pubkey, d) {
|
|
138
|
-
return
|
|
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
|
|
141
|
-
|
|
242
|
+
/** Creates an observable with the latest versions of replaceable events */
|
|
243
|
+
replaceableSet(pointers) {
|
|
142
244
|
return new Observable((observer) => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
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
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
290
|
+
inserted.unsubscribe();
|
|
291
|
+
deleted.unsubscribe();
|
|
292
|
+
updated.unsubscribe();
|
|
293
|
+
for (const [_id, event] of events) {
|
|
162
294
|
this.database.removeClaim(event, observer);
|
|
163
|
-
|
|
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
|
-
//
|
|
173
|
-
const
|
|
174
|
-
|
|
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
|
-
|
|
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
|
-
|
|
184
|
-
|
|
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
|
|
363
|
+
// subscribe to updated events
|
|
206
364
|
const updated = this.database.updated.subscribe((event) => {
|
|
207
|
-
if (
|
|
365
|
+
if (timeline.includes(event)) {
|
|
208
366
|
observer.next([...timeline]);
|
|
209
367
|
}
|
|
210
368
|
});
|
|
211
|
-
// subscribe to
|
|
369
|
+
// subscribe to deleted events
|
|
212
370
|
const deleted = this.database.deleted.subscribe((event) => {
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
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
|
});
|
package/dist/helpers/bolt11.d.ts
CHANGED
package/dist/helpers/bolt11.js
CHANGED
|
@@ -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,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,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
|
+
}
|