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.
- package/README.md +1 -1
- 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 +22 -16
- package/dist/event-store/database.js +62 -39
- package/dist/event-store/event-store.d.ts +52 -15
- package/dist/event-store/event-store.js +283 -191
- package/dist/helpers/__tests__/blossom.test.d.ts +1 -0
- package/dist/helpers/__tests__/blossom.test.js +13 -0
- package/dist/helpers/__tests__/comment.test.d.ts +1 -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/__tests__/hidden-tags.test.js +29 -0
- package/dist/helpers/__tests__/mailboxes.test.d.ts +1 -0
- package/dist/helpers/{mailboxes.test.js → __tests__/mailboxes.test.js} +14 -13
- 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/__tests__/threading.test.js +41 -0
- package/dist/helpers/blossom.d.ts +9 -0
- package/dist/helpers/blossom.js +22 -0
- package/dist/helpers/bolt11.d.ts +1 -0
- package/dist/helpers/bolt11.js +1 -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 +47 -0
- package/dist/helpers/comment.js +120 -0
- package/dist/helpers/contacts.d.ts +3 -0
- package/dist/helpers/contacts.js +25 -0
- package/dist/helpers/content.d.ts +3 -0
- package/dist/helpers/content.js +8 -0
- package/dist/helpers/delete.d.ts +3 -0
- package/dist/helpers/delete.js +7 -0
- package/dist/helpers/dns-identity.d.ts +7 -0
- package/dist/helpers/dns-identity.js +10 -0
- package/dist/helpers/emoji.d.ts +12 -1
- package/dist/helpers/emoji.js +13 -1
- package/dist/helpers/event.d.ts +17 -3
- package/dist/helpers/event.js +54 -12
- package/dist/helpers/external-id.d.ts +29 -0
- package/dist/helpers/external-id.js +20 -0
- package/dist/helpers/file-metadata.d.ts +55 -0
- package/dist/helpers/file-metadata.js +99 -0
- package/dist/helpers/filter.d.ts +4 -2
- package/dist/helpers/filter.js +36 -7
- package/dist/helpers/groups.d.ts +24 -0
- package/dist/helpers/groups.js +39 -0
- package/dist/helpers/hidden-tags.d.ts +48 -0
- package/dist/helpers/hidden-tags.js +86 -0
- package/dist/helpers/index.d.ts +28 -8
- package/dist/helpers/index.js +28 -8
- package/dist/helpers/json.d.ts +1 -0
- package/dist/helpers/json.js +1 -0
- package/dist/helpers/lists.d.ts +28 -0
- package/dist/helpers/lists.js +65 -0
- package/dist/helpers/lnurl.d.ts +4 -0
- package/dist/helpers/lnurl.js +40 -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.d.ts +38 -5
- package/dist/helpers/pointers.js +105 -25
- package/dist/helpers/profile.d.ts +6 -1
- package/dist/helpers/profile.js +5 -1
- 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/string.d.ts +6 -0
- package/dist/helpers/string.js +2 -0
- package/dist/helpers/tags.d.ts +23 -0
- package/dist/helpers/tags.js +34 -6
- package/dist/helpers/threading.d.ts +6 -6
- package/dist/helpers/threading.js +30 -9
- package/dist/helpers/url.d.ts +11 -1
- package/dist/helpers/url.js +31 -3
- package/dist/helpers/user-status.d.ts +18 -0
- package/dist/helpers/user-status.js +21 -0
- package/dist/helpers/zap.d.ts +25 -0
- package/dist/helpers/zap.js +32 -3
- 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-observable-value.d.ts +3 -0
- package/dist/observable/get-observable-value.js +9 -0
- 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/promise/deferred.d.ts +1 -0
- package/dist/promise/deferred.js +1 -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/comments.d.ts +4 -0
- package/dist/queries/comments.js +14 -0
- package/dist/queries/contacts.d.ts +3 -0
- package/dist/queries/contacts.js +12 -0
- package/dist/queries/index.d.ts +9 -2
- package/dist/queries/index.js +9 -2
- package/dist/queries/mailboxes.d.ts +1 -0
- package/dist/queries/mailboxes.js +1 -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/profile.d.ts +1 -0
- package/dist/queries/profile.js +1 -0
- package/dist/queries/reactions.d.ts +1 -1
- package/dist/queries/reactions.js +1 -1
- package/dist/queries/simple.d.ts +4 -4
- package/dist/queries/simple.js +13 -13
- package/dist/queries/thread.d.ts +2 -0
- package/dist/queries/thread.js +30 -4
- package/dist/queries/user-status.d.ts +11 -0
- package/dist/queries/user-status.js +39 -0
- package/dist/queries/zaps.d.ts +1 -0
- package/dist/queries/zaps.js +1 -0
- package/dist/query-store/index.d.ts +1 -47
- package/dist/query-store/index.js +1 -60
- 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 +24 -21
- package/dist/observable/getValue.d.ts +0 -2
- package/dist/observable/getValue.js +0 -13
- /package/dist/{helpers/mailboxes.test.d.ts → event-store/__tests__/event-store.test.d.ts} +0 -0
|
@@ -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,7 +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
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Copied from nostr-tools and modified to support undefined
|
|
11
|
+
*/
|
|
12
|
+
export declare function mergeFilters(...filters: Filter[]): Filter;
|
|
11
13
|
/** Check if two filters are equal */
|
|
12
14
|
export declare function isFilterEqual(a: Filter | Filter[], b: Filter | Filter[]): boolean;
|
package/dist/helpers/filter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getIndexableTags } from "./event.js";
|
|
2
|
-
import
|
|
2
|
+
import equal from "fast-deep-equal";
|
|
3
3
|
/**
|
|
4
4
|
* Copied from nostr-tools and modified to use getIndexableTags
|
|
5
5
|
* @see https://github.com/nbd-wtf/nostr-tools/blob/a61cde77eacc9518001f11d7f67f1a50ae05fd80/filter.ts
|
|
@@ -17,10 +17,10 @@ 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
|
-
if (values.some((v) =>
|
|
23
|
+
if (values.some((v) => tags.has(tagName + ":" + v)) === false)
|
|
24
24
|
return false;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -40,11 +40,40 @@ export function matchFilters(filters, event) {
|
|
|
40
40
|
}
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
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;
|
|
46
75
|
}
|
|
47
76
|
/** Check if two filters are equal */
|
|
48
77
|
export function isFilterEqual(a, b) {
|
|
49
|
-
return
|
|
78
|
+
return equal(a, b);
|
|
50
79
|
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { EventTemplate, NostrEvent } from "nostr-tools";
|
|
2
|
+
import { EventStore } from "../event-store/event-store.js";
|
|
3
|
+
export type HiddenTagsSigner = {
|
|
4
|
+
nip04?: {
|
|
5
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
|
6
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
|
7
|
+
};
|
|
8
|
+
nip44?: {
|
|
9
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
|
10
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export declare const HiddenTagsSymbol: unique symbol;
|
|
14
|
+
/** Various event kinds that can have encrypted tags in their content and which encryption method they use */
|
|
15
|
+
export declare const EventEncryptionMethod: Record<number, "nip04" | "nip44">;
|
|
16
|
+
/** Checks if an event can have hidden tags */
|
|
17
|
+
export declare function canHaveHiddenTags(kind: number): boolean;
|
|
18
|
+
/** Checks if an event has hidden tags */
|
|
19
|
+
export declare function hasHiddenTags(event: NostrEvent | EventTemplate): boolean;
|
|
20
|
+
/** Returns the hidden tags for an event if they are unlocked */
|
|
21
|
+
export declare function getHiddenTags(event: NostrEvent | EventTemplate): string[][] | undefined;
|
|
22
|
+
/** Checks if the hidden tags are locked */
|
|
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
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Decrypts the private list
|
|
34
|
+
* @param event The list event to decrypt
|
|
35
|
+
* @param signer A signer to use to decrypt the tags
|
|
36
|
+
* @param store An optional EventStore to notify about the update
|
|
37
|
+
* @throws
|
|
38
|
+
*/
|
|
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
|
+
/**
|
|
45
|
+
* Override the hidden tags in an event
|
|
46
|
+
* @throws
|
|
47
|
+
*/
|
|
48
|
+
export declare function overrideHiddenTags(event: NostrEvent, hidden: string[][], signer: HiddenTagsSigner): Promise<EventTemplate>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { kinds } from "nostr-tools";
|
|
2
|
+
import { GROUPS_LIST_KIND } from "./groups.js";
|
|
3
|
+
import { unixNow } from "./time.js";
|
|
4
|
+
import { isEvent } from "./event.js";
|
|
5
|
+
export const HiddenTagsSymbol = Symbol.for("hidden-tags");
|
|
6
|
+
/** Various event kinds that can have encrypted tags in their content and which encryption method they use */
|
|
7
|
+
export const EventEncryptionMethod = {
|
|
8
|
+
// NIP-60 wallet
|
|
9
|
+
37375: "nip44",
|
|
10
|
+
// NIP-51 lists
|
|
11
|
+
[kinds.BookmarkList]: "nip04",
|
|
12
|
+
[kinds.InterestsList]: "nip04",
|
|
13
|
+
[kinds.Mutelist]: "nip04",
|
|
14
|
+
[kinds.CommunitiesList]: "nip04",
|
|
15
|
+
[kinds.PublicChatsList]: "nip04",
|
|
16
|
+
[kinds.SearchRelaysList]: "nip04",
|
|
17
|
+
[GROUPS_LIST_KIND]: "nip04",
|
|
18
|
+
// NIP-51 sets
|
|
19
|
+
[kinds.Bookmarksets]: "nip04",
|
|
20
|
+
[kinds.Relaysets]: "nip04",
|
|
21
|
+
[kinds.Followsets]: "nip04",
|
|
22
|
+
[kinds.Curationsets]: "nip04",
|
|
23
|
+
[kinds.Interestsets]: "nip04",
|
|
24
|
+
};
|
|
25
|
+
/** Checks if an event can have hidden tags */
|
|
26
|
+
export function canHaveHiddenTags(kind) {
|
|
27
|
+
return EventEncryptionMethod[kind] !== undefined;
|
|
28
|
+
}
|
|
29
|
+
/** Checks if an event has hidden tags */
|
|
30
|
+
export function hasHiddenTags(event) {
|
|
31
|
+
return canHaveHiddenTags(event.kind) && event.content.length > 0;
|
|
32
|
+
}
|
|
33
|
+
/** Returns the hidden tags for an event if they are unlocked */
|
|
34
|
+
export function getHiddenTags(event) {
|
|
35
|
+
return Reflect.get(event, HiddenTagsSymbol);
|
|
36
|
+
}
|
|
37
|
+
/** Checks if the hidden tags are locked */
|
|
38
|
+
export function isHiddenTagsLocked(event) {
|
|
39
|
+
return hasHiddenTags(event) && getHiddenTags(event) === undefined;
|
|
40
|
+
}
|
|
41
|
+
/** Returns either nip04 or nip44 encryption method depending on list kind */
|
|
42
|
+
export function getListEncryptionMethods(kind, signer) {
|
|
43
|
+
const method = EventEncryptionMethod[kind];
|
|
44
|
+
const encryption = signer[method];
|
|
45
|
+
if (!encryption)
|
|
46
|
+
throw new Error(`Signer does not support ${method} encryption`);
|
|
47
|
+
return encryption;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Decrypts the private list
|
|
51
|
+
* @param event The list event to decrypt
|
|
52
|
+
* @param signer A signer to use to decrypt the tags
|
|
53
|
+
* @param store An optional EventStore to notify about the update
|
|
54
|
+
* @throws
|
|
55
|
+
*/
|
|
56
|
+
export async function unlockHiddenTags(event, signer, store) {
|
|
57
|
+
if (!canHaveHiddenTags(event.kind))
|
|
58
|
+
throw new Error("Event kind does not support hidden tags");
|
|
59
|
+
const encryption = getListEncryptionMethods(event.kind, signer);
|
|
60
|
+
const plaintext = await encryption.decrypt(event.pubkey, event.content);
|
|
61
|
+
const parsed = JSON.parse(plaintext);
|
|
62
|
+
if (!Array.isArray(parsed))
|
|
63
|
+
throw new Error("Content is not an array of tags");
|
|
64
|
+
// Convert array to tags array string[][]
|
|
65
|
+
const tags = parsed.filter((t) => Array.isArray(t)).map((t) => t.map((v) => String(v)));
|
|
66
|
+
Reflect.set(event, HiddenTagsSymbol, tags);
|
|
67
|
+
if (store && isEvent(event))
|
|
68
|
+
store.update(event);
|
|
69
|
+
return event;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Override the hidden tags in an event
|
|
73
|
+
* @throws
|
|
74
|
+
*/
|
|
75
|
+
export async function overrideHiddenTags(event, hidden, signer) {
|
|
76
|
+
if (!canHaveHiddenTags(event.kind))
|
|
77
|
+
throw new Error("Event kind does not support hidden tags");
|
|
78
|
+
const encryption = getListEncryptionMethods(event.kind, signer);
|
|
79
|
+
const ciphertext = await encryption.encrypt(event.pubkey, JSON.stringify(hidden));
|
|
80
|
+
return {
|
|
81
|
+
kind: event.kind,
|
|
82
|
+
content: ciphertext,
|
|
83
|
+
created_at: unixNow(),
|
|
84
|
+
tags: event.tags,
|
|
85
|
+
};
|
|
86
|
+
}
|
package/dist/helpers/index.d.ts
CHANGED
|
@@ -1,16 +1,36 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
1
|
+
export * from "./blossom.js";
|
|
2
|
+
export * from "./bolt11.js";
|
|
3
|
+
export * from "./bookmarks.js";
|
|
4
|
+
export * from "./cache.js";
|
|
5
|
+
export * from "./channels.js";
|
|
6
|
+
export * from "./comment.js";
|
|
7
|
+
export * from "./contacts.js";
|
|
8
|
+
export * from "./content.js";
|
|
9
|
+
export * from "./delete.js";
|
|
10
|
+
export * from "./dns-identity.js";
|
|
11
|
+
export * from "./emoji.js";
|
|
3
12
|
export * from "./event.js";
|
|
13
|
+
export * from "./external-id.js";
|
|
14
|
+
export * from "./file-metadata.js";
|
|
4
15
|
export * from "./filter.js";
|
|
16
|
+
export * from "./groups.js";
|
|
17
|
+
export * from "./hashtag.js";
|
|
18
|
+
export * from "./hidden-tags.js";
|
|
19
|
+
export * from "./json.js";
|
|
20
|
+
export * from "./lists.js";
|
|
21
|
+
export * from "./lnurl.js";
|
|
22
|
+
export * from "./lru.js";
|
|
5
23
|
export * from "./mailboxes.js";
|
|
6
|
-
export * from "./
|
|
24
|
+
export * from "./mutes.js";
|
|
25
|
+
export * from "./picture-post.js";
|
|
7
26
|
export * from "./pointers.js";
|
|
27
|
+
export * from "./profile.js";
|
|
28
|
+
export * from "./relays.js";
|
|
29
|
+
export * from "./share.js";
|
|
8
30
|
export * from "./string.js";
|
|
9
|
-
export * from "./time.js";
|
|
10
31
|
export * from "./tags.js";
|
|
11
|
-
export * from "./
|
|
12
|
-
export * from "./
|
|
13
|
-
export * from "./hashtag.js";
|
|
32
|
+
export * from "./threading.js";
|
|
33
|
+
export * from "./time.js";
|
|
14
34
|
export * from "./url.js";
|
|
35
|
+
export * from "./user-status.js";
|
|
15
36
|
export * from "./zap.js";
|
|
16
|
-
export * from "./bolt11.js";
|
package/dist/helpers/index.js
CHANGED
|
@@ -1,16 +1,36 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
1
|
+
export * from "./blossom.js";
|
|
2
|
+
export * from "./bolt11.js";
|
|
3
|
+
export * from "./bookmarks.js";
|
|
4
|
+
export * from "./cache.js";
|
|
5
|
+
export * from "./channels.js";
|
|
6
|
+
export * from "./comment.js";
|
|
7
|
+
export * from "./contacts.js";
|
|
8
|
+
export * from "./content.js";
|
|
9
|
+
export * from "./delete.js";
|
|
10
|
+
export * from "./dns-identity.js";
|
|
11
|
+
export * from "./emoji.js";
|
|
3
12
|
export * from "./event.js";
|
|
13
|
+
export * from "./external-id.js";
|
|
14
|
+
export * from "./file-metadata.js";
|
|
4
15
|
export * from "./filter.js";
|
|
16
|
+
export * from "./groups.js";
|
|
17
|
+
export * from "./hashtag.js";
|
|
18
|
+
export * from "./hidden-tags.js";
|
|
19
|
+
export * from "./json.js";
|
|
20
|
+
export * from "./lists.js";
|
|
21
|
+
export * from "./lnurl.js";
|
|
22
|
+
export * from "./lru.js";
|
|
5
23
|
export * from "./mailboxes.js";
|
|
6
|
-
export * from "./
|
|
24
|
+
export * from "./mutes.js";
|
|
25
|
+
export * from "./picture-post.js";
|
|
7
26
|
export * from "./pointers.js";
|
|
27
|
+
export * from "./profile.js";
|
|
28
|
+
export * from "./relays.js";
|
|
29
|
+
export * from "./share.js";
|
|
8
30
|
export * from "./string.js";
|
|
9
|
-
export * from "./time.js";
|
|
10
31
|
export * from "./tags.js";
|
|
11
|
-
export * from "./
|
|
12
|
-
export * from "./
|
|
13
|
-
export * from "./hashtag.js";
|
|
32
|
+
export * from "./threading.js";
|
|
33
|
+
export * from "./time.js";
|
|
14
34
|
export * from "./url.js";
|
|
35
|
+
export * from "./user-status.js";
|
|
15
36
|
export * from "./zap.js";
|
|
16
|
-
export * from "./bolt11.js";
|
package/dist/helpers/json.d.ts
CHANGED
package/dist/helpers/json.js
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19";
|
|
2
|
+
import { NostrEvent } from "nostr-tools";
|
|
3
|
+
/**
|
|
4
|
+
* Checks if an event pointer is anywhere in a list or set
|
|
5
|
+
* NOTE: Ignores the `relay` field in EventPointer
|
|
6
|
+
* NOTE: This will check the hidden tags if the list has hidden tags and they are unlocked
|
|
7
|
+
*/
|
|
8
|
+
export declare function isEventPointerInList(list: NostrEvent, pointer: string | EventPointer): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Checks if an address pointer is anywhere in a list or set
|
|
11
|
+
* NOTE: Ignores the `relay` field in AddressPointer
|
|
12
|
+
* NOTE: This will check the hidden tags if the list has hidden tags and they are unlocked
|
|
13
|
+
*/
|
|
14
|
+
export declare function isAddressPointerInList(list: NostrEvent, pointer: string | AddressPointer): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Checks if an profile pointer is anywhere in a list or set
|
|
17
|
+
* NOTE: Ignores the `relay` field in ProfilePointer
|
|
18
|
+
* NOTE: This will check the hidden tags if the list has hidden tags and they are unlocked
|
|
19
|
+
*/
|
|
20
|
+
export declare function isProfilePointerInList(list: NostrEvent, pointer: string | ProfilePointer): boolean;
|
|
21
|
+
/** Returns all the EventPointer in a list or set */
|
|
22
|
+
export declare function getEventPointersFromList(list: NostrEvent): EventPointer[];
|
|
23
|
+
/** Returns all the AddressPointer in a list or set */
|
|
24
|
+
export declare function getAddressPointersFromList(list: NostrEvent): AddressPointer[];
|
|
25
|
+
/** Returns all the ProfilePointer in a list or set */
|
|
26
|
+
export declare function getProfilePointersFromList(list: NostrEvent): ProfilePointer[];
|
|
27
|
+
/** Returns if an event is a valid list or set */
|
|
28
|
+
export declare function isValidList(event: NostrEvent): boolean;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { isParameterizedReplaceableKind, isReplaceableKind } from "nostr-tools/kinds";
|
|
2
|
+
import { getHiddenTags } from "./hidden-tags.js";
|
|
3
|
+
import { getAddressPointerFromATag, getCoordinateFromAddressPointer, getEventPointerFromETag, getProfilePointerFromPTag, } from "./pointers.js";
|
|
4
|
+
import { isATag, isETag, isPTag, processTags } from "./tags.js";
|
|
5
|
+
import { getReplaceableIdentifier } from "./event.js";
|
|
6
|
+
function listGetAllTags(list) {
|
|
7
|
+
const hidden = getHiddenTags(list);
|
|
8
|
+
return hidden ? [...hidden, ...list.tags] : list.tags;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Checks if an event pointer is anywhere in a list or set
|
|
12
|
+
* NOTE: Ignores the `relay` field in EventPointer
|
|
13
|
+
* NOTE: This will check the hidden tags if the list has hidden tags and they are unlocked
|
|
14
|
+
*/
|
|
15
|
+
export function isEventPointerInList(list, pointer) {
|
|
16
|
+
const id = typeof pointer === "string" ? pointer : pointer.id;
|
|
17
|
+
return listGetAllTags(list).some((t) => t[0] === "e" && t[1] === id);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Checks if an address pointer is anywhere in a list or set
|
|
21
|
+
* NOTE: Ignores the `relay` field in AddressPointer
|
|
22
|
+
* NOTE: This will check the hidden tags if the list has hidden tags and they are unlocked
|
|
23
|
+
*/
|
|
24
|
+
export function isAddressPointerInList(list, pointer) {
|
|
25
|
+
const cord = typeof pointer === "string" ? pointer : getCoordinateFromAddressPointer(pointer);
|
|
26
|
+
return listGetAllTags(list).some((t) => t[0] === "a" && t[1] === cord);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Checks if an profile pointer is anywhere in a list or set
|
|
30
|
+
* NOTE: Ignores the `relay` field in ProfilePointer
|
|
31
|
+
* NOTE: This will check the hidden tags if the list has hidden tags and they are unlocked
|
|
32
|
+
*/
|
|
33
|
+
export function isProfilePointerInList(list, pointer) {
|
|
34
|
+
const pubkey = typeof pointer === "string" ? pointer : pointer.pubkey;
|
|
35
|
+
return listGetAllTags(list).some((t) => t[0] === "p" && t[1] === pubkey);
|
|
36
|
+
}
|
|
37
|
+
/** Returns all the EventPointer in a list or set */
|
|
38
|
+
export function getEventPointersFromList(list) {
|
|
39
|
+
return processTags(listGetAllTags(list), (tag) => (isETag(tag) ? tag : undefined), getEventPointerFromETag);
|
|
40
|
+
}
|
|
41
|
+
/** Returns all the AddressPointer in a list or set */
|
|
42
|
+
export function getAddressPointersFromList(list) {
|
|
43
|
+
return processTags(listGetAllTags(list), (t) => (isATag(t) ? t : undefined), getAddressPointerFromATag);
|
|
44
|
+
}
|
|
45
|
+
/** Returns all the ProfilePointer in a list or set */
|
|
46
|
+
export function getProfilePointersFromList(list) {
|
|
47
|
+
return processTags(listGetAllTags(list), (t) => (isPTag(t) ? t : undefined), getProfilePointerFromPTag);
|
|
48
|
+
}
|
|
49
|
+
/** Returns if an event is a valid list or set */
|
|
50
|
+
export function isValidList(event) {
|
|
51
|
+
try {
|
|
52
|
+
if (isParameterizedReplaceableKind(event.kind)) {
|
|
53
|
+
// event is a set
|
|
54
|
+
// ensure the set has an identifier
|
|
55
|
+
getReplaceableIdentifier(event);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
else if (isReplaceableKind(event.kind) && event.kind >= 10000 && event.kind < 20000) {
|
|
59
|
+
// event is a list
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) { }
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function parseLightningAddress(address: string): URL | undefined;
|
|
2
|
+
export declare function decodeLNURL(lnurl: string): URL | undefined;
|
|
3
|
+
export declare function parseLNURLOrAddress(addressOrLNURL: string): URL | undefined;
|
|
4
|
+
export declare function getInvoice(callback: URL): Promise<string>;
|