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
|
@@ -1,10 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getAddressPointerFromATag } from "./pointers.js";
|
|
2
2
|
import { getOrComputeCachedValue } from "./cache.js";
|
|
3
|
+
import { isSafeRelayURL } from "./relays.js";
|
|
3
4
|
export const Nip10ThreadRefsSymbol = Symbol.for("nip10-thread-refs");
|
|
5
|
+
/**
|
|
6
|
+
* Gets an EventPointer form a NIP-10 threading "e" tag
|
|
7
|
+
* @throws
|
|
8
|
+
*/
|
|
9
|
+
export function getEventPointerFromThreadTag(tag) {
|
|
10
|
+
if (!tag[1])
|
|
11
|
+
throw new Error("Missing event id in tag");
|
|
12
|
+
let pointer = { id: tag[1] };
|
|
13
|
+
if (tag[2] && isSafeRelayURL(tag[2]))
|
|
14
|
+
pointer.relays = [tag[2]];
|
|
15
|
+
// get author from NIP-18 quote tags, nip-22 comments tags, or nip-10 thread tags
|
|
16
|
+
if (tag[0] === "e" &&
|
|
17
|
+
(tag[3] === "root" || tag[3] === "reply" || tag[3] === "mention") &&
|
|
18
|
+
tag[4] &&
|
|
19
|
+
tag[4].length === 64) {
|
|
20
|
+
// NIP-10 "e" tag
|
|
21
|
+
pointer.author = tag[4];
|
|
22
|
+
}
|
|
23
|
+
return pointer;
|
|
24
|
+
}
|
|
4
25
|
/** Parses NIP-10 tags and handles legacy behavior */
|
|
5
|
-
export function interpretThreadTags(
|
|
6
|
-
const eTags =
|
|
7
|
-
const aTags =
|
|
26
|
+
export function interpretThreadTags(tags) {
|
|
27
|
+
const eTags = tags.filter((t) => t[0] === "e" && t[1]);
|
|
28
|
+
const aTags = tags.filter((t) => t[0] === "a" && t[1]);
|
|
8
29
|
// find the root and reply tags.
|
|
9
30
|
let rootETag = eTags.find((t) => t[3] === "root");
|
|
10
31
|
let replyETag = eTags.find((t) => t[3] === "reply");
|
|
@@ -44,13 +65,13 @@ export function interpretThreadTags(event) {
|
|
|
44
65
|
/** Returns the parsed NIP-10 tags for an event */
|
|
45
66
|
export function getNip10References(event) {
|
|
46
67
|
return getOrComputeCachedValue(event, Nip10ThreadRefsSymbol, () => {
|
|
47
|
-
const tags = interpretThreadTags(event);
|
|
68
|
+
const tags = interpretThreadTags(event.tags);
|
|
48
69
|
let root;
|
|
49
70
|
if (tags.root) {
|
|
50
71
|
try {
|
|
51
72
|
root = {
|
|
52
|
-
e: tags.root.e &&
|
|
53
|
-
a: tags.root.a &&
|
|
73
|
+
e: tags.root.e && getEventPointerFromThreadTag(tags.root.e),
|
|
74
|
+
a: tags.root.a && getAddressPointerFromATag(tags.root.a),
|
|
54
75
|
};
|
|
55
76
|
}
|
|
56
77
|
catch (error) { }
|
|
@@ -59,8 +80,8 @@ export function getNip10References(event) {
|
|
|
59
80
|
if (tags.reply) {
|
|
60
81
|
try {
|
|
61
82
|
reply = {
|
|
62
|
-
e: tags.reply.e &&
|
|
63
|
-
a: tags.reply.a &&
|
|
83
|
+
e: tags.reply.e && getEventPointerFromThreadTag(tags.reply.e),
|
|
84
|
+
a: tags.reply.a && getAddressPointerFromATag(tags.reply.a),
|
|
64
85
|
};
|
|
65
86
|
}
|
|
66
87
|
catch (error) { }
|
package/dist/helpers/url.d.ts
CHANGED
|
@@ -4,8 +4,18 @@ export declare const IMAGE_EXT: string[];
|
|
|
4
4
|
export declare const VIDEO_EXT: string[];
|
|
5
5
|
export declare const STREAM_EXT: string[];
|
|
6
6
|
export declare const AUDIO_EXT: string[];
|
|
7
|
-
|
|
7
|
+
/** Checks if a url is a image URL */
|
|
8
8
|
export declare function isImageURL(url: string | URL): boolean;
|
|
9
|
+
/** Checks if a url is a video URL */
|
|
9
10
|
export declare function isVideoURL(url: string | URL): boolean;
|
|
11
|
+
/** Checks if a url is a stream URL */
|
|
10
12
|
export declare function isStreamURL(url: string | URL): boolean;
|
|
13
|
+
/** Checks if a url is a audio URL */
|
|
11
14
|
export declare function isAudioURL(url: string | URL): boolean;
|
|
15
|
+
/** Tests if two URLs are the same */
|
|
16
|
+
export declare function isSameURL(a: string | URL, b: string | URL): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Normalizes a string into a relay URL
|
|
19
|
+
* Does not remove the trailing slash
|
|
20
|
+
*/
|
|
21
|
+
export declare function normalizeURL<T extends string | URL>(url: T): T;
|
package/dist/helpers/url.js
CHANGED
|
@@ -4,26 +4,54 @@ export const IMAGE_EXT = [".svg", ".gif", ".png", ".jpg", ".jpeg", ".webp", ".av
|
|
|
4
4
|
export const VIDEO_EXT = [".mp4", ".mkv", ".webm", ".mov"];
|
|
5
5
|
export const STREAM_EXT = [".m3u8"];
|
|
6
6
|
export const AUDIO_EXT = [".mp3", ".wav", ".ogg", ".aac"];
|
|
7
|
-
|
|
8
|
-
return isImageURL(url) || isVideoURL(url) || isStreamURL(url);
|
|
9
|
-
}
|
|
7
|
+
/** Checks if a url is a image URL */
|
|
10
8
|
export function isImageURL(url) {
|
|
11
9
|
url = convertToUrl(url);
|
|
12
10
|
const filename = getURLFilename(url);
|
|
13
11
|
return !!filename && IMAGE_EXT.some((ext) => filename.endsWith(ext));
|
|
14
12
|
}
|
|
13
|
+
/** Checks if a url is a video URL */
|
|
15
14
|
export function isVideoURL(url) {
|
|
16
15
|
url = convertToUrl(url);
|
|
17
16
|
const filename = getURLFilename(url);
|
|
18
17
|
return !!filename && VIDEO_EXT.some((ext) => filename.endsWith(ext));
|
|
19
18
|
}
|
|
19
|
+
/** Checks if a url is a stream URL */
|
|
20
20
|
export function isStreamURL(url) {
|
|
21
21
|
url = convertToUrl(url);
|
|
22
22
|
const filename = getURLFilename(url);
|
|
23
23
|
return !!filename && STREAM_EXT.some((ext) => filename.endsWith(ext));
|
|
24
24
|
}
|
|
25
|
+
/** Checks if a url is a audio URL */
|
|
25
26
|
export function isAudioURL(url) {
|
|
26
27
|
url = convertToUrl(url);
|
|
27
28
|
const filename = getURLFilename(url);
|
|
28
29
|
return !!filename && AUDIO_EXT.some((ext) => filename.endsWith(ext));
|
|
29
30
|
}
|
|
31
|
+
/** Tests if two URLs are the same */
|
|
32
|
+
export function isSameURL(a, b) {
|
|
33
|
+
try {
|
|
34
|
+
a = normalizeURL(a);
|
|
35
|
+
b = normalizeURL(b);
|
|
36
|
+
return a === b;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Normalizes a string into a relay URL
|
|
44
|
+
* Does not remove the trailing slash
|
|
45
|
+
*/
|
|
46
|
+
export function normalizeURL(url) {
|
|
47
|
+
let p = new URL(url);
|
|
48
|
+
// remove any double slashes
|
|
49
|
+
p.pathname = p.pathname.replace(/\/+/g, "/");
|
|
50
|
+
// drop the port if its not needed
|
|
51
|
+
if ((p.port === "80" && (p.protocol === "ws:" || p.protocol === "http:")) ||
|
|
52
|
+
(p.port === "443" && (p.protocol === "wss:" || p.protocol === "https:")))
|
|
53
|
+
p.port = "";
|
|
54
|
+
// return a string if a string was passed in
|
|
55
|
+
// @ts-expect-error
|
|
56
|
+
return typeof url === "string" ? p.toString() : p;
|
|
57
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
|
+
import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19";
|
|
3
|
+
export declare const UserStatusPointerSymbol: unique symbol;
|
|
4
|
+
export type UserStatusPointer = {
|
|
5
|
+
type: "nevent";
|
|
6
|
+
data: EventPointer;
|
|
7
|
+
} | {
|
|
8
|
+
type: "nprofile";
|
|
9
|
+
data: ProfilePointer;
|
|
10
|
+
} | {
|
|
11
|
+
type: "naddr";
|
|
12
|
+
data: AddressPointer;
|
|
13
|
+
} | {
|
|
14
|
+
type: "url";
|
|
15
|
+
data: string;
|
|
16
|
+
};
|
|
17
|
+
/** Gets the {@link UserStatusPointer} for a status event */
|
|
18
|
+
export declare function getUserStatusPointer(status: NostrEvent): UserStatusPointer | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getAddressPointerFromATag, getEventPointerFromETag, getOrComputeCachedValue, getProfilePointerFromPTag, } from "applesauce-core/helpers";
|
|
2
|
+
export const UserStatusPointerSymbol = Symbol.for("user-status-pointer");
|
|
3
|
+
function getStatusPointer(status) {
|
|
4
|
+
const pTag = status.tags.find((t) => t[0] === "p" && t[1]);
|
|
5
|
+
if (pTag)
|
|
6
|
+
return { type: "nprofile", data: getProfilePointerFromPTag(pTag) };
|
|
7
|
+
const eTag = status.tags.find((t) => t[0] === "e" && t[1]);
|
|
8
|
+
if (eTag)
|
|
9
|
+
return { type: "nevent", data: getEventPointerFromETag(eTag) };
|
|
10
|
+
const aTag = status.tags.find((t) => t[0] === "a" && t[1]);
|
|
11
|
+
if (aTag)
|
|
12
|
+
return { type: "naddr", data: getAddressPointerFromATag(aTag) };
|
|
13
|
+
const rTag = status.tags.find((t) => t[0] === "r" && t[1]);
|
|
14
|
+
if (rTag)
|
|
15
|
+
return { type: "url", data: rTag[1] };
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
/** Gets the {@link UserStatusPointer} for a status event */
|
|
19
|
+
export function getUserStatusPointer(status) {
|
|
20
|
+
return getOrComputeCachedValue(status, UserStatusPointerSymbol, () => getStatusPointer(status));
|
|
21
|
+
}
|
package/dist/helpers/zap.d.ts
CHANGED
|
@@ -4,11 +4,36 @@ export declare const ZapFromSymbol: unique symbol;
|
|
|
4
4
|
export declare const ZapInvoiceSymbol: unique symbol;
|
|
5
5
|
export declare const ZapEventPointerSymbol: unique symbol;
|
|
6
6
|
export declare const ZapAddressPointerSymbol: unique symbol;
|
|
7
|
+
/** Returns the senders pubkey */
|
|
7
8
|
export declare function getZapSender(zap: NostrEvent): string;
|
|
9
|
+
/**
|
|
10
|
+
* Gets the receivers pubkey
|
|
11
|
+
* @throws
|
|
12
|
+
*/
|
|
8
13
|
export declare function getZapRecipient(zap: NostrEvent): string;
|
|
14
|
+
/** Returns the parsed bolt11 invoice */
|
|
9
15
|
export declare function getZapPayment(zap: NostrEvent): import("./bolt11.js").ParsedInvoice | undefined;
|
|
16
|
+
/** Gets the AddressPointer that was zapped */
|
|
10
17
|
export declare function getZapAddressPointer(zap: NostrEvent): import("nostr-tools/nip19").AddressPointer | null;
|
|
18
|
+
/** Gets the EventPointer that was zapped */
|
|
11
19
|
export declare function getZapEventPointer(zap: NostrEvent): import("nostr-tools/nip19").EventPointer | null;
|
|
20
|
+
/** Gets the preimage for the bolt11 invoice */
|
|
12
21
|
export declare function getZapPreimage(zap: NostrEvent): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the zap request event inside the zap receipt
|
|
24
|
+
* @throws
|
|
25
|
+
*/
|
|
13
26
|
export declare function getZapRequest(zap: NostrEvent): import("nostr-tools").Event;
|
|
27
|
+
/**
|
|
28
|
+
* Checks if the zap is valid
|
|
29
|
+
* DOES NOT validate LNURL address
|
|
30
|
+
*/
|
|
14
31
|
export declare function isValidZap(zap?: NostrEvent): boolean;
|
|
32
|
+
export type ZapSplit = {
|
|
33
|
+
pubkey: string;
|
|
34
|
+
percent: number;
|
|
35
|
+
weight: number;
|
|
36
|
+
relay?: string;
|
|
37
|
+
};
|
|
38
|
+
/** Returns the zap splits for an event */
|
|
39
|
+
export declare function getZapSplits(event: NostrEvent): ZapSplit[] | undefined;
|
package/dist/helpers/zap.js
CHANGED
|
@@ -2,43 +2,56 @@ import { kinds, nip57 } from "nostr-tools";
|
|
|
2
2
|
import { getOrComputeCachedValue } from "./cache.js";
|
|
3
3
|
import { getTagValue } from "./event.js";
|
|
4
4
|
import { isATag, isETag } from "./tags.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getAddressPointerFromATag, getEventPointerFromETag } from "./pointers.js";
|
|
6
6
|
import { parseBolt11 } from "./bolt11.js";
|
|
7
7
|
export const ZapRequestSymbol = Symbol.for("zap-request");
|
|
8
8
|
export const ZapFromSymbol = Symbol.for("zap-from");
|
|
9
9
|
export const ZapInvoiceSymbol = Symbol.for("zap-bolt11");
|
|
10
10
|
export const ZapEventPointerSymbol = Symbol.for("zap-event-pointer");
|
|
11
11
|
export const ZapAddressPointerSymbol = Symbol.for("zap-address-pointer");
|
|
12
|
+
/** Returns the senders pubkey */
|
|
12
13
|
export function getZapSender(zap) {
|
|
13
14
|
return getTagValue(zap, "P") || getZapRequest(zap).pubkey;
|
|
14
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Gets the receivers pubkey
|
|
18
|
+
* @throws
|
|
19
|
+
*/
|
|
15
20
|
export function getZapRecipient(zap) {
|
|
16
21
|
const recipient = getTagValue(zap, "p");
|
|
17
22
|
if (!recipient)
|
|
18
23
|
throw new Error("Missing recipient");
|
|
19
24
|
return recipient;
|
|
20
25
|
}
|
|
26
|
+
/** Returns the parsed bolt11 invoice */
|
|
21
27
|
export function getZapPayment(zap) {
|
|
22
28
|
return getOrComputeCachedValue(zap, ZapInvoiceSymbol, () => {
|
|
23
29
|
const bolt11 = getTagValue(zap, "bolt11");
|
|
24
30
|
return bolt11 ? parseBolt11(bolt11) : undefined;
|
|
25
31
|
});
|
|
26
32
|
}
|
|
33
|
+
/** Gets the AddressPointer that was zapped */
|
|
27
34
|
export function getZapAddressPointer(zap) {
|
|
28
35
|
return getOrComputeCachedValue(zap, ZapAddressPointerSymbol, () => {
|
|
29
36
|
const a = zap.tags.find(isATag);
|
|
30
|
-
return a ?
|
|
37
|
+
return a ? getAddressPointerFromATag(a) : null;
|
|
31
38
|
});
|
|
32
39
|
}
|
|
40
|
+
/** Gets the EventPointer that was zapped */
|
|
33
41
|
export function getZapEventPointer(zap) {
|
|
34
42
|
return getOrComputeCachedValue(zap, ZapEventPointerSymbol, () => {
|
|
35
43
|
const e = zap.tags.find(isETag);
|
|
36
|
-
return e ?
|
|
44
|
+
return e ? getEventPointerFromETag(e) : null;
|
|
37
45
|
});
|
|
38
46
|
}
|
|
47
|
+
/** Gets the preimage for the bolt11 invoice */
|
|
39
48
|
export function getZapPreimage(zap) {
|
|
40
49
|
return getTagValue(zap, "preimage");
|
|
41
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns the zap request event inside the zap receipt
|
|
53
|
+
* @throws
|
|
54
|
+
*/
|
|
42
55
|
export function getZapRequest(zap) {
|
|
43
56
|
return getOrComputeCachedValue(zap, ZapRequestSymbol, () => {
|
|
44
57
|
const description = getTagValue(zap, "description");
|
|
@@ -50,6 +63,10 @@ export function getZapRequest(zap) {
|
|
|
50
63
|
return JSON.parse(description);
|
|
51
64
|
});
|
|
52
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Checks if the zap is valid
|
|
68
|
+
* DOES NOT validate LNURL address
|
|
69
|
+
*/
|
|
53
70
|
export function isValidZap(zap) {
|
|
54
71
|
if (!zap)
|
|
55
72
|
return false;
|
|
@@ -64,3 +81,15 @@ export function isValidZap(zap) {
|
|
|
64
81
|
return false;
|
|
65
82
|
}
|
|
66
83
|
}
|
|
84
|
+
/** Returns the zap splits for an event */
|
|
85
|
+
export function getZapSplits(event) {
|
|
86
|
+
const tags = event.tags.filter((t) => t[0] === "zap" && t[1] && t[3]);
|
|
87
|
+
if (tags.length > 0) {
|
|
88
|
+
const targets = tags
|
|
89
|
+
.map((t) => ({ pubkey: t[1], relay: t[2], weight: parseFloat(t[3]) }))
|
|
90
|
+
.filter((p) => Number.isFinite(p.weight));
|
|
91
|
+
const total = targets.reduce((v, p) => v + p.weight, 0);
|
|
92
|
+
return targets.map((p) => ({ ...p, percent: p.weight / total }));
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Database } from "../../event-store/database.js";
|
|
3
|
+
import { claimEvents } from "../claim-events.js";
|
|
4
|
+
const event = {
|
|
5
|
+
content: '{"name":"hzrd149","picture":"https://cdn.hzrd149.com/5ed3fe5df09a74e8c126831eac999364f9eb7624e2b86d521521b8021de20bdc.png","about":"JavaScript developer working on some nostr stuff\\n- noStrudel https://nostrudel.ninja/ \\n- Blossom https://github.com/hzrd149/blossom \\n- Applesauce https://hzrd149.github.io/applesauce/","website":"https://hzrd149.com","nip05":"_@hzrd149.com","lud16":"hzrd1499@minibits.cash","pubkey":"266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5","display_name":"hzrd149","displayName":"hzrd149","banner":""}',
|
|
6
|
+
created_at: 1738362529,
|
|
7
|
+
id: "e9df8d5898c4ccfbd21fcd59f3f48abb3ff0ab7259b19570e2f1756de1e9306b",
|
|
8
|
+
kind: 0,
|
|
9
|
+
pubkey: "266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5",
|
|
10
|
+
relays: [""],
|
|
11
|
+
sig: "465a47b93626a587bf81dadc2b306b8f713a62db31d6ce1533198e9ae1e665a6eaf376a03250bf9ffbb02eb9059c8eafbd37ae1092d05d215757575bd8357586",
|
|
12
|
+
tags: [],
|
|
13
|
+
};
|
|
14
|
+
describe("claimEvents", () => {
|
|
15
|
+
it("it should claim events", () => {
|
|
16
|
+
const database = new Database();
|
|
17
|
+
const sub = database.inserted.pipe(claimEvents(database)).subscribe();
|
|
18
|
+
database.addEvent(event);
|
|
19
|
+
expect(database.isClaimed(event)).toBe(true);
|
|
20
|
+
sub.unsubscribe();
|
|
21
|
+
expect(database.isClaimed(event)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Database } from "../../event-store/database.js";
|
|
3
|
+
import { claimLatest } from "../claim-latest.js";
|
|
4
|
+
const event1 = {
|
|
5
|
+
content: '{"name":"hzrd149","picture":"https://cdn.hzrd149.com/5ed3fe5df09a74e8c126831eac999364f9eb7624e2b86d521521b8021de20bdc.png","about":"JavaScript developer working on some nostr stuff\\n- noStrudel https://nostrudel.ninja/ \\n- Blossom https://github.com/hzrd149/blossom \\n- Applesauce https://hzrd149.github.io/applesauce/","website":"https://hzrd149.com","nip05":"_@hzrd149.com","lud16":"hzrd1499@minibits.cash","pubkey":"266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5","display_name":"hzrd149","displayName":"hzrd149","banner":""}',
|
|
6
|
+
created_at: 1738362529,
|
|
7
|
+
id: "e9df8d5898c4ccfbd21fcd59f3f48abb3ff0ab7259b19570e2f1756de1e9306b",
|
|
8
|
+
kind: 0,
|
|
9
|
+
pubkey: "266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5",
|
|
10
|
+
relays: [""],
|
|
11
|
+
sig: "465a47b93626a587bf81dadc2b306b8f713a62db31d6ce1533198e9ae1e665a6eaf376a03250bf9ffbb02eb9059c8eafbd37ae1092d05d215757575bd8357586",
|
|
12
|
+
tags: [],
|
|
13
|
+
};
|
|
14
|
+
const event2 = {
|
|
15
|
+
content: '{"name":"Cesar Dias","website":"dev.nosotros.app","picture":"https://nostr.build/i/5b0e4387b0fdfff9897ee7f8dcc554761fe377583a5fb71bbf3b915e7c4971c2.jpg","display_name":"Cesar Dias","nip05":"_@nosotros.app","lud16":"cesardias@getalby.com","about":"Developer 🇧🇷, building a client https://dev.nosotros.app and nostr-editor https://github.com/cesardeazevedo/nostr-editor","banner":"https://image.nostr.build/87dbc55a6391d15bddda206561d53867a5679dd95e84fe8ed62bfe2e3adcadf3.jpg\\",\\"ox 87dbc55a6391d15bddda206561d53867a5679dd95e84fe8ed62bfe2e3adcadf3"}',
|
|
16
|
+
created_at: 1727998492,
|
|
17
|
+
id: "c771fe19ac255ea28690c5547258a5e146d2f47805f7f48093b773478bdd137c",
|
|
18
|
+
kind: 0,
|
|
19
|
+
pubkey: "c6603b0f1ccfec625d9c08b753e4f774eaf7d1cf2769223125b5fd4da728019e",
|
|
20
|
+
relays: [""],
|
|
21
|
+
sig: "5220d6a8cdb4837b2569c26a84a2ac6a44427a224cb1602c05c578c6a63fe122a37e16455b09cb38bf297fc8161a8e715d7b444d017624c044d87a77e092c881",
|
|
22
|
+
tags: [["alt", "User profile for Cesar Dias"]],
|
|
23
|
+
};
|
|
24
|
+
describe("claimLatest", () => {
|
|
25
|
+
it("it should claim events", () => {
|
|
26
|
+
const database = new Database();
|
|
27
|
+
const sub = database.inserted.pipe(claimLatest(database)).subscribe();
|
|
28
|
+
database.addEvent(event1);
|
|
29
|
+
expect(database.isClaimed(event1)).toBe(true);
|
|
30
|
+
database.addEvent(event2);
|
|
31
|
+
expect(database.isClaimed(event1)).toBe(false);
|
|
32
|
+
expect(database.isClaimed(event2)).toBe(true);
|
|
33
|
+
sub.unsubscribe();
|
|
34
|
+
expect(database.isClaimed(event1)).toBe(false);
|
|
35
|
+
expect(database.isClaimed(event2)).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { Observable, Subject, firstValueFrom } from "rxjs";
|
|
3
|
+
import { simpleTimeout, TimeoutError } from "../simple-timeout.js";
|
|
4
|
+
describe("simpleTimeout operator", () => {
|
|
5
|
+
it("should throw TimeoutError after specified timeout period", async () => {
|
|
6
|
+
const subject = new Subject();
|
|
7
|
+
const obs = subject.pipe(simpleTimeout(10));
|
|
8
|
+
const promise = firstValueFrom(obs);
|
|
9
|
+
await expect(promise).rejects.toThrow(TimeoutError);
|
|
10
|
+
await expect(promise).rejects.toThrow("Timeout");
|
|
11
|
+
});
|
|
12
|
+
it("should throw TimeoutError with custom message", async () => {
|
|
13
|
+
const subject = new Subject();
|
|
14
|
+
const customMessage = "Custom timeout message";
|
|
15
|
+
const obs = subject.pipe(simpleTimeout(10, customMessage));
|
|
16
|
+
const promise = firstValueFrom(obs);
|
|
17
|
+
await expect(promise).rejects.toThrow(TimeoutError);
|
|
18
|
+
await expect(promise).rejects.toThrow(customMessage);
|
|
19
|
+
});
|
|
20
|
+
it("should not throw when value emitted before timeout", async () => {
|
|
21
|
+
const subject = new Subject();
|
|
22
|
+
const obs = subject.pipe(simpleTimeout(1000));
|
|
23
|
+
const promise = firstValueFrom(obs);
|
|
24
|
+
subject.next("test value");
|
|
25
|
+
await expect(promise).resolves.toBe("test value");
|
|
26
|
+
});
|
|
27
|
+
it("should complete without error when source emits non-null value before timeout", async () => {
|
|
28
|
+
const source = new Observable((subscriber) => {
|
|
29
|
+
subscriber.next("test value");
|
|
30
|
+
});
|
|
31
|
+
const result = await firstValueFrom(source.pipe(simpleTimeout(10)));
|
|
32
|
+
expect(result).toBe("test value");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { MonoTypeOperatorFunction } from "rxjs";
|
|
2
|
+
import { NostrEvent } from "nostr-tools";
|
|
3
|
+
import { Database } from "../event-store/database.js";
|
|
4
|
+
/** keep a claim on any event that goes through this observable, claims are removed when the observable completes */
|
|
5
|
+
export declare function claimEvents<T extends NostrEvent[] | NostrEvent | undefined>(database: Database): MonoTypeOperatorFunction<T>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { finalize, tap } from "rxjs";
|
|
2
|
+
/** keep a claim on any event that goes through this observable, claims are removed when the observable completes */
|
|
3
|
+
export function claimEvents(database) {
|
|
4
|
+
return (source) => {
|
|
5
|
+
const seen = new Set();
|
|
6
|
+
return source.pipe(
|
|
7
|
+
// claim all events
|
|
8
|
+
tap((message) => {
|
|
9
|
+
if (message === undefined)
|
|
10
|
+
return;
|
|
11
|
+
if (Array.isArray(message)) {
|
|
12
|
+
for (const event of message) {
|
|
13
|
+
seen.add(event);
|
|
14
|
+
database.claimEvent(event, source);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
seen.add(message);
|
|
19
|
+
database.claimEvent(message, source);
|
|
20
|
+
}
|
|
21
|
+
}),
|
|
22
|
+
// remove claims on cleanup
|
|
23
|
+
finalize(() => {
|
|
24
|
+
for (const e of seen)
|
|
25
|
+
database.removeClaim(e, source);
|
|
26
|
+
}));
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { finalize, tap } from "rxjs";
|
|
2
|
+
export function claimLatest(database) {
|
|
3
|
+
return (source) => {
|
|
4
|
+
let latest = undefined;
|
|
5
|
+
return source.pipe(tap((event) => {
|
|
6
|
+
// remove old claim
|
|
7
|
+
if (latest)
|
|
8
|
+
database.removeClaim(latest, source);
|
|
9
|
+
// claim new event
|
|
10
|
+
if (event)
|
|
11
|
+
database.claimEvent(event, source);
|
|
12
|
+
// update state
|
|
13
|
+
latest = event;
|
|
14
|
+
}), finalize(() => {
|
|
15
|
+
// late claim
|
|
16
|
+
if (latest)
|
|
17
|
+
database.removeClaim(latest, source);
|
|
18
|
+
}));
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BehaviorSubject, firstValueFrom } from "rxjs";
|
|
2
|
+
/** Subscribes and returns the observables current or next value */
|
|
3
|
+
export function getObservableValue(observable) {
|
|
4
|
+
if (observable instanceof BehaviorSubject)
|
|
5
|
+
return observable.value;
|
|
6
|
+
if (Reflect.has(observable, "value"))
|
|
7
|
+
return Reflect.get(observable, "value");
|
|
8
|
+
return firstValueFrom(observable);
|
|
9
|
+
}
|
package/dist/observable/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OperatorFunction } from "rxjs";
|
|
2
2
|
/**
|
|
3
3
|
* Creates an operator that adds a 'value' property and multiplexes the source
|
|
4
4
|
* @param config Optional ShareConfig for customizing sharing behavior
|
|
5
5
|
*/
|
|
6
|
-
export declare function shareLatestValue<T>(
|
|
7
|
-
value: T | undefined;
|
|
8
|
-
};
|
|
6
|
+
export declare function shareLatestValue<T>(): OperatorFunction<T, T | undefined>;
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import { share } from "rxjs";
|
|
2
|
-
import { tap } from "rxjs/operators";
|
|
1
|
+
import { BehaviorSubject, share } from "rxjs";
|
|
3
2
|
/**
|
|
4
3
|
* Creates an operator that adds a 'value' property and multiplexes the source
|
|
5
4
|
* @param config Optional ShareConfig for customizing sharing behavior
|
|
6
5
|
*/
|
|
7
|
-
export function shareLatestValue(
|
|
8
|
-
return (source) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
export function shareLatestValue() {
|
|
7
|
+
return (source$) => source$.pipe(share({ connector: () => new BehaviorSubject(undefined) }));
|
|
8
|
+
// return (source: Observable<T>): Observable<T> & { value: T | undefined } => {
|
|
9
|
+
// // Create storage for latest value
|
|
10
|
+
// let latestValue: T | undefined = undefined;
|
|
11
|
+
// // Create shared source with value tracking
|
|
12
|
+
// const shared$ = source.pipe(
|
|
13
|
+
// tap((value) => {
|
|
14
|
+
// latestValue = value;
|
|
15
|
+
// }),
|
|
16
|
+
// share(config),
|
|
17
|
+
// );
|
|
18
|
+
// // Add value property
|
|
19
|
+
// Object.defineProperty(shared$, "value", {
|
|
20
|
+
// get: () => latestValue,
|
|
21
|
+
// });
|
|
22
|
+
// return shared$ as Observable<T> & { value: T | undefined };
|
|
23
|
+
// };
|
|
21
24
|
}
|
package/dist/promise/deferred.js
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { map } from "rxjs/operators";
|
|
2
|
+
import { BLOSSOM_SERVER_LIST_KIND, getBlossomServersFromList } from "../helpers/blossom.js";
|
|
3
|
+
export function UserBlossomServersQuery(pubkey) {
|
|
4
|
+
return {
|
|
5
|
+
key: pubkey,
|
|
6
|
+
run: (store) => store
|
|
7
|
+
.replaceable(BLOSSOM_SERVER_LIST_KIND, pubkey)
|
|
8
|
+
.pipe(map((event) => event && getBlossomServersFromList(event))),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Bookmarks } from "../helpers/bookmarks.js";
|
|
2
|
+
import { Query } from "../query-store/index.js";
|
|
3
|
+
export declare function UserBookmarkQuery(pubkey: string): Query<Bookmarks | undefined>;
|
|
4
|
+
export declare function UserHiddenBookmarkQuery(pubkey: string): Query<(Bookmarks & {
|
|
5
|
+
locked: false;
|
|
6
|
+
}) | {
|
|
7
|
+
locked: true;
|
|
8
|
+
} | undefined>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { kinds } from "nostr-tools";
|
|
2
|
+
import { map } from "rxjs/operators";
|
|
3
|
+
import { isHiddenTagsLocked } from "../helpers/index.js";
|
|
4
|
+
import { getBookmarks, getHiddenBookmarks } from "../helpers/bookmarks.js";
|
|
5
|
+
export function UserBookmarkQuery(pubkey) {
|
|
6
|
+
return {
|
|
7
|
+
key: pubkey,
|
|
8
|
+
run: (store) => store.replaceable(kinds.Mutelist, pubkey).pipe(map((event) => event && getBookmarks(event))),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function UserHiddenBookmarkQuery(pubkey) {
|
|
12
|
+
return {
|
|
13
|
+
key: pubkey,
|
|
14
|
+
run: (store) => store.replaceable(kinds.Mutelist, pubkey).pipe(map((event) => {
|
|
15
|
+
if (!event)
|
|
16
|
+
return undefined;
|
|
17
|
+
const bookmarks = getHiddenBookmarks(event);
|
|
18
|
+
if (isHiddenTagsLocked(event) || !bookmarks)
|
|
19
|
+
return { locked: true };
|
|
20
|
+
return { locked: false, ...bookmarks };
|
|
21
|
+
})),
|
|
22
|
+
};
|
|
23
|
+
}
|