applesauce-core 0.9.0 → 0.11.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 (153) hide show
  1. package/README.md +1 -1
  2. package/dist/__tests__/fixtures.d.ts +8 -0
  3. package/dist/__tests__/fixtures.js +20 -0
  4. package/dist/event-store/__tests__/event-store.test.js +259 -0
  5. package/dist/event-store/database.d.ts +22 -16
  6. package/dist/event-store/database.js +62 -39
  7. package/dist/event-store/event-store.d.ts +52 -15
  8. package/dist/event-store/event-store.js +283 -191
  9. package/dist/helpers/__tests__/blossom.test.d.ts +1 -0
  10. package/dist/helpers/__tests__/blossom.test.js +13 -0
  11. package/dist/helpers/__tests__/comment.test.d.ts +1 -0
  12. package/dist/helpers/__tests__/comment.test.js +235 -0
  13. package/dist/helpers/__tests__/emoji.test.d.ts +1 -0
  14. package/dist/helpers/__tests__/emoji.test.js +15 -0
  15. package/dist/helpers/__tests__/event.test.d.ts +1 -0
  16. package/dist/helpers/__tests__/event.test.js +36 -0
  17. package/dist/helpers/__tests__/file-metadata.test.d.ts +1 -0
  18. package/dist/helpers/__tests__/file-metadata.test.js +103 -0
  19. package/dist/helpers/__tests__/hidden-tags.test.d.ts +1 -0
  20. package/dist/helpers/__tests__/hidden-tags.test.js +29 -0
  21. package/dist/helpers/__tests__/mailboxes.test.d.ts +1 -0
  22. package/dist/helpers/{mailboxes.test.js → __tests__/mailboxes.test.js} +14 -13
  23. package/dist/helpers/__tests__/relays.test.d.ts +1 -0
  24. package/dist/helpers/__tests__/relays.test.js +21 -0
  25. package/dist/helpers/__tests__/tags.test.d.ts +1 -0
  26. package/dist/helpers/__tests__/tags.test.js +24 -0
  27. package/dist/helpers/__tests__/threading.test.d.ts +1 -0
  28. package/dist/helpers/__tests__/threading.test.js +41 -0
  29. package/dist/helpers/blossom.d.ts +9 -0
  30. package/dist/helpers/blossom.js +22 -0
  31. package/dist/helpers/bolt11.d.ts +1 -0
  32. package/dist/helpers/bolt11.js +1 -0
  33. package/dist/helpers/bookmarks.d.ts +15 -0
  34. package/dist/helpers/bookmarks.js +27 -0
  35. package/dist/helpers/channels.d.ts +10 -0
  36. package/dist/helpers/channels.js +27 -0
  37. package/dist/helpers/comment.d.ts +47 -0
  38. package/dist/helpers/comment.js +120 -0
  39. package/dist/helpers/contacts.d.ts +3 -0
  40. package/dist/helpers/contacts.js +25 -0
  41. package/dist/helpers/content.d.ts +3 -0
  42. package/dist/helpers/content.js +8 -0
  43. package/dist/helpers/delete.d.ts +3 -0
  44. package/dist/helpers/delete.js +7 -0
  45. package/dist/helpers/dns-identity.d.ts +7 -0
  46. package/dist/helpers/dns-identity.js +10 -0
  47. package/dist/helpers/emoji.d.ts +12 -1
  48. package/dist/helpers/emoji.js +13 -1
  49. package/dist/helpers/event.d.ts +17 -3
  50. package/dist/helpers/event.js +54 -12
  51. package/dist/helpers/external-id.d.ts +29 -0
  52. package/dist/helpers/external-id.js +20 -0
  53. package/dist/helpers/file-metadata.d.ts +55 -0
  54. package/dist/helpers/file-metadata.js +99 -0
  55. package/dist/helpers/filter.d.ts +4 -2
  56. package/dist/helpers/filter.js +36 -7
  57. package/dist/helpers/groups.d.ts +24 -0
  58. package/dist/helpers/groups.js +39 -0
  59. package/dist/helpers/hidden-tags.d.ts +48 -0
  60. package/dist/helpers/hidden-tags.js +86 -0
  61. package/dist/helpers/index.d.ts +28 -8
  62. package/dist/helpers/index.js +28 -8
  63. package/dist/helpers/json.d.ts +1 -0
  64. package/dist/helpers/json.js +1 -0
  65. package/dist/helpers/lists.d.ts +28 -0
  66. package/dist/helpers/lists.js +65 -0
  67. package/dist/helpers/lnurl.d.ts +4 -0
  68. package/dist/helpers/lnurl.js +40 -0
  69. package/dist/helpers/mailboxes.js +16 -9
  70. package/dist/helpers/mutes.d.ts +14 -0
  71. package/dist/helpers/mutes.js +23 -0
  72. package/dist/helpers/picture-post.d.ts +4 -0
  73. package/dist/helpers/picture-post.js +6 -0
  74. package/dist/helpers/pointers.d.ts +38 -5
  75. package/dist/helpers/pointers.js +105 -25
  76. package/dist/helpers/profile.d.ts +6 -1
  77. package/dist/helpers/profile.js +5 -1
  78. package/dist/helpers/relays.d.ts +6 -3
  79. package/dist/helpers/relays.js +25 -18
  80. package/dist/helpers/share.d.ts +4 -0
  81. package/dist/helpers/share.js +12 -0
  82. package/dist/helpers/string.d.ts +6 -0
  83. package/dist/helpers/string.js +2 -0
  84. package/dist/helpers/tags.d.ts +23 -0
  85. package/dist/helpers/tags.js +34 -6
  86. package/dist/helpers/threading.d.ts +6 -6
  87. package/dist/helpers/threading.js +30 -9
  88. package/dist/helpers/url.d.ts +11 -1
  89. package/dist/helpers/url.js +31 -3
  90. package/dist/helpers/user-status.d.ts +18 -0
  91. package/dist/helpers/user-status.js +21 -0
  92. package/dist/helpers/zap.d.ts +25 -0
  93. package/dist/helpers/zap.js +32 -3
  94. package/dist/observable/__tests__/claim-events.test.d.ts +1 -0
  95. package/dist/observable/__tests__/claim-events.test.js +23 -0
  96. package/dist/observable/__tests__/claim-latest.test.d.ts +1 -0
  97. package/dist/observable/__tests__/claim-latest.test.js +37 -0
  98. package/dist/observable/__tests__/simple-timeout.test.d.ts +1 -0
  99. package/dist/observable/__tests__/simple-timeout.test.js +34 -0
  100. package/dist/observable/claim-events.d.ts +5 -0
  101. package/dist/observable/claim-events.js +28 -0
  102. package/dist/observable/claim-latest.d.ts +4 -0
  103. package/dist/observable/claim-latest.js +20 -0
  104. package/dist/observable/get-observable-value.d.ts +3 -0
  105. package/dist/observable/get-observable-value.js +9 -0
  106. package/dist/observable/index.d.ts +2 -1
  107. package/dist/observable/index.js +2 -1
  108. package/dist/observable/share-latest-value.d.ts +2 -4
  109. package/dist/observable/share-latest-value.js +19 -16
  110. package/dist/observable/simple-timeout.d.ts +4 -0
  111. package/dist/observable/simple-timeout.js +6 -0
  112. package/dist/promise/deferred.d.ts +1 -0
  113. package/dist/promise/deferred.js +1 -0
  114. package/dist/queries/blossom.d.ts +2 -0
  115. package/dist/queries/blossom.js +10 -0
  116. package/dist/queries/bookmarks.d.ts +8 -0
  117. package/dist/queries/bookmarks.js +23 -0
  118. package/dist/queries/channels.d.ts +11 -0
  119. package/dist/queries/channels.js +73 -0
  120. package/dist/queries/comments.d.ts +4 -0
  121. package/dist/queries/comments.js +14 -0
  122. package/dist/queries/contacts.d.ts +3 -0
  123. package/dist/queries/contacts.js +12 -0
  124. package/dist/queries/index.d.ts +9 -2
  125. package/dist/queries/index.js +9 -2
  126. package/dist/queries/mailboxes.d.ts +1 -0
  127. package/dist/queries/mailboxes.js +1 -0
  128. package/dist/queries/mutes.d.ts +8 -0
  129. package/dist/queries/mutes.js +23 -0
  130. package/dist/queries/pins.d.ts +3 -0
  131. package/dist/queries/pins.js +12 -0
  132. package/dist/queries/profile.d.ts +1 -0
  133. package/dist/queries/profile.js +1 -0
  134. package/dist/queries/reactions.d.ts +1 -1
  135. package/dist/queries/reactions.js +1 -1
  136. package/dist/queries/simple.d.ts +4 -4
  137. package/dist/queries/simple.js +13 -13
  138. package/dist/queries/thread.d.ts +2 -0
  139. package/dist/queries/thread.js +30 -4
  140. package/dist/queries/user-status.d.ts +11 -0
  141. package/dist/queries/user-status.js +39 -0
  142. package/dist/queries/zaps.d.ts +1 -0
  143. package/dist/queries/zaps.js +1 -0
  144. package/dist/query-store/index.d.ts +1 -47
  145. package/dist/query-store/index.js +1 -60
  146. package/dist/query-store/query-store.d.ts +51 -0
  147. package/dist/query-store/query-store.js +88 -0
  148. package/dist/query-store/query-store.test.d.ts +1 -0
  149. package/dist/query-store/query-store.test.js +33 -0
  150. package/package.json +24 -21
  151. package/dist/observable/getValue.d.ts +0 -2
  152. package/dist/observable/getValue.js +0 -13
  153. /package/dist/{helpers/mailboxes.test.d.ts → event-store/__tests__/event-store.test.d.ts} +0 -0
