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,22 @@
1
+ import { isNameValueTag, processTags } from "./tags.js";
2
+ export const BLOSSOM_SERVER_LIST_KIND = 10063;
3
+ /** Check if two servers are the same */
4
+ export function areBlossomServersEqual(a, b) {
5
+ const hostnameA = new URL("/", a).toString();
6
+ const hostnameB = new URL("/", b).toString();
7
+ return hostnameA === hostnameB;
8
+ }
9
+ /** Checks if a string is a sha256 hash */
10
+ export function isSha256(str) {
11
+ return !!str.match(/^[0-9a-f]{64}$/);
12
+ }
13
+ /** Returns an ordered array of servers found in a server list event (10063) */
14
+ export function getBlossomServersFromList(event) {
15
+ const tags = Array.isArray(event) ? event : event.tags;
16
+ return processTags(tags, (tag) => {
17
+ if (isNameValueTag(tag, "server") && URL.canParse(tag[1]))
18
+ return new URL("/", tag[1]);
19
+ else
20
+ return undefined;
21
+ });
22
+ }
@@ -5,4 +5,5 @@ export type ParsedInvoice = {
5
5
  timestamp: number;
6
6
  expiry: number;
7
7
  };
8
+ /** Parses a lightning invoice */
8
9
  export declare function parseBolt11(paymentRequest: string): ParsedInvoice;
@@ -1,4 +1,5 @@
1
1
  import { decode } from "light-bolt11-decoder";
