applesauce-core 0.1.0 → 0.3.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.
Files changed (50) hide show
  1. package/README.md +39 -0
  2. package/dist/event-store/database.d.ts +57 -13
  3. package/dist/event-store/database.js +52 -15
  4. package/dist/event-store/event-store.d.ts +4 -1
  5. package/dist/event-store/event-store.js +122 -31
  6. package/dist/event-store/index.d.ts +2 -1
  7. package/dist/event-store/index.js +2 -1
  8. package/dist/helpers/channel.d.ts +15 -0
  9. package/dist/helpers/channel.js +27 -0
  10. package/dist/helpers/event.d.ts +12 -0
  11. package/dist/helpers/event.js +13 -8
  12. package/dist/helpers/filter.d.ts +5 -0
  13. package/dist/helpers/filter.js +5 -2
  14. package/dist/helpers/index.d.ts +1 -1
  15. package/dist/helpers/index.js +1 -1
  16. package/dist/helpers/json.d.ts +1 -0
  17. package/dist/helpers/json.js +8 -0
  18. package/dist/helpers/mailboxes.d.ts +14 -0
  19. package/dist/helpers/mailboxes.js +14 -7
  20. package/dist/helpers/mailboxes.test.d.ts +1 -0
  21. package/dist/helpers/mailboxes.test.js +80 -0
  22. package/dist/helpers/mute.d.ts +21 -0
  23. package/dist/helpers/mute.js +52 -0
  24. package/dist/helpers/profile.d.ts +6 -0
  25. package/dist/helpers/profile.js +4 -4
  26. package/dist/helpers/relays.d.ts +6 -0
  27. package/dist/helpers/relays.js +7 -6
  28. package/dist/promise/index.d.ts +1 -1
  29. package/dist/promise/index.js +1 -1
  30. package/dist/query-store/index.d.ts +35 -17
  31. package/dist/query-store/index.js +40 -58
  32. package/dist/query-store/queries/channel.d.ts +11 -0
  33. package/dist/query-store/queries/channel.js +72 -0
  34. package/dist/query-store/queries/index.d.ts +6 -0
  35. package/dist/query-store/queries/index.js +6 -0
  36. package/dist/query-store/queries/mailboxes.d.ts +5 -0
  37. package/dist/query-store/queries/mailboxes.js +11 -0
  38. package/dist/query-store/queries/mute.d.ts +7 -0
  39. package/dist/query-store/queries/mute.js +16 -0
  40. package/dist/query-store/queries/profile.d.ts +3 -0
  41. package/dist/query-store/queries/profile.js +10 -0
  42. package/dist/query-store/queries/reactions.d.ts +4 -0
  43. package/dist/query-store/queries/reactions.js +19 -0
  44. package/dist/query-store/queries/simple.d.ts +5 -0
  45. package/dist/query-store/queries/simple.js +20 -0
  46. package/dist/utils/lru.d.ts +32 -0
  47. package/dist/utils/lru.js +148 -0
  48. package/package.json +19 -3
  49. package/dist/helpers/symbols.d.ts +0 -17
  50. package/dist/helpers/symbols.js +0 -10
@@ -1,3 +1,17 @@
1
1
  import { NostrEvent } from "nostr-tools";
2
+ export declare const MailboxesInboxesSymbol: unique symbol;
3
+ export declare const MailboxesOutboxesSymbol: unique symbol;
4
+ declare module "nostr-tools" {
5
+ interface Event {
6
+ [MailboxesInboxesSymbol]?: Set<string>;
7
+ [MailboxesOutboxesSymbol]?: Set<string>;
8
+ }
9
+ }
10
+ /**
11
+ * Parses a 10002 event and stores the inboxes in the event using the {@link MailboxesInboxesSymbol} symbol
12
+ */
2
13
  export declare function getInboxes(event: NostrEvent): Set<string>;
14
+ /**
15
+ * Parses a 10002 event and stores the outboxes in the event using the {@link MailboxesOutboxesSymbol} symbol
16
+ */
3
17
  export declare function getOutboxes(event: NostrEvent): Set<string>;