@@ -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,4 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { Query } from "../query-store/index.js";
3
+ /** Returns all NIP-22 comment replies for the event */
4
+ export declare function CommentsQuery(parent: NostrEvent): Query<NostrEvent[]>;
@@ -0,0 +1,14 @@
1
+ import { COMMENT_KIND, getEventUID } from "../helpers/index.js";
2
+ import { isParameterizedReplaceableKind } from "nostr-tools/kinds";
3
+ /** Returns all NIP-22 comment replies for the event */
4
+ export function CommentsQuery(parent) {
5
+ return {
6
+ key: `${getEventUID(parent)}-comments`,
7
+ run: (events) => {
8
+ const filter = { kinds: [COMMENT_KIND], "#e": [parent.id] };
9
+ if (isParameterizedReplaceableKind(parent.kind))
10
+ filter["#a"] = [getEventUID(parent)];
11
+ return events.timeline(filter);
12
+ },
13
+ };
14
+ }
@@ -0,0 +1,3 @@
1
+ import { ProfilePointer } from "nostr-tools/nip19";
2
+ import { Query } from "../query-store/index.js";
3
+ export declare function UserContactsQuery(pubkey: string): Query<ProfilePointer[] | undefined>;
@@ -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
+ }
@@ -1,6 +1,13 @@
1
- export * from "./simple.js";
2
- export * from "./profile.js";
1
+ export * from "./blossom.js";
2
+ export * from "./bookmarks.js";
3
+ export * from "./channels.js";
4
+ export * from "./comments.js";
5
+ export * from "./contacts.js";
3
6
  export * from "./mailboxes.js";
