applesauce-core 0.0.0-next-20250423181842 → 0.0.0-next-20250424153457
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/dist/event-store/database.js +3 -3
- package/dist/event-store/event-store.js +4 -4
- package/dist/helpers/__tests__/events.test.d.ts +1 -0
- package/dist/helpers/__tests__/events.test.js +32 -0
- package/dist/helpers/__tests__/mutes.test.d.ts +1 -0
- package/dist/helpers/__tests__/mutes.test.js +55 -0
- package/dist/helpers/event.d.ts +8 -3
- package/dist/helpers/event.js +21 -13
- package/dist/helpers/lists.js +2 -2
- package/dist/helpers/mutes.d.ts +4 -0
- package/dist/helpers/mutes.js +39 -0
- package/dist/helpers/pointers.js +3 -3
- package/dist/observable/__tests__/listen-latest-updates.test.d.ts +1 -0
- package/dist/observable/__tests__/listen-latest-updates.test.js +55 -0
- package/dist/queries/thread.js +2 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { binarySearch, insertEventIntoDescendingList } from "nostr-tools/utils";
|
|
2
2
|
import { Subject } from "rxjs";
|
|
3
|
-
import { getEventUID, getIndexableTags,
|
|
3
|
+
import { getEventUID, getIndexableTags, createReplaceableAddress, isReplaceable } from "../helpers/event.js";
|
|
4
4
|
import { INDEXABLE_TAGS } from "./common.js";
|
|
5
5
|
import { logger } from "../logger.js";
|
|
6
6
|
import { LRU } from "../helpers/lru.js";
|
|
@@ -73,12 +73,12 @@ export class Database {
|
|
|
73
73
|
}
|
|
74
74
|
/** Checks if the database contains a replaceable event without touching it */
|
|
75
75
|
hasReplaceable(kind, pubkey, d) {
|
|
76
|
-
const events = this.replaceable.get(
|
|
76
|
+
const events = this.replaceable.get(createReplaceableAddress(kind, pubkey, d));
|
|
77
77
|
return !!events && events.length > 0;
|
|
78
78
|
}
|
|
79
79
|
/** Gets an array of replaceable events */
|
|
80
80
|
getReplaceable(kind, pubkey, d) {
|
|
81
|
-
return this.replaceable.get(
|
|
81
|
+
return this.replaceable.get(createReplaceableAddress(kind, pubkey, d));
|
|
82
82
|
}
|
|
83
83
|
/** Inserts an event into the database and notifies all subscriptions */
|
|
84
84
|
addEvent(event) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { kinds } from "nostr-tools";
|
|
2
2
|
import { insertEventIntoDescendingList } from "nostr-tools/utils";
|
|
3
|
-
import {
|
|
3
|
+
import { isAddressableKind } from "nostr-tools/kinds";
|
|
4
4
|
import { defer, distinctUntilChanged, EMPTY, endWith, filter, finalize, from, map, merge, mergeMap, mergeWith, of, repeat, scan, take, takeUntil, tap, } from "rxjs";
|
|
5
5
|
import { Database } from "./database.js";
|
|
6
|
-
import { FromCacheSymbol, getEventUID, getReplaceableIdentifier,
|
|
6
|
+
import { FromCacheSymbol, getEventUID, getReplaceableIdentifier, createReplaceableAddress, getTagValue, isReplaceable, } from "../helpers/event.js";
|
|
7
7
|
import { matchFilters } from "../helpers/filter.js";
|
|
8
8
|
import { addSeenRelay, getSeenRelays } from "../helpers/relays.js";
|
|
9
9
|
import { getDeleteCoordinates, getDeleteIds } from "../helpers/delete.js";
|
|
@@ -54,7 +54,7 @@ export class EventStore {
|
|
|
54
54
|
else {
|
|
55
55
|
if (this.deletedIds.has(event.id))
|
|
56
56
|
return true;
|
|
57
|
-
if (
|
|
57
|
+
if (isAddressableKind(event.kind)) {
|
|
58
58
|
const deleted = this.deletedCoords.get(getEventUID(event));
|
|
59
59
|
if (deleted)
|
|
60
60
|
return deleted > event.created_at;
|
|
@@ -295,7 +295,7 @@ export class EventStore {
|
|
|
295
295
|
}
|
|
296
296
|
/** Creates an observable that subscribes to the latest version of an array of replaceable events*/
|
|
297
297
|
replaceableSet(pointers) {
|
|
298
|
-
const uids = new Set(pointers.map((p) =>
|
|
298
|
+
const uids = new Set(pointers.map((p) => createReplaceableAddress(p.kind, p.pubkey, p.identifier)));
|
|
299
299
|
return merge(
|
|
300
300
|
// start with existing events
|
|
301
301
|
defer(() => from(pointers.map((p) => this.getReplaceable(p.kind, p.pubkey, p.identifier)))),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { kinds } from "nostr-tools";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { FakeUser } from "../../__tests__/fixtures.js";
|
|
4
|
+
import { getReplaceableAddress } from "../event.js";
|
|
5
|
+
const user = new FakeUser();
|
|
6
|
+
describe("getReplaceableAddress", () => {
|
|
7
|
+
it("should throw an error for non-replaceable events", () => {
|
|
8
|
+
const normalEvent = user.note("Hello world");
|
|
9
|
+
expect(() => {
|
|
10
|
+
getReplaceableAddress(normalEvent);
|
|
11
|
+
}).toThrow("Event is not replaceable or addressable");
|
|
12
|
+
});
|
|
13
|
+
it("should return the correct address for replaceable events", () => {
|
|
14
|
+
const replaceableEvent = user.event({
|
|
15
|
+
kind: kinds.Metadata,
|
|
16
|
+
content: JSON.stringify({ name: "Test User" }),
|
|
17
|
+
tags: [],
|
|
18
|
+
});
|
|
19
|
+
const expectedAddress = `${kinds.Metadata}:${user.pubkey}:`;
|
|
20
|
+
expect(getReplaceableAddress(replaceableEvent)).toBe(expectedAddress);
|
|
21
|
+
});
|
|
22
|
+
it("should include the identifier for addressable events", () => {
|
|
23
|
+
const identifier = "test-profile";
|
|
24
|
+
const addressableEvent = user.event({
|
|
25
|
+
kind: 30000, // Parameterized replaceable event
|
|
26
|
+
content: "Test content",
|
|
27
|
+
tags: [["d", identifier]],
|
|
28
|
+
});
|
|
29
|
+
const expectedAddress = `30000:${user.pubkey}:${identifier}`;
|
|
30
|
+
expect(getReplaceableAddress(addressableEvent)).toBe(expectedAddress);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { matchMutes } from "../mutes.js";
|
|
3
|
+
import { FakeUser } from "../../__tests__/fixtures.js";
|
|
4
|
+
const mutedUser = new FakeUser();
|
|
5
|
+
const nonMutedUser = new FakeUser();
|
|
6
|
+
const thread = nonMutedUser.note("Hello world");
|
|
7
|
+
// Create a mutes object with a pubkey to mute
|
|
8
|
+
const mutes = {
|
|
9
|
+
pubkeys: new Set([mutedUser.pubkey]),
|
|
10
|
+
threads: new Set([thread.id]),
|
|
11
|
+
hashtags: new Set(["nostr"]),
|
|
12
|
+
words: new Set(["GM"]),
|
|
13
|
+
};
|
|
14
|
+
describe("matchMutes", () => {
|
|
15
|
+
it("should match events with muted pubkeys", () => {
|
|
16
|
+
const mutedEvent = mutedUser.note("Hello world");
|
|
17
|
+
const nonMutedEvent = nonMutedUser.note("Hello world");
|
|
18
|
+
// The event with the muted pubkey should match
|
|
19
|
+
expect(matchMutes(mutes, mutedEvent)).toBe(true);
|
|
20
|
+
// The event with a different pubkey should not match
|
|
21
|
+
expect(matchMutes(mutes, nonMutedEvent)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
it("should match events with muted hashtags", () => {
|
|
24
|
+
// Create events with and without the muted hashtag
|
|
25
|
+
const eventWithMutedHashtag = nonMutedUser.note("Hello world");
|
|
26
|
+
eventWithMutedHashtag.tags.push(["t", "nostr"]);
|
|
27
|
+
const eventWithDifferentHashtag = nonMutedUser.note("Hello world");
|
|
28
|
+
eventWithDifferentHashtag.tags.push(["t", "bitcoin"]);
|
|
29
|
+
const eventWithNoHashtag = nonMutedUser.note("Hello world");
|
|
30
|
+
// The event with the muted hashtag should match
|
|
31
|
+
expect(matchMutes(mutes, eventWithMutedHashtag)).toBe(true);
|
|
32
|
+
// The events without the muted hashtag should not match
|
|
33
|
+
expect(matchMutes(mutes, eventWithDifferentHashtag)).toBe(false);
|
|
34
|
+
expect(matchMutes(mutes, eventWithNoHashtag)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
it("should match events within threads", () => {
|
|
37
|
+
// Create a reply to the thread
|
|
38
|
+
const reply = nonMutedUser.note("Hello world");
|
|
39
|
+
reply.tags.push(["e", thread.id, "", "root"]);
|
|
40
|
+
// The reply should match the mute
|
|
41
|
+
expect(matchMutes(mutes, reply)).toBe(true);
|
|
42
|
+
// The thread should not match the mute
|
|
43
|
+
expect(matchMutes(mutes, thread)).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
it("should match events with muted words", () => {
|
|
46
|
+
// The event with the muted word should match
|
|
47
|
+
expect(matchMutes(mutes, nonMutedUser.note("GM"))).toBe(true);
|
|
48
|
+
// Should not match other words that contain the muted word
|
|
49
|
+
expect(matchMutes(mutes, nonMutedUser.note("GMing"))).toBe(false);
|
|
50
|
+
// Should be case-insensitive
|
|
51
|
+
expect(matchMutes(mutes, nonMutedUser.note("gm"))).toBe(true);
|
|
52
|
+
// Should match if the muted word
|
|
53
|
+
expect(matchMutes(mutes, nonMutedUser.note("Hello GM world"))).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
});
|
package/dist/helpers/event.d.ts
CHANGED
|
@@ -24,11 +24,16 @@ export declare function isReplaceable(kind: number): boolean;
|
|
|
24
24
|
/**
|
|
25
25
|
* Returns the events Unique ID
|
|
26
26
|
* For normal or ephemeral events this is ( event.id )
|
|
27
|
-
* For replaceable events this is ( event.kind + ":" + event.pubkey )
|
|
28
|
-
* For parametrized replaceable events this is ( event.kind + ":" + event.pubkey + ":" + event.tags.d
|
|
27
|
+
* For replaceable events this is ( event.kind + ":" + event.pubkey + ":" )
|
|
28
|
+
* For parametrized replaceable events this is ( event.kind + ":" + event.pubkey + ":" + event.tags.d )
|
|
29
29
|
*/
|
|
30
30
|
export declare function getEventUID(event: NostrEvent): string;
|
|
31
|
-
|
|
31
|
+
/** Returns the replaceable event address for an addressable event */
|
|
32
|
+
export declare function getReplaceableAddress(event: NostrEvent): string;
|
|
33
|
+
/** Creates a replaceable event address from a kind, pubkey, and identifier */
|
|
34
|
+
export declare function createReplaceableAddress(kind: number, pubkey: string, identifier?: string): string;
|
|
35
|
+
/** @deprecated use createReplaceableAddress instead */
|
|
36
|
+
export declare const getReplaceableUID: typeof createReplaceableAddress;
|
|
32
37
|
/** Returns a Set of tag names and values that are indexable */
|
|
33
38
|
export declare function getIndexableTags(event: NostrEvent): Set<string>;
|
|
34
39
|
/**
|
package/dist/helpers/event.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { verifiedSymbol } from "nostr-tools";
|
|
2
2
|
import { INDEXABLE_TAGS } from "../event-store/common.js";
|
|
3
3
|
import { getHiddenTags } from "./hidden-tags.js";
|
|
4
4
|
import { getOrComputeCachedValue } from "./cache.js";
|
|
5
|
-
import {
|
|
5
|
+
import { isAddressableKind, isReplaceableKind } from "nostr-tools/kinds";
|
|
6
6
|
import { EventStoreSymbol } from "../event-store/event-store.js";
|
|
7
7
|
export const EventUIDSymbol = Symbol.for("event-uid");
|
|
8
8
|
export const EventIndexableTagsSymbol = Symbol.for("indexable-tags");
|
|
@@ -29,30 +29,38 @@ export function isEvent(event) {
|
|
|
29
29
|
* or parameterized replaceable ( 30000 <= n < 40000 )
|
|
30
30
|
*/
|
|
31
31
|
export function isReplaceable(kind) {
|
|
32
|
-
return
|
|
32
|
+
return isReplaceableKind(kind) || isAddressableKind(kind);
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Returns the events Unique ID
|
|
36
36
|
* For normal or ephemeral events this is ( event.id )
|
|
37
|
-
* For replaceable events this is ( event.kind + ":" + event.pubkey )
|
|
38
|
-
* For parametrized replaceable events this is ( event.kind + ":" + event.pubkey + ":" + event.tags.d
|
|
37
|
+
* For replaceable events this is ( event.kind + ":" + event.pubkey + ":" )
|
|
38
|
+
* For parametrized replaceable events this is ( event.kind + ":" + event.pubkey + ":" + event.tags.d )
|
|
39
39
|
*/
|
|
40
40
|
export function getEventUID(event) {
|
|
41
41
|
let uid = event[EventUIDSymbol];
|
|
42
42
|
if (!uid) {
|
|
43
|
-
if (isReplaceable(event.kind))
|
|
44
|
-
|
|
45
|
-
uid = getReplaceableUID(event.kind, event.pubkey, d);
|
|
46
|
-
}
|
|
43
|
+
if (isReplaceable(event.kind))
|
|
44
|
+
uid = getReplaceableAddress(event);
|
|
47
45
|
else
|
|
48
46
|
uid = event.id;
|
|
49
47
|
event[EventUIDSymbol] = uid;
|
|
50
48
|
}
|
|
51
49
|
return uid;
|
|
52
50
|
}
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
/** Returns the replaceable event address for an addressable event */
|
|
52
|
+
export function getReplaceableAddress(event) {
|
|
53
|
+
if (!isReplaceable(event.kind))
|
|
54
|
+
throw new Error("Event is not replaceable or addressable");
|
|
55
|
+
const identifier = isAddressableKind(event.kind) ? getReplaceableIdentifier(event) : undefined;
|
|
56
|
+
return createReplaceableAddress(event.kind, event.pubkey, identifier);
|
|
57
|
+
}
|
|
58
|
+
/** Creates a replaceable event address from a kind, pubkey, and identifier */
|
|
59
|
+
export function createReplaceableAddress(kind, pubkey, identifier) {
|
|
60
|
+
return kind + ":" + pubkey + ":" + (identifier ?? "");
|
|
55
61
|
}
|
|
62
|
+
/** @deprecated use createReplaceableAddress instead */
|
|
63
|
+
export const getReplaceableUID = createReplaceableAddress;
|
|
56
64
|
/** Returns a Set of tag names and values that are indexable */
|
|
57
65
|
export function getIndexableTags(event) {
|
|
58
66
|
let indexable = event[EventIndexableTagsSymbol];
|
|
@@ -100,8 +108,8 @@ export function getParentEventStore(event) {
|
|
|
100
108
|
* @throws
|
|
101
109
|
*/
|
|
102
110
|
export function getReplaceableIdentifier(event) {
|
|
103
|
-
if (!
|
|
104
|
-
throw new Error("Event is not
|
|
111
|
+
if (!isAddressableKind(event.kind))
|
|
112
|
+
throw new Error("Event is not addressable");
|
|
105
113
|
return getOrComputeCachedValue(event, ReplaceableIdentifierSymbol, () => {
|
|
106
114
|
const d = getTagValue(event, "d");
|
|
107
115
|
if (d === undefined)
|
package/dist/helpers/lists.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isAddressableKind, isReplaceableKind } from "nostr-tools/kinds";
|
|
2
2
|
import { getHiddenTags } from "./hidden-tags.js";
|
|
3
3
|
import { getAddressPointerFromATag, getCoordinateFromAddressPointer, getEventPointerFromETag, getProfilePointerFromPTag, } from "./pointers.js";
|
|
4
4
|
import { isATag, isETag, isPTag, processTags } from "./tags.js";
|
|
@@ -49,7 +49,7 @@ export function getProfilePointersFromList(list) {
|
|
|
49
49
|
/** Returns if an event is a valid list or set */
|
|
50
50
|
export function isValidList(event) {
|
|
51
51
|
try {
|
|
52
|
-
if (
|
|
52
|
+
if (isAddressableKind(event.kind)) {
|
|
53
53
|
// event is a set
|
|
54
54
|
// ensure the set has an identifier
|
|
55
55
|
getReplaceableIdentifier(event);
|
package/dist/helpers/mutes.d.ts
CHANGED
|
@@ -17,3 +17,7 @@ export declare function getMutedThings(mute: NostrEvent): Mutes;
|
|
|
17
17
|
export declare function getPublicMutedThings(mute: NostrEvent): Mutes;
|
|
18
18
|
/** Returns the hidden muted content if the event is unlocked */
|
|
19
19
|
export declare function getHiddenMutedThings(mute: NostrEvent): Mutes | undefined;
|
|
20
|
+
/** Creates a RegExp for matching muted words */
|
|
21
|
+
export declare function createMutedWordsRegExp(mutedWords: string[]): RegExp;
|
|
22
|
+
/** Returns true if the event matches the mutes */
|
|
23
|
+
export declare function matchMutes(mutes: Mutes, event: NostrEvent): boolean;
|
package/dist/helpers/mutes.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { kinds } from "nostr-tools";
|
|
1
2
|
import { isETag, isPTag, isTTag } from "./tags.js";
|
|
2
3
|
import { getOrComputeCachedValue } from "./cache.js";
|
|
3
4
|
import { getHiddenTags, isHiddenTagsLocked } from "./hidden-tags.js";
|
|
5
|
+
import { getIndexableTags, getNip10References } from "./index.js";
|
|
4
6
|
export const MutePublicSymbol = Symbol.for("mute-public");
|
|
5
7
|
export const MuteHiddenSymbol = Symbol.for("mute-hidden");
|
|
6
8
|
/** Merges any number of mute sets */
|
|
@@ -44,3 +46,40 @@ export function getHiddenMutedThings(mute) {
|
|
|
44
46
|
return undefined;
|
|
45
47
|
return getOrComputeCachedValue(mute, MuteHiddenSymbol, () => parseMutedTags(getHiddenTags(mute)));
|
|
46
48
|
}
|
|
49
|
+
/** Creates a RegExp for matching muted words */
|
|
50
|
+
export function createMutedWordsRegExp(mutedWords) {
|
|
51
|
+
// Escape special characters and join with |
|
|
52
|
+
const escapedWords = mutedWords.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
53
|
+
// Create the RegExp with word boundaries and case insensitive flag
|
|
54
|
+
return new RegExp(`\\b(${escapedWords.join("|")})\\b`, "gi");
|
|
55
|
+
}
|
|
56
|
+
/** Returns true if the event matches the mutes */
|
|
57
|
+
export function matchMutes(mutes, event) {
|
|
58
|
+
// Filter on muted pubkeys
|
|
59
|
+
if (mutes.pubkeys.size > 0) {
|
|
60
|
+
if (mutes.pubkeys.has(event.pubkey))
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
// Filter on muted hashtags`
|
|
64
|
+
if (mutes.hashtags.size > 0) {
|
|
65
|
+
const tags = getIndexableTags(event);
|
|
66
|
+
for (let tag of mutes.hashtags) {
|
|
67
|
+
if (tags.has("t:" + tag))
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Filter on muted threads
|
|
72
|
+
if (mutes.threads.size > 0 && event.kind === kinds.ShortTextNote) {
|
|
73
|
+
const refs = getNip10References(event);
|
|
74
|
+
if (refs.root?.e && mutes.threads.has(refs.root.e.id))
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
// Filter on muted words
|
|
78
|
+
if (mutes.words.size > 0) {
|
|
79
|
+
const regExp = createMutedWordsRegExp(Array.from(mutes.words));
|
|
80
|
+
if (regExp.test(event.content))
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
// Event does not match any mutes
|
|
84
|
+
return false;
|
|
85
|
+
}
|
package/dist/helpers/pointers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { naddrEncode, neventEncode, noteEncode, nprofileEncode, npubEncode, nsecEncode, } from "nostr-tools/nip19";
|
|
2
2
|
import { getPublicKey, kinds } from "nostr-tools";
|
|
3
3
|
import { getReplaceableIdentifier } from "./event.js";
|
|
4
|
-
import {
|
|
4
|
+
import { isAddressableKind } from "nostr-tools/kinds";
|
|
5
5
|
import { isSafeRelayURL } from "./relays.js";
|
|
6
6
|
export function parseCoordinate(a, requireD = false, silent = true) {
|
|
7
7
|
const parts = a.split(":");
|
|
@@ -155,7 +155,7 @@ export function getCoordinateFromAddressPointer(pointer) {
|
|
|
155
155
|
* @throws
|
|
156
156
|
*/
|
|
157
157
|
export function getAddressPointerForEvent(event, relays) {
|
|
158
|
-
if (!
|
|
158
|
+
if (!isAddressableKind(event.kind))
|
|
159
159
|
throw new Error("Cant get AddressPointer for non-replaceable event");
|
|
160
160
|
const d = getReplaceableIdentifier(event);
|
|
161
161
|
return {
|
|
@@ -179,7 +179,7 @@ export function getEventPointerForEvent(event, relays) {
|
|
|
179
179
|
}
|
|
180
180
|
/** Returns a pointer for a given event */
|
|
181
181
|
export function getPointerForEvent(event, relays) {
|
|
182
|
-
if (kinds.
|
|
182
|
+
if (kinds.isAddressableKind(event.kind)) {
|
|
183
183
|
const d = getReplaceableIdentifier(event);
|
|
184
184
|
return {
|
|
185
185
|
type: "naddr",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { subscribeSpyTo } from "@hirez_io/observer-spy";
|
|
3
|
+
import { of } from "rxjs";
|
|
4
|
+
import { EventStore } from "../../event-store/event-store.js";
|
|
5
|
+
import { listenLatestUpdates } from "../listen-latest-updates.js";
|
|
6
|
+
import { FakeUser } from "../../__tests__/fixtures.js";
|
|
7
|
+
let eventStore;
|
|
8
|
+
let user;
|
|
9
|
+
let event;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
eventStore = new EventStore();
|
|
12
|
+
user = new FakeUser();
|
|
13
|
+
event = user.note("original content");
|
|
14
|
+
});
|
|
15
|
+
describe("listenLatestUpdates", () => {
|
|
16
|
+
it("should emit the initial event", () => {
|
|
17
|
+
const source = of(event);
|
|
18
|
+
const spy = subscribeSpyTo(source.pipe(listenLatestUpdates(eventStore)));
|
|
19
|
+
expect(spy.getValues()).toEqual([event]);
|
|
20
|
+
});
|
|
21
|
+
it("should emit the event again when it's updated in the event store", () => {
|
|
22
|
+
// Add the event to the store first
|
|
23
|
+
eventStore.add(event);
|
|
24
|
+
// Create a source that emits the event
|
|
25
|
+
const source = of(event);
|
|
26
|
+
const spy = subscribeSpyTo(source.pipe(listenLatestUpdates(eventStore)));
|
|
27
|
+
// Create an updated version of the event
|
|
28
|
+
Reflect.set(event, Symbol.for("new-prop"), "testing");
|
|
29
|
+
// Update the event in the store
|
|
30
|
+
eventStore.update(event);
|
|
31
|
+
// Should have received both the original and updated event
|
|
32
|
+
expect(spy.getValues()).toEqual([event, event]);
|
|
33
|
+
});
|
|
34
|
+
it("should not emit updates for other events", () => {
|
|
35
|
+
// Add the event to the store
|
|
36
|
+
eventStore.add(event);
|
|
37
|
+
// Create a source that emits the event
|
|
38
|
+
const source = of(event);
|
|
39
|
+
const spy = subscribeSpyTo(source.pipe(listenLatestUpdates(eventStore)));
|
|
40
|
+
// Create a different event
|
|
41
|
+
const otherEvent = user.note("other content");
|
|
42
|
+
// Add the other event to the store
|
|
43
|
+
eventStore.add(otherEvent);
|
|
44
|
+
// Should only have received the original event
|
|
45
|
+
expect(spy.getValues()).toEqual([event]);
|
|
46
|
+
});
|
|
47
|
+
it("should handle undefined initial event", () => {
|
|
48
|
+
const source = of(undefined);
|
|
49
|
+
const spy = subscribeSpyTo(source.pipe(listenLatestUpdates(eventStore)));
|
|
50
|
+
expect(spy.getValues()).toEqual([undefined]);
|
|
51
|
+
// Adding events to the store should not trigger emissions
|
|
52
|
+
eventStore.add(event);
|
|
53
|
+
expect(spy.getValues()).toEqual([undefined]);
|
|
54
|
+
});
|
|
55
|
+
});
|
package/dist/queries/thread.js
CHANGED
|
@@ -3,7 +3,7 @@ import { isAddressableKind } from "nostr-tools/kinds";
|
|
|
3
3
|
import { map } from "rxjs/operators";
|
|
4
4
|
import { getNip10References, interpretThreadTags } from "../helpers/threading.js";
|
|
5
5
|
import { getCoordinateFromAddressPointer, isAddressPointer, isEventPointer } from "../helpers/pointers.js";
|
|
6
|
-
import { getEventUID,
|
|
6
|
+
import { getEventUID, createReplaceableAddress, getTagValue, isEvent } from "../helpers/event.js";
|
|
7
7
|
import { COMMENT_KIND } from "../helpers/comment.js";
|
|
8
8
|
const defaultOptions = {
|
|
9
9
|
kinds: [kinds.ShortTextNote],
|
|
@@ -71,7 +71,7 @@ export function RepliesQuery(event, overrideKinds) {
|
|
|
71
71
|
if (isEvent(parent) || isEventPointer(event))
|
|
72
72
|
filter["#e"] = [event.id];
|
|
73
73
|
const address = isAddressableKind(event.kind)
|
|
74
|
-
?
|
|
74
|
+
? createReplaceableAddress(event.kind, event.pubkey, getTagValue(event, "d"))
|
|
75
75
|
: undefined;
|
|
76
76
|
if (address) {
|
|
77
77
|
filter["#a"] = [address];
|