@@ -1,7 +1,11 @@
1
1
  import { safeRelayUrl } from "./relays.js";
2
- import { MailboxesInboxes, MailboxesOutboxes } from "./symbols.js";
2
+ export const MailboxesInboxesSymbol = Symbol.for("mailboxes-inboxes");
3
+ export const MailboxesOutboxesSymbol = Symbol.for("mailboxes-outboxes");
4
+ /**
5
+ * Parses a 10002 event and stores the inboxes in the event using the {@link MailboxesInboxesSymbol} symbol
6
+ */
3
7
  export function getInboxes(event) {
4
- if (!event[MailboxesInboxes]) {
8
+ if (!event[MailboxesInboxesSymbol]) {
5
9
  const inboxes = new Set();
6
10
  for (const tag of event.tags) {
7
11
  if (tag[0] === "r" && tag[1] && (tag[2] === "read" || tag[2] === undefined)) {
@@ -10,12 +14,15 @@ export function getInboxes(event) {
10
14
  inboxes.add(url);
11
15
  }
12
16
  }
13
- event[MailboxesInboxes] = inboxes;
17
+ event[MailboxesInboxesSymbol] = inboxes;
14
18
  }
15
- return event[MailboxesInboxes];
19
+ return event[MailboxesInboxesSymbol];
16
20
  }
21
+ /**
22
+ * Parses a 10002 event and stores the outboxes in the event using the {@link MailboxesOutboxesSymbol} symbol
23
+ */
17
24
  export function getOutboxes(event) {
18
- if (!event[MailboxesOutboxes]) {
25
+ if (!event[MailboxesOutboxesSymbol]) {
19
26
  const outboxes = new Set();
20
27
  for (const tag of event.tags) {
21
28
  if (tag[0] === "r" && tag[1] && (tag[2] === "write" || tag[2] === undefined)) {
@@ -24,7 +31,7 @@ export function getOutboxes(event) {
24
31
  outboxes.add(url);
25
32
  }
26
33
  }
27
- event[MailboxesOutboxes] = outboxes;
34
+ event[MailboxesOutboxesSymbol] = outboxes;
28
35
  }
29
- return event[MailboxesOutboxes];
36
+ return event[MailboxesOutboxesSymbol];
30
37
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,80 @@
1
+ import { getInboxes, getOutboxes } from "./mailboxes.js";
2
+ const emptyEvent = {
3
+ kind: 10002,
4
+ content: "",
5
+ tags: [],
6
+ created_at: 0,
7
+ sig: "",
8
+ id: "",
9
+ pubkey: "",
10
+ };
11
+ describe("Mailboxes", () => {
12
+ describe("getInboxes", () => {
13
+ test("should transform urls", () => {
14
+ expect(Array.from(getInboxes({
15
+ ...emptyEvent,
16
+ tags: [["r", "wss://inbox.com"]],
17
+ }))).toIncludeAllMembers(["wss://inbox.com/"]);
18
+ });
19
+ test("should remove bad urls", () => {
20
+ expect(Array.from(getInboxes({
21
+ ...emptyEvent,
22
+ tags: [["r", "bad://inbox.com"]],
23
+ }))).toBeArrayOfSize(0);
24
+ expect(Array.from(getInboxes({
25
+ ...emptyEvent,
26
+ tags: [["r", "something that is not a url"]],
27
+ }))).toBeArrayOfSize(0);
28
+ expect(Array.from(getInboxes({
29
+ ...emptyEvent,
30
+ tags: [["r", "wss://inbox.com,wss://inbox.org"]],
31
+ }))).toBeArrayOfSize(0);
32
+ });
33
+ test("without marker", () => {
34
+ expect(Array.from(getInboxes({
35
+ ...emptyEvent,
36
+ tags: [["r", "wss://inbox.com/"]],
37
+ }))).toIncludeAllMembers(["wss://inbox.com/"]);
38
+ });
39
+ test("with marker", () => {
40
+ expect(Array.from(getInboxes({
41
+ ...emptyEvent,
42
+ tags: [["r", "wss://inbox.com/", "read"]],
43
+ }))).toIncludeAllMembers(["wss://inbox.com/"]);
44
+ });
45
+ });
46
+ describe("getOutboxes", () => {
47
+ test("should transform urls", () => {
48
+ expect(Array.from(getOutboxes({
49
+ ...emptyEvent,
50
+ tags: [["r", "wss://outbox.com"]],
51
+ }))).toIncludeAllMembers(["wss://outbox.com/"]);
52
+ });
53
+ test("should remove bad urls", () => {
54
+ expect(Array.from(getOutboxes({
55
+ ...emptyEvent,
56
+ tags: [["r", "bad://inbox.com"]],
57
+ }))).toBeArrayOfSize(0);
58
+ expect(Array.from(getOutboxes({
59
+ ...emptyEvent,
60
+ tags: [["r", "something that is not a url"]],
61
+ }))).toBeArrayOfSize(0);
62
+ expect(Array.from(getOutboxes({
63
+ ...emptyEvent,
64
+ tags: [["r", "wss://outbox.com,wss://inbox.org"]],
65
+ }))).toBeArrayOfSize(0);
66
+ });
67
+ test("without marker", () => {
68
+ expect(Array.from(getOutboxes({
69
+ ...emptyEvent,
70
+ tags: [["r", "wss://outbox.com/"]],
71
+ }))).toIncludeAllMembers(["wss://outbox.com/"]);
72
+ });
73
+ test("with marker", () => {
74
+ expect(Array.from(getOutboxes({
75
+ ...emptyEvent,
76
+ tags: [["r", "wss://outbox.com/", "write"]],
77
+ }))).toIncludeAllMembers(["wss://outbox.com/"]);
78
+ });
79
+ });
80
+ });
@@ -0,0 +1,21 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ export declare const MutePubkeysSymbol: unique symbol;
3
+ export declare const MuteThreadsSymbol: unique symbol;
4
+ export declare const MuteHashtagsSymbol: unique symbol;
5
+ export declare const MuteWordsSymbol: unique symbol;
6
+ declare module "nostr-tools" {
7
+ interface Event {
8
+ [MutePubkeysSymbol]?: Set<string>;
9
+ [MuteThreadsSymbol]?: Set<string>;
10
+ [MuteHashtagsSymbol]?: Set<string>;
11
+ [MuteWordsSymbol]?: Set<string>;
12
+ }
13
+ }
14
+ /** Returns a set of muted pubkeys */
15
+ export declare function getMutedPubkeys(mute: NostrEvent): Set<string>;
16
+ /** Returns a set of muted threads */
17
+ export declare function getMutedThreads(mute: NostrEvent): Set<string>;
18
+ /** Returns a set of muted words ( lowercase ) */
19
+ export declare function getMutedWords(mute: NostrEvent): Set<string>;
20
+ /** Returns a set of muted hashtags ( lowercase ) */
21
+ export declare function getMutedHashtags(mute: NostrEvent): Set<string>;
@@ -0,0 +1,52 @@
1
+ export const MutePubkeysSymbol = Symbol.for("mute-pubkeys");
2
+ export const MuteThreadsSymbol = Symbol.for("mute-threads");
3
+ export const MuteHashtagsSymbol = Symbol.for("mute-hashtags");
4
+ export const MuteWordsSymbol = Symbol.for("mute-words");
5
+ /** Returns a set of muted pubkeys */
6
+ export function getMutedPubkeys(mute) {
7
+ let pubkeys = mute[MutePubkeysSymbol];
8
+ if (!pubkeys) {
9
+ pubkeys = mute[MutePubkeysSymbol] = new Set();
10
+ for (const tag of mute.tags) {
11
+ if (tag[0] === "p" && tag[1])
12
+ pubkeys.add(tag[1]);
13
+ }
14
+ }
15
+ return pubkeys;
16
+ }
17
+ /** Returns a set of muted threads */
18
+ export function getMutedThreads(mute) {
19
+ let threads = mute[MuteThreadsSymbol];
20
+ if (!threads) {
21
+ threads = mute[MuteThreadsSymbol] = new Set();
22
+ for (const tag of mute.tags) {
23
+ if (tag[0] === "e" && tag[1])
24
+ threads.add(tag[1]);
25
+ }
26
+ }
27
+ return threads;
28
+ }
29
+ /** Returns a set of muted words ( lowercase ) */
30
+ export function getMutedWords(mute) {
31
+ let words = mute[MuteWordsSymbol];
32
+ if (!words) {
33
+ words = mute[MuteWordsSymbol] = new Set();
34
+ for (const tag of mute.tags) {
35
+ if (tag[0] === "word" && tag[1])
36
+ words.add(tag[1].toLocaleLowerCase());
37
+ }
38
+ }
39
+ return words;
40
+ }
41
+ /** Returns a set of muted hashtags ( lowercase ) */
42
+ export function getMutedHashtags(mute) {
43
+ let hashtags = mute[MuteHashtagsSymbol];
44
+ if (!hashtags) {
45
+ hashtags = mute[MuteHashtagsSymbol] = new Set();
46
+ for (const tag of mute.tags) {
47
+ if (tag[0] === "t" && tag[1])
48
+ hashtags.add(tag[1].toLocaleLowerCase());
49
+ }
50
+ }
51
+ return hashtags;
52
+ }
@@ -1,4 +1,10 @@
1
1
  import { NostrEvent } from "nostr-tools";
2
+ export declare const ProfileContentSymbol: unique symbol;
3
+ declare module "nostr-tools" {
4
+ interface Event {
5
+ [ProfileContentSymbol]?: ProfileContent | Error;
6
+ }
7
+ }
2
8
  export type ProfileContent = {
3
9
  name?: string;
4
10
  display_name?: string;
@@ -1,17 +1,17 @@
1
- import { ProfileContent } from "./symbols.js";
1
+ export const ProfileContentSymbol = Symbol.for("profile-content");
2
2
  export function getProfileContent(event, quite = false) {
3
- let cached = event[ProfileContent];
3
+ let cached = event[ProfileContentSymbol];
4
4
  if (!cached) {
5
5
  try {
6
6
  const profile = JSON.parse(event.content);
7
7
  // ensure nip05 is a string
8
8
  if (profile.nip05 && typeof profile.nip05 !== "string")
9
9
  profile.nip05 = String(profile.nip05);
10
- cached = event[ProfileContent] = profile;
10
+ cached = event[ProfileContentSymbol] = profile;
11
11
  }
12
12
  catch (e) {
13
13
  if (e instanceof Error)
14
- cached = event[ProfileContent] = e;
14
+ cached = event[ProfileContentSymbol] = e;
15
15
  }
16
16
  }
17
17
  if (cached === undefined) {
@@ -1,4 +1,10 @@
1
1
  import { NostrEvent } from "nostr-tools";
2
+ export declare const SeenRelaysSymbol: unique symbol;
3
+ declare module "nostr-tools" {
4
+ interface Event {
5
+ [SeenRelaysSymbol]?: Set<string>;
6
+ }
7
+ }
2
8
  export declare function addSeenRelay(event: NostrEvent, relay: string): Set<string>;
3
9
  export declare function getSeenRelays(event: NostrEvent): Set<string> | undefined;
4
10
  export declare function validateRelayURL(relay: string | URL): URL;
@@ -1,12 +1,13 @@
1
- import { FromRelays } from "./symbols.js";
1
+ export const SeenRelaysSymbol = Symbol.for("seen-relays");
2
+ // Seen relays
2
3
  export function addSeenRelay(event, relay) {
3
- if (!event[FromRelays])
4
- event[FromRelays] = new Set();
5
- event[FromRelays].add(relay);
6
- return event[FromRelays];
4
+ if (!event[SeenRelaysSymbol])
5
+ event[SeenRelaysSymbol] = new Set();
6
+ event[SeenRelaysSymbol].add(relay);
7
+ return event[SeenRelaysSymbol];
7
8
  }
8
9
  export function getSeenRelays(event) {
9
- return event[FromRelays];
10
+ return event[SeenRelaysSymbol];
10
11
  }
11
12
  // Relay URLs
12
13
  export function validateRelayURL(relay) {
@@ -1 +1 @@
1
- export * from './deferred.js';
1
+ export * from "./deferred.js";
@@ -1 +1 @@
1
- export * from './deferred.js';
1
+ export * from "./deferred.js";
@@ -1,26 +1,44 @@
1
1
  import Observable from "zen-observable";
2
- import { LRU } from "tiny-lru";
3
2
  import { Filter, NostrEvent } from "nostr-tools";
4
3
  import { EventStore } from "../event-store/event-store.js";
5
- import { ProfileContent } from "../helpers/profile.js";
4
+ import { LRU } from "../utils/lru.js";
5
+ import * as Queries from "./queries/index.js";
6
+ export type Query<T extends unknown> = {
7
+ key: string;
8
+ run: (events: EventStore, store: QueryStore) => Observable<T>;
9
+ };
10
+ export type QueryConstructor<T extends unknown, Args extends Array<any>> = (...args: Args) => Query<T>;
6
11
  export declare class QueryStore {
12
+ static Queries: typeof Queries;
7
13
  store: EventStore;
8
14
  constructor(store: EventStore);
9
- singleEvents: LRU<Observable<import("nostr-tools").Event>>;
10
- getEvent(id: string): Observable<import("nostr-tools").Event>;
11
- getReplaceable(kind: number, pubkey: string, d?: string): Observable<import("nostr-tools").Event>;
12
- timelines: LRU<Observable<import("nostr-tools").Event[]>>;
13
- getTimeline(filters: Filter[]): Observable<import("nostr-tools").Event[]>;
14
- profiles: LRU<Observable<ProfileContent>>;
15
- getProfile(pubkey: string): Observable<ProfileContent>;
16
- reactions: LRU<Observable<import("nostr-tools").Event[]>>;
17
- getReactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
18
- mailboxes: LRU<Observable<{
15
+ queries: LRU<Observable<any>>;
16
+ /** Creates a cached query */
17
+ runQuery<T extends unknown, Args extends Array<any>>(queryConstructor: (...args: Args) => {
18
+ key: string;
19
+ run: (events: EventStore, store: QueryStore) => Observable<T>;
20
+ }): (...args: Args) => Observable<T>;
21
+ /** Returns a single event */
22
+ event(id: string): Observable<import("nostr-tools").Event | undefined>;
23
+ /** Returns the latest version of a replaceable event */
24
+ replaceable(kind: number, pubkey: string, d?: string): Observable<import("nostr-tools").Event | undefined>;
25
+ /** Returns an array of events that match the filter */
26
+ timeline(filters: Filter | Filter[]): Observable<import("nostr-tools").Event[]>;
27
+ /** Returns the parsed profile (0) for a pubkey */
28
+ profile(pubkey: string): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
29
+ /** Returns all reactions for an event (supports replaceable events) */
30
+ reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
31
+ /** Returns the parsed relay list (10002) for the pubkey */
32
+ mailboxes(pubkey: string): Observable<{
19
33
  inboxes: Set<string>;
20
34
  outboxes: Set<string>;
21
- }>>;
22
- getMailboxes(pubkey: string): Observable<{
23
- inboxes: Set<string>;
24
- outboxes: Set<string>;
25
- }>;
35
+ } | undefined>;
36
+ /** Returns the parsed mute list for the pubkey */
37
+ mute(pubkey: string): Observable<{
38
+ words: Set<string>;
39
+ pubkeys: Set<string>;
40
+ threads: Set<string>;
41
+ hashtags: Set<string>;
42
+ } | undefined>;
26
43
  }
44
+ export { Queries };
@@ -1,71 +1,53 @@
1
- import { LRU } from "tiny-lru";
2
- import { kinds } from "nostr-tools";
3
- import stringify from "json-stringify-deterministic";
4
1
  import { stateful } from "../observable/stateful.js";
5
- import { getProfileContent } from "../helpers/profile.js";
6
- import { getEventUID, getReplaceableUID, isReplaceable } from "../helpers/event.js";
7
- import { getInboxes, getOutboxes } from "../helpers/mailboxes.js";
2
+ import { LRU } from "../utils/lru.js";
3
+ import * as Queries from "./queries/index.js";
8
4
  export class QueryStore {
5
+ static Queries = Queries;
9
6
  store;
10
7
  constructor(store) {
11
8
  this.store = store;
12
9
  }
13
- singleEvents = new LRU();
14
- getEvent(id) {
15
- if (!this.singleEvents.has(id)) {
16
- const observable = stateful(this.store.single(id));
17
- this.singleEvents.set(id, observable);
18
- }
19
- return this.singleEvents.get(id);
10
+ queries = new LRU();
11
+ /** Creates a cached query */
12
+ runQuery(queryConstructor) {
13
+ return (...args) => {
14
+ const query = queryConstructor(...args);
15
+ const key = `${queryConstructor.name}|${query.key}`;
16
+ if (!this.queries.has(key)) {
17
+ const observable = stateful(query.run(this.store, this));
18
+ this.queries.set(key, observable);
19
+ return observable;
20
+ }
21
+ return this.queries.get(key);
22
+ };
20
23
  }
21
- getReplaceable(kind, pubkey, d) {
22
- return this.getEvent(getReplaceableUID(kind, pubkey, d));
24
+ /** Returns a single event */
25
+ event(id) {
26
+ return this.runQuery(Queries.SingleEventQuery)(id);
23
27
  }
24
- timelines = new LRU();
25
- getTimeline(filters) {
26
- const key = stringify(filters);
27
- if (!this.singleEvents.has(key)) {
28
- const observable = stateful(this.store.timeline(filters));
29
- this.timelines.set(key, observable);
30
- }
31
- return this.timelines.get(key);
28
+ /** Returns the latest version of a replaceable event */
29
+ replaceable(kind, pubkey, d) {
30
+ return this.runQuery(Queries.ReplaceableQuery)(kind, pubkey, d);
32
31
  }
33
- profiles = new LRU();
34
- getProfile(pubkey) {
35
- if (!this.profiles.has(pubkey)) {
36
- const observable = stateful(this.getReplaceable(kinds.Metadata, pubkey).map((event) => getProfileContent(event)));
37
- this.profiles.set(pubkey, observable);
38
- }
39
- return this.profiles.get(pubkey);
32
+ /** Returns an array of events that match the filter */
33
+ timeline(filters) {
34
+ return this.runQuery(Queries.TimelineQuery)(filters);
40
35
  }
41
- reactions = new LRU();
42
- getReactions(event) {
43
- const uid = getEventUID(event);
44
- if (!this.reactions.has(uid)) {
45
- const observable = this.getTimeline(isReplaceable(event.kind)
46
- ? [
47
- { kinds: [kinds.Reaction], "#e": [event.id] },
48
- { kinds: [kinds.Reaction], "#a": [uid] },
49
- ]
50
- : [
51
- {
52
- kinds: [kinds.Reaction],
53
- "#e": [event.id],
54
- },
55
- ]);
56
- this.reactions.set(uid, observable);
57
- }
58
- return this.reactions.get(uid);
36
+ /** Returns the parsed profile (0) for a pubkey */
37
+ profile(pubkey) {
38
+ return this.runQuery(Queries.ProfileQuery)(pubkey);
59
39
  }
60
- mailboxes = new LRU();
61
- getMailboxes(pubkey) {
62
- if (!this.mailboxes.has(pubkey)) {
63
- const observable = stateful(this.getReplaceable(kinds.RelayList, pubkey).map((event) => ({
64
- inboxes: getInboxes(event),
65
- outboxes: getOutboxes(event),
66
- })));
67
- this.mailboxes.set(pubkey, observable);
68
- }
69
- return this.mailboxes.get(pubkey);
40
+ /** Returns all reactions for an event (supports replaceable events) */
41
+ reactions(event) {
42
+ return this.runQuery(Queries.ReactionsQuery)(event);
43
+ }
44
+ /** Returns the parsed relay list (10002) for the pubkey */
45
+ mailboxes(pubkey) {
46
+ return this.runQuery(Queries.MailboxesQuery)(pubkey);
47
+ }
48
+ /** Returns the parsed mute list for the pubkey */
49
+ mute(pubkey) {
50
+ return this.runQuery(Queries.UserMuteQuery)(pubkey);
70
51
  }
71
52
  }
53
+ export { Queries };
@@ -0,0 +1,11 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { Query } from "../index.js";
3
+ import { ChannelMetadataContent } from "../../helpers/channel.js";
4
+ /** Creates a query that returns the latest parsed metadata */
5
+ export declare function ChannelMetadataQuery(channel: NostrEvent): Query<ChannelMetadataContent | undefined>;
6
+ /** Creates a query that returns a map of hidden messages Map<id, reason> */
7
+ export declare function ChannelHiddenQuery(channel: NostrEvent, authors?: string[]): Query<Map<string, string>>;
8
+ /** Creates a query that returns a map of muted users Map<pubkey, reason> */
9
+ export declare function ChannelMutedQuery(channel: NostrEvent, authors?: string[]): Query<Map<string, string>>;
10
+ /** Creates a query that returns all messages in a channel */
11
+ export declare function ChannelMessagesQuery(channel: NostrEvent): Query<NostrEvent[]>;
@@ -0,0 +1,72 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { getChannelMetadataContent } from "../../helpers/channel.js";
3
+ import { safeParse } from "../../helpers/json.js";
4
+ /** Creates a query that returns the latest parsed metadata */
5
+ export function ChannelMetadataQuery(channel) {
6
+ return {
7
+ key: channel.id,
8
+ run: (events) => {
9
+ const filters = [
10
+ { ids: [channel.id] },
11
+ { kinds: [kinds.ChannelMetadata], "#e": [channel.id], authors: [channel.pubkey] },
12
+ ];
13
+ let latest = channel;
14
+ return events.stream(filters).map((event) => {
15
+ try {
16
+ if (event.pubkey === latest.pubkey && event.created_at > latest.created_at) {
17
+ latest = event;
18
+ }
19
+ return getChannelMetadataContent(latest);
20
+ }
21
+ catch (error) {
22
+ return undefined;
23
+ }
24
+ });
25
+ },
26
+ };
27
+ }
28
+ /** Creates a query that returns a map of hidden messages Map<id, reason> */
29
+ export function ChannelHiddenQuery(channel, authors = []) {
30
+ return {
31
+ key: channel.id,
32
+ run: (events) => {
33
+ const hidden = new Map();
34
+ return events
35
+ .stream([{ kinds: [kinds.ChannelHideMessage], "#e": [channel.id], authors: [channel.pubkey, ...authors] }])
36
+ .map((event) => {
37
+ const reason = safeParse(event.content)?.reason;
38
+ for (const tag of event.tags) {
39
+ if (tag[0] === "e" && tag[1])
40
+ hidden.set(tag[1], reason ?? "");
41
+ }
42
+ return hidden;
43
+ });
44
+ },
45
+ };
46
+ }
47
+ /** Creates a query that returns a map of muted users Map<pubkey, reason> */
48
+ export function ChannelMutedQuery(channel, authors = []) {
49
+ return {
50
+ key: channel.id + authors.join(","),
51
+ run: (events) => {
52
+ const muted = new Map();
53
+ return events
54
+ .stream([{ kinds: [kinds.ChannelMuteUser], "#e": [channel.id], authors: [channel.pubkey, ...authors] }])
55
+ .map((event) => {
56
+ const reason = safeParse(event.content)?.reason;
57
+ for (const tag of event.tags) {
58
+ if (tag[0] === "p" && tag[1])
59
+ muted.set(tag[1], reason ?? "");
60
+ }
61
+ return muted;
62
+ });
63
+ },
64
+ };
65
+ }
66
+ /** Creates a query that returns all messages in a channel */
67
+ export function ChannelMessagesQuery(channel) {
68
+ return {
69
+ key: channel.id,
70
+ run: (events) => events.timeline([{ kinds: [kinds.ChannelMessage], "#e": [channel.id] }]),
71
+ };
72
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./simple.js";
2
+ export * from "./profile.js";
3
+ export * from "./mailboxes.js";
4
+ export * from "./reactions.js";
5
+ export * from "./channel.js";
6
+ export * from "./mute.js";
@@ -0,0 +1,6 @@
1
+ export * from "./simple.js";
2
+ export * from "./profile.js";
3
+ export * from "./mailboxes.js";
4
+ export * from "./reactions.js";
5
+ export * from "./channel.js";
6
+ export * from "./mute.js";
@@ -0,0 +1,5 @@
1
+ import { Query } from "../index.js";
2
+ export declare function MailboxesQuery(pubkey: string): Query<{
3
+ inboxes: Set<string>;
4
+ outboxes: Set<string>;
5
+ } | undefined>;
@@ -0,0 +1,11 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { getInboxes, getOutboxes } from "../../helpers/mailboxes.js";
3
+ export function MailboxesQuery(pubkey) {
4
+ return {
5
+ key: pubkey,
6
+ run: (events) => events.replaceable(kinds.RelayList, pubkey).map((event) => event && {
7
+ inboxes: getInboxes(event),
8
+ outboxes: getOutboxes(event),
9
+ }),
10
+ };
11
+ }
@@ -0,0 +1,7 @@
1
+ import { Query } from "../index.js";
2
+ export declare function UserMuteQuery(pubkey: string): Query<{
3
+ words: Set<string>;
4
+ pubkeys: Set<string>;
5
+ threads: Set<string>;
6
+ hashtags: Set<string>;
7
+ } | undefined>;
@@ -0,0 +1,16 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { getMutedHashtags, getMutedPubkeys, getMutedThreads, getMutedWords } from "../../helpers/mute.js";
3
+ export function UserMuteQuery(pubkey) {
4
+ return {
5
+ key: pubkey,
6
+ run: (store) => store.replaceable(kinds.Mutelist, pubkey).map((event) => {
7
+ if (!event)
8
+ return;
9
+ const pubkeys = getMutedPubkeys(event);
10
+ const threads = getMutedThreads(event);
11
+ const hashtags = getMutedHashtags(event);
12
+ const words = getMutedWords(event);
13
+ return { pubkeys, threads, hashtags, words };
14
+ }),
15
+ };
16
+ }
@@ -0,0 +1,3 @@
1
+ import { ProfileContent } from "../../helpers/profile.js";
2
+ import { Query } from "../index.js";
3
+ export declare function ProfileQuery(pubkey: string): Query<ProfileContent | undefined>;
@@ -0,0 +1,10 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { getProfileContent } from "../../helpers/profile.js";
3
+ export function ProfileQuery(pubkey) {
4
+ return {
5
+ key: pubkey,
6
+ run: (events) => {
7
+ return events.replaceable(kinds.Metadata, pubkey).map((event) => event && getProfileContent(event));
8
+ },
9
+ };
10
+ }
@@ -0,0 +1,4 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { Query } from "../index.js";
3
+ /** Creates a query that returns all reactions to an event (supports replaceable events) */
4
+ export declare function ReactionsQuery(event: NostrEvent): Query<NostrEvent[]>;