7
+ export * from "./mutes.js";
8
+ export * from "./pins.js";
9
+ export * from "./profile.js";
4
10
  export * from "./reactions.js";
11
+ export * from "./simple.js";
5
12
  export * from "./thread.js";
6
13
  export * from "./zaps.js";
@@ -1,6 +1,13 @@
1
- export * from "./simple.js";
2
- export * from "./profile.js";
1
+ export * from "./blossom.js";
2
+ export * from "./bookmarks.js";
3
+ export * from "./channels.js";
4
+ export * from "./comments.js";
5
+ export * from "./contacts.js";
3
6
  export * from "./mailboxes.js";
7
+ export * from "./mutes.js";
8
+ export * from "./pins.js";
9
+ export * from "./profile.js";
4
10
  export * from "./reactions.js";
11
+ export * from "./simple.js";
5
12
  export * from "./thread.js";
6
13
  export * from "./zaps.js";
@@ -1,4 +1,5 @@
1
1
  import { Query } from "../query-store/index.js";
2
+ /** A query that gets and parses the inbox and outbox relays for a pubkey */
2
3
  export declare function MailboxesQuery(pubkey: string): Query<{
3
4
  inboxes: string[];
4
5
  outboxes: string[];
@@ -1,6 +1,7 @@
1
1
  import { kinds } from "nostr-tools";
2
2
  import { map } from "rxjs/operators";
3
3
  import { getInboxes, getOutboxes } from "../helpers/mailboxes.js";
4
+ /** A query that gets and parses the inbox and outbox relays for a pubkey */
4
5
  export function MailboxesQuery(pubkey) {
5
6
  return {
6
7
  key: pubkey,
@@ -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: (store) => store.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,3 @@
1
+ import { Query } from "applesauce-core";
2
+ import { EventPointer } from "nostr-tools/nip19";
3
+ export declare function UserPinnedQuery(pubkey: string): Query<EventPointer[] | undefined>;
@@ -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
+ }
@@ -1,3 +1,4 @@
1
1
  import { ProfileContent } from "../helpers/profile.js";
2
2
  import { Query } from "../query-store/index.js";
3
+ /** A query that gets and parses the kind 0 metadata for a pubkey */
3
4
  export declare function ProfileQuery(pubkey: string): Query<ProfileContent | undefined>;
@@ -1,6 +1,7 @@
1
1
  import { kinds } from "nostr-tools";
2
2
  import { filter, map } from "rxjs/operators";
3
3
  import { getProfileContent, isValidProfile } from "../helpers/profile.js";
4
+ /** A query that gets and parses the kind 0 metadata for a pubkey */
4
5
  export function ProfileQuery(pubkey) {
5
6
  return {
6
7
  key: pubkey,
@@ -1,4 +1,4 @@
1
1
  import { NostrEvent } from "nostr-tools";
2
2
  import { Query } from "../query-store/index.js";
3
- /** Creates a query that returns all reactions to an event (supports replaceable events) */
3
+ /** A query that returns all reactions to an event (supports replaceable events) */
4
4
  export declare function ReactionsQuery(event: NostrEvent): Query<NostrEvent[]>;
@@ -1,6 +1,6 @@
1
1
  import { kinds } from "nostr-tools";
2
2
  import { getEventUID, isReplaceable } from "../helpers/event.js";
3
- /** Creates a query that returns all reactions to an event (supports replaceable events) */
3
+ /** A query that returns all reactions to an event (supports replaceable events) */
4
4
  export function ReactionsQuery(event) {
5
5
  return {
6
6
  key: getEventUID(event),
@@ -1,16 +1,16 @@
1
1
  import { Filter, NostrEvent } from "nostr-tools";
2
2
  import { Query } from "../query-store/index.js";
3
3
  /** Creates a Query that returns a single event or undefined */
4
- export declare function SingleEventQuery(uid: string): Query<NostrEvent | undefined>;
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(uids: string[]): Query<Map<string, NostrEvent>>;
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[]): Query<NostrEvent[]>;
10
+ export declare function TimelineQuery(filters: Filter | Filter[], keepOldVersions?: 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<Map<string, NostrEvent>>;
16
+ }[]): Query<Record<string, NostrEvent>>;
@@ -1,17 +1,17 @@
1
- import stringify from "json-stringify-deterministic";
1
+ import hash_sum from "hash-sum";
2
2
  import { getReplaceableUID } from "../helpers/event.js";
3
3
  /** Creates a Query that returns a single event or undefined */
4
- export function SingleEventQuery(uid) {
4
+ export function SingleEventQuery(id) {
5
5
  return {
6
- key: uid,
7
- run: (events) => events.event(uid),
6
+ key: id,
7
+ run: (events) => events.event(id),
8
8
  };
9
9
  }
10
10
  /** Creates a Query that returns a multiple events in a map */
11
- export function MultipleEventsQuery(uids) {
11
+ export function MultipleEventsQuery(ids) {
12
12
  return {
13
- key: uids.join(","),
14
- run: (events) => events.events(uids),
13
+ key: ids.join(","),
14
+ run: (events) => events.events(ids),
15
15
  };
16
16
  }
17
17
  /** Creates a Query returning the latest version of a replaceable event */
@@ -22,17 +22,17 @@ 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, keepOldVersions) {
26
+ filters = Array.isArray(filters) ? filters : [filters];
26
27
  return {
27
- key: stringify(filters),
28
- run: (events) => events.timeline(Array.isArray(filters) ? filters : [filters]),
28
+ key: hash_sum(filters) + (keepOldVersions ? "-history" : ""),
29
+ run: (events) => events.timeline(filters, keepOldVersions),
29
30
  };
30
31
  }
31
32
  /** Creates a Query that returns a directory of events by their UID */
32
33
  export function ReplaceableSetQuery(pointers) {
33
- const cords = pointers.map((pointer) => getReplaceableUID(pointer.kind, pointer.pubkey, pointer.identifier));
34
34
  return {
35
- key: stringify(pointers),
36
- run: (events) => events.events(cords),
35
+ key: hash_sum(pointers),
36
+ run: (events) => events.replaceableSet(pointers),
37
37
  };
38
38
  }
@@ -21,3 +21,5 @@ export type ThreadQueryOptions = {
21
21
  kinds?: number[];
22
22
  };
23
23
  export declare function ThreadQuery(root: string | AddressPointer | EventPointer, opts?: ThreadQueryOptions): Query<Thread>;
24
+ /** A query that gets all legacy and NIP-10, and NIP-22 replies for an event */
25
+ export declare function RepliesQuery(event: NostrEvent, overrideKinds?: number[]): Query<NostrEvent[]>;
@@ -1,8 +1,10 @@
1
1
  import { kinds } from "nostr-tools";
2
+ import { isParameterizedReplaceableKind } from "nostr-tools/kinds";
2
3
  import { map } from "rxjs/operators";
3
- import { getNip10References } from "../helpers/threading.js";
4
- import { getCoordinateFromAddressPointer, isAddressPointer } from "../helpers/pointers.js";
5
- import { getEventUID } from "../helpers/event.js";
4
+ import { getNip10References, interpretThreadTags } from "../helpers/threading.js";
5
+ import { getCoordinateFromAddressPointer, isAddressPointer, isEventPointer } from "../helpers/pointers.js";
6
+ import { getEventUID, getReplaceableUID, getTagValue, isEvent } from "../helpers/event.js";
7
+ import { COMMENT_KIND } from "../helpers/comment.js";
6
8
  const defaultOptions = {
7
9
  kinds: [kinds.ShortTextNote],
8
10
  };
@@ -32,7 +34,7 @@ export function ThreadQuery(root, opts) {
32
34
  }
33
35
  return {
34
36
  key: `${rootUID}-${kinds.join(",")}`,
35
- run: (events) => events.stream([rootFilter, replyFilter]).pipe(map((event) => {
37
+ run: (events) => events.filters([rootFilter, replyFilter]).pipe(map((event) => {
36
38
  if (!items.has(getEventUID(event))) {
37
39
  const refs = getNip10References(event);
38
40
  const replies = parentReferences.get(getEventUID(event)) || new Set();
@@ -64,3 +66,27 @@ export function ThreadQuery(root, opts) {
64
66
  })),
65
67
  };
66
68
  }
69
+ /** A query that gets all legacy and NIP-10, and NIP-22 replies for an event */
70
+ export function RepliesQuery(event, overrideKinds) {
71
+ return {
72
+ key: getEventUID(event),
73
+ run: (events) => {
74
+ const kinds = overrideKinds || event.kind === 1 ? [1, COMMENT_KIND] : [COMMENT_KIND];
75
+ const filter = { kinds };
76
+ if (isEvent(parent) || isEventPointer(event))
77
+ filter["#e"] = [event.id];
78
+ const address = isParameterizedReplaceableKind(event.kind)
79
+ ? getReplaceableUID(event.kind, event.pubkey, getTagValue(event, "d"))
80
+ : undefined;
81
+ if (address) {
82
+ filter["#a"] = [address];
83
+ }
84
+ return events.timeline(filter).pipe(map((events) => {
85
+ return events.filter((e) => {
86
+ const refs = interpretThreadTags(e.tags);
87
+ return refs.reply?.e?.[1] === event.id || refs.reply?.a?.[1] === address;
88
+ });
89
+ }));
90
+ },
91
+ };
92
+ }
@@ -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
+ }
@@ -1,4 +1,5 @@
1
1
  import { AddressPointer, EventPointer } from "nostr-tools/nip19";
2
2
  import { NostrEvent } from "nostr-tools";
3
3
  import { Query } from "../query-store/index.js";
4
+ /** A query that gets all zap events for an event */
4
5
  export declare function EventZapsQuery(id: string | EventPointer | AddressPointer): Query<NostrEvent[]>;
@@ -2,6 +2,7 @@ import { map } from "rxjs";
2
2
  import { kinds } from "nostr-tools";
3
3
  import { getCoordinateFromAddressPointer, isAddressPointer } from "../helpers/pointers.js";
4
4
  import { isValidZap } from "../helpers/zap.js";
5
+ /** A query that gets all zap events for an event */
5
6
  export function EventZapsQuery(id) {
6
7
  return {
7
8
  key: JSON.stringify(id),
@@ -1,47 +1 @@
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
- key: string;
9
- run: (events: EventStore, store: QueryStore) => Observable<T>;
10
- };
11
- export type QueryConstructor<T extends unknown, Args extends Array<any>> = (...args: Args) => Query<T>;
12
- export declare class QueryStore {
13
- static Queries: typeof Queries;
14
- store: EventStore;
15
- constructor(store: EventStore);
16
- queries: LRU<Observable<any> | BehaviorSubject<any>>;
17
- /** Creates a cached query */
18
- runQuery<T extends unknown, Args extends Array<any>>(queryConstructor: (...args: Args) => {
19
- key: string;
20
- run: (events: EventStore, store: QueryStore) => Observable<T>;
21
- }): (...args: Args) => Observable<T>;
22
- /** Returns a single event */
23
- event(id: string): Observable<import("nostr-tools").Event | undefined>;
24
- /** Returns a single event */
25
- events(ids: string[]): Observable<Map<string, import("nostr-tools").Event>>;
26
- /** Returns the latest version of a replaceable event */
27
- replaceable(kind: number, pubkey: string, d?: string): Observable<import("nostr-tools").Event | undefined>;
28
- /** Returns a directory of events by their UID */
29
- replaceableSet(pointers: {
30
- kind: number;
31
- pubkey: string;
32
- identifier?: string;
33
- }[]): Observable<Map<string, import("nostr-tools").Event>>;
34
- /** Returns an array of events that match the filter */
35
- timeline(filters: Filter | Filter[]): Observable<import("nostr-tools").Event[]>;
36
- /** Returns the parsed profile (0) for a pubkey */
37
- profile(pubkey: string): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
38
- /** Returns all reactions for an event (supports replaceable events) */
39
- reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
40
- /** Returns the parsed relay list (10002) for the pubkey */
41
- mailboxes(pubkey: string): Observable<{
42
- inboxes: string[];
43
- outboxes: string[];
44
- } | undefined>;
45
- thread(root: string | EventPointer | AddressPointer): Observable<Queries.Thread>;
46
- }
47
- export { Queries };
1
+ export * from "./query-store.js";
@@ -1,60 +1 @@
1
- import { LRU } from "../helpers/lru.js";
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
- this.store = store;
9
- }
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 = query.run(this.store, this).pipe(shareLatestValue());
18
- this.queries.set(key, observable);
19
- return observable;
20
- }
21
- return this.queries.get(key);
22
- };
23
- }
24
- /** Returns a single event */
25
- event(id) {
26
- return this.runQuery(Queries.SingleEventQuery)(id);
27
- }
28
- /** Returns a single event */
29
- events(ids) {
30
- return this.runQuery(Queries.MultipleEventsQuery)(ids);
31
- }
32
- /** Returns the latest version of a replaceable event */
33
- replaceable(kind, pubkey, d) {
34
- return this.runQuery(Queries.ReplaceableQuery)(kind, pubkey, d);
35
- }
36
- /** Returns a directory of events by their UID */
37
- replaceableSet(pointers) {
38
- return this.runQuery(Queries.ReplaceableSetQuery)(pointers);
39
- }
40
- /** Returns an array of events that match the filter */
41
- timeline(filters) {
42
- return this.runQuery(Queries.TimelineQuery)(filters);
43
- }
44
- /** Returns the parsed profile (0) for a pubkey */
45
- profile(pubkey) {
46
- return this.runQuery(Queries.ProfileQuery)(pubkey);
47
- }
48
- /** Returns all reactions for an event (supports replaceable events) */
49
- reactions(event) {
50
- return this.runQuery(Queries.ReactionsQuery)(event);
51
- }
52
- /** Returns the parsed relay list (10002) for the pubkey */
53
- mailboxes(pubkey) {
54
- return this.runQuery(Queries.MailboxesQuery)(pubkey);
55
- }
56
- thread(root) {
57
- return this.runQuery(Queries.ThreadQuery)(root);
58
- }
59
- }
60
- export { Queries };
1
+ export * from "./query-store.js";
@@ -0,0 +1,51 @@
1
+ import { Observable } from "rxjs";
2
+ import { Filter, NostrEvent } from "nostr-tools";
3
+ import type { AddressPointer, EventPointer } from "nostr-tools/nip19";
4
+ import { EventStore } from "../event-store/event-store.js";
5
+ import * as Queries from "../queries/index.js";
6
+ export type Query<T extends unknown> = {
7
+ /** A unique key for this query. this is used to detect duplicate queries */
8
+ key: string;
9
+ /** The meat of the query, this should return an Observables that subscribes to the eventStore in some way */
10
+ run: (events: EventStore, store: QueryStore) => Observable<T>;
11
+ };
12
+ export type QueryConstructor<T extends unknown, Args extends Array<any>> = (...args: Args) => Query<T>;
13
+ export declare class QueryStore {
14
+ static Queries: typeof Queries;
15
+ store: EventStore;
16
+ constructor(store: EventStore);
17
+ /** A directory of all active queries */
18
+ queries: Map<QueryConstructor<any, any[]>, Map<string, Observable<any>>>;
19
+ /** How long a query should be kept "warm" while nothing is subscribed to it */
20
+ queryKeepWarmTimeout: number;
21
+ /** Creates a cached query */
22
+ createQuery<T extends unknown, Args extends Array<any>>(queryConstructor: QueryConstructor<T, Args>, ...args: Args): Observable<T | undefined>;
23
+ /** Creates a query and waits for the next value */
24
+ executeQuery<T extends unknown, Args extends Array<any>>(queryConstructor: QueryConstructor<T, Args>, ...args: Args): Promise<T>;
25
+ /** Creates a SingleEventQuery */
26
+ event(id: string): Observable<import("nostr-tools").Event | undefined>;
27
+ /** Creates a MultipleEventsQuery */
28
+ events(ids: string[]): Observable<Record<string, import("nostr-tools").Event> | undefined>;
29
+ /** Creates a ReplaceableQuery */
30
+ replaceable(kind: number, pubkey: string, d?: string): Observable<import("nostr-tools").Event | undefined>;
31
+ /** Creates a ReplaceableSetQuery */
32
+ replaceableSet(pointers: {
33
+ kind: number;
34
+ pubkey: string;
35
+ identifier?: string;
36
+ }[]): Observable<Record<string, import("nostr-tools").Event> | undefined>;
37
+ /** Creates a TimelineQuery */
38
+ timeline(filters: Filter | Filter[], keepOldVersions?: boolean): Observable<import("nostr-tools").Event[] | undefined>;
39
+ /** Creates a ProfileQuery */
40
+ profile(pubkey: string): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
41
+ /** Creates a ReactionsQuery */
42
+ reactions(event: NostrEvent): Observable<import("nostr-tools").Event[] | undefined>;
43
+ /** Creates a MailboxesQuery */
44
+ mailboxes(pubkey: string): Observable<{
45
+ inboxes: string[];
46
+ outboxes: string[];
47
+ } | undefined>;
48
+ /** Creates a ThreadQuery */
49
+ thread(root: string | EventPointer | AddressPointer): Observable<Queries.Thread | undefined>;
50
+ }
51
+ export { Queries };