applesauce-core 0.10.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.
- package/dist/__tests__/fixtures.d.ts +8 -0
- package/dist/__tests__/fixtures.js +20 -0
- package/dist/event-store/__tests__/event-store.test.js +259 -0
- package/dist/event-store/database.d.ts +6 -4
- package/dist/event-store/database.js +13 -7
- package/dist/event-store/event-store.d.ts +30 -16
- package/dist/event-store/event-store.js +248 -309
- package/dist/helpers/__tests__/blossom.test.js +13 -0
- package/dist/helpers/__tests__/comment.test.js +235 -0
- package/dist/helpers/__tests__/emoji.test.d.ts +1 -0
- package/dist/helpers/__tests__/emoji.test.js +15 -0
- package/dist/helpers/__tests__/event.test.d.ts +1 -0
- package/dist/helpers/__tests__/event.test.js +36 -0
- package/dist/helpers/__tests__/file-metadata.test.d.ts +1 -0
- package/dist/helpers/__tests__/file-metadata.test.js +103 -0
- package/dist/helpers/__tests__/hidden-tags.test.d.ts +1 -0
- package/dist/helpers/{hidden-tags.test.js → __tests__/hidden-tags.test.js} +2 -1
- package/dist/helpers/__tests__/mailboxes.test.d.ts +1 -0
- package/dist/helpers/{mailboxes.test.js → __tests__/mailboxes.test.js} +1 -1
- package/dist/helpers/__tests__/relays.test.d.ts +1 -0
- package/dist/helpers/__tests__/relays.test.js +21 -0
- package/dist/helpers/__tests__/tags.test.d.ts +1 -0
- package/dist/helpers/__tests__/tags.test.js +24 -0
- package/dist/helpers/__tests__/threading.test.d.ts +1 -0
- package/dist/helpers/{threading.test.js → __tests__/threading.test.js} +1 -1
- package/dist/helpers/blossom.d.ts +9 -0
- package/dist/helpers/blossom.js +22 -0
- package/dist/helpers/bookmarks.d.ts +15 -0
- package/dist/helpers/bookmarks.js +27 -0
- package/dist/helpers/channels.d.ts +10 -0
- package/dist/helpers/channels.js +27 -0
- package/dist/helpers/comment.d.ts +3 -4
- package/dist/helpers/comment.js +20 -16
- package/dist/helpers/contacts.d.ts +3 -0
- package/dist/helpers/contacts.js +25 -0
- package/dist/helpers/dns-identity.d.ts +7 -0
- package/dist/helpers/dns-identity.js +10 -0
- package/dist/helpers/emoji.d.ts +3 -1
- package/dist/helpers/emoji.js +2 -2
- package/dist/helpers/event.d.ts +8 -2
- package/dist/helpers/event.js +29 -11
- package/dist/helpers/file-metadata.d.ts +55 -0
- package/dist/helpers/file-metadata.js +99 -0
- package/dist/helpers/filter.d.ts +4 -0
- package/dist/helpers/filter.js +34 -1
- package/dist/helpers/groups.d.ts +24 -0
- package/dist/helpers/groups.js +39 -0
- package/dist/helpers/hidden-tags.d.ts +15 -15
- package/dist/helpers/hidden-tags.js +9 -31
- package/dist/helpers/index.d.ts +13 -1
- package/dist/helpers/index.js +13 -1
- package/dist/helpers/lists.d.ts +28 -0
- package/dist/helpers/lists.js +65 -0
- package/dist/helpers/mailboxes.js +16 -9
- package/dist/helpers/mutes.d.ts +14 -0
- package/dist/helpers/mutes.js +23 -0
- package/dist/helpers/picture-post.d.ts +4 -0
- package/dist/helpers/picture-post.js +6 -0
- package/dist/helpers/pointers.js +13 -17
- package/dist/helpers/profile.d.ts +6 -1
- package/dist/helpers/profile.js +4 -0
- package/dist/helpers/relays.d.ts +6 -3
- package/dist/helpers/relays.js +25 -18
- package/dist/helpers/share.d.ts +4 -0
- package/dist/helpers/share.js +12 -0
- package/dist/helpers/tags.d.ts +17 -0
- package/dist/helpers/tags.js +28 -6
- package/dist/helpers/threading.js +3 -3
- package/dist/helpers/url.d.ts +7 -0
- package/dist/helpers/url.js +27 -0
- package/dist/helpers/user-status.d.ts +18 -0
- package/dist/helpers/user-status.js +21 -0
- package/dist/observable/__tests__/claim-events.test.d.ts +1 -0
- package/dist/observable/__tests__/claim-events.test.js +23 -0
- package/dist/observable/__tests__/claim-latest.test.d.ts +1 -0
- package/dist/observable/__tests__/claim-latest.test.js +37 -0
- package/dist/observable/__tests__/simple-timeout.test.d.ts +1 -0
- package/dist/observable/__tests__/simple-timeout.test.js +34 -0
- package/dist/observable/claim-events.d.ts +5 -0
- package/dist/observable/claim-events.js +28 -0
- package/dist/observable/claim-latest.d.ts +4 -0
- package/dist/observable/claim-latest.js +20 -0
- package/dist/observable/{get-value.d.ts → get-observable-value.d.ts} +1 -1
- package/dist/observable/{get-value.js → get-observable-value.js} +3 -8
- package/dist/observable/index.d.ts +2 -1
- package/dist/observable/index.js +2 -1
- package/dist/observable/share-latest-value.d.ts +2 -4
- package/dist/observable/share-latest-value.js +19 -16
- package/dist/observable/simple-timeout.d.ts +4 -0
- package/dist/observable/simple-timeout.js +6 -0
- package/dist/queries/blossom.d.ts +2 -0
- package/dist/queries/blossom.js +10 -0
- package/dist/queries/bookmarks.d.ts +8 -0
- package/dist/queries/bookmarks.js +23 -0
- package/dist/queries/channels.d.ts +11 -0
- package/dist/queries/channels.js +73 -0
- package/dist/queries/contacts.d.ts +3 -0
- package/dist/queries/contacts.js +12 -0
- package/dist/queries/index.d.ts +6 -0
- package/dist/queries/index.js +6 -0
- package/dist/queries/mutes.d.ts +8 -0
- package/dist/queries/mutes.js +23 -0
- package/dist/queries/pins.d.ts +3 -0
- package/dist/queries/pins.js +12 -0
- package/dist/queries/simple.d.ts +2 -2
- package/dist/queries/thread.js +1 -1
- package/dist/queries/user-status.d.ts +11 -0
- package/dist/queries/user-status.js +39 -0
- package/dist/query-store/index.d.ts +1 -57
- package/dist/query-store/index.js +1 -66
- package/dist/query-store/query-store.d.ts +51 -0
- package/dist/query-store/query-store.js +88 -0
- package/dist/query-store/query-store.test.d.ts +1 -0
- package/dist/query-store/query-store.test.js +33 -0
- package/package.json +19 -8
- package/dist/helpers/media-attachment.d.ts +0 -33
- package/dist/helpers/media-attachment.js +0 -60
- /package/dist/{helpers/hidden-tags.test.d.ts → event-store/__tests__/event-store.test.d.ts} +0 -0
- /package/dist/helpers/{mailboxes.test.d.ts → __tests__/blossom.test.d.ts} +0 -0
- /package/dist/helpers/{threading.test.d.ts → __tests__/comment.test.d.ts} +0 -0
package/dist/helpers/comment.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getExternalPointerFromTag } from "./external-id.js";
|
|
2
2
|
import { getOrComputeCachedValue } from "./cache.js";
|
|
3
3
|
import { getAddressPointerFromATag } from "./pointers.js";
|
|
4
|
-
import {
|
|
4
|
+
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");
|
|
@@ -10,16 +10,18 @@ export const CommentReplyPointerSymbol = Symbol.for("comment-reply-pointer");
|
|
|
10
10
|
* @throws
|
|
11
11
|
*/
|
|
12
12
|
export function getCommentEventPointer(tags, root = false) {
|
|
13
|
-
const
|
|
13
|
+
const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
|
|
14
14
|
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
|
|
15
|
-
if (
|
|
15
|
+
if (eTag) {
|
|
16
16
|
if (!kind)
|
|
17
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;
|
|
18
20
|
const pointer = {
|
|
19
|
-
id:
|
|
21
|
+
id: eTag[1],
|
|
20
22
|
kind: parseInt(kind),
|
|
21
|
-
pubkey:
|
|
22
|
-
relay:
|
|
23
|
+
pubkey: eTag[3] || rootPubkey || undefined,
|
|
24
|
+
relay: eTag[2] && isSafeRelayURL(eTag[2]) ? eTag[2] : undefined,
|
|
23
25
|
};
|
|
24
26
|
return pointer;
|
|
25
27
|
}
|
|
@@ -30,16 +32,19 @@ export function getCommentEventPointer(tags, root = false) {
|
|
|
30
32
|
* @throws
|
|
31
33
|
*/
|
|
32
34
|
export function getCommentAddressPointer(tags, root = false) {
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
+
const aTag = tags.find((t) => t[0] === (root ? "A" : "a"));
|
|
36
|
+
const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
|
|
35
37
|
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
|
|
36
|
-
if (
|
|
38
|
+
if (aTag) {
|
|
37
39
|
if (!kind)
|
|
38
40
|
throw new Error("Missing kind tag");
|
|
41
|
+
const addressPointer = getAddressPointerFromATag(aTag);
|
|
39
42
|
const pointer = {
|
|
40
|
-
id,
|
|
41
|
-
|
|
42
|
-
|
|
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],
|
|
43
48
|
};
|
|
44
49
|
return pointer;
|
|
45
50
|
}
|
|
@@ -50,13 +55,12 @@ export function getCommentAddressPointer(tags, root = false) {
|
|
|
50
55
|
* @throws
|
|
51
56
|
*/
|
|
52
57
|
export function getCommentExternalPointer(tags, root = false) {
|
|
53
|
-
const
|
|
58
|
+
const iTag = tags.find((t) => t[0] === (root ? "I" : "i"));
|
|
54
59
|
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
|
|
55
|
-
if (
|
|
60
|
+
if (iTag) {
|
|
56
61
|
if (!kind)
|
|
57
62
|
throw new Error("Missing kind tag");
|
|
58
|
-
|
|
59
|
-
return pointer;
|
|
63
|
+
return getExternalPointerFromTag(iTag);
|
|
60
64
|
}
|
|
61
65
|
return null;
|
|
62
66
|
}
|
|
@@ -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,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 };
|
package/dist/helpers/emoji.d.ts
CHANGED
|
@@ -4,7 +4,9 @@ export declare function getEmojiTag(event: NostrEvent | EventTemplate, code: str
|
|
|
4
4
|
/** Returns the name of a NIP-30 emoji pack */
|
|
5
5
|
export declare function getPackName(pack: NostrEvent): string | undefined;
|
|
6
6
|
export type Emoji = {
|
|
7
|
-
|
|
7
|
+
/** The emoji shortcode (without the ::) */
|
|
8
|
+
shortcode: string;
|
|
9
|
+
/** The URL to the emoji image */
|
|
8
10
|
url: string;
|
|
9
11
|
};
|
|
10
12
|
/** Returns an array of emojis from a NIP-30 emoji pack */
|
package/dist/helpers/emoji.js
CHANGED
|
@@ -2,7 +2,7 @@ import { getTagValue } from "./event.js";
|
|
|
2
2
|
/** Gets an "emoji" tag that matches an emoji code */
|
|
3
3
|
export function getEmojiTag(event, code) {
|
|
4
4
|
code = code.replace(/^:|:$/g, "").toLocaleLowerCase();
|
|
5
|
-
return event.tags.
|
|
5
|
+
return event.tags.find((t) => t[0] === "emoji" && t.length >= 3 && t[1].toLowerCase() === code);
|
|
6
6
|
}
|
|
7
7
|
/** Returns the name of a NIP-30 emoji pack */
|
|
8
8
|
export function getPackName(pack) {
|
|
@@ -12,5 +12,5 @@ export function getPackName(pack) {
|
|
|
12
12
|
export function getEmojis(pack) {
|
|
13
13
|
return pack.tags
|
|
14
14
|
.filter((t) => t[0] === "emoji" && t[1] && t[2])
|
|
15
|
-
.map((t) => ({
|
|
15
|
+
.map((t) => ({ shortcode: t[1], url: t[2] }));
|
|
16
16
|
}
|
package/dist/helpers/event.d.ts
CHANGED
|
@@ -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;
|
|
@@ -33,10 +34,15 @@ export declare function getIndexableTags(event: NostrEvent): Set<string>;
|
|
|
33
34
|
* Returns the second index ( tag[1] ) of the first tag that matches the name
|
|
34
35
|
* If the event has any hidden tags they will be searched first
|
|
35
36
|
*/
|
|
36
|
-
export declare function getTagValue(event: NostrEvent, name: string): string | undefined;
|
|
37
|
+
export declare function getTagValue(event: NostrEvent | EventTemplate, name: string): string | undefined;
|
|
37
38
|
/** Sets events verified flag without checking anything */
|
|
38
39
|
export declare function fakeVerifyEvent(event: NostrEvent): event is VerifiedEvent;
|
|
39
40
|
/** Marks an event as being from a cache */
|
|
40
41
|
export declare function markFromCache(event: NostrEvent): void;
|
|
41
42
|
/** Returns if an event was from a cache */
|
|
42
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;
|
package/dist/helpers/event.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { kinds, verifiedSymbol } from "nostr-tools";
|
|
2
2
|
import { INDEXABLE_TAGS } from "../event-store/common.js";
|
|
3
3
|
import { getHiddenTags } from "./hidden-tags.js";
|
|
4
|
+
import { getOrComputeCachedValue } from "./cache.js";
|
|
5
|
+
import { isParameterizedReplaceableKind } from "nostr-tools/kinds";
|
|
4
6
|
export const EventUIDSymbol = Symbol.for("event-uid");
|
|
5
7
|
export const EventIndexableTagsSymbol = Symbol.for("indexable-tags");
|
|
6
8
|
export const FromCacheSymbol = Symbol.for("from-cache");
|
|
9
|
+
export const ReplaceableIdentifierSymbol = Symbol.for("replaceable-identifier");
|
|
7
10
|
/**
|
|
8
11
|
* Checks if an object is a nostr event
|
|
9
12
|
* NOTE: does not validation the signature on the event
|
|
@@ -34,20 +37,20 @@ export function isReplaceable(kind) {
|
|
|
34
37
|
* For parametrized replaceable events this is ( event.kind + ":" + event.pubkey + ":" + event.tags.d.1 )
|
|
35
38
|
*/
|
|
36
39
|
export function getEventUID(event) {
|
|
37
|
-
let
|
|
38
|
-
if (!
|
|
40
|
+
let uid = event[EventUIDSymbol];
|
|
41
|
+
if (!uid) {
|
|
39
42
|
if (isReplaceable(event.kind)) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
id = event.id;
|
|
43
|
+
let d = event.tags.find((t) => t[0] === "d")?.[1];
|
|
44
|
+
uid = getReplaceableUID(event.kind, event.pubkey, d);
|
|
45
45
|
}
|
|
46
|
+
else
|
|
47
|
+
uid = event.id;
|
|
48
|
+
event[EventUIDSymbol] = uid;
|
|
46
49
|
}
|
|
47
|
-
return
|
|
50
|
+
return uid;
|
|
48
51
|
}
|
|
49
52
|
export function getReplaceableUID(kind, pubkey, d) {
|
|
50
|
-
return d ?
|
|
53
|
+
return d ? kind + ":" + pubkey + ":" + d : kind + ":" + pubkey;
|
|
51
54
|
}
|
|
52
55
|
/** Returns a Set of tag names and values that are indexable */
|
|
53
56
|
export function getIndexableTags(event) {
|
|
@@ -55,7 +58,7 @@ export function getIndexableTags(event) {
|
|
|
55
58
|
if (!indexable) {
|
|
56
59
|
const tags = new Set();
|
|
57
60
|
for (const tag of event.tags) {
|
|
58
|
-
if (tag[0] && INDEXABLE_TAGS.has(tag[0])
|
|
61
|
+
if (tag.length >= 2 && tag[0].length === 1 && INDEXABLE_TAGS.has(tag[0])) {
|
|
59
62
|
tags.add(tag[0] + ":" + tag[1]);
|
|
60
63
|
}
|
|
61
64
|
}
|
|
@@ -76,7 +79,8 @@ export function getTagValue(event, name) {
|
|
|
76
79
|
}
|
|
77
80
|
/** Sets events verified flag without checking anything */
|
|
78
81
|
export function fakeVerifyEvent(event) {
|
|
79
|
-
|
|
82
|
+
event[verifiedSymbol] = true;
|
|
83
|
+
return true;
|
|
80
84
|
}
|
|
81
85
|
/** Marks an event as being from a cache */
|
|
82
86
|
export function markFromCache(event) {
|
|
@@ -86,3 +90,17 @@ export function markFromCache(event) {
|
|
|
86
90
|
export function isFromCache(event) {
|
|
87
91
|
return !!event[FromCacheSymbol];
|
|
88
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,55 @@
|
|
|
1
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
|
+
export type FileMetadata = {
|
|
3
|
+
/** URL of the file */
|
|
4
|
+
url: string;
|
|
5
|
+
/** MIME type */
|
|
6
|
+
type?: string;
|
|
7
|
+
/** sha256 hash of the file */
|
|
8
|
+
sha256?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The original sha256 hash before the file was transformed
|
|
11
|
+
* @deprecated
|
|
12
|
+
*/
|
|
13
|
+
originalSha256?: string;
|
|
14
|
+
/** size of the file in bytes */
|
|
15
|
+
size?: number;
|
|
16
|
+
/** size of file in pixels in the form <width>x<height> */
|
|
17
|
+
dimensions?: string;
|
|
18
|
+
/** magnet */
|
|
19
|
+
magnet?: string;
|
|
20
|
+
/** torrent infohash */
|
|
21
|
+
infohash?: string;
|
|
22
|
+
/** URL to a thumbnail */
|
|
23
|
+
thumbnail?: string;
|
|
24
|
+
/** URL to a preview image with the same dimensions */
|
|
25
|
+
image?: string;
|
|
26
|
+
/** summary */
|
|
27
|
+
summary?: string;
|
|
28
|
+
/** description for accessability */
|
|
29
|
+
alt?: string;
|
|
30
|
+
/** blurhash */
|
|
31
|
+
blurhash?: string;
|
|
32
|
+
/** fallback URLs */
|
|
33
|
+
fallback?: string[];
|
|
34
|
+
};
|
|
35
|
+
export type MediaAttachment = FileMetadata;
|
|
36
|
+
/**
|
|
37
|
+
* Parses file metadata tags into {@link FileMetadata}
|
|
38
|
+
* @throws
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseFileMetadataTags(tags: string[][]): FileMetadata;
|
|
41
|
+
/**
|
|
42
|
+
* Parses a imeta tag into a {@link FileMetadata}
|
|
43
|
+
* @throws
|
|
44
|
+
*/
|
|
45
|
+
export declare function getFileMetadataFromImetaTag(tag: string[]): FileMetadata;
|
|
46
|
+
export declare const MediaAttachmentsSymbol: unique symbol;
|
|
47
|
+
/** Gets all the media attachments on an event */
|
|
48
|
+
export declare function getMediaAttachments(event: NostrEvent): FileMetadata[];
|
|
49
|
+
/**
|
|
50
|
+
* Gets {@link FileMetadata} for a NIP-94 kind 1063 event
|
|
51
|
+
* @throws
|
|
52
|
+
*/
|
|
53
|
+
export declare function getFileMetadata(file: NostrEvent): FileMetadata;
|
|
54
|
+
/** Returns the last 64 length hex string in a URL */
|
|
55
|
+
export declare function getSha256FromURL(url: string | URL): string | undefined;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { getOrComputeCachedValue } from "./cache.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parses file metadata tags into {@link FileMetadata}
|
|
4
|
+
* @throws
|
|
5
|
+
*/
|
|
6
|
+
export function parseFileMetadataTags(tags) {
|
|
7
|
+
const fields = {};
|
|
8
|
+
let fallback = undefined;
|
|
9
|
+
for (const [name, value] of tags) {
|
|
10
|
+
switch (name) {
|
|
11
|
+
case "fallback":
|
|
12
|
+
fallback = fallback ? [...fallback, value] : [value];
|
|
13
|
+
break;
|
|
14
|
+
default:
|
|
15
|
+
fields[name] = value;
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (!fields.url)
|
|
20
|
+
throw new Error("Missing required url in file metadata");
|
|
21
|
+
const metadata = { url: fields.url, fallback };
|
|
22
|
+
// parse size
|
|
23
|
+
if (fields.size)
|
|
24
|
+
metadata.size = parseInt(fields.size);
|
|
25
|
+
// copy optional fields
|
|
26
|
+
if (fields.m)
|
|
27
|
+
metadata.type = fields.m;
|
|
28
|
+
if (fields.x)
|
|
29
|
+
metadata.sha256 = fields.x;
|
|
30
|
+
if (fields.ox)
|
|
31
|
+
metadata.originalSha256 = fields.ox;
|
|
32
|
+
if (fields.dim)
|
|
33
|
+
metadata.dimensions = fields.dim;
|
|
34
|
+
if (fields.magnet)
|
|
35
|
+
metadata.magnet = fields.magnet;
|
|
36
|
+
if (fields.i)
|
|
37
|
+
metadata.infohash = fields.i;
|
|
38
|
+
if (fields.thumb)
|
|
39
|
+
metadata.thumbnail = fields.thumb;
|
|
40
|
+
if (fields.image)
|
|
41
|
+
metadata.image = fields.image;
|
|
42
|
+
if (fields.summary)
|
|
43
|
+
metadata.summary = fields.summary;
|
|
44
|
+
if (fields.alt)
|
|
45
|
+
metadata.alt = fields.alt;
|
|
46
|
+
if (fields.blurhash)
|
|
47
|
+
metadata.blurhash = fields.blurhash;
|
|
48
|
+
return metadata;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parses a imeta tag into a {@link FileMetadata}
|
|
52
|
+
* @throws
|
|
53
|
+
*/
|
|
54
|
+
export function getFileMetadataFromImetaTag(tag) {
|
|
55
|
+
const parts = tag.slice(1);
|
|
56
|
+
const tags = [];
|
|
57
|
+
for (const part of parts) {
|
|
58
|
+
const match = part.match(/^(.+?)\s(.+)$/);
|
|
59
|
+
if (match) {
|
|
60
|
+
const [_, name, value] = match;
|
|
61
|
+
tags.push([name, value]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return parseFileMetadataTags(tags);
|
|
65
|
+
}
|
|
66
|
+
export const MediaAttachmentsSymbol = Symbol.for("media-attachments");
|
|
67
|
+
/** Gets all the media attachments on an event */
|
|
68
|
+
export function getMediaAttachments(event) {
|
|
69
|
+
return getOrComputeCachedValue(event, MediaAttachmentsSymbol, () => {
|
|
70
|
+
return event.tags
|
|
71
|
+
.filter((t) => t[0] === "imeta")
|
|
72
|
+
.map((tag) => {
|
|
73
|
+
try {
|
|
74
|
+
return getFileMetadataFromImetaTag(tag);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// ignore invalid attachments
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
.filter((a) => !!a);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Gets {@link FileMetadata} for a NIP-94 kind 1063 event
|
|
86
|
+
* @throws
|
|
87
|
+
*/
|
|
88
|
+
export function getFileMetadata(file) {
|
|
89
|
+
return parseFileMetadataTags(file.tags);
|
|
90
|
+
}
|
|
91
|
+
/** Returns the last 64 length hex string in a URL */
|
|
92
|
+
export function getSha256FromURL(url) {
|
|
93
|
+
if (typeof url === "string")
|
|
94
|
+
url = new URL(url);
|
|
95
|
+
const hashes = Array.from(url.pathname.matchAll(/[0-9a-f]{64}/gi));
|
|
96
|
+
if (hashes.length > 0)
|
|
97
|
+
return hashes[hashes.length - 1][0];
|
|
98
|
+
return;
|
|
99
|
+
}
|
package/dist/helpers/filter.d.ts
CHANGED
|
@@ -6,5 +6,9 @@ import { Filter, NostrEvent } from "nostr-tools";
|
|
|
6
6
|
export declare function matchFilter(filter: Filter, event: NostrEvent): boolean;
|
|
7
7
|
/** Copied from nostr-tools */
|
|
8
8
|
export declare function matchFilters(filters: Filter[], event: NostrEvent): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Copied from nostr-tools and modified to support undefined
|
|
11
|
+
*/
|
|
12
|
+
export declare function mergeFilters(...filters: Filter[]): Filter;
|
|
9
13
|
/** Check if two filters are equal */
|
|
10
14
|
export declare function isFilterEqual(a: Filter | Filter[], b: Filter | Filter[]): boolean;
|
package/dist/helpers/filter.js
CHANGED
|
@@ -17,7 +17,7 @@ export function matchFilter(filter, event) {
|
|
|
17
17
|
for (let f in filter) {
|
|
18
18
|
if (f[0] === "#") {
|
|
19
19
|
let tagName = f.slice(1);
|
|
20
|
-
let values = filter[
|
|
20
|
+
let values = filter[f];
|
|
21
21
|
if (values) {
|
|
22
22
|
const tags = getIndexableTags(event);
|
|
23
23
|
if (values.some((v) => tags.has(tagName + ":" + v)) === false)
|
|
@@ -40,6 +40,39 @@ export function matchFilters(filters, event) {
|
|
|
40
40
|
}
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Copied from nostr-tools and modified to support undefined
|
|
45
|
+
*/
|
|
46
|
+
export function mergeFilters(...filters) {
|
|
47
|
+
let result = {};
|
|
48
|
+
for (let i = 0; i < filters.length; i++) {
|
|
49
|
+
let filter = filters[i];
|
|
50
|
+
Object.entries(filter).forEach(([property, values]) => {
|
|
51
|
+
// skip undefined
|
|
52
|
+
if (values === undefined)
|
|
53
|
+
return;
|
|
54
|
+
if (property === "kinds" || property === "ids" || property === "authors" || property[0] === "#") {
|
|
55
|
+
// @ts-ignore
|
|
56
|
+
result[property] = result[property] || [];
|
|
57
|
+
// @ts-ignore
|
|
58
|
+
for (let v = 0; v < values.length; v++) {
|
|
59
|
+
// @ts-ignore
|
|
60
|
+
let value = values[v];
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
if (!result[property].includes(value))
|
|
63
|
+
result[property].push(value);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
if (filter.limit && (!result.limit || filter.limit > result.limit))
|
|
68
|
+
result.limit = filter.limit;
|
|
69
|
+
if (filter.until && (!result.until || filter.until > result.until))
|
|
70
|
+
result.until = filter.until;
|
|
71
|
+
if (filter.since && (!result.since || filter.since < result.since))
|
|
72
|
+
result.since = filter.since;
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
43
76
|
/** Check if two filters are equal */
|
|
44
77
|
export function isFilterEqual(a, b) {
|
|
45
78
|
return equal(a, b);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
|
+
export declare const GROUPS_LIST_KIND = 10009;
|
|
3
|
+
export declare const GROUP_MESSAGE_KIND = 9;
|
|
4
|
+
/** NIP-29 group pointer */
|
|
5
|
+
export type GroupPointer = {
|
|
6
|
+
id: string;
|
|
7
|
+
relay: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* decodes a group identifier into a group pointer object
|
|
12
|
+
* @throws
|
|
13
|
+
*/
|
|
14
|
+
export declare function decodeGroupPointer(str: string): GroupPointer;
|
|
15
|
+
/** Converts a group pointer into a group identifier */
|
|
16
|
+
export declare function encodeGroupPointer(pointer: GroupPointer): string;
|
|
17
|
+
export declare const GroupsPublicSymbol: unique symbol;
|
|
18
|
+
export declare const GroupsHiddenSymbol: unique symbol;
|
|
19
|
+
/** gets a {@link GroupPointer} from a "group" tag */
|
|
20
|
+
export declare function getGroupPointerFromGroupTag(tag: string[]): GroupPointer;
|
|
21
|
+
/** Returns all the public groups from a k:10009 list */
|
|
22
|
+
export declare function getPublicGroups(bookmark: NostrEvent): GroupPointer[];
|
|
23
|
+
/** Returns all the hidden groups from a k:10009 list */
|
|
24
|
+
export declare function getHiddenGroups(bookmark: NostrEvent): GroupPointer[] | undefined;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { getOrComputeCachedValue } from "./cache.js";
|
|
2
|
+
import { processTags } from "./tags.js";
|
|
3
|
+
import { getHiddenTags } from "./hidden-tags.js";
|
|
4
|
+
export const GROUPS_LIST_KIND = 10009;
|
|
5
|
+
export const GROUP_MESSAGE_KIND = 9;
|
|
6
|
+
/**
|
|
7
|
+
* decodes a group identifier into a group pointer object
|
|
8
|
+
* @throws
|
|
9
|
+
*/
|
|
10
|
+
export function decodeGroupPointer(str) {
|
|
11
|
+
const [relay, id] = str.split("'");
|
|
12
|
+
if (!relay)
|
|
13
|
+
throw new Error("Group pointer missing relay");
|
|
14
|
+
return { relay, id: id || "_" };
|
|
15
|
+
}
|
|
16
|
+
/** Converts a group pointer into a group identifier */
|
|
17
|
+
export function encodeGroupPointer(pointer) {
|
|
18
|
+
const hostname = URL.canParse(pointer.relay) ? new URL(pointer.relay).hostname : pointer.relay;
|
|
19
|
+
return `${hostname}'${pointer.id}`;
|
|
20
|
+
}
|
|
21
|
+
export const GroupsPublicSymbol = Symbol.for("groups-public");
|
|
22
|
+
export const GroupsHiddenSymbol = Symbol.for("groups-hidden");
|
|
23
|
+
/** gets a {@link GroupPointer} from a "group" tag */
|
|
24
|
+
export function getGroupPointerFromGroupTag(tag) {
|
|
25
|
+
const [_, id, relay, name] = tag;
|
|
26
|
+
return { id, relay, name };
|
|
27
|
+
}
|
|
28
|
+
/** Returns all the public groups from a k:10009 list */
|
|
29
|
+
export function getPublicGroups(bookmark) {
|
|
30
|
+
return getOrComputeCachedValue(bookmark, GroupsPublicSymbol, () => processTags(bookmark.tags.filter((t) => t[0] === "group"), getGroupPointerFromGroupTag));
|
|
31
|
+
}
|
|
32
|
+
/** Returns all the hidden groups from a k:10009 list */
|
|
33
|
+
export function getHiddenGroups(bookmark) {
|
|
34
|
+
return getOrComputeCachedValue(bookmark, GroupsHiddenSymbol, () => {
|
|
35
|
+
const tags = getHiddenTags(bookmark);
|
|
36
|
+
return (tags &&
|
|
37
|
+
processTags(bookmark.tags.filter((t) => t[0] === "group"), getGroupPointerFromGroupTag));
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EventTemplate, NostrEvent
|
|
2
|
-
import { EventStore } from "
|
|
1
|
+
import { EventTemplate, NostrEvent } from "nostr-tools";
|
|
2
|
+
import { EventStore } from "../event-store/event-store.js";
|
|
3
3
|
export type HiddenTagsSigner = {
|
|
4
4
|
nip04?: {
|
|
5
5
|
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
|
@@ -10,7 +10,6 @@ export type HiddenTagsSigner = {
|
|
|
10
10
|
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
|
11
11
|
};
|
|
12
12
|
};
|
|
13
|
-
export type TagOperation = (tags: string[][]) => string[][];
|
|
14
13
|
export declare const HiddenTagsSymbol: unique symbol;
|
|
15
14
|
/** Various event kinds that can have encrypted tags in their content and which encryption method they use */
|
|
16
15
|
export declare const EventEncryptionMethod: Record<number, "nip04" | "nip44">;
|
|
@@ -22,6 +21,14 @@ export declare function hasHiddenTags(event: NostrEvent | EventTemplate): boolea
|
|
|
22
21
|
export declare function getHiddenTags(event: NostrEvent | EventTemplate): string[][] | undefined;
|
|
23
22
|
/** Checks if the hidden tags are locked */
|
|
24
23
|
export declare function isHiddenTagsLocked(event: NostrEvent): boolean;
|
|
24
|
+
/** Returns either nip04 or nip44 encryption method depending on list kind */
|
|
25
|
+
export declare function getListEncryptionMethods(kind: number, signer: HiddenTagsSigner): {
|
|
26
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
|
27
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
|
28
|
+
} | {
|
|
29
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
|
30
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
|
31
|
+
};
|
|
25
32
|
/**
|
|
26
33
|
* Decrypts the private list
|
|
27
34
|
* @param event The list event to decrypt
|
|
@@ -29,18 +36,11 @@ export declare function isHiddenTagsLocked(event: NostrEvent): boolean;
|
|
|
29
36
|
* @param store An optional EventStore to notify about the update
|
|
30
37
|
* @throws
|
|
31
38
|
*/
|
|
32
|
-
export declare function unlockHiddenTags
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
* @param signer A signer to use to decrypt the tags
|
|
38
|
-
* @throws
|
|
39
|
-
*/
|
|
40
|
-
export declare function modifyEventTags(event: NostrEvent | UnsignedEvent, operations: {
|
|
41
|
-
public?: TagOperation;
|
|
42
|
-
hidden?: TagOperation;
|
|
43
|
-
}, signer?: HiddenTagsSigner): Promise<EventTemplate>;
|
|
39
|
+
export declare function unlockHiddenTags<T extends {
|
|
40
|
+
kind: number;
|
|
41
|
+
pubkey: string;
|
|
42
|
+
content: string;
|
|
43
|
+
}>(event: T, signer: HiddenTagsSigner, store?: EventStore): Promise<T>;
|
|
44
44
|
/**
|
|
45
45
|
* Override the hidden tags in an event
|
|
46
46
|
* @throws
|