applesauce-core 0.12.1 → 1.2.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 +28 -10
- package/dist/__tests__/exports.test.d.ts +1 -0
- package/dist/__tests__/exports.test.js +17 -0
- package/dist/event-store/__tests__/event-store.test.js +52 -1
- package/dist/event-store/database.js +3 -3
- package/dist/event-store/event-store.js +15 -8
- package/dist/event-store/interface.d.ts +11 -7
- package/dist/helpers/__tests__/bookmarks.test.d.ts +1 -0
- package/dist/helpers/__tests__/bookmarks.test.js +88 -0
- package/dist/helpers/__tests__/comment.test.js +14 -0
- package/dist/helpers/__tests__/contacts.test.d.ts +1 -0
- package/dist/helpers/__tests__/contacts.test.js +34 -0
- package/dist/helpers/__tests__/events.test.d.ts +1 -0
- package/dist/helpers/__tests__/events.test.js +32 -0
- package/dist/helpers/__tests__/exports.test.d.ts +1 -0
- package/dist/helpers/__tests__/exports.test.js +220 -0
- package/dist/helpers/__tests__/mutes.test.d.ts +1 -0
- package/dist/helpers/__tests__/mutes.test.js +55 -0
- package/dist/helpers/blossom.d.ts +2 -0
- package/dist/helpers/blossom.js +18 -0
- package/dist/helpers/bookmarks.d.ts +6 -1
- package/dist/helpers/bookmarks.js +52 -7
- package/dist/helpers/comment.d.ts +7 -3
- package/dist/helpers/comment.js +6 -1
- package/dist/helpers/contacts.d.ts +11 -0
- package/dist/helpers/contacts.js +34 -0
- package/dist/helpers/event.d.ts +8 -3
- package/dist/helpers/event.js +21 -13
- package/dist/helpers/lists.d.ts +40 -12
- package/dist/helpers/lists.js +62 -23
- package/dist/helpers/mutes.d.ts +8 -0
- package/dist/helpers/mutes.js +66 -5
- package/dist/helpers/nip-19.d.ts +14 -0
- package/dist/helpers/nip-19.js +29 -0
- package/dist/helpers/pointers.js +6 -6
- package/dist/helpers/profile.js +1 -1
- package/dist/observable/__tests__/exports.test.d.ts +1 -0
- package/dist/observable/__tests__/exports.test.js +18 -0
- 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/observable/defined.d.ts +3 -0
- package/dist/observable/defined.js +5 -0
- package/dist/observable/get-observable-value.d.ts +4 -1
- package/dist/observable/get-observable-value.js +4 -1
- package/dist/observable/index.d.ts +5 -1
- package/dist/observable/index.js +6 -1
- package/dist/observable/listen-latest-updates.d.ts +5 -0
- package/dist/observable/listen-latest-updates.js +12 -0
- package/dist/promise/__tests__/exports.test.d.ts +1 -0
- package/dist/promise/__tests__/exports.test.js +11 -0
- package/dist/queries/__tests__/exports.test.d.ts +1 -0
- package/dist/queries/__tests__/exports.test.js +41 -0
- package/dist/queries/blossom.js +1 -6
- package/dist/queries/bookmarks.d.ts +5 -5
- package/dist/queries/bookmarks.js +18 -17
- package/dist/queries/channels.js +41 -53
- package/dist/queries/comments.js +6 -9
- package/dist/queries/contacts.d.ts +6 -1
- package/dist/queries/contacts.js +21 -9
- package/dist/queries/index.d.ts +1 -0
- package/dist/queries/index.js +1 -0
- package/dist/queries/mailboxes.js +4 -7
- package/dist/queries/mutes.d.ts +6 -6
- package/dist/queries/mutes.js +20 -19
- package/dist/queries/pins.d.ts +1 -0
- package/dist/queries/pins.js +4 -6
- package/dist/queries/profile.js +1 -6
- package/dist/queries/reactions.js +11 -14
- package/dist/queries/relays.d.ts +27 -0
- package/dist/queries/relays.js +44 -0
- package/dist/queries/simple.js +5 -22
- package/dist/queries/thread.js +45 -51
- package/dist/queries/user-status.js +23 -29
- package/dist/queries/zaps.js +10 -13
- package/dist/query-store/__tests__/exports.test.d.ts +1 -0
- package/dist/query-store/__tests__/exports.test.js +12 -0
- package/dist/query-store/query-store.d.ts +7 -6
- package/dist/query-store/query-store.js +13 -8
- package/package.json +3 -3
- package/dist/observable/share-latest-value.d.ts +0 -6
- package/dist/observable/share-latest-value.js +0 -24
package/dist/queries/thread.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { kinds } from "nostr-tools";
|
|
2
|
-
import {
|
|
2
|
+
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],
|
|
@@ -32,61 +32,55 @@ export function ThreadQuery(root, opts) {
|
|
|
32
32
|
rootFilter.ids = [root.id];
|
|
33
33
|
replyFilter["#e"] = [root.id];
|
|
34
34
|
}
|
|
35
|
-
return {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
return (events) => events.filters([rootFilter, replyFilter]).pipe(map((event) => {
|
|
36
|
+
if (!items.has(getEventUID(event))) {
|
|
37
|
+
const refs = getNip10References(event);
|
|
38
|
+
const replies = parentReferences.get(getEventUID(event)) || new Set();
|
|
39
|
+
const item = { event, refs, replies };
|
|
40
|
+
for (const child of replies) {
|
|
41
|
+
child.parent = item;
|
|
42
|
+
}
|
|
43
|
+
// add item to parent
|
|
44
|
+
if (refs.reply?.e || refs.reply?.a) {
|
|
45
|
+
let uid = refs.reply.e ? refs.reply.e.id : getCoordinateFromAddressPointer(refs.reply.a);
|
|
46
|
+
item.parent = items.get(uid);
|
|
47
|
+
if (item.parent) {
|
|
48
|
+
item.parent.replies.add(item);
|
|
44
49
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
let
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
// parent isn't created yet, store ref for later
|
|
54
|
-
let set = parentReferences.get(uid);
|
|
55
|
-
if (!set) {
|
|
56
|
-
set = new Set();
|
|
57
|
-
parentReferences.set(uid, set);
|
|
58
|
-
}
|
|
59
|
-
set.add(item);
|
|
50
|
+
else {
|
|
51
|
+
// parent isn't created yet, store ref for later
|
|
52
|
+
let set = parentReferences.get(uid);
|
|
53
|
+
if (!set) {
|
|
54
|
+
set = new Set();
|
|
55
|
+
parentReferences.set(uid, set);
|
|
60
56
|
}
|
|
57
|
+
set.add(item);
|
|
61
58
|
}
|
|
62
|
-
// add item to map
|
|
63
|
-
items.set(getEventUID(event), item);
|
|
64
59
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
// add item to map
|
|
61
|
+
items.set(getEventUID(event), item);
|
|
62
|
+
}
|
|
63
|
+
return { root: items.get(rootUID), all: items };
|
|
64
|
+
}));
|
|
68
65
|
}
|
|
69
66
|
/** A query that gets all legacy and NIP-10, and NIP-22 replies for an event */
|
|
70
67
|
export function RepliesQuery(event, overrideKinds) {
|
|
71
|
-
return {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
});
|
|
89
|
-
}));
|
|
90
|
-
},
|
|
68
|
+
return (events) => {
|
|
69
|
+
const kinds = overrideKinds || event.kind === 1 ? [1, COMMENT_KIND] : [COMMENT_KIND];
|
|
70
|
+
const filter = { kinds };
|
|
71
|
+
if (isEvent(parent) || isEventPointer(event))
|
|
72
|
+
filter["#e"] = [event.id];
|
|
73
|
+
const address = isAddressableKind(event.kind)
|
|
74
|
+
? createReplaceableAddress(event.kind, event.pubkey, getTagValue(event, "d"))
|
|
75
|
+
: undefined;
|
|
76
|
+
if (address) {
|
|
77
|
+
filter["#a"] = [address];
|
|
78
|
+
}
|
|
79
|
+
return events.timeline(filter).pipe(map((events) => {
|
|
80
|
+
return events.filter((e) => {
|
|
81
|
+
const refs = interpretThreadTags(e.tags);
|
|
82
|
+
return refs.reply?.e?.[1] === event.id || refs.reply?.a?.[1] === address;
|
|
83
|
+
});
|
|
84
|
+
}));
|
|
91
85
|
};
|
|
92
86
|
}
|
|
@@ -4,36 +4,30 @@ import { getUserStatusPointer } from "../helpers/user-status.js";
|
|
|
4
4
|
import { getReplaceableIdentifier } from "../helpers/event.js";
|
|
5
5
|
/** Creates a Query that returns a parsed {@link UserStatus} for a certain type */
|
|
6
6
|
export function UserStatusQuery(pubkey, type = "general") {
|
|
7
|
-
return {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
20
|
-
})),
|
|
21
|
-
};
|
|
7
|
+
return (events) => events.replaceable(kinds.UserStatuses, pubkey, type).pipe(map((event) => {
|
|
8
|
+
if (!event)
|
|
9
|
+
return undefined;
|
|
10
|
+
const pointer = getUserStatusPointer(event);
|
|
11
|
+
if (!pointer)
|
|
12
|
+
return null;
|
|
13
|
+
return {
|
|
14
|
+
...pointer,
|
|
15
|
+
event,
|
|
16
|
+
content: event.content,
|
|
17
|
+
};
|
|
18
|
+
}));
|
|
22
19
|
}
|
|
23
20
|
/** Creates a Query that returns a directory of parsed {@link UserStatus} for a pubkey */
|
|
24
21
|
export function UserStatusesQuery(pubkey) {
|
|
25
|
-
return {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}, {});
|
|
37
|
-
})),
|
|
38
|
-
};
|
|
22
|
+
return (events) => events.timeline([{ kinds: [kinds.UserStatuses], authors: [pubkey] }]).pipe(map((events) => {
|
|
23
|
+
return events.reduce((dir, event) => {
|
|
24
|
+
try {
|
|
25
|
+
const d = getReplaceableIdentifier(event);
|
|
26
|
+
return { ...dir, [d]: { event, ...getUserStatusPointer(event), content: event.content } };
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
return dir;
|
|
30
|
+
}
|
|
31
|
+
}, {});
|
|
32
|
+
}));
|
|
39
33
|
}
|
package/dist/queries/zaps.js
CHANGED
|
@@ -4,18 +4,15 @@ import { getCoordinateFromAddressPointer, isAddressPointer } from "../helpers/po
|
|
|
4
4
|
import { isValidZap } from "../helpers/zap.js";
|
|
5
5
|
/** A query that gets all zap events for an event */
|
|
6
6
|
export function EventZapsQuery(id) {
|
|
7
|
-
return {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return events.timeline([{ kinds: [kinds.Zap], "#e": [id] }]).pipe(map((events) => events.filter(isValidZap)));
|
|
18
|
-
}
|
|
19
|
-
},
|
|
7
|
+
return (events) => {
|
|
8
|
+
if (isAddressPointer(id)) {
|
|
9
|
+
return events
|
|
10
|
+
.timeline([{ kinds: [kinds.Zap], "#a": [getCoordinateFromAddressPointer(id)] }])
|
|
11
|
+
.pipe(map((events) => events.filter(isValidZap)));
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
id = typeof id === "string" ? id : id.id;
|
|
15
|
+
return events.timeline([{ kinds: [kinds.Zap], "#e": [id] }]).pipe(map((events) => events.filter(isValidZap)));
|
|
16
|
+
}
|
|
20
17
|
};
|
|
21
18
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import * as exports from "../index.js";
|
|
3
|
+
describe("exports", () => {
|
|
4
|
+
it("should export the expected functions", () => {
|
|
5
|
+
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
+
[
|
|
7
|
+
"Queries",
|
|
8
|
+
"QueryStore",
|
|
9
|
+
]
|
|
10
|
+
`);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -3,13 +3,10 @@ import { Filter, NostrEvent } from "nostr-tools";
|
|
|
3
3
|
import type { AddressPointer, EventPointer } from "nostr-tools/nip19";
|
|
4
4
|
import { IEventStore } from "../event-store/interface.js";
|
|
5
5
|
import * as Queries from "../queries/index.js";
|
|
6
|
-
export type Query<T extends unknown> =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/** The meat of the query, this should return an Observables that subscribes to the eventStore in some way */
|
|
10
|
-
run: (events: IEventStore, store: QueryStore) => Observable<T>;
|
|
6
|
+
export type Query<T extends unknown> = (events: IEventStore, store: QueryStore) => Observable<T>;
|
|
7
|
+
export type QueryConstructor<T extends unknown, Args extends Array<any>> = ((...args: Args) => Query<T>) & {
|
|
8
|
+
getKey?: (...args: Args) => string;
|
|
11
9
|
};
|
|
12
|
-
export type QueryConstructor<T extends unknown, Args extends Array<any>> = (...args: Args) => Query<T>;
|
|
13
10
|
export declare class QueryStore {
|
|
14
11
|
static Queries: typeof Queries;
|
|
15
12
|
store: IEventStore;
|
|
@@ -38,6 +35,10 @@ export declare class QueryStore {
|
|
|
38
35
|
timeline(filters: Filter | Filter[], keepOldVersions?: boolean): Observable<import("nostr-tools").Event[] | undefined>;
|
|
39
36
|
/** Creates a ProfileQuery */
|
|
40
37
|
profile(pubkey: string): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
|
|
38
|
+
/** Creates a ContactsQuery */
|
|
39
|
+
contacts(pubkey: string): Observable<import("nostr-tools/nip19").ProfilePointer[] | undefined>;
|
|
40
|
+
/** Creates a MuteQuery */
|
|
41
|
+
mutes(pubkey: string): Observable<import("../helpers/mutes.js").Mutes | undefined>;
|
|
41
42
|
/** Creates a ReactionsQuery */
|
|
42
43
|
reactions(event: NostrEvent): Observable<import("nostr-tools").Event[] | undefined>;
|
|
43
44
|
/** Creates a MailboxesQuery */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { filter, finalize, ReplaySubject, share, timer } from "rxjs";
|
|
1
|
+
import { filter, finalize, firstValueFrom, ReplaySubject, share, timer } from "rxjs";
|
|
2
2
|
import hash_sum from "hash-sum";
|
|
3
3
|
import * as Queries from "../queries/index.js";
|
|
4
|
-
import { getObservableValue } from "../observable/get-observable-value.js";
|
|
5
4
|
import { withImmediateValueOrDefault } from "../observable/with-immediate-value.js";
|
|
6
5
|
export class QueryStore {
|
|
7
6
|
static Queries = Queries;
|
|
@@ -22,19 +21,17 @@ export class QueryStore {
|
|
|
22
21
|
observables = new Map();
|
|
23
22
|
this.queries.set(queryConstructor, observables);
|
|
24
23
|
}
|
|
25
|
-
const key = hash_sum(args);
|
|
24
|
+
const key = queryConstructor.getKey ? queryConstructor.getKey(...args) : hash_sum(args);
|
|
26
25
|
let observable = observables.get(key);
|
|
27
26
|
if (!observable) {
|
|
28
27
|
const cleanup = () => {
|
|
29
28
|
if (observables.get(key) === observable)
|
|
30
29
|
observables.delete(key);
|
|
31
30
|
};
|
|
32
|
-
observable = queryConstructor(...args)
|
|
33
|
-
.run(this.store, this)
|
|
34
|
-
.pipe(
|
|
31
|
+
observable = queryConstructor(...args)(this.store, this).pipe(
|
|
35
32
|
// always emit undefined so the observable is sync
|
|
36
33
|
withImmediateValueOrDefault(undefined),
|
|
37
|
-
// remove the observable when its
|
|
34
|
+
// remove the observable when its unsubscribed
|
|
38
35
|
finalize(cleanup),
|
|
39
36
|
// only create a single observable for all components
|
|
40
37
|
share({
|
|
@@ -51,7 +48,7 @@ export class QueryStore {
|
|
|
51
48
|
/** Creates a query and waits for the next value */
|
|
52
49
|
async executeQuery(queryConstructor, ...args) {
|
|
53
50
|
const query = this.createQuery(queryConstructor, ...args).pipe(filter((v) => v !== undefined));
|
|
54
|
-
return
|
|
51
|
+
return firstValueFrom(query);
|
|
55
52
|
}
|
|
56
53
|
/** Creates a SingleEventQuery */
|
|
57
54
|
event(id) {
|
|
@@ -77,6 +74,14 @@ export class QueryStore {
|
|
|
77
74
|
profile(pubkey) {
|
|
78
75
|
return this.createQuery(Queries.ProfileQuery, pubkey);
|
|
79
76
|
}
|
|
77
|
+
/** Creates a ContactsQuery */
|
|
78
|
+
contacts(pubkey) {
|
|
79
|
+
return this.createQuery(Queries.ContactsQuery, pubkey);
|
|
80
|
+
}
|
|
81
|
+
/** Creates a MuteQuery */
|
|
82
|
+
mutes(pubkey) {
|
|
83
|
+
return this.createQuery(Queries.MuteQuery, pubkey);
|
|
84
|
+
}
|
|
80
85
|
/** Creates a ReactionsQuery */
|
|
81
86
|
reactions(event) {
|
|
82
87
|
return this.createQuery(Queries.ReactionsQuery, event);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
"@hirez_io/observer-spy": "^2.2.0",
|
|
77
77
|
"@types/debug": "^4.1.12",
|
|
78
78
|
"@types/hash-sum": "^1.0.2",
|
|
79
|
-
"typescript": "^5.
|
|
80
|
-
"vitest": "^3.
|
|
79
|
+
"typescript": "^5.8.3",
|
|
80
|
+
"vitest": "^3.1.1"
|
|
81
81
|
},
|
|
82
82
|
"funding": {
|
|
83
83
|
"type": "lightning",
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { OperatorFunction } from "rxjs";
|
|
2
|
-
/**
|
|
3
|
-
* Creates an operator that adds a 'value' property and multiplexes the source
|
|
4
|
-
* @param config Optional ShareConfig for customizing sharing behavior
|
|
5
|
-
*/
|
|
6
|
-
export declare function shareLatestValue<T>(): OperatorFunction<T, T | undefined>;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { BehaviorSubject, share } from "rxjs";
|
|
2
|
-
/**
|
|
3
|
-
* Creates an operator that adds a 'value' property and multiplexes the source
|
|
4
|
-
* @param config Optional ShareConfig for customizing sharing behavior
|
|
5
|
-
*/
|
|
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
|
-
// };
|
|
24
|
-
}
|