applesauce-core 3.1.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/event-store/async-event-store.d.ts +136 -0
- package/dist/event-store/async-event-store.js +364 -0
- package/dist/event-store/{event-set.d.ts → event-memory.d.ts} +17 -25
- package/dist/event-store/{event-set.js → event-memory.js} +54 -53
- package/dist/event-store/event-store.d.ts +59 -63
- package/dist/event-store/event-store.js +126 -190
- package/dist/event-store/index.d.ts +2 -1
- package/dist/event-store/index.js +2 -1
- package/dist/event-store/interface.d.ts +115 -38
- package/dist/event-store/model-mixin.d.ts +59 -0
- package/dist/event-store/model-mixin.js +147 -0
- package/dist/helpers/app-data.d.ts +39 -0
- package/dist/helpers/app-data.js +68 -0
- package/dist/helpers/bookmarks.d.ts +11 -1
- package/dist/helpers/bookmarks.js +29 -4
- package/dist/helpers/comment.d.ts +13 -20
- package/dist/helpers/comment.js +16 -27
- package/dist/helpers/contacts.d.ts +10 -1
- package/dist/helpers/contacts.js +30 -3
- package/dist/helpers/encrypted-content-cache.js +7 -7
- package/dist/helpers/encrypted-content.d.ts +9 -2
- package/dist/helpers/encrypted-content.js +12 -9
- package/dist/helpers/event-cache.d.ts +3 -1
- package/dist/helpers/event-cache.js +3 -1
- package/dist/helpers/event-tags.d.ts +6 -0
- package/dist/helpers/event-tags.js +4 -0
- package/dist/helpers/event.d.ts +8 -1
- package/dist/helpers/event.js +6 -0
- package/dist/helpers/file-metadata.d.ts +4 -9
- package/dist/helpers/file-metadata.js +2 -10
- package/dist/helpers/filter.d.ts +4 -3
- package/dist/helpers/filter.js +3 -3
- package/dist/helpers/gift-wraps.d.ts +35 -14
- package/dist/helpers/gift-wraps.js +59 -50
- package/dist/helpers/groups.d.ts +2 -5
- package/dist/helpers/groups.js +2 -5
- package/dist/helpers/hidden-content.d.ts +14 -5
- package/dist/helpers/hidden-content.js +19 -8
- package/dist/helpers/hidden-tags.d.ts +16 -7
- package/dist/helpers/hidden-tags.js +47 -26
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/helpers/legacy-messages.d.ts +17 -13
- package/dist/helpers/legacy-messages.js +21 -19
- package/dist/helpers/lists.js +2 -1
- package/dist/helpers/lnurl.d.ts +4 -0
- package/dist/helpers/lnurl.js +7 -3
- package/dist/helpers/mailboxes.d.ts +2 -6
- package/dist/helpers/mailboxes.js +26 -20
- package/dist/helpers/mutes.d.ts +11 -1
- package/dist/helpers/mutes.js +30 -5
- package/dist/helpers/picture-post.d.ts +2 -1
- package/dist/helpers/pointers.d.ts +3 -1
- package/dist/helpers/pointers.js +4 -1
- package/dist/helpers/profile.d.ts +7 -3
- package/dist/helpers/profile.js +7 -8
- package/dist/helpers/relay-selection.d.ts +17 -0
- package/dist/helpers/relay-selection.js +102 -0
- package/dist/helpers/relays.d.ts +3 -1
- package/dist/helpers/relays.js +18 -2
- package/dist/helpers/url.js +3 -3
- package/dist/helpers/wrapped-messages.d.ts +5 -3
- package/dist/helpers/wrapped-messages.js +5 -3
- package/dist/helpers/zap.d.ts +18 -14
- package/dist/helpers/zap.js +26 -28
- package/dist/models/common.d.ts +4 -4
- package/dist/models/common.js +79 -40
- package/dist/models/gift-wrap.d.ts +1 -1
- package/dist/models/gift-wrap.js +4 -4
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +1 -0
- package/dist/models/legacy-messages.d.ts +2 -2
- package/dist/models/legacy-messages.js +13 -10
- package/dist/models/outbox.d.ts +13 -0
- package/dist/models/outbox.js +18 -0
- package/dist/models/profile.d.ts +1 -1
- package/dist/models/zaps.d.ts +5 -4
- package/dist/models/zaps.js +2 -2
- package/dist/observable/index.d.ts +4 -3
- package/dist/observable/index.js +5 -4
- package/dist/observable/map-events-to-store.d.ts +5 -3
- package/dist/observable/map-events-to-store.js +14 -3
- package/dist/observable/map-events-to-timeline.js +12 -0
- package/dist/observable/relay-selection.d.ts +9 -0
- package/dist/observable/relay-selection.js +43 -0
- package/package.json +5 -3
- package/dist/observable/map-events-timeline.js +0 -9
- /package/dist/observable/{map-events-timeline.d.ts → map-events-to-timeline.d.ts} +0 -0
package/dist/helpers/comment.js
CHANGED
|
@@ -5,16 +5,14 @@ import { isSafeRelayURL } from "./relays.js";
|
|
|
5
5
|
export const COMMENT_KIND = 1111;
|
|
6
6
|
export const CommentRootPointerSymbol = Symbol.for("comment-root-pointer");
|
|
7
7
|
export const CommentReplyPointerSymbol = Symbol.for("comment-reply-pointer");
|
|
8
|
-
/**
|
|
9
|
-
* Gets the EventPointer from an array of tags
|
|
10
|
-
* @throws
|
|
11
|
-
*/
|
|
8
|
+
/** Gets the EventPointer from an array of tags */
|
|
12
9
|
export function getCommentEventPointer(tags, root = false) {
|
|
13
10
|
const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
|
|
14
11
|
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
|
|
15
12
|
if (eTag) {
|
|
13
|
+
// Missing kind tag, return null
|
|
16
14
|
if (!kind)
|
|
17
|
-
|
|
15
|
+
return null;
|
|
18
16
|
// 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
17
|
const rootPubkey = root ? tags.find((t) => t[0] === "P")?.[1] : undefined;
|
|
20
18
|
const pointer = {
|
|
@@ -28,17 +26,15 @@ export function getCommentEventPointer(tags, root = false) {
|
|
|
28
26
|
}
|
|
29
27
|
return null;
|
|
30
28
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Gets the AddressPointer from an array of tags
|
|
33
|
-
* @throws
|
|
34
|
-
*/
|
|
29
|
+
/** Gets the AddressPointer from an array of tags */
|
|
35
30
|
export function getCommentAddressPointer(tags, root = false) {
|
|
36
31
|
const aTag = tags.find((t) => t[0] === (root ? "A" : "a"));
|
|
37
32
|
const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
|
|
38
33
|
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
|
|
39
34
|
if (aTag) {
|
|
35
|
+
// Missing kind tag, return null
|
|
40
36
|
if (!kind)
|
|
41
|
-
|
|
37
|
+
return null;
|
|
42
38
|
const addressPointer = getAddressPointerFromATag(aTag);
|
|
43
39
|
const pointer = {
|
|
44
40
|
type: "address",
|
|
@@ -52,16 +48,10 @@ export function getCommentAddressPointer(tags, root = false) {
|
|
|
52
48
|
}
|
|
53
49
|
return null;
|
|
54
50
|
}
|
|
55
|
-
/**
|
|
56
|
-
* Gets the ExternalPointer from an array of tags
|
|
57
|
-
* @throws
|
|
58
|
-
*/
|
|
51
|
+
/** Gets the ExternalPointer from an array of tags */
|
|
59
52
|
export function getCommentExternalPointer(tags, root = false) {
|
|
60
53
|
const iTag = tags.find((t) => t[0] === (root ? "I" : "i"));
|
|
61
|
-
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
|
|
62
54
|
if (iTag) {
|
|
63
|
-
if (!kind)
|
|
64
|
-
throw new Error("Missing kind tag");
|
|
65
55
|
return {
|
|
66
56
|
type: "external",
|
|
67
57
|
...getExternalPointerFromTag(iTag),
|
|
@@ -69,13 +59,9 @@ export function getCommentExternalPointer(tags, root = false) {
|
|
|
69
59
|
}
|
|
70
60
|
return null;
|
|
71
61
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Returns the root pointer for a comment
|
|
74
|
-
* @throws
|
|
75
|
-
*/
|
|
76
62
|
export function getCommentRootPointer(comment) {
|
|
77
63
|
if (comment.kind !== COMMENT_KIND)
|
|
78
|
-
|
|
64
|
+
return null;
|
|
79
65
|
return getOrComputeCachedValue(comment, CommentRootPointerSymbol, () => {
|
|
80
66
|
// check for address pointer first since it can also have E tags
|
|
81
67
|
const A = getCommentAddressPointer(comment.tags, true);
|
|
@@ -90,13 +76,10 @@ export function getCommentRootPointer(comment) {
|
|
|
90
76
|
return null;
|
|
91
77
|
});
|
|
92
78
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Returns the reply pointer for a comment
|
|
95
|
-
* @throws
|
|
96
|
-
*/
|
|
79
|
+
/** Returns the reply pointer for a comment */
|
|
97
80
|
export function getCommentReplyPointer(comment) {
|
|
98
81
|
if (comment.kind !== COMMENT_KIND)
|
|
99
|
-
|
|
82
|
+
return null;
|
|
100
83
|
return getOrComputeCachedValue(comment, CommentReplyPointerSymbol, () => {
|
|
101
84
|
// check for address pointer first since it can also have E tags
|
|
102
85
|
const A = getCommentAddressPointer(comment.tags, false);
|
|
@@ -111,15 +94,21 @@ export function getCommentReplyPointer(comment) {
|
|
|
111
94
|
return null;
|
|
112
95
|
});
|
|
113
96
|
}
|
|
97
|
+
/** Checks if a pointer is a {@link CommentEventPointer} */
|
|
114
98
|
export function isCommentEventPointer(pointer) {
|
|
115
99
|
return (Reflect.has(pointer, "id") &&
|
|
116
100
|
Reflect.has(pointer, "kind") &&
|
|
117
101
|
!Reflect.has(pointer, "identifier") &&
|
|
118
102
|
typeof pointer.kind === "number");
|
|
119
103
|
}
|
|
104
|
+
/** Checks if a pointer is a {@link CommentAddressPointer} */
|
|
120
105
|
export function isCommentAddressPointer(pointer) {
|
|
121
106
|
return (Reflect.has(pointer, "identifier") &&
|
|
122
107
|
Reflect.has(pointer, "pubkey") &&
|
|
123
108
|
Reflect.has(pointer, "kind") &&
|
|
124
109
|
typeof pointer.kind === "number");
|
|
125
110
|
}
|
|
111
|
+
/** Checks if a comment event is valid */
|
|
112
|
+
export function isValidComment(comment) {
|
|
113
|
+
return (comment.kind === COMMENT_KIND && getCommentRootPointer(comment) !== null && getCommentReplyPointer(comment) !== null);
|
|
114
|
+
}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { NostrEvent } from "nostr-tools";
|
|
2
2
|
import { ProfilePointer } from "nostr-tools/nip19";
|
|
3
|
+
import { HiddenContentSigner } from "./hidden-content.js";
|
|
3
4
|
export declare const ContactsRelaysSymbol: unique symbol;
|
|
4
5
|
export declare const PublicContactsSymbol: unique symbol;
|
|
5
6
|
export declare const HiddenContactsSymbol: unique symbol;
|
|
6
|
-
|
|
7
|
+
/** Type for contact events with unlocked hidden tags */
|
|
8
|
+
export type UnlockedContacts = {
|
|
9
|
+
[HiddenContactsSymbol]: ProfilePointer[];
|
|
10
|
+
};
|
|
11
|
+
export declare function getRelaysFromContactsEvent(event: NostrEvent): Map<string, "inbox" | "outbox" | "all"> | null;
|
|
7
12
|
/** Merges any number of contact lists into a single list */
|
|
8
13
|
export declare function mergeContacts(...pointers: (ProfilePointer | undefined | (ProfilePointer | undefined)[])[]): ProfilePointer[];
|
|
9
14
|
/** Returns all public and hidden contacts from a contacts list event */
|
|
10
15
|
export declare function getContacts(event: NostrEvent): ProfilePointer[];
|
|
11
16
|
/** Returns only the public contacts from a contacts list event */
|
|
12
17
|
export declare function getPublicContacts(event: NostrEvent): ProfilePointer[];
|
|
18
|
+
/** Checks if the hidden contacts are unlocked */
|
|
19
|
+
export declare function isHiddenContactsUnlocked<T extends NostrEvent>(event: T): event is T & UnlockedContacts;
|
|
13
20
|
/** Returns only the hidden contacts from a contacts list event */
|
|
14
21
|
export declare function getHiddenContacts(event: NostrEvent): ProfilePointer[] | undefined;
|
|
22
|
+
/** Unlocks the hidden contacts */
|
|
23
|
+
export declare function unlockHiddenContacts(event: NostrEvent, signer: HiddenContentSigner): Promise<ProfilePointer[]>;
|
package/dist/helpers/contacts.js
CHANGED
|
@@ -2,7 +2,8 @@ import { getOrComputeCachedValue } from "./cache.js";
|
|
|
2
2
|
import { isSafeRelayURL } from "./relays.js";
|
|
3
3
|
import { isPTag, processTags } from "./tags.js";
|
|
4
4
|
import { getProfilePointerFromPTag } from "./pointers.js";
|
|
5
|
-
import { getHiddenTags,
|
|
5
|
+
import { getHiddenTags, isHiddenTagsUnlocked, unlockHiddenTags } from "./hidden-tags.js";
|
|
6
|
+
import { notifyEventUpdate } from "./index.js";
|
|
6
7
|
export const ContactsRelaysSymbol = Symbol.for("contacts-relays");
|
|
7
8
|
export const PublicContactsSymbol = Symbol.for("public-contacts");
|
|
8
9
|
export const HiddenContactsSymbol = Symbol.for("hidden-contacts");
|
|
@@ -51,9 +52,35 @@ export function getContacts(event) {
|
|
|
51
52
|
export function getPublicContacts(event) {
|
|
52
53
|
return getOrComputeCachedValue(event, PublicContactsSymbol, () => processTags(event.tags, (t) => (isPTag(t) ? t : undefined), getProfilePointerFromPTag));
|
|
53
54
|
}
|
|
55
|
+
/** Checks if the hidden contacts are unlocked */
|
|
56
|
+
export function isHiddenContactsUnlocked(event) {
|
|
57
|
+
return isHiddenTagsUnlocked(event) && Reflect.has(event, HiddenContactsSymbol);
|
|
58
|
+
}
|
|
54
59
|
/** Returns only the hidden contacts from a contacts list event */
|
|
55
60
|
export function getHiddenContacts(event) {
|
|
56
|
-
if (
|
|
61
|
+
if (isHiddenContactsUnlocked(event))
|
|
62
|
+
return event[HiddenContactsSymbol];
|
|
63
|
+
// Get hidden tags
|
|
64
|
+
const tags = getHiddenTags(event);
|
|
65
|
+
if (!tags)
|
|
57
66
|
return undefined;
|
|
58
|
-
|
|
67
|
+
// Parse tags
|
|
68
|
+
const contacts = processTags(tags, (t) => (isPTag(t) ? t : undefined), getProfilePointerFromPTag);
|
|
69
|
+
// Set cache and notify event store
|
|
70
|
+
Reflect.set(event, HiddenContactsSymbol, contacts);
|
|
71
|
+
return contacts;
|
|
72
|
+
}
|
|
73
|
+
/** Unlocks the hidden contacts */
|
|
74
|
+
export async function unlockHiddenContacts(event, signer) {
|
|
75
|
+
if (isHiddenContactsUnlocked(event))
|
|
76
|
+
return event[HiddenContactsSymbol];
|
|
77
|
+
// Unlock hidden tags
|
|
78
|
+
await unlockHiddenTags(event, signer);
|
|
79
|
+
// Get hidden contacts
|
|
80
|
+
const contacts = getHiddenContacts(event);
|
|
81
|
+
if (!contacts)
|
|
82
|
+
throw new Error("Failed to unlock hidden contacts");
|
|
83
|
+
// Set cache and notify event store
|
|
84
|
+
notifyEventUpdate(event);
|
|
85
|
+
return contacts;
|
|
59
86
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { kinds } from "nostr-tools";
|
|
2
2
|
import { catchError, combineLatest, distinct, EMPTY, filter, isObservable, map, merge, mergeMap, of, switchMap, } from "rxjs";
|
|
3
3
|
import { logger } from "../logger.js";
|
|
4
|
-
import { canHaveEncryptedContent, getEncryptedContent,
|
|
4
|
+
import { canHaveEncryptedContent, getEncryptedContent, isEncryptedContentUnlocked, setEncryptedContentCache, } from "./encrypted-content.js";
|
|
5
5
|
import { notifyEventUpdate } from "./event.js";
|
|
6
6
|
import { getGiftWrapSeal, getSealGiftWrap, getSealRumor } from "./gift-wraps.js";
|
|
7
7
|
/** A symbol that is used to mark encrypted content as being from a cache */
|
|
@@ -32,7 +32,7 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
|
|
|
32
32
|
const restore = eventStore.insert$
|
|
33
33
|
.pipe(
|
|
34
34
|
// Look for events that support encrypted content and are locked
|
|
35
|
-
filter((e) => canHaveEncryptedContent(e.kind) &&
|
|
35
|
+
filter((e) => canHaveEncryptedContent(e.kind) && isEncryptedContentUnlocked(e) === false),
|
|
36
36
|
// Get the encrypted content from storage
|
|
37
37
|
mergeMap((event) =>
|
|
38
38
|
// Wait for storage to be available
|
|
@@ -52,11 +52,11 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
|
|
|
52
52
|
const restoreSeals = eventStore.update$
|
|
53
53
|
.pipe(
|
|
54
54
|
// Look for gift wraps that are unlocked
|
|
55
|
-
filter((e) => e.kind === kinds.GiftWrap &&
|
|
55
|
+
filter((e) => e.kind === kinds.GiftWrap && isEncryptedContentUnlocked(e)),
|
|
56
56
|
// Get the seal event
|
|
57
57
|
map((gift) => getGiftWrapSeal(gift)),
|
|
58
58
|
// Look for gift wraps with locked seals
|
|
59
|
-
filter((seal) => seal !== undefined &&
|
|
59
|
+
filter((seal) => seal !== undefined && isEncryptedContentUnlocked(seal) === false),
|
|
60
60
|
// Only attempt to unlock seals once
|
|
61
61
|
distinct((seal) => seal.id),
|
|
62
62
|
// Get encrypted content from storage
|
|
@@ -84,7 +84,7 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
|
|
|
84
84
|
.pipe(
|
|
85
85
|
// Look for events that support encrypted content and are unlocked and not from the cache
|
|
86
86
|
filter(([event]) => canHaveEncryptedContent(event.kind) &&
|
|
87
|
-
|
|
87
|
+
isEncryptedContentUnlocked(event) &&
|
|
88
88
|
!isEncryptedContentFromCache(event)),
|
|
89
89
|
// Only persist the encrypted content once
|
|
90
90
|
distinct(([event]) => event.id))
|
|
@@ -106,13 +106,13 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
|
|
|
106
106
|
const persistSeals = combineLatest([merge(eventStore.update$, eventStore.insert$), storage$])
|
|
107
107
|
.pipe(
|
|
108
108
|
// Look for gift wraps that are unlocked
|
|
109
|
-
filter(([event]) => event.kind === kinds.GiftWrap &&
|
|
109
|
+
filter(([event]) => event.kind === kinds.GiftWrap && isEncryptedContentUnlocked(event)),
|
|
110
110
|
// Get the seal event
|
|
111
111
|
map(([gift, storage]) => [getGiftWrapSeal(gift), storage]),
|
|
112
112
|
// Make sure the seal is defined
|
|
113
113
|
filter(([seal]) => seal !== undefined),
|
|
114
114
|
// Make sure seal is unlocked and not from cache
|
|
115
|
-
filter(([seal]) =>
|
|
115
|
+
filter(([seal]) => isEncryptedContentUnlocked(seal) && !isEncryptedContentFromCache(seal)),
|
|
116
116
|
// Only persist the seal once
|
|
117
117
|
distinct(([seal]) => seal.id))
|
|
118
118
|
.subscribe(async ([seal, storage]) => {
|
|
@@ -10,7 +10,12 @@ export interface EncryptedContentSigner {
|
|
|
10
10
|
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
+
/** Encryption method types */
|
|
13
14
|
export type EncryptionMethod = "nip04" | "nip44";
|
|
15
|
+
/** Type for an event who's encrypted content is unlocked */
|
|
16
|
+
export type UnlockedEncryptedContent = {
|
|
17
|
+
[EncryptedContentSymbol]: string;
|
|
18
|
+
};
|
|
14
19
|
/** A pair of encryption methods for encrypting and decrypting event content */
|
|
15
20
|
export interface EncryptionMethods {
|
|
16
21
|
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
|
@@ -37,14 +42,16 @@ export declare function hasEncryptedContent<T extends {
|
|
|
37
42
|
content: string;
|
|
38
43
|
}>(event: T): boolean;
|
|
39
44
|
/** Returns the encrypted content for an event if it is unlocked */
|
|
45
|
+
export declare function getEncryptedContent<T extends UnlockedEncryptedContent>(event: T): string;
|
|
40
46
|
export declare function getEncryptedContent<T extends object>(event: T): string | undefined;
|
|
41
|
-
/** Checks if the encrypted content is
|
|
42
|
-
export declare function
|
|
47
|
+
/** Checks if the encrypted content is unlocked and casts it to the {@link UnlockedEncryptedContent} type */
|
|
48
|
+
export declare function isEncryptedContentUnlocked<T extends object>(event: T): event is T & UnlockedEncryptedContent;
|
|
43
49
|
/**
|
|
44
50
|
* Unlocks the encrypted content in an event and caches it
|
|
45
51
|
* @param event The event with content to decrypt
|
|
46
52
|
* @param pubkey The other pubkey that encrypted the content
|
|
47
53
|
* @param signer A signer to use to decrypt the content
|
|
54
|
+
* @throws If the event kind does not support encrypted content
|
|
48
55
|
*/
|
|
49
56
|
export declare function unlockEncryptedContent<T extends {
|
|
50
57
|
kind: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { kinds } from "nostr-tools";
|
|
2
|
-
import {
|
|
2
|
+
import { notifyEventUpdate } from "./event.js";
|
|
3
3
|
/** A symbol use to store the encrypted content of an event in memory */
|
|
4
4
|
export const EncryptedContentSymbol = Symbol.for("encrypted-content");
|
|
5
5
|
/** Various event kinds that can have encrypted content and which encryption method they use */
|
|
@@ -39,37 +39,40 @@ export function canHaveEncryptedContent(kind) {
|
|
|
39
39
|
export function hasEncryptedContent(event) {
|
|
40
40
|
return event.content.length > 0;
|
|
41
41
|
}
|
|
42
|
-
/** Returns the encrypted content for an event if it is unlocked */
|
|
43
42
|
export function getEncryptedContent(event) {
|
|
44
43
|
return Reflect.get(event, EncryptedContentSymbol);
|
|
45
44
|
}
|
|
46
|
-
/** Checks if the encrypted content is
|
|
47
|
-
export function
|
|
48
|
-
return Reflect.has(event, EncryptedContentSymbol) ===
|
|
45
|
+
/** Checks if the encrypted content is unlocked and casts it to the {@link UnlockedEncryptedContent} type */
|
|
46
|
+
export function isEncryptedContentUnlocked(event) {
|
|
47
|
+
return Reflect.has(event, EncryptedContentSymbol) === true;
|
|
49
48
|
}
|
|
50
49
|
/**
|
|
51
50
|
* Unlocks the encrypted content in an event and caches it
|
|
52
51
|
* @param event The event with content to decrypt
|
|
53
52
|
* @param pubkey The other pubkey that encrypted the content
|
|
54
53
|
* @param signer A signer to use to decrypt the content
|
|
54
|
+
* @throws If the event kind does not support encrypted content
|
|
55
55
|
*/
|
|
56
56
|
export async function unlockEncryptedContent(event, pubkey, signer) {
|
|
57
|
+
if (!canHaveEncryptedContent(event.kind))
|
|
58
|
+
throw new Error("Event kind does not support encrypted content");
|
|
59
|
+
// Get the encryption methods from the signer
|
|
57
60
|
const encryption = getEncryptedContentEncryptionMethods(event.kind, signer);
|
|
58
61
|
const plaintext = await encryption.decrypt(pubkey, event.content);
|
|
62
|
+
// Set the cached value and trigger update
|
|
59
63
|
setEncryptedContentCache(event, plaintext);
|
|
64
|
+
// Return the decrypted content
|
|
60
65
|
return plaintext;
|
|
61
66
|
}
|
|
62
67
|
/** Sets the encrypted content on an event and updates it if its part of an event store */
|
|
63
68
|
export function setEncryptedContentCache(event, plaintext) {
|
|
64
69
|
Reflect.set(event, EncryptedContentSymbol, plaintext);
|
|
65
70
|
// if the event has been added to an event store, notify it
|
|
66
|
-
|
|
67
|
-
notifyEventUpdate(event);
|
|
71
|
+
notifyEventUpdate(event);
|
|
68
72
|
}
|
|
69
73
|
/** Removes the encrypted content cache on an event */
|
|
70
74
|
export function lockEncryptedContent(event) {
|
|
71
75
|
Reflect.deleteProperty(event, EncryptedContentSymbol);
|
|
72
76
|
// if the event has been added to an event store, notify it
|
|
73
|
-
|
|
74
|
-
notifyEventUpdate(event);
|
|
77
|
+
notifyEventUpdate(event);
|
|
75
78
|
}
|
|
@@ -9,7 +9,9 @@ import { IEventStoreStreams } from "../event-store/interface.js";
|
|
|
9
9
|
* @param opts.maxBatchSize - The maximum number of events to write in a batch
|
|
10
10
|
* @returns A function to stop the process
|
|
11
11
|
*/
|
|
12
|
-
export declare function
|
|
12
|
+
export declare function persistEventsToCache(eventStore: IEventStoreStreams, write: (events: NostrEvent[]) => Promise<void>, opts?: {
|
|
13
13
|
maxBatchSize?: number;
|
|
14
14
|
batchTime?: number;
|
|
15
15
|
}): () => void;
|
|
16
|
+
/** @deprecated Use persistEventsToCache instead */
|
|
17
|
+
export declare const presistEventsToCache: typeof persistEventsToCache;
|
|
@@ -11,7 +11,7 @@ const log = logger.extend("event-cache");
|
|
|
11
11
|
* @param opts.maxBatchSize - The maximum number of events to write in a batch
|
|
12
12
|
* @returns A function to stop the process
|
|
13
13
|
*/
|
|
14
|
-
export function
|
|
14
|
+
export function persistEventsToCache(eventStore, write, opts) {
|
|
15
15
|
const time = opts?.batchTime ?? 5_000;
|
|
16
16
|
// Save all new events to the cache
|
|
17
17
|
const sub = eventStore.insert$
|
|
@@ -30,3 +30,5 @@ export function presistEventsToCache(eventStore, write, opts) {
|
|
|
30
30
|
});
|
|
31
31
|
return () => sub.unsubscribe();
|
|
32
32
|
}
|
|
33
|
+
/** @deprecated Use persistEventsToCache instead */
|
|
34
|
+
export const presistEventsToCache = persistEventsToCache;
|
|
@@ -10,5 +10,11 @@ export declare function getTagValue<T extends {
|
|
|
10
10
|
tags: string[][];
|
|
11
11
|
content: string;
|
|
12
12
|
}>(event: T, name: string): string | undefined;
|
|
13
|
+
/** Checks if an event has a public name / value tag*/
|
|
14
|
+
export declare function hasNameValueTag<T extends {
|
|
15
|
+
kind: number;
|
|
16
|
+
tags: string[][];
|
|
17
|
+
content: string;
|
|
18
|
+
}>(event: T, name: string, value: string): boolean;
|
|
13
19
|
/** Returns a Set of tag names and values that are indexable */
|
|
14
20
|
export declare function getIndexableTags(event: NostrEvent): Set<string>;
|
|
@@ -13,6 +13,10 @@ export function getTagValue(event, name) {
|
|
|
13
13
|
return hiddenValue;
|
|
14
14
|
return event.tags.find((t) => t[0] === name)?.[1];
|
|
15
15
|
}
|
|
16
|
+
/** Checks if an event has a public name / value tag*/
|
|
17
|
+
export function hasNameValueTag(event, name, value) {
|
|
18
|
+
return event.tags.some((t) => t[0] === name && t[1] === value);
|
|
19
|
+
}
|
|
16
20
|
/** Returns a Set of tag names and values that are indexable */
|
|
17
21
|
export function getIndexableTags(event) {
|
|
18
22
|
let indexable = Reflect.get(event, EventIndexableTagsSymbol);
|
package/dist/helpers/event.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { NostrEvent, VerifiedEvent } from "nostr-tools";
|
|
2
2
|
import { IEventStore } from "../event-store/interface.js";
|
|
3
|
+
export { NostrEvent, EventTemplate, UnsignedEvent, verifiedSymbol, verifyEvent, VerifiedEvent } from "nostr-tools/pure";
|
|
4
|
+
export { bytesToHex, hexToBytes, insertEventIntoAscendingList, insertEventIntoDescendingList, binarySearch, } from "nostr-tools/utils";
|
|
5
|
+
export * as kinds from "nostr-tools/kinds";
|
|
6
|
+
/** An event with a known kind. this is used to know if events have been validated */
|
|
7
|
+
export type KnownEvent<K extends number> = Omit<NostrEvent, "kind"> & {
|
|
8
|
+
kind: K;
|
|
9
|
+
};
|
|
3
10
|
/** A symbol on an event that marks which event store its part of */
|
|
4
11
|
export declare const EventStoreSymbol: unique symbol;
|
|
5
12
|
export declare const EventUIDSymbol: unique symbol;
|
|
@@ -38,7 +45,7 @@ export declare function isFromCache(event: NostrEvent): boolean;
|
|
|
38
45
|
/** Returns the EventStore of an event if its been added to one */
|
|
39
46
|
export declare function getParentEventStore<T extends object>(event: T): IEventStore | undefined;
|
|
40
47
|
/** Notifies the events parent store that an event has been updated */
|
|
41
|
-
export declare function notifyEventUpdate(event:
|
|
48
|
+
export declare function notifyEventUpdate(event: any): void;
|
|
42
49
|
/** Returns the replaceable identifier for a replaceable event */
|
|
43
50
|
export declare function getReplaceableIdentifier(event: NostrEvent): string;
|
|
44
51
|
/** Checks if an event is a NIP-70 protected event */
|
package/dist/helpers/event.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { verifiedSymbol } from "nostr-tools";
|
|
2
2
|
import { isAddressableKind, isReplaceableKind } from "nostr-tools/kinds";
|
|
3
3
|
import { getOrComputeCachedValue } from "./cache.js";
|
|
4
|
+
// Re-export types from nostr-tools
|
|
5
|
+
export { verifiedSymbol, verifyEvent } from "nostr-tools/pure";
|
|
6
|
+
export { bytesToHex, hexToBytes, insertEventIntoAscendingList, insertEventIntoDescendingList, binarySearch, } from "nostr-tools/utils";
|
|
7
|
+
export * as kinds from "nostr-tools/kinds";
|
|
4
8
|
/** A symbol on an event that marks which event store its part of */
|
|
5
9
|
export const EventStoreSymbol = Symbol.for("event-store");
|
|
6
10
|
export const EventUIDSymbol = Symbol.for("event-uid");
|
|
@@ -81,6 +85,8 @@ export function getParentEventStore(event) {
|
|
|
81
85
|
}
|
|
82
86
|
/** Notifies the events parent store that an event has been updated */
|
|
83
87
|
export function notifyEventUpdate(event) {
|
|
88
|
+
if (!isEvent(event))
|
|
89
|
+
return;
|
|
84
90
|
const eventStore = getParentEventStore(event);
|
|
85
91
|
if (eventStore)
|
|
86
92
|
eventStore.update(event);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NostrEvent } from "nostr-tools";
|
|
2
2
|
export type FileMetadata = {
|
|
3
3
|
/** URL of the file */
|
|
4
|
-
url
|
|
4
|
+
url?: string;
|
|
5
5
|
/** MIME type */
|
|
6
6
|
type?: string;
|
|
7
7
|
/** sha256 hash of the file */
|
|
@@ -32,24 +32,19 @@ export type FileMetadata = {
|
|
|
32
32
|
/** fallback URLs */
|
|
33
33
|
fallback?: string[];
|
|
34
34
|
};
|
|
35
|
+
/** Alias for {@link FileMetadata} */
|
|
35
36
|
export type MediaAttachment = FileMetadata;
|
|
36
37
|
/**
|
|
37
38
|
* Parses file metadata tags into {@link FileMetadata}
|
|
38
39
|
* @throws
|
|
39
40
|
*/
|
|
40
41
|
export declare function parseFileMetadataTags(tags: string[][]): FileMetadata;
|
|
41
|
-
/**
|
|
42
|
-
* Parses a imeta tag into a {@link FileMetadata}
|
|
43
|
-
* @throws
|
|
44
|
-
*/
|
|
42
|
+
/** Parses a imeta tag into a {@link FileMetadata} */
|
|
45
43
|
export declare function getFileMetadataFromImetaTag(tag: string[]): FileMetadata;
|
|
46
44
|
export declare const MediaAttachmentsSymbol: unique symbol;
|
|
47
45
|
/** Gets all the media attachments on an event */
|
|
48
46
|
export declare function getMediaAttachments(event: NostrEvent): FileMetadata[];
|
|
49
|
-
/**
|
|
50
|
-
* Gets {@link FileMetadata} for a NIP-94 kind 1063 event
|
|
51
|
-
* @throws
|
|
52
|
-
*/
|
|
47
|
+
/** Gets {@link FileMetadata} for a NIP-94 kind 1063 event */
|
|
53
48
|
export declare function getFileMetadata(file: NostrEvent): FileMetadata;
|
|
54
49
|
/** Returns the last 64 length hex string in a URL */
|
|
55
50
|
export declare function getSha256FromURL(url: string | URL): string | undefined;
|
|
@@ -16,8 +16,6 @@ export function parseFileMetadataTags(tags) {
|
|
|
16
16
|
break;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
if (!fields.url)
|
|
20
|
-
throw new Error("Missing required url in file metadata");
|
|
21
19
|
const metadata = { url: fields.url, fallback };
|
|
22
20
|
// parse size
|
|
23
21
|
if (fields.size)
|
|
@@ -47,10 +45,7 @@ export function parseFileMetadataTags(tags) {
|
|
|
47
45
|
metadata.blurhash = fields.blurhash;
|
|
48
46
|
return metadata;
|
|
49
47
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Parses a imeta tag into a {@link FileMetadata}
|
|
52
|
-
* @throws
|
|
53
|
-
*/
|
|
48
|
+
/** Parses a imeta tag into a {@link FileMetadata} */
|
|
54
49
|
export function getFileMetadataFromImetaTag(tag) {
|
|
55
50
|
const parts = tag.slice(1);
|
|
56
51
|
const tags = [];
|
|
@@ -81,10 +76,7 @@ export function getMediaAttachments(event) {
|
|
|
81
76
|
.filter((a) => !!a);
|
|
82
77
|
});
|
|
83
78
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Gets {@link FileMetadata} for a NIP-94 kind 1063 event
|
|
86
|
-
* @throws
|
|
87
|
-
*/
|
|
79
|
+
/** Gets {@link FileMetadata} for a NIP-94 kind 1063 event */
|
|
88
80
|
export function getFileMetadata(file) {
|
|
89
81
|
return parseFileMetadataTags(file.tags);
|
|
90
82
|
}
|
package/dist/helpers/filter.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Filter, NostrEvent } from "nostr-tools";
|
|
2
|
+
export { Filter } from "nostr-tools/filter";
|
|
2
3
|
/**
|
|
3
|
-
* Copied from nostr-tools and modified to use getIndexableTags
|
|
4
|
+
* Copied from nostr-tools and modified to use {@link getIndexableTags}
|
|
4
5
|
* @see https://github.com/nbd-wtf/nostr-tools/blob/a61cde77eacc9518001f11d7f67f1a50ae05fd80/filter.ts
|
|
5
6
|
*/
|
|
6
7
|
export declare function matchFilter(filter: Filter, event: NostrEvent): boolean;
|
|
7
|
-
/** Copied from nostr-tools */
|
|
8
|
+
/** Copied from nostr-tools and modified to use {@link matchFilter} */
|
|
8
9
|
export declare function matchFilters(filters: Filter[], event: NostrEvent): boolean;
|
|
9
|
-
/** Copied from nostr-tools and modified to support undefined
|
|
10
|
+
/** Copied from nostr-tools and modified to support undefined values */
|
|
10
11
|
export declare function mergeFilters(...filters: Filter[]): Filter;
|
|
11
12
|
/** Check if two filters are equal */
|
|
12
13
|
export declare function isFilterEqual(a: Filter | Filter[], b: Filter | Filter[]): boolean;
|
package/dist/helpers/filter.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import equal from "fast-deep-equal";
|
|
2
2
|
import { getIndexableTags } from "./event-tags.js";
|
|
3
3
|
/**
|
|
4
|
-
* Copied from nostr-tools and modified to use getIndexableTags
|
|
4
|
+
* Copied from nostr-tools and modified to use {@link getIndexableTags}
|
|
5
5
|
* @see https://github.com/nbd-wtf/nostr-tools/blob/a61cde77eacc9518001f11d7f67f1a50ae05fd80/filter.ts
|
|
6
6
|
*/
|
|
7
7
|
export function matchFilter(filter, event) {
|
|
@@ -31,7 +31,7 @@ export function matchFilter(filter, event) {
|
|
|
31
31
|
return false;
|
|
32
32
|
return true;
|
|
33
33
|
}
|
|
34
|
-
/** Copied from nostr-tools */
|
|
34
|
+
/** Copied from nostr-tools and modified to use {@link matchFilter} */
|
|
35
35
|
export function matchFilters(filters, event) {
|
|
36
36
|
for (let i = 0; i < filters.length; i++) {
|
|
37
37
|
if (matchFilter(filters[i], event)) {
|
|
@@ -40,7 +40,7 @@ export function matchFilters(filters, event) {
|
|
|
40
40
|
}
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
|
-
/** Copied from nostr-tools and modified to support undefined
|
|
43
|
+
/** Copied from nostr-tools and modified to support undefined values */
|
|
44
44
|
export function mergeFilters(...filters) {
|
|
45
45
|
let result = {};
|
|
46
46
|
for (let i = 0; i < filters.length; i++) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { NostrEvent, UnsignedEvent } from "nostr-tools";
|
|
2
|
-
import {
|
|
1
|
+
import { kinds, NostrEvent, UnsignedEvent } from "nostr-tools";
|
|
2
|
+
import { EventMemory } from "../event-store/event-memory.js";
|
|
3
3
|
import { EncryptedContentSigner } from "./encrypted-content.js";
|
|
4
|
+
import { KnownEvent } from "./event.js";
|
|
4
5
|
/**
|
|
5
6
|
* An internal event set to keep track of seals and rumors
|
|
6
|
-
* This is
|
|
7
|
+
* This is intentionally isolated from the main applications event store so to prevent seals and rumors from being leaked
|
|
7
8
|
*/
|
|
8
|
-
export declare const internalGiftWrapEvents:
|
|
9
|
+
export declare const internalGiftWrapEvents: EventMemory;
|
|
9
10
|
export type Rumor = UnsignedEvent & {
|
|
10
11
|
id: string;
|
|
11
12
|
};
|
|
@@ -15,24 +16,43 @@ export declare const SealSymbol: unique symbol;
|
|
|
15
16
|
export declare const RumorSymbol: unique symbol;
|
|
16
17
|
/** Used to store a reference to the parent gift wrap event on seals (upstream) */
|
|
17
18
|
export declare const GiftWrapSymbol: unique symbol;
|
|
19
|
+
/** A gift wrap event that knows its seal event */
|
|
20
|
+
export type UnlockedGiftWrapEvent = KnownEvent<kinds.GiftWrap> & {
|
|
21
|
+
/** Downstream seal event */
|
|
22
|
+
[SealSymbol]: UnlockedSeal;
|
|
23
|
+
};
|
|
24
|
+
/** A seal that knows its parent gift wrap event */
|
|
25
|
+
export type UnlockedSeal = KnownEvent<kinds.Seal> & {
|
|
26
|
+
/** Upstream gift wrap event */
|
|
27
|
+
[SealSymbol]: UnlockedGiftWrapEvent;
|
|
28
|
+
/** Downstream rumor event */
|
|
29
|
+
[RumorSymbol]: Rumor;
|
|
30
|
+
};
|
|
18
31
|
/** Checks if an event is a rumor (normal event with "id" and no "sig") */
|
|
19
32
|
export declare function isRumor(event: any): event is Rumor;
|
|
20
33
|
/** Returns all the parent gift wraps for a seal event */
|
|
21
|
-
export declare function getSealGiftWrap(seal:
|
|
34
|
+
export declare function getSealGiftWrap(seal: UnlockedSeal): UnlockedGiftWrapEvent;
|
|
35
|
+
export declare function getSealGiftWrap(seal: NostrEvent): UnlockedGiftWrapEvent | undefined;
|
|
22
36
|
/** Returns all the parent seals for a rumor event */
|
|
23
|
-
export declare function getRumorSeals(rumor: Rumor):
|
|
37
|
+
export declare function getRumorSeals(rumor: Rumor): UnlockedSeal[];
|
|
24
38
|
/** Returns all the parent gift wraps for a rumor event */
|
|
25
|
-
export declare function getRumorGiftWraps(rumor: Rumor):
|
|
26
|
-
/** Checks if a seal event is locked */
|
|
27
|
-
export declare function
|
|
28
|
-
/**
|
|
39
|
+
export declare function getRumorGiftWraps(rumor: Rumor): UnlockedGiftWrapEvent[];
|
|
40
|
+
/** Checks if a seal event is locked and casts it to the {@link UnlockedSeal} type */
|
|
41
|
+
export declare function isSealUnlocked(seal: NostrEvent): seal is UnlockedSeal;
|
|
42
|
+
/** Returns if a gift-wrap event or gift-wrap seal is locked */
|
|
43
|
+
export declare function isGiftWrapUnlocked(gift: NostrEvent): gift is UnlockedGiftWrapEvent;
|
|
44
|
+
/**
|
|
45
|
+
* Gets the rumor from a seal event
|
|
46
|
+
* @throws {Error} If the author of the rumor event does not match the author of the seal
|
|
47
|
+
*/
|
|
48
|
+
export declare function getSealRumor(seal: UnlockedSeal): Rumor;
|
|
29
49
|
export declare function getSealRumor(seal: NostrEvent): Rumor | undefined;
|
|
30
|
-
/** Returns the seal event in a gift-wrap
|
|
50
|
+
/** Returns the seal event in a gift-wrap -> seal (downstream) */
|
|
51
|
+
export declare function getGiftWrapSeal(gift: UnlockedGiftWrapEvent): UnlockedSeal;
|
|
31
52
|
export declare function getGiftWrapSeal(gift: NostrEvent): NostrEvent | undefined;
|
|
32
|
-
/** Returns the unsigned rumor in the gift-wrap */
|
|
53
|
+
/** Returns the unsigned rumor in the gift-wrap -> seal -> rumor (downstream) */
|
|
54
|
+
export declare function getGiftWrapRumor(gift: UnlockedGiftWrapEvent): Rumor;
|
|
33
55
|
export declare function getGiftWrapRumor(gift: NostrEvent): Rumor | undefined;
|
|
34
|
-
/** Returns if a gift-wrap event or gift-wrap seal is locked */
|
|
35
|
-
export declare function isGiftWrapLocked(gift: NostrEvent): boolean;
|
|
36
56
|
/**
|
|
37
57
|
* Unlocks a seal event and returns the rumor event
|
|
38
58
|
* @throws {Error} If the author of the rumor event does not match the author of the seal
|
|
@@ -43,4 +63,5 @@ export declare function unlockSeal(seal: NostrEvent, signer: EncryptedContentSig
|
|
|
43
63
|
* @throws {Error} If the author of the rumor event does not match the author of the seal
|
|
44
64
|
*/
|
|
45
65
|
export declare function unlockGiftWrap(gift: NostrEvent, signer: EncryptedContentSigner): Promise<Rumor>;
|
|
66
|
+
/** Locks a gift-wrap event and seals its seal event */
|
|
46
67
|
export declare function lockGiftWrap(gift: NostrEvent): void;
|