applesauce-core 0.10.0 → 0.12.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/dist/__tests__/fixtures.d.ts +8 -0
- package/dist/__tests__/fixtures.js +20 -0
- package/dist/event-store/__tests__/event-store.test.js +272 -0
- package/dist/event-store/database.d.ts +7 -5
- package/dist/event-store/database.js +14 -8
- package/dist/event-store/event-store.d.ts +40 -20
- package/dist/event-store/event-store.js +269 -314
- package/dist/event-store/index.d.ts +1 -0
- package/dist/event-store/index.js +1 -0
- package/dist/event-store/interface.d.ts +27 -0
- package/dist/helpers/__tests__/blossom.test.js +13 -0
- package/dist/helpers/__tests__/comment.test.d.ts +1 -0
- package/dist/helpers/__tests__/comment.test.js +235 -0
- package/dist/helpers/__tests__/emoji.test.d.ts +1 -0
- package/dist/helpers/__tests__/emoji.test.js +15 -0
- package/dist/helpers/__tests__/event.test.d.ts +1 -0
- package/dist/helpers/__tests__/event.test.js +36 -0
- package/dist/helpers/__tests__/file-metadata.test.d.ts +1 -0
- package/dist/helpers/__tests__/file-metadata.test.js +103 -0
- package/dist/helpers/__tests__/hidden-tags.test.d.ts +1 -0
- package/dist/helpers/{hidden-tags.test.js → __tests__/hidden-tags.test.js} +2 -1
- package/dist/helpers/__tests__/mailboxes.test.d.ts +1 -0
- package/dist/helpers/{mailboxes.test.js → __tests__/mailboxes.test.js} +1 -1
- package/dist/helpers/__tests__/nip-19.test.d.ts +1 -0
- package/dist/helpers/__tests__/nip-19.test.js +42 -0
- package/dist/helpers/__tests__/relays.test.d.ts +1 -0
- package/dist/helpers/__tests__/relays.test.js +21 -0
- package/dist/helpers/__tests__/tags.test.d.ts +1 -0
- package/dist/helpers/__tests__/tags.test.js +24 -0
- package/dist/helpers/__tests__/threading.test.d.ts +1 -0
- package/dist/helpers/{threading.test.js → __tests__/threading.test.js} +1 -1
- package/dist/helpers/blossom.d.ts +9 -0
- package/dist/helpers/blossom.js +22 -0
- package/dist/helpers/bookmarks.d.ts +15 -0
- package/dist/helpers/bookmarks.js +27 -0
- package/dist/helpers/cache.d.ts +3 -4
- package/dist/helpers/cache.js +1 -1
- package/dist/helpers/channels.d.ts +10 -0
- package/dist/helpers/channels.js +27 -0
- package/dist/helpers/comment.d.ts +3 -4
- package/dist/helpers/comment.js +20 -16
- package/dist/helpers/contacts.d.ts +3 -0
- package/dist/helpers/contacts.js +25 -0
- package/dist/helpers/direct-messages.d.ts +4 -0
- package/dist/helpers/direct-messages.js +5 -0
- package/dist/helpers/dns-identity.d.ts +7 -0
- package/dist/helpers/dns-identity.js +10 -0
- package/dist/helpers/emoji.d.ts +3 -1
- package/dist/helpers/emoji.js +2 -2
- package/dist/helpers/event.d.ts +15 -1
- package/dist/helpers/event.js +34 -11
- package/dist/helpers/file-metadata.d.ts +55 -0
- package/dist/helpers/file-metadata.js +99 -0
- package/dist/helpers/filter.d.ts +4 -0
- package/dist/helpers/filter.js +34 -1
- package/dist/helpers/gift-wraps.d.ts +12 -0
- package/dist/helpers/gift-wraps.js +49 -0
- package/dist/helpers/groups.d.ts +24 -0
- package/dist/helpers/groups.js +39 -0
- package/dist/helpers/hidden-content.d.ts +48 -0
- package/dist/helpers/hidden-content.js +88 -0
- package/dist/helpers/hidden-tags.d.ts +17 -35
- package/dist/helpers/hidden-tags.js +26 -83
- package/dist/helpers/index.d.ts +16 -1
- package/dist/helpers/index.js +16 -1
- package/dist/helpers/lists.d.ts +28 -0
- package/dist/helpers/lists.js +65 -0
- package/dist/helpers/mailboxes.js +16 -9
- package/dist/helpers/mutes.d.ts +15 -0
- package/dist/helpers/mutes.js +24 -0
- package/dist/helpers/nip-19.d.ts +4 -0
- package/dist/helpers/nip-19.js +27 -0
- package/dist/helpers/picture-post.d.ts +4 -0
- package/dist/helpers/picture-post.js +6 -0
- package/dist/helpers/pointers.js +13 -17
- package/dist/helpers/profile.d.ts +6 -1
- package/dist/helpers/profile.js +4 -0
- package/dist/helpers/relays.d.ts +6 -3
- package/dist/helpers/relays.js +25 -18
- package/dist/helpers/share.d.ts +4 -0
- package/dist/helpers/share.js +12 -0
- package/dist/helpers/tags.d.ts +17 -0
- package/dist/helpers/tags.js +28 -6
- package/dist/helpers/threading.js +3 -3
- package/dist/helpers/url.d.ts +7 -0
- package/dist/helpers/url.js +27 -0
- package/dist/helpers/user-status.d.ts +18 -0
- package/dist/helpers/user-status.js +21 -0
- package/dist/observable/__tests__/claim-events.test.d.ts +1 -0
- package/dist/observable/__tests__/claim-events.test.js +23 -0
- package/dist/observable/__tests__/claim-latest.test.d.ts +1 -0
- package/dist/observable/__tests__/claim-latest.test.js +37 -0
- package/dist/observable/__tests__/simple-timeout.test.d.ts +1 -0
- package/dist/observable/__tests__/simple-timeout.test.js +34 -0
- package/dist/observable/claim-events.d.ts +5 -0
- package/dist/observable/claim-events.js +28 -0
- package/dist/observable/claim-latest.d.ts +5 -0
- package/dist/observable/claim-latest.js +21 -0
- package/dist/observable/{get-value.d.ts → get-observable-value.d.ts} +1 -1
- package/dist/observable/{get-value.js → get-observable-value.js} +3 -8
- package/dist/observable/index.d.ts +2 -1
- package/dist/observable/index.js +2 -1
- package/dist/observable/share-latest-value.d.ts +2 -4
- package/dist/observable/share-latest-value.js +19 -16
- package/dist/observable/simple-timeout.d.ts +4 -0
- package/dist/observable/simple-timeout.js +6 -0
- package/dist/observable/with-immediate-value.d.ts +3 -0
- package/dist/observable/with-immediate-value.js +19 -0
- package/dist/queries/blossom.d.ts +2 -0
- package/dist/queries/blossom.js +10 -0
- package/dist/queries/bookmarks.d.ts +8 -0
- package/dist/queries/bookmarks.js +23 -0
- package/dist/queries/channels.d.ts +11 -0
- package/dist/queries/channels.js +73 -0
- package/dist/queries/contacts.d.ts +3 -0
- package/dist/queries/contacts.js +12 -0
- package/dist/queries/index.d.ts +6 -0
- package/dist/queries/index.js +6 -0
- package/dist/queries/mutes.d.ts +8 -0
- package/dist/queries/mutes.js +23 -0
- package/dist/queries/pins.d.ts +3 -0
- package/dist/queries/pins.js +12 -0
- package/dist/queries/simple.d.ts +3 -3
- package/dist/queries/simple.js +3 -3
- package/dist/queries/thread.js +1 -1
- package/dist/queries/user-status.d.ts +11 -0
- package/dist/queries/user-status.js +39 -0
- package/dist/query-store/__tests__/query-store.test.d.ts +1 -0
- package/dist/query-store/__tests__/query-store.test.js +63 -0
- package/dist/query-store/index.d.ts +1 -57
- package/dist/query-store/index.js +1 -66
- package/dist/query-store/query-store.d.ts +53 -0
- package/dist/query-store/query-store.js +97 -0
- package/package.json +20 -8
- package/dist/helpers/media-attachment.d.ts +0 -33
- package/dist/helpers/media-attachment.js +0 -60
- /package/dist/{helpers/hidden-tags.test.d.ts → event-store/__tests__/event-store.test.d.ts} +0 -0
- /package/dist/{helpers/mailboxes.test.d.ts → event-store/interface.js} +0 -0
- /package/dist/helpers/{threading.test.d.ts → __tests__/blossom.test.d.ts} +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { finalize, tap } from "rxjs";
|
|
2
|
+
/** keep a claim on any event that goes through this observable, claims are removed when the observable completes */
|
|
3
|
+
export function claimEvents(database) {
|
|
4
|
+
return (source) => {
|
|
5
|
+
const seen = new Set();
|
|
6
|
+
return source.pipe(
|
|
7
|
+
// claim all events
|
|
8
|
+
tap((message) => {
|
|
9
|
+
if (message === undefined)
|
|
10
|
+
return;
|
|
11
|
+
if (Array.isArray(message)) {
|
|
12
|
+
for (const event of message) {
|
|
13
|
+
seen.add(event);
|
|
14
|
+
database.claimEvent(event, source);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
seen.add(message);
|
|
19
|
+
database.claimEvent(message, source);
|
|
20
|
+
}
|
|
21
|
+
}),
|
|
22
|
+
// remove claims on cleanup
|
|
23
|
+
finalize(() => {
|
|
24
|
+
for (const e of seen)
|
|
25
|
+
database.removeClaim(e, source);
|
|
26
|
+
}));
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { MonoTypeOperatorFunction } from "rxjs";
|
|
2
|
+
import { NostrEvent } from "nostr-tools";
|
|
3
|
+
import { type Database } from "../event-store/database.js";
|
|
4
|
+
/** An operator that claims the latest event with the database */
|
|
5
|
+
export declare function claimLatest<T extends NostrEvent | undefined>(database: Database): MonoTypeOperatorFunction<T>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { finalize, tap } from "rxjs";
|
|
2
|
+
/** An operator that claims the latest event with the database */
|
|
3
|
+
export function claimLatest(database) {
|
|
4
|
+
return (source) => {
|
|
5
|
+
let latest = undefined;
|
|
6
|
+
return source.pipe(tap((event) => {
|
|
7
|
+
// remove old claim
|
|
8
|
+
if (latest)
|
|
9
|
+
database.removeClaim(latest, source);
|
|
10
|
+
// claim new event
|
|
11
|
+
if (event)
|
|
12
|
+
database.claimEvent(event, source);
|
|
13
|
+
// update state
|
|
14
|
+
latest = event;
|
|
15
|
+
}), finalize(() => {
|
|
16
|
+
// remove latest claim
|
|
17
|
+
if (latest)
|
|
18
|
+
database.removeClaim(latest, source);
|
|
19
|
+
}));
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Observable } from "rxjs";
|
|
2
2
|
/** Subscribes and returns the observables current or next value */
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function getObservableValue<T>(observable: Observable<T>): T | Promise<T>;
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import { BehaviorSubject } from "rxjs";
|
|
1
|
+
import { BehaviorSubject, firstValueFrom } from "rxjs";
|
|
2
2
|
/** Subscribes and returns the observables current or next value */
|
|
3
|
-
export function
|
|
3
|
+
export function getObservableValue(observable) {
|
|
4
4
|
if (observable instanceof BehaviorSubject)
|
|
5
5
|
return observable.value;
|
|
6
6
|
if (Reflect.has(observable, "value"))
|
|
7
7
|
return Reflect.get(observable, "value");
|
|
8
|
-
return
|
|
9
|
-
const sub = observable.subscribe((v) => {
|
|
10
|
-
res(v);
|
|
11
|
-
sub.unsubscribe();
|
|
12
|
-
});
|
|
13
|
-
});
|
|
8
|
+
return firstValueFrom(observable);
|
|
14
9
|
}
|
package/dist/observable/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OperatorFunction } from "rxjs";
|
|
2
2
|
/**
|
|
3
3
|
* Creates an operator that adds a 'value' property and multiplexes the source
|
|
4
4
|
* @param config Optional ShareConfig for customizing sharing behavior
|
|
5
5
|
*/
|
|
6
|
-
export declare function shareLatestValue<T>(
|
|
7
|
-
value: T | undefined;
|
|
8
|
-
};
|
|
6
|
+
export declare function shareLatestValue<T>(): OperatorFunction<T, T | undefined>;
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import { share } from "rxjs";
|
|
2
|
-
import { tap } from "rxjs/operators";
|
|
1
|
+
import { BehaviorSubject, share } from "rxjs";
|
|
3
2
|
/**
|
|
4
3
|
* Creates an operator that adds a 'value' property and multiplexes the source
|
|
5
4
|
* @param config Optional ShareConfig for customizing sharing behavior
|
|
6
5
|
*/
|
|
7
|
-
export function shareLatestValue(
|
|
8
|
-
return (source) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
export function shareLatestValue() {
|
|
7
|
+
return (source$) => source$.pipe(share({ connector: () => new BehaviorSubject(undefined) }));
|
|
8
|
+
// return (source: Observable<T>): Observable<T> & { value: T | undefined } => {
|
|
9
|
+
// // Create storage for latest value
|
|
10
|
+
// let latestValue: T | undefined = undefined;
|
|
11
|
+
// // Create shared source with value tracking
|
|
12
|
+
// const shared$ = source.pipe(
|
|
13
|
+
// tap((value) => {
|
|
14
|
+
// latestValue = value;
|
|
15
|
+
// }),
|
|
16
|
+
// share(config),
|
|
17
|
+
// );
|
|
18
|
+
// // Add value property
|
|
19
|
+
// Object.defineProperty(shared$, "value", {
|
|
20
|
+
// get: () => latestValue,
|
|
21
|
+
// });
|
|
22
|
+
// return shared$ as Observable<T> & { value: T | undefined };
|
|
23
|
+
// };
|
|
21
24
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
/** if a synchronous value is not emitted, default is emitted */
|
|
3
|
+
export function withImmediateValueOrDefault(defaultValue) {
|
|
4
|
+
return (source) => new Observable((observer) => {
|
|
5
|
+
let seen = false;
|
|
6
|
+
const sub = source.subscribe({
|
|
7
|
+
next: (v) => {
|
|
8
|
+
seen = true;
|
|
9
|
+
observer.next(v);
|
|
10
|
+
},
|
|
11
|
+
error: (err) => observer.error(err),
|
|
12
|
+
complete: () => observer.complete(),
|
|
13
|
+
});
|
|
14
|
+
// if a value is not emitted sync, emit default
|
|
15
|
+
if (!seen)
|
|
16
|
+
observer.next(defaultValue);
|
|
17
|
+
return sub;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { map } from "rxjs/operators";
|
|
2
|
+
import { BLOSSOM_SERVER_LIST_KIND, getBlossomServersFromList } from "../helpers/blossom.js";
|
|
3
|
+
export function UserBlossomServersQuery(pubkey) {
|
|
4
|
+
return {
|
|
5
|
+
key: pubkey,
|
|
6
|
+
run: (store) => store
|
|
7
|
+
.replaceable(BLOSSOM_SERVER_LIST_KIND, pubkey)
|
|
8
|
+
.pipe(map((event) => event && getBlossomServersFromList(event))),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Bookmarks } from "../helpers/bookmarks.js";
|
|
2
|
+
import { Query } from "../query-store/index.js";
|
|
3
|
+
export declare function UserBookmarkQuery(pubkey: string): Query<Bookmarks | undefined>;
|
|
4
|
+
export declare function UserHiddenBookmarkQuery(pubkey: string): Query<(Bookmarks & {
|
|
5
|
+
locked: false;
|
|
6
|
+
}) | {
|
|
7
|
+
locked: true;
|
|
8
|
+
} | undefined>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { kinds } from "nostr-tools";
|
|
2
|
+
import { map } from "rxjs/operators";
|
|
3
|
+
import { isHiddenTagsLocked } from "../helpers/index.js";
|
|
4
|
+
import { getBookmarks, getHiddenBookmarks } from "../helpers/bookmarks.js";
|
|
5
|
+
export function UserBookmarkQuery(pubkey) {
|
|
6
|
+
return {
|
|
7
|
+
key: pubkey,
|
|
8
|
+
run: (store) => store.replaceable(kinds.Mutelist, pubkey).pipe(map((event) => event && getBookmarks(event))),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function UserHiddenBookmarkQuery(pubkey) {
|
|
12
|
+
return {
|
|
13
|
+
key: pubkey,
|
|
14
|
+
run: (store) => store.replaceable(kinds.Mutelist, pubkey).pipe(map((event) => {
|
|
15
|
+
if (!event)
|
|
16
|
+
return undefined;
|
|
17
|
+
const bookmarks = getHiddenBookmarks(event);
|
|
18
|
+
if (isHiddenTagsLocked(event) || !bookmarks)
|
|
19
|
+
return { locked: true };
|
|
20
|
+
return { locked: false, ...bookmarks };
|
|
21
|
+
})),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Query } from "applesauce-core";
|
|
2
|
+
import { NostrEvent } from "nostr-tools";
|
|
3
|
+
import { ChannelMetadataContent } from "../helpers/channels.js";
|
|
4
|
+
/** A query that returns a map of hidden messages Map<id, reason> */
|
|
5
|
+
export declare function ChannelHiddenQuery(channel: NostrEvent, authors?: string[]): Query<Map<string, string>>;
|
|
6
|
+
/** A query that returns all messages in a channel */
|
|
7
|
+
export declare function ChannelMessagesQuery(channel: NostrEvent): Query<NostrEvent[]>;
|
|
8
|
+
/** A query that returns the latest parsed metadata */
|
|
9
|
+
export declare function ChannelMetadataQuery(channel: NostrEvent): Query<ChannelMetadataContent | undefined>;
|
|
10
|
+
/** A query that returns a map of muted users Map<pubkey, reason> */
|
|
11
|
+
export declare function ChannelMutedQuery(channel: NostrEvent, authors?: string[]): Query<Map<string, string>>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { safeParse } from "applesauce-core/helpers/json";
|
|
2
|
+
import { kinds } from "nostr-tools";
|
|
3
|
+
import { map } from "rxjs";
|
|
4
|
+
import { getChannelMetadataContent } from "../helpers/channels.js";
|
|
5
|
+
/** A query that returns a map of hidden messages Map<id, reason> */
|
|
6
|
+
export function ChannelHiddenQuery(channel, authors = []) {
|
|
7
|
+
return {
|
|
8
|
+
key: channel.id,
|
|
9
|
+
run: (events) => {
|
|
10
|
+
const hidden = new Map();
|
|
11
|
+
return events
|
|
12
|
+
.filters([{ kinds: [kinds.ChannelHideMessage], "#e": [channel.id], authors: [channel.pubkey, ...authors] }])
|
|
13
|
+
.pipe(map((event) => {
|
|
14
|
+
const reason = safeParse(event.content)?.reason;
|
|
15
|
+
for (const tag of event.tags) {
|
|
16
|
+
if (tag[0] === "e" && tag[1])
|
|
17
|
+
hidden.set(tag[1], reason ?? "");
|
|
18
|
+
}
|
|
19
|
+
return hidden;
|
|
20
|
+
}));
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** A query that returns all messages in a channel */
|
|
25
|
+
export function ChannelMessagesQuery(channel) {
|
|
26
|
+
return {
|
|
27
|
+
key: channel.id,
|
|
28
|
+
run: (events) => events.timeline([{ kinds: [kinds.ChannelMessage], "#e": [channel.id] }]),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** A query that returns the latest parsed metadata */
|
|
32
|
+
export function ChannelMetadataQuery(channel) {
|
|
33
|
+
return {
|
|
34
|
+
key: channel.id,
|
|
35
|
+
run: (events) => {
|
|
36
|
+
const filters = [
|
|
37
|
+
{ ids: [channel.id] },
|
|
38
|
+
{ kinds: [kinds.ChannelMetadata], "#e": [channel.id], authors: [channel.pubkey] },
|
|
39
|
+
];
|
|
40
|
+
let latest = channel;
|
|
41
|
+
return events.filters(filters).pipe(map((event) => {
|
|
42
|
+
try {
|
|
43
|
+
if (event.pubkey === latest.pubkey && event.created_at > latest.created_at) {
|
|
44
|
+
latest = event;
|
|
45
|
+
}
|
|
46
|
+
return getChannelMetadataContent(latest);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** A query that returns a map of muted users Map<pubkey, reason> */
|
|
56
|
+
export function ChannelMutedQuery(channel, authors = []) {
|
|
57
|
+
return {
|
|
58
|
+
key: channel.id + authors.join(","),
|
|
59
|
+
run: (events) => {
|
|
60
|
+
const muted = new Map();
|
|
61
|
+
return events
|
|
62
|
+
.filters([{ kinds: [kinds.ChannelMuteUser], "#e": [channel.id], authors: [channel.pubkey, ...authors] }])
|
|
63
|
+
.pipe(map((event) => {
|
|
64
|
+
const reason = safeParse(event.content)?.reason;
|
|
65
|
+
for (const tag of event.tags) {
|
|
66
|
+
if (tag[0] === "p" && tag[1])
|
|
67
|
+
muted.set(tag[1], reason ?? "");
|
|
68
|
+
}
|
|
69
|
+
return muted;
|
|
70
|
+
}));
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { kinds } from "nostr-tools";
|
|
2
|
+
import { map } from "rxjs/operators";
|
|
3
|
+
import { isPTag, processTags } from "../helpers/tags.js";
|
|
4
|
+
import { getProfilePointerFromPTag } from "../helpers/pointers.js";
|
|
5
|
+
export function UserContactsQuery(pubkey) {
|
|
6
|
+
return {
|
|
7
|
+
key: pubkey,
|
|
8
|
+
run: (store) => store
|
|
9
|
+
.replaceable(kinds.Contacts, pubkey)
|
|
10
|
+
.pipe(map((event) => event && processTags(event.tags.filter(isPTag), getProfilePointerFromPTag))),
|
|
11
|
+
};
|
|
12
|
+
}
|
package/dist/queries/index.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
export * from "./blossom.js";
|
|
2
|
+
export * from "./bookmarks.js";
|
|
3
|
+
export * from "./channels.js";
|
|
1
4
|
export * from "./comments.js";
|
|
5
|
+
export * from "./contacts.js";
|
|
2
6
|
export * from "./mailboxes.js";
|
|
7
|
+
export * from "./mutes.js";
|
|
8
|
+
export * from "./pins.js";
|
|
3
9
|
export * from "./profile.js";
|
|
4
10
|
export * from "./reactions.js";
|
|
5
11
|
export * from "./simple.js";
|
package/dist/queries/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
export * from "./blossom.js";
|
|
2
|
+
export * from "./bookmarks.js";
|
|
3
|
+
export * from "./channels.js";
|
|
1
4
|
export * from "./comments.js";
|
|
5
|
+
export * from "./contacts.js";
|
|
2
6
|
export * from "./mailboxes.js";
|
|
7
|
+
export * from "./mutes.js";
|
|
8
|
+
export * from "./pins.js";
|
|
3
9
|
export * from "./profile.js";
|
|
4
10
|
export * from "./reactions.js";
|
|
5
11
|
export * from "./simple.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Mutes } from "../helpers/mutes.js";
|
|
2
|
+
import { Query } from "../query-store/index.js";
|
|
3
|
+
export declare function UserMuteQuery(pubkey: string): Query<Mutes | undefined>;
|
|
4
|
+
export declare function UserHiddenMuteQuery(pubkey: string): Query<(Mutes & {
|
|
5
|
+
locked: false;
|
|
6
|
+
}) | {
|
|
7
|
+
locked: true;
|
|
8
|
+
} | undefined>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { kinds } from "nostr-tools";
|
|
2
|
+
import { map } from "rxjs/operators";
|
|
3
|
+
import { getHiddenMutedThings, getMutedThings } from "../helpers/mutes.js";
|
|
4
|
+
import { isHiddenTagsLocked } from "../helpers/hidden-tags.js";
|
|
5
|
+
export function UserMuteQuery(pubkey) {
|
|
6
|
+
return {
|
|
7
|
+
key: pubkey,
|
|
8
|
+
run: (event) => event.replaceable(kinds.Mutelist, pubkey).pipe(map((event) => event && getMutedThings(event))),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function UserHiddenMuteQuery(pubkey) {
|
|
12
|
+
return {
|
|
13
|
+
key: pubkey,
|
|
14
|
+
run: (store) => store.replaceable(kinds.Mutelist, pubkey).pipe(map((event) => {
|
|
15
|
+
if (!event)
|
|
16
|
+
return undefined;
|
|
17
|
+
const muted = getHiddenMutedThings(event);
|
|
18
|
+
if (isHiddenTagsLocked(event) || !muted)
|
|
19
|
+
return { locked: true };
|
|
20
|
+
return { locked: false, ...muted };
|
|
21
|
+
})),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { kinds } from "nostr-tools";
|
|
2
|
+
import { map } from "rxjs/operators";
|
|
3
|
+
import { isETag, processTags } from "../helpers/tags.js";
|
|
4
|
+
import { getEventPointerFromETag } from "../helpers/pointers.js";
|
|
5
|
+
export function UserPinnedQuery(pubkey) {
|
|
6
|
+
return {
|
|
7
|
+
key: pubkey,
|
|
8
|
+
run: (store) => store
|
|
9
|
+
.replaceable(kinds.Pinlist, pubkey)
|
|
10
|
+
.pipe(map((event) => event && processTags(event.tags.filter(isETag), getEventPointerFromETag))),
|
|
11
|
+
};
|
|
12
|
+
}
|
package/dist/queries/simple.d.ts
CHANGED
|
@@ -3,14 +3,14 @@ import { Query } from "../query-store/index.js";
|
|
|
3
3
|
/** Creates a Query that returns a single event or undefined */
|
|
4
4
|
export declare function SingleEventQuery(id: string): Query<NostrEvent | undefined>;
|
|
5
5
|
/** Creates a Query that returns a multiple events in a map */
|
|
6
|
-
export declare function MultipleEventsQuery(ids: string[]): Query<
|
|
6
|
+
export declare function MultipleEventsQuery(ids: string[]): Query<Record<string, NostrEvent>>;
|
|
7
7
|
/** Creates a Query returning the latest version of a replaceable event */
|
|
8
8
|
export declare function ReplaceableQuery(kind: number, pubkey: string, d?: string): Query<NostrEvent | undefined>;
|
|
9
9
|
/** Creates a Query that returns an array of sorted events matching the filters */
|
|
10
|
-
export declare function TimelineQuery(filters: Filter | Filter[],
|
|
10
|
+
export declare function TimelineQuery(filters: Filter | Filter[], includeOldVersion?: boolean): Query<NostrEvent[]>;
|
|
11
11
|
/** Creates a Query that returns a directory of events by their UID */
|
|
12
12
|
export declare function ReplaceableSetQuery(pointers: {
|
|
13
13
|
kind: number;
|
|
14
14
|
pubkey: string;
|
|
15
15
|
identifier?: string;
|
|
16
|
-
}[]): Query<
|
|
16
|
+
}[]): Query<Record<string, NostrEvent>>;
|
package/dist/queries/simple.js
CHANGED
|
@@ -22,11 +22,11 @@ export function ReplaceableQuery(kind, pubkey, d) {
|
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
/** Creates a Query that returns an array of sorted events matching the filters */
|
|
25
|
-
export function TimelineQuery(filters,
|
|
25
|
+
export function TimelineQuery(filters, includeOldVersion) {
|
|
26
26
|
filters = Array.isArray(filters) ? filters : [filters];
|
|
27
27
|
return {
|
|
28
|
-
key: hash_sum(filters) + (
|
|
29
|
-
run: (events) => events.timeline(filters,
|
|
28
|
+
key: hash_sum(filters) + (includeOldVersion ? "-history" : ""),
|
|
29
|
+
run: (events) => events.timeline(filters, includeOldVersion),
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
/** Creates a Query that returns a directory of events by their UID */
|
package/dist/queries/thread.js
CHANGED
|
@@ -34,7 +34,7 @@ export function ThreadQuery(root, opts) {
|
|
|
34
34
|
}
|
|
35
35
|
return {
|
|
36
36
|
key: `${rootUID}-${kinds.join(",")}`,
|
|
37
|
-
run: (events) => events.
|
|
37
|
+
run: (events) => events.filters([rootFilter, replyFilter]).pipe(map((event) => {
|
|
38
38
|
if (!items.has(getEventUID(event))) {
|
|
39
39
|
const refs = getNip10References(event);
|
|
40
40
|
const replies = parentReferences.get(getEventUID(event)) || new Set();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
|
+
import { UserStatusPointer } from "../helpers/user-status.js";
|
|
3
|
+
import { Query } from "../query-store/index.js";
|
|
4
|
+
export type UserStatus = UserStatusPointer & {
|
|
5
|
+
event: NostrEvent;
|
|
6
|
+
content: string;
|
|
7
|
+
};
|
|
8
|
+
/** Creates a Query that returns a parsed {@link UserStatus} for a certain type */
|
|
9
|
+
export declare function UserStatusQuery(pubkey: string, type?: string): Query<UserStatus | undefined | null>;
|
|
10
|
+
/** Creates a Query that returns a directory of parsed {@link UserStatus} for a pubkey */
|
|
11
|
+
export declare function UserStatusesQuery(pubkey: string): Query<Record<string, UserStatus>>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { kinds } from "nostr-tools";
|
|
2
|
+
import { map } from "rxjs";
|
|
3
|
+
import { getUserStatusPointer } from "../helpers/user-status.js";
|
|
4
|
+
import { getReplaceableIdentifier } from "../helpers/event.js";
|
|
5
|
+
/** Creates a Query that returns a parsed {@link UserStatus} for a certain type */
|
|
6
|
+
export function UserStatusQuery(pubkey, type = "general") {
|
|
7
|
+
return {
|
|
8
|
+
key: pubkey,
|
|
9
|
+
run: (events) => events.replaceable(kinds.UserStatuses, pubkey, type).pipe(map((event) => {
|
|
10
|
+
if (!event)
|
|
11
|
+
return undefined;
|
|
12
|
+
const pointer = getUserStatusPointer(event);
|
|
13
|
+
if (!pointer)
|
|
14
|
+
return null;
|
|
15
|
+
return {
|
|
16
|
+
...pointer,
|
|
17
|
+
event,
|
|
18
|
+
content: event.content,
|
|
19
|
+
};
|
|
20
|
+
})),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Creates a Query that returns a directory of parsed {@link UserStatus} for a pubkey */
|
|
24
|
+
export function UserStatusesQuery(pubkey) {
|
|
25
|
+
return {
|
|
26
|
+
key: pubkey,
|
|
27
|
+
run: (events) => events.timeline([{ kinds: [kinds.UserStatuses], authors: [pubkey] }]).pipe(map((events) => {
|
|
28
|
+
return events.reduce((dir, event) => {
|
|
29
|
+
try {
|
|
30
|
+
const d = getReplaceableIdentifier(event);
|
|
31
|
+
return { ...dir, [d]: { event, ...getUserStatusPointer(event), content: event.content } };
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return dir;
|
|
35
|
+
}
|
|
36
|
+
}, {});
|
|
37
|
+
})),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { subscribeSpyTo } from "@hirez_io/observer-spy";
|
|
3
|
+
import { EventStore } from "../../event-store/event-store.js";
|
|
4
|
+
import { QueryStore } from "../query-store.js";
|
|
5
|
+
import { ProfileQuery } from "../../queries/profile.js";
|
|
6
|
+
import { SingleEventQuery } from "../../queries/simple.js";
|
|
7
|
+
let eventStore;
|
|
8
|
+
let queryStore;
|
|
9
|
+
const event = {
|
|
10
|
+
content: '{"name":"hzrd149","picture":"https://cdn.hzrd149.com/5ed3fe5df09a74e8c126831eac999364f9eb7624e2b86d521521b8021de20bdc.png","about":"JavaScript developer working on some nostr stuff\\n- noStrudel https://nostrudel.ninja/ \\n- Blossom https://github.com/hzrd149/blossom \\n- Applesauce https://hzrd149.github.io/applesauce/","website":"https://hzrd149.com","nip05":"_@hzrd149.com","lud16":"hzrd1499@minibits.cash","pubkey":"266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5","display_name":"hzrd149","displayName":"hzrd149","banner":""}',
|
|
11
|
+
created_at: 1738362529,
|
|
12
|
+
id: "e9df8d5898c4ccfbd21fcd59f3f48abb3ff0ab7259b19570e2f1756de1e9306b",
|
|
13
|
+
kind: 0,
|
|
14
|
+
pubkey: "266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5",
|
|
15
|
+
sig: "465a47b93626a587bf81dadc2b306b8f713a62db31d6ce1533198e9ae1e665a6eaf376a03250bf9ffbb02eb9059c8eafbd37ae1092d05d215757575bd8357586",
|
|
16
|
+
tags: [],
|
|
17
|
+
};
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
eventStore = new EventStore();
|
|
20
|
+
queryStore = new QueryStore(eventStore);
|
|
21
|
+
});
|
|
22
|
+
describe("createQuery", () => {
|
|
23
|
+
it("should emit synchronous value if it exists", () => {
|
|
24
|
+
let value = undefined;
|
|
25
|
+
eventStore.add(event);
|
|
26
|
+
queryStore.createQuery(ProfileQuery, event.pubkey).subscribe((v) => (value = v));
|
|
27
|
+
expect(value).not.toBe(undefined);
|
|
28
|
+
});
|
|
29
|
+
it("should not emit undefined if value exists", () => {
|
|
30
|
+
eventStore.add(event);
|
|
31
|
+
const spy = subscribeSpyTo(queryStore.createQuery(SingleEventQuery, event.id));
|
|
32
|
+
expect(spy.getValues()).toEqual([event]);
|
|
33
|
+
});
|
|
34
|
+
it("should emit synchronous undefined if value does not exists", () => {
|
|
35
|
+
let value = 0;
|
|
36
|
+
queryStore.createQuery(ProfileQuery, event.pubkey).subscribe((v) => {
|
|
37
|
+
value = v;
|
|
38
|
+
});
|
|
39
|
+
expect(value).not.toBe(0);
|
|
40
|
+
expect(value).toBe(undefined);
|
|
41
|
+
});
|
|
42
|
+
it("should share latest value", () => {
|
|
43
|
+
eventStore.add(event);
|
|
44
|
+
const spy = subscribeSpyTo(queryStore.createQuery(SingleEventQuery, event.id));
|
|
45
|
+
const spy2 = subscribeSpyTo(queryStore.createQuery(SingleEventQuery, event.id));
|
|
46
|
+
expect(spy.getValues()).toEqual([event]);
|
|
47
|
+
expect(spy2.getValues()).toEqual([event]);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe("executeQuery", () => {
|
|
51
|
+
it("should resolve when value is already present", async () => {
|
|
52
|
+
eventStore.add(event);
|
|
53
|
+
expect(await queryStore.executeQuery(ProfileQuery, event.pubkey)).toEqual(expect.objectContaining({ name: "hzrd149" }));
|
|
54
|
+
});
|
|
55
|
+
it("should resolve when value is added to event store", async () => {
|
|
56
|
+
const p = queryStore.executeQuery(ProfileQuery, event.pubkey);
|
|
57
|
+
// delay adding the event
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
eventStore.add(event);
|
|
60
|
+
}, 10);
|
|
61
|
+
await expect(p).resolves.toEqual(expect.objectContaining({ name: "hzrd149" }));
|
|
62
|
+
});
|
|
63
|
+
});
|