2
+ /** Parses a lightning invoice */
2
3
  export function parseBolt11(paymentRequest) {
3
4
  const decoded = decode(paymentRequest);
4
5
  const timestamp = decoded.sections.find((s) => s.name === "timestamp")?.value ?? 0;
@@ -0,0 +1,15 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { AddressPointer, EventPointer } from "nostr-tools/nip19";
3
+ export declare const BookmarkPublicSymbol: unique symbol;
4
+ export declare const BookmarkHiddenSymbol: unique symbol;
5
+ export type Bookmarks = {
6
+ notes: EventPointer[];
7
+ articles: AddressPointer[];
8
+ hashtags: string[];
9
+ urls: string[];
10
+ };
11
+ export declare function parseBookmarkTags(tags: string[][]): Bookmarks;
12
+ /** Returns the public bookmarks of the event */
13
+ export declare function getBookmarks(bookmark: NostrEvent): Bookmarks;
14
+ /** Returns the bookmarks of the event if its unlocked */
15
+ export declare function getHiddenBookmarks(bookmark: NostrEvent): Bookmarks | undefined;
@@ -0,0 +1,27 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { getAddressPointerFromATag, getEventPointerFromETag } from "./pointers.js";
3
+ import { getOrComputeCachedValue } from "./cache.js";
4
+ import { getHiddenTags } from "./index.js";
5
+ export const BookmarkPublicSymbol = Symbol.for("bookmark-public");
6
+ export const BookmarkHiddenSymbol = Symbol.for("bookmark-hidden");
7
+ export function parseBookmarkTags(tags) {
8
+ const notes = tags.filter((t) => t[0] === "e" && t[1]).map(getEventPointerFromETag);
9
+ const articles = tags
10
+ .filter((t) => t[0] === "a" && t[1])
11
+ .map(getAddressPointerFromATag)
12
+ .filter((addr) => addr.kind === kinds.LongFormArticle);
13
+ const hashtags = tags.filter((t) => t[0] === "t" && t[1]).map((t) => t[1]);
14
+ const urls = tags.filter((t) => t[0] === "r" && t[1]).map((t) => t[1]);
15
+ return { notes, articles, hashtags, urls };
16
+ }
17
+ /** Returns the public bookmarks of the event */
18
+ export function getBookmarks(bookmark) {
19
+ return getOrComputeCachedValue(bookmark, BookmarkPublicSymbol, () => parseBookmarkTags(bookmark.tags));
20
+ }
21
+ /** Returns the bookmarks of the event if its unlocked */
22
+ export function getHiddenBookmarks(bookmark) {
23
+ return getOrComputeCachedValue(bookmark, BookmarkHiddenSymbol, () => {
24
+ const tags = getHiddenTags(bookmark);
25
+ return tags && parseBookmarkTags(tags);
26
+ });
27
+ }
@@ -0,0 +1,10 @@
1
+ import { nip19, NostrEvent } from "nostr-tools";
2
+ import { ChannelMetadata } from "nostr-tools/nip28";
3
+ export declare const ChannelMetadataSymbol: unique symbol;
4
+ export type ChannelMetadataContent = ChannelMetadata & {
5
+ relays?: string[];
6
+ };
7
+ /** Gets the parsed metadata on a channel creation or channel metadata event */
8
+ export declare function getChannelMetadataContent(channel: NostrEvent): ChannelMetadataContent;
9
+ /** gets the EventPointer for a channel message or metadata event */
10
+ export declare function getChannelPointer(event: NostrEvent): nip19.EventPointer | undefined;
@@ -0,0 +1,27 @@
1
+ import { getOrComputeCachedValue } from "./cache.js";
2
+ export const ChannelMetadataSymbol = Symbol.for("channel-metadata");
3
+ function parseChannelMetadataContent(channel) {
4
+ const metadata = JSON.parse(channel.content);
5
+ if (metadata.name === undefined)
6
+ throw new Error("Missing name");
7
+ if (metadata.about === undefined)
8
+ throw new Error("Missing about");
9
+ if (metadata.picture === undefined)
10
+ throw new Error("Missing picture");
11
+ if (metadata.relays && !Array.isArray(metadata.relays))
12
+ throw new Error("Invalid relays");
13
+ return metadata;
14
+ }
15
+ /** Gets the parsed metadata on a channel creation or channel metadata event */
16
+ export function getChannelMetadataContent(channel) {
17
+ return getOrComputeCachedValue(channel, ChannelMetadataSymbol, () => {
18
+ return parseChannelMetadataContent(channel);
19
+ });
20
+ }
21
+ /** gets the EventPointer for a channel message or metadata event */
22
+ export function getChannelPointer(event) {
23
+ const tag = event.tags.find((t) => t[0] === "e" && t[1]);
24
+ if (!tag)
25
+ return undefined;
26
+ return tag[2] ? { id: tag[1], relays: [tag[2]] } : { id: tag[1] };
27
+ }
@@ -0,0 +1,47 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { ExternalPointer, ExternalIdentifiers } from "./external-id.js";
3
+ export declare const COMMENT_KIND = 1111;
4
+ export type CommentEventPointer = {
5
+ id: string;
6
+ kind: number;
7
+ pubkey?: string;
8
+ relay?: string;
9
+ };
10
+ export type CommentAddressPointer = {
11
+ id?: string;
12
+ kind: number;
13
+ pubkey: string;
14
+ identifier: string;
15
+ relay?: string;
16
+ };
17
+ export type CommentExternalPointer = ExternalPointer<keyof ExternalIdentifiers>;
18
+ export type CommentPointer = CommentEventPointer | CommentAddressPointer | CommentExternalPointer;
19
+ export declare const CommentRootPointerSymbol: unique symbol;
20
+ export declare const CommentReplyPointerSymbol: unique symbol;
21
+ /**
22
+ * Gets the EventPointer from an array of tags
23
+ * @throws
24
+ */
25
+ export declare function getCommentEventPointer(tags: string[][], root?: boolean): CommentEventPointer | null;
26
+ /**
27
+ * Gets the AddressPointer from an array of tags
28
+ * @throws
29
+ */
30
+ export declare function getCommentAddressPointer(tags: string[][], root?: boolean): CommentAddressPointer | null;
31
+ /**
32
+ * Gets the ExternalPointer from an array of tags
33
+ * @throws
34
+ */
35
+ export declare function getCommentExternalPointer(tags: string[][], root?: boolean): CommentExternalPointer | null;
36
+ /**
37
+ * Returns the root pointer for a comment
38
+ * @throws
39
+ */
40
+ export declare function getCommentRootPointer(comment: NostrEvent): CommentPointer | null;
41
+ /**
42
+ * Returns the reply pointer for a comment
43
+ * @throws
44
+ */
45
+ export declare function getCommentReplyPointer(comment: NostrEvent): CommentPointer | null;
46
+ export declare function isCommentEventPointer(pointer: any): pointer is CommentEventPointer;
47
+ export declare function isCommentAddressPointer(pointer: any): pointer is CommentAddressPointer;
@@ -0,0 +1,120 @@
1
+ import { getExternalPointerFromTag } from "./external-id.js";
2
+ import { getOrComputeCachedValue } from "./cache.js";
3
+ import { getAddressPointerFromATag } from "./pointers.js";
4
+ import { isSafeRelayURL } from "./relays.js";
5
+ export const COMMENT_KIND = 1111;
6
+ export const CommentRootPointerSymbol = Symbol.for("comment-root-pointer");
7
+ export const CommentReplyPointerSymbol = Symbol.for("comment-reply-pointer");
8
+ /**
9
+ * Gets the EventPointer from an array of tags
10
+ * @throws
11
+ */
12
+ export function getCommentEventPointer(tags, root = false) {
13
+ const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
14
+ const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
15
+ if (eTag) {
16
+ if (!kind)
17
+ throw new Error("Missing kind tag");
18
+ // only the root pubkey can be gotten from the tags, since due to quotes and mentions there will be many "p" tags for replies
19
+ const rootPubkey = root ? tags.find((t) => t[0] === "P")?.[1] : undefined;
20
+ const pointer = {
21
+ id: eTag[1],
22
+ kind: parseInt(kind),
23
+ pubkey: eTag[3] || rootPubkey || undefined,
24
+ relay: eTag[2] && isSafeRelayURL(eTag[2]) ? eTag[2] : undefined,
25
+ };
26
+ return pointer;
27
+ }
28
+ return null;
29
+ }
30
+ /**
31
+ * Gets the AddressPointer from an array of tags
32
+ * @throws
33
+ */
34
+ export function getCommentAddressPointer(tags, root = false) {
35
+ const aTag = tags.find((t) => t[0] === (root ? "A" : "a"));
36
+ const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
37
+ const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
38
+ if (aTag) {
39
+ if (!kind)
40
+ throw new Error("Missing kind tag");
41
+ const addressPointer = getAddressPointerFromATag(aTag);
42
+ const pointer = {
43
+ id: eTag?.[1],
44
+ pubkey: addressPointer.pubkey,
45
+ identifier: addressPointer.identifier,
46
+ kind: addressPointer.kind || parseInt(kind),
47
+ relay: addressPointer.relays?.[0] || eTag?.[2],
48
+ };
49
+ return pointer;
50
+ }
51
+ return null;
52
+ }
53
+ /**
54
+ * Gets the ExternalPointer from an array of tags
55
+ * @throws
56
+ */
57
+ export function getCommentExternalPointer(tags, root = false) {
58
+ const iTag = tags.find((t) => t[0] === (root ? "I" : "i"));
59
+ const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
60
+ if (iTag) {
61
+ if (!kind)
62
+ throw new Error("Missing kind tag");
63
+ return getExternalPointerFromTag(iTag);
64
+ }
65
+ return null;
66
+ }
67
+ /**
68
+ * Returns the root pointer for a comment
69
+ * @throws
70
+ */
71
+ export function getCommentRootPointer(comment) {
72
+ if (comment.kind !== COMMENT_KIND)
73
+ throw new Error("Event is not a comment");
74
+ return getOrComputeCachedValue(comment, CommentRootPointerSymbol, () => {
75
+ // check for address pointer first since it can also have E tags
76
+ const A = getCommentAddressPointer(comment.tags, true);
77
+ if (A)
78
+ return A;
79
+ const E = getCommentEventPointer(comment.tags, true);
80
+ if (E)
81
+ return E;
82
+ const I = getCommentExternalPointer(comment.tags, true);
83
+ if (I)
84
+ return I;
85
+ return null;
86
+ });
87
+ }
88
+ /**
89
+ * Returns the reply pointer for a comment
90
+ * @throws
91
+ */
92
+ export function getCommentReplyPointer(comment) {
93
+ if (comment.kind !== COMMENT_KIND)
94
+ throw new Error("Event is not a comment");
95
+ return getOrComputeCachedValue(comment, CommentReplyPointerSymbol, () => {
96
+ // check for address pointer first since it can also have E tags
97
+ const A = getCommentAddressPointer(comment.tags, false);
98
+ if (A)
99
+ return A;
100
+ const E = getCommentEventPointer(comment.tags, false);
101
+ if (E)
102
+ return E;
103
+ const I = getCommentExternalPointer(comment.tags, false);
104
+ if (I)
105
+ return I;
106
+ return null;
107
+ });
108
+ }
109
+ export function isCommentEventPointer(pointer) {
110
+ return (Reflect.has(pointer, "id") &&
111
+ Reflect.has(pointer, "kind") &&
112
+ !Reflect.has(pointer, "identifier") &&
113
+ typeof pointer.kind === "number");
114
+ }
115
+ export function isCommentAddressPointer(pointer) {
116
+ return (Reflect.has(pointer, "identifier") &&
117
+ Reflect.has(pointer, "pubkey") &&
118
+ Reflect.has(pointer, "kind") &&
119
+ typeof pointer.kind === "number");
120
+ }
@@ -0,0 +1,3 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ export declare const ContactsRelaysSymbol: unique symbol;
3
+ export declare function getRelaysFromContactsEvent(event: NostrEvent): Map<string, "all" | "inbox" | "outbox"> | null;
@@ -0,0 +1,25 @@
1
+ import { getOrComputeCachedValue } from "./cache.js";
2
+ import { isSafeRelayURL } from "./relays.js";
3
+ export const ContactsRelaysSymbol = Symbol.for("contacts-relays");
4
+ export function getRelaysFromContactsEvent(event) {
5
+ return getOrComputeCachedValue(event, ContactsRelaysSymbol, () => {
6
+ try {
7
+ const relayJson = JSON.parse(event.content);
8
+ const relays = new Map();
9
+ for (const [url, opts] of Object.entries(relayJson)) {
10
+ if (!isSafeRelayURL(url))
11
+ continue;
12
+ if (opts.write && opts.read)
13
+ relays.set(url, "all");
14
+ else if (opts.read)
15
+ relays.set(url, "inbox");
16
+ else if (opts.write)
17
+ relays.set(url, "outbox");
18
+ }
19
+ return relays;
20
+ }
21
+ catch (error) {
22
+ return null;
23
+ }
24
+ });
25
+ }
@@ -0,0 +1,3 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ /** Returns the NIP-36 content-warning for an event. returns boolean if there is no "reason" */
3
+ export declare function getContentWarning(event: NostrEvent): string | boolean;
@@ -0,0 +1,8 @@
1
+ /** Returns the NIP-36 content-warning for an event. returns boolean if there is no "reason" */
2
+ export function getContentWarning(event) {
3
+ const tag = event.tags.find((t) => t[0] === "content-warning");
4
+ if (tag)
5
+ return tag[1] || true;
6
+ else
7
+ return false;
8
+ }
@@ -0,0 +1,3 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ export declare function getDeleteIds(deleteEvent: NostrEvent): string[];
3
+ export declare function getDeleteCoordinates(deleteEvent: NostrEvent): string[];
@@ -0,0 +1,7 @@
1
+ import { isATag, isETag } from "./tags.js";
2
+ export function getDeleteIds(deleteEvent) {
3
+ return deleteEvent.tags.filter(isETag).map((t) => t[1]);
4
+ }
5
+ export function getDeleteCoordinates(deleteEvent) {
6
+ return deleteEvent.tags.filter(isATag).map((t) => t[1]);
7
+ }
@@ -0,0 +1,7 @@
1
+ import { isNip05, NIP05_REGEX } from "nostr-tools/nip05";
2
+ /** Returns the name and domain for a NIP-05 address */
3
+ export declare function parseNIP05Address(address: string): {
4
+ name: string;
5
+ domain: string;
6
+ } | null;
7
+ export { isNip05, NIP05_REGEX };
@@ -0,0 +1,10 @@
1
+ import { isNip05, NIP05_REGEX } from "nostr-tools/nip05";
2
+ /** Returns the name and domain for a NIP-05 address */
3
+ export function parseNIP05Address(address) {
4
+ const match = address.toLowerCase().match(NIP05_REGEX);
5
+ if (!match)
6
+ return null;
7
+ const [, name = "_", domain] = match;
8
+ return { name, domain };
9
+ }
10
+ export { isNip05, NIP05_REGEX };
@@ -1,2 +1,13 @@
1
1
  import { EventTemplate, NostrEvent } from "nostr-tools";
2
- export declare function getEmojiTag(event: NostrEvent | EventTemplate, code: string): ["emoji", string, string];
2
+ /** Gets an "emoji" tag that matches an emoji code */
3
+ export declare function getEmojiTag(event: NostrEvent | EventTemplate, code: string): ["emoji", string, string] | undefined;
4
+ /** Returns the name of a NIP-30 emoji pack */
5
+ export declare function getPackName(pack: NostrEvent): string | undefined;
6
+ export type Emoji = {
7
+ /** The emoji shortcode (without the ::) */
8
+ shortcode: string;
9
+ /** The URL to the emoji image */
10
+ url: string;
11
+ };
12
+ /** Returns an array of emojis from a NIP-30 emoji pack */
13
+ export declare function getEmojis(pack: NostrEvent): Emoji[];
@@ -1,4 +1,16 @@
1
+ import { getTagValue } from "./event.js";
2
+ /** Gets an "emoji" tag that matches an emoji code */
1
3
  export function getEmojiTag(event, code) {
2
4
  code = code.replace(/^:|:$/g, "").toLocaleLowerCase();
3
- return event.tags.filter((t) => t[0] === "emoji" && t[1] && t[2]).find((t) => t[1].toLowerCase() === code);
5
+ return event.tags.find((t) => t[0] === "emoji" && t.length >= 3 && t[1].toLowerCase() === code);
6
+ }
7
+ /** Returns the name of a NIP-30 emoji pack */
8
+ export function getPackName(pack) {
9
+ return getTagValue(pack, "title") || getTagValue(pack, "d");
10
+ }
11
+ /** Returns an array of emojis from a NIP-30 emoji pack */
12
+ export function getEmojis(pack) {
13
+ return pack.tags
14
+ .filter((t) => t[0] === "emoji" && t[1] && t[2])
15
+ .map((t) => ({ shortcode: t[1], url: t[2] }));
4
16
  }
@@ -1,7 +1,8 @@
1
- import { NostrEvent, VerifiedEvent } from "nostr-tools";
1
+ import { EventTemplate, NostrEvent, VerifiedEvent } from "nostr-tools";
2
2
  export declare const EventUIDSymbol: unique symbol;
3
3
  export declare const EventIndexableTagsSymbol: unique symbol;
4
4
  export declare const FromCacheSymbol: unique symbol;
5
+ export declare const ReplaceableIdentifierSymbol: unique symbol;
5
6
  declare module "nostr-tools" {
6
7
  interface Event {
7
8
  [EventUIDSymbol]?: string;
@@ -9,6 +10,11 @@ declare module "nostr-tools" {
9
10
  [FromCacheSymbol]?: boolean;
10
11
  }
11
12
  }
13
+ /**
14
+ * Checks if an object is a nostr event
15
+ * NOTE: does not validation the signature on the event
16
+ */
17
+ export declare function isEvent(event: any): event is NostrEvent;
12
18
  /**
13
19
  * Returns if a kind is replaceable ( 10000 <= n < 20000 || n == 0 || n == 3 )
14
20
  * or parameterized replaceable ( 30000 <= n < 40000 )
@@ -24,11 +30,19 @@ export declare function getEventUID(event: NostrEvent): string;
24
30
  export declare function getReplaceableUID(kind: number, pubkey: string, d?: string): string;
25
31
  /** Returns a Set of tag names and values that are indexable */
26
32
  export declare function getIndexableTags(event: NostrEvent): Set<string>;
27
- /** Returns the second index ( tag[1] ) of the first tag that matches the name */
28
- export declare function getTagValue(event: NostrEvent, name: string): string | undefined;
33
+ /**
34
+ * Returns the second index ( tag[1] ) of the first tag that matches the name
35
+ * If the event has any hidden tags they will be searched first
36
+ */
37
+ export declare function getTagValue(event: NostrEvent | EventTemplate, name: string): string | undefined;
29
38
  /** Sets events verified flag without checking anything */
30
39
  export declare function fakeVerifyEvent(event: NostrEvent): event is VerifiedEvent;
31
40
  /** Marks an event as being from a cache */
32
41
  export declare function markFromCache(event: NostrEvent): void;
33
42
  /** Returns if an event was from a cache */
34
43
  export declare function isFromCache(event: NostrEvent): boolean;
44
+ /**
45
+ * Returns the replaceable identifier for a replaceable event
46
+ * @throws
47
+ */
48
+ export declare function getReplaceableIdentifier(event: NostrEvent): string;
@@ -1,8 +1,28 @@
1
1
  import { kinds, verifiedSymbol } from "nostr-tools";
2
2
  import { INDEXABLE_TAGS } from "../event-store/common.js";
3
+ import { getHiddenTags } from "./hidden-tags.js";
4
+ import { getOrComputeCachedValue } from "./cache.js";
5
+ import { isParameterizedReplaceableKind } from "nostr-tools/kinds";
3
6
  export const EventUIDSymbol = Symbol.for("event-uid");
4
7
  export const EventIndexableTagsSymbol = Symbol.for("indexable-tags");
5
8
  export const FromCacheSymbol = Symbol.for("from-cache");
9
+ export const ReplaceableIdentifierSymbol = Symbol.for("replaceable-identifier");
10
+ /**
11
+ * Checks if an object is a nostr event
12
+ * NOTE: does not validation the signature on the event
13
+ */
14
+ export function isEvent(event) {
15
+ if (event === undefined || event === null)
16
+ return false;
17
+ return (event.id?.length === 64 &&
18
+ typeof event.sig === "string" &&
19
+ typeof event.pubkey === "string" &&
20
+ event.pubkey.length === 64 &&
21
+ typeof event.content === "string" &&
22
+ Array.isArray(event.tags) &&
23
+ typeof event.created_at === "number" &&
24
+ event.created_at > 0);
25
+ }
6
26
  /**
7
27
  * Returns if a kind is replaceable ( 10000 <= n < 20000 || n == 0 || n == 3 )
8
28
  * or parameterized replaceable ( 30000 <= n < 40000 )
@@ -17,20 +37,20 @@ export function isReplaceable(kind) {
17
37
  * For parametrized replaceable events this is ( event.kind + ":" + event.pubkey + ":" + event.tags.d.1 )
18
38
  */
19
39
  export function getEventUID(event) {
20
- let id = event[EventUIDSymbol];
21
- if (!id) {
40
+ let uid = event[EventUIDSymbol];
41
+ if (!uid) {
22
42
  if (isReplaceable(event.kind)) {
23
- const d = event.tags.find((t) => t[0] === "d")?.[1];
24
- id = getReplaceableUID(event.kind, event.pubkey, d);
25
- }
26
- else {
27
- id = event.id;
43
+ let d = event.tags.find((t) => t[0] === "d")?.[1];
44
+ uid = getReplaceableUID(event.kind, event.pubkey, d);
28
45
  }
46
+ else
47
+ uid = event.id;
48
+ event[EventUIDSymbol] = uid;
29
49
  }
30
- return id;
50
+ return uid;
31
51
  }
32
52
  export function getReplaceableUID(kind, pubkey, d) {
33
- return d ? `${kind}:${pubkey}:${d}` : `${kind}:${pubkey}`;
53
+ return d ? kind + ":" + pubkey + ":" + d : kind + ":" + pubkey;
34
54
  }
35
55
  /** Returns a Set of tag names and values that are indexable */
36
56
  export function getIndexableTags(event) {
@@ -38,7 +58,7 @@ export function getIndexableTags(event) {
38
58
  if (!indexable) {
39
59
  const tags = new Set();
40
60
  for (const tag of event.tags) {
41
- if (tag[0] && INDEXABLE_TAGS.has(tag[0]) && tag[1]) {
61
+ if (tag.length >= 2 && tag[0].length === 1 && INDEXABLE_TAGS.has(tag[0])) {
42
62
  tags.add(tag[0] + ":" + tag[1]);
43
63
  }
44
64
  }
@@ -46,13 +66,21 @@ export function getIndexableTags(event) {
46
66
  }
47
67
  return indexable;
48
68
  }
49
- /** Returns the second index ( tag[1] ) of the first tag that matches the name */
69
+ /**
70
+ * Returns the second index ( tag[1] ) of the first tag that matches the name
71
+ * If the event has any hidden tags they will be searched first
72
+ */
50
73
  export function getTagValue(event, name) {
74
+ const hidden = getHiddenTags(event);
75
+ const hiddenValue = hidden?.find((t) => t[0] === name)?.[1];
76
+ if (hiddenValue)
77
+ return hiddenValue;
51
78
  return event.tags.find((t) => t[0] === name)?.[1];
52
79
  }
53
80
  /** Sets events verified flag without checking anything */
54
81
  export function fakeVerifyEvent(event) {
55
- return (event[verifiedSymbol] = true);
82
+ event[verifiedSymbol] = true;
83
+ return true;
56
84
  }
57
85
  /** Marks an event as being from a cache */
58
86
  export function markFromCache(event) {
@@ -62,3 +90,17 @@ export function markFromCache(event) {
62
90
  export function isFromCache(event) {
63
91
  return !!event[FromCacheSymbol];
64
92
  }
93
+ /**
94
+ * Returns the replaceable identifier for a replaceable event
95
+ * @throws
96
+ */
97
+ export function getReplaceableIdentifier(event) {
98
+ if (!isParameterizedReplaceableKind(event.kind))
99
+ throw new Error("Event is not replaceable");
100
+ return getOrComputeCachedValue(event, ReplaceableIdentifierSymbol, () => {
101
+ const d = getTagValue(event, "d");
102
+ if (d === undefined)
103
+ throw new Error("Event missing identifier");
104
+ return d;
105
+ });
106
+ }
@@ -0,0 +1,29 @@
1
+ export type ExternalIdentifiers = {
2
+ "#": `#${string}`;
3
+ geo: `geo:${string}`;
4
+ isbn: `isbn:${string}`;
5
+ "podcast:guid": `podcast:guid:${string}`;
6
+ "podcast:item:guid": `podcast:item:guid:${string}`;
7
+ "podcast:publisher:guid": `podcast:publisher:guid:${string}`;
8
+ isan: `isan:${string}`;
9
+ doi: `doi:${string}`;
10
+ };
11
+ export type ExternalPointer<Prefix extends keyof ExternalIdentifiers> = {
12
+ kind: Prefix;
13
+ identifier: ExternalIdentifiers[Prefix];
14
+ };
15
+ export type ParseResult = {
16
+ [P in keyof ExternalIdentifiers]: ExternalPointer<P>;
17
+ }[keyof ExternalIdentifiers];
18
+ /**
19
+ * Parses a NIP-73 external identifier
20
+ * @throws
21
+ */
22
+ export declare function parseExternalPointer<Prefix extends keyof ExternalIdentifiers>(identifier: `${Prefix}1${string}`): ExternalPointer<Prefix>;
23
+ export declare function parseExternalPointer(identifier: string): ParseResult;
24
+ /**
25
+ * Gets an ExternalPointer for a "i" tag
26
+ * @throws
27
+ */
28
+ export declare function getExternalPointerFromTag<Prefix extends keyof ExternalIdentifiers>(tag: string[]): ExternalPointer<Prefix>;
29
+ export declare function getExternalPointerFromTag(tag: string[]): ParseResult;
@@ -0,0 +1,20 @@
1
+ export function parseExternalPointer(identifier) {
2
+ if (identifier.startsWith("#"))
3
+ return { kind: "#", identifier: identifier };
4
+ if (identifier.startsWith("geo:"))
5
+ return { kind: "geo", identifier: identifier };
6
+ if (identifier.startsWith("podcast:guid:"))
7
+ return { kind: "podcast:guid", identifier: identifier };
8
+ if (identifier.startsWith("podcast:item:guid:"))
9
+ return { kind: "podcast:item:guid", identifier: identifier };
10
+ if (identifier.startsWith("podcast:publisher:guid:"))
11
+ return { kind: "podcast:publisher:guid", identifier: identifier };
12
+ if (identifier.startsWith("isan:"))
13
+ return { kind: "isan", identifier: identifier };
14
+ if (identifier.startsWith("doi:"))
15
+ return { kind: "doi", identifier: identifier };
16
+ throw new Error("Failed to parse external identifier");
17
+ }
18
+ export function getExternalPointerFromTag(tag) {
19
+ return parseExternalPointer(tag[1]);
20
+ }