applesauce-core 0.0.0-next-20250206231639 → 0.0.0-next-20250207162618
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/event-store.js +17 -10
- package/dist/event-store/event-store.test.d.ts +1 -0
- package/dist/event-store/event-store.test.js +74 -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 +1 -1
- package/dist/observable/index.js +1 -1
- package/dist/query-store/index.d.ts +1 -57
- package/dist/query-store/index.js +1 -68
- package/dist/query-store/query-store.d.ts +62 -0
- package/dist/query-store/query-store.js +75 -0
- package/dist/query-store/query-store.test.d.ts +1 -0
- package/dist/query-store/query-store.test.js +33 -0
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import { Observable } from "rxjs";
|
|
|
5
5
|
import { Database } from "./database.js";
|
|
6
6
|
import { getEventUID, getReplaceableUID, getTagValue, isReplaceable } from "../helpers/event.js";
|
|
7
7
|
import { matchFilters } from "../helpers/filter.js";
|
|
8
|
-
import { addSeenRelay } from "../helpers/relays.js";
|
|
8
|
+
import { addSeenRelay, getSeenRelays } from "../helpers/relays.js";
|
|
9
9
|
import { getDeleteCoordinates, getDeleteIds } from "../helpers/delete.js";
|
|
10
10
|
export class EventStore {
|
|
11
11
|
database;
|
|
@@ -33,21 +33,28 @@ export class EventStore {
|
|
|
33
33
|
return event;
|
|
34
34
|
// insert event into database
|
|
35
35
|
const inserted = this.database.addEvent(event);
|
|
36
|
+
// copy seen relays to new event
|
|
37
|
+
const relays = getSeenRelays(event);
|
|
38
|
+
if (relays) {
|
|
39
|
+
for (const relay of relays)
|
|
40
|
+
addSeenRelay(inserted, relay);
|
|
41
|
+
}
|
|
42
|
+
// attach relay this event was from
|
|
43
|
+
if (fromRelay)
|
|
44
|
+
addSeenRelay(inserted, fromRelay);
|
|
36
45
|
// remove all old version of the replaceable event
|
|
37
46
|
if (!this.keepOldVersions && isReplaceable(event.kind)) {
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
-
const older = Array.from(
|
|
47
|
+
const existing = this.database.getReplaceable(event.kind, event.pubkey, getTagValue(event, "d"));
|
|
48
|
+
if (existing) {
|
|
49
|
+
const older = Array.from(existing).filter((e) => e.created_at < event.created_at);
|
|
41
50
|
for (const old of older)
|
|
42
51
|
this.database.deleteEvent(old);
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
// return the newest version of the replaceable event
|
|
53
|
+
// most of the time this will be === event, but not always
|
|
54
|
+
if (existing.length !== older.length)
|
|
55
|
+
return existing[0];
|
|
46
56
|
}
|
|
47
57
|
}
|
|
48
|
-
// attach relay this event was from
|
|
49
|
-
if (fromRelay)
|
|
50
|
-
addSeenRelay(inserted, fromRelay);
|
|
51
58
|
return inserted;
|
|
52
59
|
}
|
|
53
60
|
/** Removes an event from the database and updates subscriptions */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { kinds } from "nostr-tools";
|
|
3
|
+
import { EventStore } from "./event-store.js";
|
|
4
|
+
import { addSeenRelay, getSeenRelays } from "../helpers/relays.js";
|
|
5
|
+
let eventStore;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
eventStore = new EventStore();
|
|
8
|
+
});
|
|
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
|
+
describe("add", () => {
|
|
19
|
+
it("should return original event in case of duplicates", () => {
|
|
20
|
+
const a = { ...event };
|
|
21
|
+
expect(eventStore.add(a)).toBe(a);
|
|
22
|
+
const b = { ...event };
|
|
23
|
+
expect(eventStore.add(b)).toBe(a);
|
|
24
|
+
const c = { ...event };
|
|
25
|
+
expect(eventStore.add(c)).toBe(a);
|
|
26
|
+
});
|
|
27
|
+
it("should merge seen relays on duplicate events", () => {
|
|
28
|
+
const a = { ...event };
|
|
29
|
+
addSeenRelay(a, "wss://relay.a.com");
|
|
30
|
+
eventStore.add(a);
|
|
31
|
+
const b = { ...event };
|
|
32
|
+
addSeenRelay(b, "wss://relay.b.com");
|
|
33
|
+
eventStore.add(b);
|
|
34
|
+
expect(eventStore.getEvent(event.id)).toBeDefined();
|
|
35
|
+
expect([...getSeenRelays(eventStore.getEvent(event.id))]).toEqual(expect.arrayContaining(["wss://relay.a.com", "wss://relay.b.com"]));
|
|
36
|
+
});
|
|
37
|
+
it("should ignore deleted events", () => {
|
|
38
|
+
const deleteEvent = {
|
|
39
|
+
id: "delete event id",
|
|
40
|
+
kind: kinds.EventDeletion,
|
|
41
|
+
created_at: event.created_at + 100,
|
|
42
|
+
pubkey: event.pubkey,
|
|
43
|
+
tags: [["e", event.id]],
|
|
44
|
+
sig: "this should be ignored for the test",
|
|
45
|
+
content: "test",
|
|
46
|
+
};
|
|
47
|
+
// add delete event first
|
|
48
|
+
eventStore.add(deleteEvent);
|
|
49
|
+
// now event should be ignored
|
|
50
|
+
eventStore.add(event);
|
|
51
|
+
expect(eventStore.getEvent(event.id)).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("verifyEvent", () => {
|
|
55
|
+
it("should be called for all events added", () => {
|
|
56
|
+
const verifyEvent = vi.fn().mockReturnValue(true);
|
|
57
|
+
eventStore.verifyEvent = verifyEvent;
|
|
58
|
+
eventStore.add(event);
|
|
59
|
+
expect(verifyEvent).toHaveBeenCalledWith(event);
|
|
60
|
+
});
|
|
61
|
+
it("should not be called for duplicate events", () => {
|
|
62
|
+
const verifyEvent = vi.fn().mockReturnValue(true);
|
|
63
|
+
eventStore.verifyEvent = verifyEvent;
|
|
64
|
+
const a = { ...event };
|
|
65
|
+
eventStore.add(a);
|
|
66
|
+
expect(verifyEvent).toHaveBeenCalledWith(a);
|
|
67
|
+
const b = { ...event };
|
|
68
|
+
eventStore.add(b);
|
|
69
|
+
expect(verifyEvent).toHaveBeenCalledTimes(1);
|
|
70
|
+
const c = { ...event };
|
|
71
|
+
eventStore.add(c);
|
|
72
|
+
expect(verifyEvent).toHaveBeenCalledTimes(1);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -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
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./get-value.js";
|
|
1
|
+
export * from "./get-observable-value.js";
|
|
2
2
|
export * from "./share-latest-value.js";
|
package/dist/observable/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./get-value.js";
|
|
1
|
+
export * from "./get-observable-value.js";
|
|
2
2
|
export * from "./share-latest-value.js";
|
|
@@ -1,57 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { Filter, NostrEvent } from "nostr-tools";
|
|
3
|
-
import { EventStore } from "../event-store/event-store.js";
|
|
4
|
-
import { LRU } from "../helpers/lru.js";
|
|
5
|
-
import * as Queries from "../queries/index.js";
|
|
6
|
-
import { AddressPointer, EventPointer } from "nostr-tools/nip19";
|
|
7
|
-
export type Query<T extends unknown> = {
|
|
8
|
-
/**
|
|
9
|
-
* A unique key for this query. this is used to detect duplicate queries
|
|
10
|
-
*/
|
|
11
|
-
key: string;
|
|
12
|
-
/** The args array this query was created with. This is mostly for debugging */
|
|
13
|
-
args?: Array<any>;
|
|
14
|
-
/**
|
|
15
|
-
* The meat of the query, this should return an Observables that subscribes to the eventStore in some way
|
|
16
|
-
*/
|
|
17
|
-
run: (events: EventStore, store: QueryStore) => Observable<T>;
|
|
18
|
-
};
|
|
19
|
-
export type QueryConstructor<T extends unknown, Args extends Array<any>> = (...args: Args) => Query<T>;
|
|
20
|
-
export declare class QueryStore {
|
|
21
|
-
static Queries: typeof Queries;
|
|
22
|
-
store: EventStore;
|
|
23
|
-
constructor(store: EventStore);
|
|
24
|
-
queries: LRU<Query<any>>;
|
|
25
|
-
observables: WeakMap<Query<any>, Observable<any> | BehaviorSubject<any>>;
|
|
26
|
-
/** Creates a cached query */
|
|
27
|
-
createQuery<T extends unknown, Args extends Array<any>>(queryConstructor: (...args: Args) => {
|
|
28
|
-
key: string;
|
|
29
|
-
run: (events: EventStore, store: QueryStore) => Observable<T>;
|
|
30
|
-
}, ...args: Args): Observable<T>;
|
|
31
|
-
/** Creates a SingleEventQuery */
|
|
32
|
-
event(id: string): Observable<import("nostr-tools").Event | undefined>;
|
|
33
|
-
/** Creates a MultipleEventsQuery */
|
|
34
|
-
events(ids: string[]): Observable<Record<string, import("nostr-tools").Event>>;
|
|
35
|
-
/** Creates a ReplaceableQuery */
|
|
36
|
-
replaceable(kind: number, pubkey: string, d?: string): Observable<import("nostr-tools").Event | undefined>;
|
|
37
|
-
/** Creates a ReplaceableSetQuery */
|
|
38
|
-
replaceableSet(pointers: {
|
|
39
|
-
kind: number;
|
|
40
|
-
pubkey: string;
|
|
41
|
-
identifier?: string;
|
|
42
|
-
}[]): Observable<Record<string, import("nostr-tools").Event>>;
|
|
43
|
-
/** Creates a TimelineQuery */
|
|
44
|
-
timeline(filters: Filter | Filter[], keepOldVersions?: boolean): Observable<import("nostr-tools").Event[]>;
|
|
45
|
-
/** Creates a ProfileQuery */
|
|
46
|
-
profile(pubkey: string): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
|
|
47
|
-
/** Creates a ReactionsQuery */
|
|
48
|
-
reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
|
|
49
|
-
/** Creates a MailboxesQuery */
|
|
50
|
-
mailboxes(pubkey: string): Observable<{
|
|
51
|
-
inboxes: string[];
|
|
52
|
-
outboxes: string[];
|
|
53
|
-
} | undefined>;
|
|
54
|
-
/** Creates a ThreadQuery */
|
|
55
|
-
thread(root: string | EventPointer | AddressPointer): Observable<Queries.Thread>;
|
|
56
|
-
}
|
|
57
|
-
export { Queries };
|
|
1
|
+
export * from "./query-store.js";
|
|
@@ -1,68 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import * as Queries from "../queries/index.js";
|
|
3
|
-
import { shareLatestValue } from "../observable/share-latest-value.js";
|
|
4
|
-
export class QueryStore {
|
|
5
|
-
static Queries = Queries;
|
|
6
|
-
store;
|
|
7
|
-
constructor(store) {
|
|
8
|
-
if (!store)
|
|
9
|
-
throw new Error("EventStore required");
|
|
10
|
-
this.store = store;
|
|
11
|
-
}
|
|
12
|
-
queries = new LRU();
|
|
13
|
-
observables = new WeakMap();
|
|
14
|
-
/** Creates a cached query */
|
|
15
|
-
createQuery(queryConstructor, ...args) {
|
|
16
|
-
const tempQuery = queryConstructor(...args);
|
|
17
|
-
const key = `${queryConstructor.name}|${tempQuery.key}`;
|
|
18
|
-
let query = this.queries.get(key);
|
|
19
|
-
if (!query) {
|
|
20
|
-
query = tempQuery;
|
|
21
|
-
this.queries.set(key, tempQuery);
|
|
22
|
-
}
|
|
23
|
-
if (!this.observables.has(query)) {
|
|
24
|
-
query.args = args;
|
|
25
|
-
const observable = query.run(this.store, this).pipe(shareLatestValue());
|
|
26
|
-
this.observables.set(query, observable);
|
|
27
|
-
return observable;
|
|
28
|
-
}
|
|
29
|
-
return this.observables.get(query);
|
|
30
|
-
}
|
|
31
|
-
/** Creates a SingleEventQuery */
|
|
32
|
-
event(id) {
|
|
33
|
-
return this.createQuery(Queries.SingleEventQuery, id);
|
|
34
|
-
}
|
|
35
|
-
/** Creates a MultipleEventsQuery */
|
|
36
|
-
events(ids) {
|
|
37
|
-
return this.createQuery(Queries.MultipleEventsQuery, ids);
|
|
38
|
-
}
|
|
39
|
-
/** Creates a ReplaceableQuery */
|
|
40
|
-
replaceable(kind, pubkey, d) {
|
|
41
|
-
return this.createQuery(Queries.ReplaceableQuery, kind, pubkey, d);
|
|
42
|
-
}
|
|
43
|
-
/** Creates a ReplaceableSetQuery */
|
|
44
|
-
replaceableSet(pointers) {
|
|
45
|
-
return this.createQuery(Queries.ReplaceableSetQuery, pointers);
|
|
46
|
-
}
|
|
47
|
-
/** Creates a TimelineQuery */
|
|
48
|
-
timeline(filters, keepOldVersions) {
|
|
49
|
-
return this.createQuery(Queries.TimelineQuery, filters, keepOldVersions);
|
|
50
|
-
}
|
|
51
|
-
/** Creates a ProfileQuery */
|
|
52
|
-
profile(pubkey) {
|
|
53
|
-
return this.createQuery(Queries.ProfileQuery, pubkey);
|
|
54
|
-
}
|
|
55
|
-
/** Creates a ReactionsQuery */
|
|
56
|
-
reactions(event) {
|
|
57
|
-
return this.createQuery(Queries.ReactionsQuery, event);
|
|
58
|
-
}
|
|
59
|
-
/** Creates a MailboxesQuery */
|
|
60
|
-
mailboxes(pubkey) {
|
|
61
|
-
return this.createQuery(Queries.MailboxesQuery, pubkey);
|
|
62
|
-
}
|
|
63
|
-
/** Creates a ThreadQuery */
|
|
64
|
-
thread(root) {
|
|
65
|
-
return this.createQuery(Queries.ThreadQuery, root);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
export { Queries };
|
|
1
|
+
export * from "./query-store.js";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { BehaviorSubject, Observable } from "rxjs";
|
|
2
|
+
import { Filter, NostrEvent } from "nostr-tools";
|
|
3
|
+
import { EventStore } from "../event-store/event-store.js";
|
|
4
|
+
import { LRU } from "../helpers/lru.js";
|
|
5
|
+
import * as Queries from "../queries/index.js";
|
|
6
|
+
import { AddressPointer, EventPointer } from "nostr-tools/nip19";
|
|
7
|
+
export type Query<T extends unknown> = {
|
|
8
|
+
/**
|
|
9
|
+
* A unique key for this query. this is used to detect duplicate queries
|
|
10
|
+
*/
|
|
11
|
+
key: string;
|
|
12
|
+
/** The args array this query was created with. This is mostly for debugging */
|
|
13
|
+
args?: Array<any>;
|
|
14
|
+
/**
|
|
15
|
+
* The meat of the query, this should return an Observables that subscribes to the eventStore in some way
|
|
16
|
+
*/
|
|
17
|
+
run: (events: EventStore, store: QueryStore) => Observable<T>;
|
|
18
|
+
};
|
|
19
|
+
export type QueryConstructor<T extends unknown, Args extends Array<any>> = (...args: Args) => Query<T>;
|
|
20
|
+
export declare class QueryStore {
|
|
21
|
+
static Queries: typeof Queries;
|
|
22
|
+
store: EventStore;
|
|
23
|
+
constructor(store: EventStore);
|
|
24
|
+
queries: LRU<Query<any>>;
|
|
25
|
+
observables: WeakMap<Query<any>, Observable<any> | BehaviorSubject<any>>;
|
|
26
|
+
/** Creates a cached query */
|
|
27
|
+
createQuery<T extends unknown, Args extends Array<any>>(queryConstructor: (...args: Args) => {
|
|
28
|
+
key: string;
|
|
29
|
+
run: (events: EventStore, store: QueryStore) => Observable<T>;
|
|
30
|
+
}, ...args: Args): Observable<T>;
|
|
31
|
+
/** Creates a query and waits for the next value */
|
|
32
|
+
executeQuery<T extends unknown, Args extends Array<any>>(queryConstructor: (...args: Args) => {
|
|
33
|
+
key: string;
|
|
34
|
+
run: (events: EventStore, store: QueryStore) => Observable<T>;
|
|
35
|
+
}, ...args: Args): Promise<T>;
|
|
36
|
+
/** Creates a SingleEventQuery */
|
|
37
|
+
event(id: string): Observable<import("nostr-tools").Event | undefined>;
|
|
38
|
+
/** Creates a MultipleEventsQuery */
|
|
39
|
+
events(ids: string[]): Observable<Record<string, import("nostr-tools").Event>>;
|
|
40
|
+
/** Creates a ReplaceableQuery */
|
|
41
|
+
replaceable(kind: number, pubkey: string, d?: string): Observable<import("nostr-tools").Event | undefined>;
|
|
42
|
+
/** Creates a ReplaceableSetQuery */
|
|
43
|
+
replaceableSet(pointers: {
|
|
44
|
+
kind: number;
|
|
45
|
+
pubkey: string;
|
|
46
|
+
identifier?: string;
|
|
47
|
+
}[]): Observable<Record<string, import("nostr-tools").Event>>;
|
|
48
|
+
/** Creates a TimelineQuery */
|
|
49
|
+
timeline(filters: Filter | Filter[], keepOldVersions?: boolean): Observable<import("nostr-tools").Event[]>;
|
|
50
|
+
/** Creates a ProfileQuery */
|
|
51
|
+
profile(pubkey: string): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
|
|
52
|
+
/** Creates a ReactionsQuery */
|
|
53
|
+
reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
|
|
54
|
+
/** Creates a MailboxesQuery */
|
|
55
|
+
mailboxes(pubkey: string): Observable<{
|
|
56
|
+
inboxes: string[];
|
|
57
|
+
outboxes: string[];
|
|
58
|
+
} | undefined>;
|
|
59
|
+
/** Creates a ThreadQuery */
|
|
60
|
+
thread(root: string | EventPointer | AddressPointer): Observable<Queries.Thread>;
|
|
61
|
+
}
|
|
62
|
+
export { Queries };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { filter } from "rxjs";
|
|
2
|
+
import { LRU } from "../helpers/lru.js";
|
|
3
|
+
import * as Queries from "../queries/index.js";
|
|
4
|
+
import { shareLatestValue } from "../observable/share-latest-value.js";
|
|
5
|
+
import { getObservableValue } from "../observable/get-observable-value.js";
|
|
6
|
+
export class QueryStore {
|
|
7
|
+
static Queries = Queries;
|
|
8
|
+
store;
|
|
9
|
+
constructor(store) {
|
|
10
|
+
if (!store)
|
|
11
|
+
throw new Error("EventStore required");
|
|
12
|
+
this.store = store;
|
|
13
|
+
}
|
|
14
|
+
queries = new LRU();
|
|
15
|
+
observables = new WeakMap();
|
|
16
|
+
/** Creates a cached query */
|
|
17
|
+
createQuery(queryConstructor, ...args) {
|
|
18
|
+
const tempQuery = queryConstructor(...args);
|
|
19
|
+
const key = `${queryConstructor.name}|${tempQuery.key}`;
|
|
20
|
+
let query = this.queries.get(key);
|
|
21
|
+
if (!query) {
|
|
22
|
+
query = tempQuery;
|
|
23
|
+
this.queries.set(key, tempQuery);
|
|
24
|
+
}
|
|
25
|
+
if (!this.observables.has(query)) {
|
|
26
|
+
query.args = args;
|
|
27
|
+
const observable = query.run(this.store, this).pipe(shareLatestValue());
|
|
28
|
+
this.observables.set(query, observable);
|
|
29
|
+
return observable;
|
|
30
|
+
}
|
|
31
|
+
return this.observables.get(query);
|
|
32
|
+
}
|
|
33
|
+
/** Creates a query and waits for the next value */
|
|
34
|
+
async executeQuery(queryConstructor, ...args) {
|
|
35
|
+
const query = this.createQuery(queryConstructor, ...args).pipe(filter((v) => v !== undefined));
|
|
36
|
+
return getObservableValue(query);
|
|
37
|
+
}
|
|
38
|
+
/** Creates a SingleEventQuery */
|
|
39
|
+
event(id) {
|
|
40
|
+
return this.createQuery(Queries.SingleEventQuery, id);
|
|
41
|
+
}
|
|
42
|
+
/** Creates a MultipleEventsQuery */
|
|
43
|
+
events(ids) {
|
|
44
|
+
return this.createQuery(Queries.MultipleEventsQuery, ids);
|
|
45
|
+
}
|
|
46
|
+
/** Creates a ReplaceableQuery */
|
|
47
|
+
replaceable(kind, pubkey, d) {
|
|
48
|
+
return this.createQuery(Queries.ReplaceableQuery, kind, pubkey, d);
|
|
49
|
+
}
|
|
50
|
+
/** Creates a ReplaceableSetQuery */
|
|
51
|
+
replaceableSet(pointers) {
|
|
52
|
+
return this.createQuery(Queries.ReplaceableSetQuery, pointers);
|
|
53
|
+
}
|
|
54
|
+
/** Creates a TimelineQuery */
|
|
55
|
+
timeline(filters, keepOldVersions) {
|
|
56
|
+
return this.createQuery(Queries.TimelineQuery, filters, keepOldVersions);
|
|
57
|
+
}
|
|
58
|
+
/** Creates a ProfileQuery */
|
|
59
|
+
profile(pubkey) {
|
|
60
|
+
return this.createQuery(Queries.ProfileQuery, pubkey);
|
|
61
|
+
}
|
|
62
|
+
/** Creates a ReactionsQuery */
|
|
63
|
+
reactions(event) {
|
|
64
|
+
return this.createQuery(Queries.ReactionsQuery, event);
|
|
65
|
+
}
|
|
66
|
+
/** Creates a MailboxesQuery */
|
|
67
|
+
mailboxes(pubkey) {
|
|
68
|
+
return this.createQuery(Queries.MailboxesQuery, pubkey);
|
|
69
|
+
}
|
|
70
|
+
/** Creates a ThreadQuery */
|
|
71
|
+
thread(root) {
|
|
72
|
+
return this.createQuery(Queries.ThreadQuery, root);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export { Queries };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { EventStore } from "../event-store/event-store.js";
|
|
3
|
+
import { QueryStore } from "./query-store.js";
|
|
4
|
+
import { ProfileQuery } from "../queries/profile.js";
|
|
5
|
+
let eventStore;
|
|
6
|
+
let queryStore;
|
|
7
|
+
const event = {
|
|
8
|
+
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":""}',
|
|
9
|
+
created_at: 1738362529,
|
|
10
|
+
id: "e9df8d5898c4ccfbd21fcd59f3f48abb3ff0ab7259b19570e2f1756de1e9306b",
|
|
11
|
+
kind: 0,
|
|
12
|
+
pubkey: "266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5",
|
|
13
|
+
sig: "465a47b93626a587bf81dadc2b306b8f713a62db31d6ce1533198e9ae1e665a6eaf376a03250bf9ffbb02eb9059c8eafbd37ae1092d05d215757575bd8357586",
|
|
14
|
+
tags: [],
|
|
15
|
+
};
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
eventStore = new EventStore();
|
|
18
|
+
queryStore = new QueryStore(eventStore);
|
|
19
|
+
});
|
|
20
|
+
describe("executeQuery", () => {
|
|
21
|
+
it("should resolve when value is already present", async () => {
|
|
22
|
+
eventStore.add(event);
|
|
23
|
+
expect(await queryStore.executeQuery(ProfileQuery, event.pubkey)).toEqual(expect.objectContaining({ name: "hzrd149" }));
|
|
24
|
+
});
|
|
25
|
+
it("should resolve when value is added to event store", async () => {
|
|
26
|
+
const p = queryStore.executeQuery(ProfileQuery, event.pubkey);
|
|
27
|
+
// delay adding the event
|
|
28
|
+
setTimeout(() => {
|
|
29
|
+
eventStore.add(event);
|
|
30
|
+
}, 10);
|
|
31
|
+
await expect(p).resolves.toEqual(expect.objectContaining({ name: "hzrd149" }));
|
|
32
|
+
});
|
|
33
|
+
});
|