applesauce-core 0.0.0-next-20241103143210
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/LICENSE +21 -0
- package/README.md +39 -0
- package/dist/event-store/common.d.ts +1 -0
- package/dist/event-store/common.js +2 -0
- package/dist/event-store/database.d.ts +60 -0
- package/dist/event-store/database.js +294 -0
- package/dist/event-store/event-store.d.ts +26 -0
- package/dist/event-store/event-store.js +236 -0
- package/dist/event-store/index.d.ts +2 -0
- package/dist/event-store/index.js +2 -0
- package/dist/helpers/bolt11.d.ts +8 -0
- package/dist/helpers/bolt11.js +14 -0
- package/dist/helpers/cache.d.ts +5 -0
- package/dist/helpers/cache.js +17 -0
- package/dist/helpers/emoji.d.ts +2 -0
- package/dist/helpers/emoji.js +4 -0
- package/dist/helpers/event.d.ts +34 -0
- package/dist/helpers/event.js +64 -0
- package/dist/helpers/filter.d.ts +12 -0
- package/dist/helpers/filter.js +50 -0
- package/dist/helpers/hashtag.d.ts +2 -0
- package/dist/helpers/hashtag.js +7 -0
- package/dist/helpers/index.d.ts +16 -0
- package/dist/helpers/index.js +16 -0
- package/dist/helpers/json.d.ts +1 -0
- package/dist/helpers/json.js +8 -0
- package/dist/helpers/lru.d.ts +32 -0
- package/dist/helpers/lru.js +148 -0
- package/dist/helpers/mailboxes.d.ts +17 -0
- package/dist/helpers/mailboxes.js +37 -0
- package/dist/helpers/mailboxes.test.d.ts +1 -0
- package/dist/helpers/mailboxes.test.js +80 -0
- package/dist/helpers/pointers.d.ts +22 -0
- package/dist/helpers/pointers.js +127 -0
- package/dist/helpers/profile.d.ts +25 -0
- package/dist/helpers/profile.js +28 -0
- package/dist/helpers/relays.d.ts +12 -0
- package/dist/helpers/relays.js +31 -0
- package/dist/helpers/string.d.ts +4 -0
- package/dist/helpers/string.js +13 -0
- package/dist/helpers/tags.d.ts +6 -0
- package/dist/helpers/tags.js +18 -0
- package/dist/helpers/threading.d.ts +55 -0
- package/dist/helpers/threading.js +73 -0
- package/dist/helpers/time.d.ts +2 -0
- package/dist/helpers/time.js +4 -0
- package/dist/helpers/url.d.ts +11 -0
- package/dist/helpers/url.js +29 -0
- package/dist/helpers/zap.d.ts +12 -0
- package/dist/helpers/zap.js +51 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.js +2 -0
- package/dist/observable/getValue.d.ts +2 -0
- package/dist/observable/getValue.js +13 -0
- package/dist/observable/index.d.ts +2 -0
- package/dist/observable/index.js +2 -0
- package/dist/observable/share-behavior.d.ts +2 -0
- package/dist/observable/share-behavior.js +7 -0
- package/dist/observable/share-latest-value.d.ts +8 -0
- package/dist/observable/share-latest-value.js +21 -0
- package/dist/observable/stateful.d.ts +10 -0
- package/dist/observable/stateful.js +60 -0
- package/dist/observable/throttle.d.ts +3 -0
- package/dist/observable/throttle.js +23 -0
- package/dist/promise/deferred.d.ts +5 -0
- package/dist/promise/deferred.js +14 -0
- package/dist/promise/index.d.ts +1 -0
- package/dist/promise/index.js +1 -0
- package/dist/queries/index.d.ts +6 -0
- package/dist/queries/index.js +6 -0
- package/dist/queries/mailboxes.d.ts +5 -0
- package/dist/queries/mailboxes.js +12 -0
- package/dist/queries/profile.d.ts +3 -0
- package/dist/queries/profile.js +11 -0
- package/dist/queries/reactions.d.ts +4 -0
- package/dist/queries/reactions.js +19 -0
- package/dist/queries/simple.d.ts +16 -0
- package/dist/queries/simple.js +38 -0
- package/dist/queries/thread.d.ts +23 -0
- package/dist/queries/thread.js +66 -0
- package/dist/queries/zaps.d.ts +4 -0
- package/dist/queries/zaps.js +16 -0
- package/dist/query-store/index.d.ts +47 -0
- package/dist/query-store/index.js +60 -0
- package/dist/utils/lru.d.ts +32 -0
- package/dist/utils/lru.js +148 -0
- package/package.json +83 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { naddrEncode, neventEncode, noteEncode, nprofileEncode, npubEncode, nrelayEncode, nsecEncode, } from "nostr-tools/nip19";
|
|
2
|
+
import { getPublicKey } from "nostr-tools";
|
|
3
|
+
import { safeRelayUrls } from "./relays.js";
|
|
4
|
+
export function parseCoordinate(a, requireD = false, silent = true) {
|
|
5
|
+
const parts = a.split(":");
|
|
6
|
+
const kind = parts[0] && parseInt(parts[0]);
|
|
7
|
+
const pubkey = parts[1];
|
|
8
|
+
const d = parts[2];
|
|
9
|
+
if (!kind) {
|
|
10
|
+
if (silent)
|
|
11
|
+
return null;
|
|
12
|
+
else
|
|
13
|
+
throw new Error("Missing kind");
|
|
14
|
+
}
|
|
15
|
+
if (!pubkey) {
|
|
16
|
+
if (silent)
|
|
17
|
+
return null;
|
|
18
|
+
else
|
|
19
|
+
throw new Error("Missing pubkey");
|
|
20
|
+
}
|
|
21
|
+
if (requireD && d === undefined) {
|
|
22
|
+
if (silent)
|
|
23
|
+
return null;
|
|
24
|
+
else
|
|
25
|
+
throw new Error("Missing identifier");
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
kind,
|
|
29
|
+
pubkey,
|
|
30
|
+
identifier: d,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function getPubkeyFromDecodeResult(result) {
|
|
34
|
+
if (!result)
|
|
35
|
+
return;
|
|
36
|
+
switch (result.type) {
|
|
37
|
+
case "naddr":
|
|
38
|
+
case "nprofile":
|
|
39
|
+
return result.data.pubkey;
|
|
40
|
+
case "npub":
|
|
41
|
+
return result.data;
|
|
42
|
+
case "nsec":
|
|
43
|
+
return getPublicKey(result.data);
|
|
44
|
+
default:
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function encodeDecodeResult(result) {
|
|
49
|
+
switch (result.type) {
|
|
50
|
+
case "naddr":
|
|
51
|
+
return naddrEncode(result.data);
|
|
52
|
+
case "nprofile":
|
|
53
|
+
return nprofileEncode(result.data);
|
|
54
|
+
case "nevent":
|
|
55
|
+
return neventEncode(result.data);
|
|
56
|
+
case "nrelay":
|
|
57
|
+
return nrelayEncode(result.data);
|
|
58
|
+
case "nsec":
|
|
59
|
+
return nsecEncode(result.data);
|
|
60
|
+
case "npub":
|
|
61
|
+
return npubEncode(result.data);
|
|
62
|
+
case "note":
|
|
63
|
+
return noteEncode(result.data);
|
|
64
|
+
}
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
export function getEventPointerFromTag(tag) {
|
|
68
|
+
if (!tag[1])
|
|
69
|
+
throw new Error("Missing event id in tag");
|
|
70
|
+
let pointer = { id: tag[1] };
|
|
71
|
+
if (tag[2])
|
|
72
|
+
pointer.relays = safeRelayUrls([tag[2]]);
|
|
73
|
+
return pointer;
|
|
74
|
+
}
|
|
75
|
+
export function getAddressPointerFromTag(tag) {
|
|
76
|
+
if (!tag[1])
|
|
77
|
+
throw new Error("Missing coordinate in tag");
|
|
78
|
+
const pointer = parseCoordinate(tag[1], true, false);
|
|
79
|
+
if (tag[2])
|
|
80
|
+
pointer.relays = safeRelayUrls([tag[2]]);
|
|
81
|
+
return pointer;
|
|
82
|
+
}
|
|
83
|
+
export function getProfilePointerFromTag(tag) {
|
|
84
|
+
if (!tag[1])
|
|
85
|
+
throw new Error("Missing pubkey in tag");
|
|
86
|
+
const pointer = { pubkey: tag[1] };
|
|
87
|
+
if (tag[2])
|
|
88
|
+
pointer.relays = safeRelayUrls([tag[2]]);
|
|
89
|
+
return pointer;
|
|
90
|
+
}
|
|
91
|
+
export function getPointerFromTag(tag) {
|
|
92
|
+
try {
|
|
93
|
+
switch (tag[0]) {
|
|
94
|
+
case "e":
|
|
95
|
+
return { type: "nevent", data: getEventPointerFromTag(tag) };
|
|
96
|
+
case "a":
|
|
97
|
+
return {
|
|
98
|
+
type: "naddr",
|
|
99
|
+
data: getAddressPointerFromTag(tag),
|
|
100
|
+
};
|
|
101
|
+
case "p":
|
|
102
|
+
return { type: "nprofile", data: getProfilePointerFromTag(tag) };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) { }
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
export function isAddressPointer(pointer) {
|
|
109
|
+
return (typeof pointer !== "string" &&
|
|
110
|
+
Object.hasOwn(pointer, "identifier") &&
|
|
111
|
+
Object.hasOwn(pointer, "pubkey") &&
|
|
112
|
+
Object.hasOwn(pointer, "kind"));
|
|
113
|
+
}
|
|
114
|
+
export function isEventPointer(pointer) {
|
|
115
|
+
return typeof pointer !== "string" && Object.hasOwn(pointer, "id");
|
|
116
|
+
}
|
|
117
|
+
export function getCoordinateFromAddressPointer(pointer) {
|
|
118
|
+
return `${pointer.kind}:${pointer.pubkey}:${pointer.identifier}`;
|
|
119
|
+
}
|
|
120
|
+
export function getATagFromAddressPointer(pointer) {
|
|
121
|
+
const relay = pointer.relays?.[0];
|
|
122
|
+
const coordinate = getCoordinateFromAddressPointer(pointer);
|
|
123
|
+
return relay ? ["a", coordinate, relay] : ["a", coordinate];
|
|
124
|
+
}
|
|
125
|
+
export function getETagFromEventPointer(pointer) {
|
|
126
|
+
return pointer.relays?.length ? ["e", pointer.id, pointer.relays[0]] : ["e", pointer.id];
|
|
127
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
|
+
export declare const ProfileContentSymbol: unique symbol;
|
|
3
|
+
declare module "nostr-tools" {
|
|
4
|
+
interface Event {
|
|
5
|
+
[ProfileContentSymbol]?: ProfileContent | Error;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export type ProfileContent = {
|
|
9
|
+
name?: string;
|
|
10
|
+
display_name?: string;
|
|
11
|
+
displayName?: string;
|
|
12
|
+
about?: string;
|
|
13
|
+
/** @deprecated */
|
|
14
|
+
image?: string;
|
|
15
|
+
picture?: string;
|
|
16
|
+
banner?: string;
|
|
17
|
+
website?: string;
|
|
18
|
+
lud16?: string;
|
|
19
|
+
lud06?: string;
|
|
20
|
+
nip05?: string;
|
|
21
|
+
};
|
|
22
|
+
/** Returns the parsed profile content for a kind 0 event */
|
|
23
|
+
export declare function getProfileContent(event: NostrEvent): ProfileContent;
|
|
24
|
+
export declare function getProfileContent(event: NostrEvent, quite: false): ProfileContent;
|
|
25
|
+
export declare function getProfileContent(event: NostrEvent, quite: true): ProfileContent | Error;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const ProfileContentSymbol = Symbol.for("profile-content");
|
|
2
|
+
export function getProfileContent(event, quite = false) {
|
|
3
|
+
let cached = event[ProfileContentSymbol];
|
|
4
|
+
if (!cached) {
|
|
5
|
+
try {
|
|
6
|
+
const profile = JSON.parse(event.content);
|
|
7
|
+
// ensure nip05 is a string
|
|
8
|
+
if (profile.nip05 && typeof profile.nip05 !== "string")
|
|
9
|
+
profile.nip05 = String(profile.nip05);
|
|
10
|
+
cached = event[ProfileContentSymbol] = profile;
|
|
11
|
+
}
|
|
12
|
+
catch (e) {
|
|
13
|
+
if (e instanceof Error)
|
|
14
|
+
cached = event[ProfileContentSymbol] = e;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (cached === undefined) {
|
|
18
|
+
throw new Error("Failed to parse profile");
|
|
19
|
+
}
|
|
20
|
+
else if (cached instanceof Error) {
|
|
21
|
+
if (!quite)
|
|
22
|
+
throw cached;
|
|
23
|
+
else
|
|
24
|
+
return cached;
|
|
25
|
+
}
|
|
26
|
+
else
|
|
27
|
+
return cached;
|
|
28
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
|
+
export declare const SeenRelaysSymbol: unique symbol;
|
|
3
|
+
declare module "nostr-tools" {
|
|
4
|
+
interface Event {
|
|
5
|
+
[SeenRelaysSymbol]?: Set<string>;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export declare function addSeenRelay(event: NostrEvent, relay: string): Set<string>;
|
|
9
|
+
export declare function getSeenRelays(event: NostrEvent): Set<string> | undefined;
|
|
10
|
+
export declare function validateRelayURL(relay: string | URL): URL;
|
|
11
|
+
export declare function safeRelayUrl(relayUrl: string | URL): string | null;
|
|
12
|
+
export declare function safeRelayUrls(urls: Iterable<string>): string[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const SeenRelaysSymbol = Symbol.for("seen-relays");
|
|
2
|
+
// Seen relays
|
|
3
|
+
export function addSeenRelay(event, relay) {
|
|
4
|
+
if (!event[SeenRelaysSymbol])
|
|
5
|
+
event[SeenRelaysSymbol] = new Set();
|
|
6
|
+
event[SeenRelaysSymbol].add(relay);
|
|
7
|
+
return event[SeenRelaysSymbol];
|
|
8
|
+
}
|
|
9
|
+
export function getSeenRelays(event) {
|
|
10
|
+
return event[SeenRelaysSymbol];
|
|
11
|
+
}
|
|
12
|
+
// Relay URLs
|
|
13
|
+
export function validateRelayURL(relay) {
|
|
14
|
+
if (typeof relay === "string" && relay.includes(",ws"))
|
|
15
|
+
throw new Error("Can not have multiple relays in one string");
|
|
16
|
+
const url = typeof relay === "string" ? new URL(relay) : relay;
|
|
17
|
+
if (url.protocol !== "wss:" && url.protocol !== "ws:")
|
|
18
|
+
throw new Error("Incorrect protocol");
|
|
19
|
+
return url;
|
|
20
|
+
}
|
|
21
|
+
export function safeRelayUrl(relayUrl) {
|
|
22
|
+
try {
|
|
23
|
+
return validateRelayURL(relayUrl).toString();
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function safeRelayUrls(urls) {
|
|
30
|
+
return Array.from(urls).map(safeRelayUrl).filter(Boolean);
|
|
31
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function isHex(str) {
|
|
2
|
+
if (str?.match(/^[0-9a-f]+$/i))
|
|
3
|
+
return true;
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
export function isHexKey(key) {
|
|
7
|
+
if (key?.toLowerCase()?.match(/^[0-9a-f]{64}$/))
|
|
8
|
+
return true;
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
export function stripInvisibleChar(str) {
|
|
12
|
+
return str && str.replaceAll(/[\p{Cf}\p{Zs}]/gu, "");
|
|
13
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function isETag(tag: string[]): tag is ["e", string, ...string[]];
|
|
2
|
+
export declare function isPTag(tag: string[]): tag is ["p", string, ...string[]];
|
|
3
|
+
export declare function isRTag(tag: string[]): tag is ["r", string, ...string[]];
|
|
4
|
+
export declare function isDTag(tag: string[]): tag is ["d", string, ...string[]];
|
|
5
|
+
export declare function isATag(tag: string[]): tag is ["a", string, ...string[]];
|
|
6
|
+
export declare function isTTag(tag: string[]): tag is ["t", string, ...string[]];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function isETag(tag) {
|
|
2
|
+
return tag[0] === "e" && tag[1] !== undefined;
|
|
3
|
+
}
|
|
4
|
+
export function isPTag(tag) {
|
|
5
|
+
return tag[0] === "p" && tag[1] !== undefined;
|
|
6
|
+
}
|
|
7
|
+
export function isRTag(tag) {
|
|
8
|
+
return tag[0] === "r" && tag[1] !== undefined;
|
|
9
|
+
}
|
|
10
|
+
export function isDTag(tag) {
|
|
11
|
+
return tag[0] === "d" && tag[1] !== undefined;
|
|
12
|
+
}
|
|
13
|
+
export function isATag(tag) {
|
|
14
|
+
return tag[0] === "a" && tag[1] !== undefined;
|
|
15
|
+
}
|
|
16
|
+
export function isTTag(tag) {
|
|
17
|
+
return tag[0] === "a" && tag[1] !== undefined;
|
|
18
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { EventTemplate, NostrEvent } from "nostr-tools";
|
|
2
|
+
import { AddressPointer, EventPointer } from "nostr-tools/nip19";
|
|
3
|
+
export type ThreadReferences = {
|
|
4
|
+
root?: {
|
|
5
|
+
e: EventPointer;
|
|
6
|
+
a: undefined;
|
|
7
|
+
} | {
|
|
8
|
+
e: undefined;
|
|
9
|
+
a: AddressPointer;
|
|
10
|
+
} | {
|
|
11
|
+
e: EventPointer;
|
|
12
|
+
a: AddressPointer;
|
|
13
|
+
};
|
|
14
|
+
reply?: {
|
|
15
|
+
e: EventPointer;
|
|
16
|
+
a: undefined;
|
|
17
|
+
} | {
|
|
18
|
+
e: undefined;
|
|
19
|
+
a: AddressPointer;
|
|
20
|
+
} | {
|
|
21
|
+
e: EventPointer;
|
|
22
|
+
a: AddressPointer;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export declare const Nip10ThreadRefsSymbol: unique symbol;
|
|
26
|
+
declare module "nostr-tools" {
|
|
27
|
+
interface Event {
|
|
28
|
+
[Nip10ThreadRefsSymbol]?: ThreadReferences;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Parses NIP-10 tags and handles legacy behavior */
|
|
32
|
+
export declare function interpretThreadTags(event: NostrEvent | EventTemplate): {
|
|
33
|
+
root?: {
|
|
34
|
+
e: string[];
|
|
35
|
+
a: undefined;
|
|
36
|
+
} | {
|
|
37
|
+
e: undefined;
|
|
38
|
+
a: string[];
|
|
39
|
+
} | {
|
|
40
|
+
e: string[];
|
|
41
|
+
a: string[];
|
|
42
|
+
};
|
|
43
|
+
reply?: {
|
|
44
|
+
e: string[];
|
|
45
|
+
a: undefined;
|
|
46
|
+
} | {
|
|
47
|
+
e: undefined;
|
|
48
|
+
a: string[];
|
|
49
|
+
} | {
|
|
50
|
+
e: string[];
|
|
51
|
+
a: string[];
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
/** Returns the parsed NIP-10 tags for an event */
|
|
55
|
+
export declare function getNip10References(event: NostrEvent | EventTemplate): ThreadReferences;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { getAddressPointerFromTag, getEventPointerFromTag } from "./pointers.js";
|
|
2
|
+
import { getOrComputeCachedValue } from "./cache.js";
|
|
3
|
+
export const Nip10ThreadRefsSymbol = Symbol.for("nip10-thread-refs");
|
|
4
|
+
/** Parses NIP-10 tags and handles legacy behavior */
|
|
5
|
+
export function interpretThreadTags(event) {
|
|
6
|
+
const eTags = event.tags.filter((t) => t[0] === "e" && t[1]);
|
|
7
|
+
const aTags = event.tags.filter((t) => t[0] === "a" && t[1]);
|
|
8
|
+
// find the root and reply tags.
|
|
9
|
+
let rootETag = eTags.find((t) => t[3] === "root");
|
|
10
|
+
let replyETag = eTags.find((t) => t[3] === "reply");
|
|
11
|
+
let rootATag = aTags.find((t) => t[3] === "root");
|
|
12
|
+
let replyATag = aTags.find((t) => t[3] === "reply");
|
|
13
|
+
if (!rootETag || !replyETag) {
|
|
14
|
+
// a direct reply does not need a "reply" reference
|
|
15
|
+
// https://github.com/nostr-protocol/nips/blob/master/10.md
|
|
16
|
+
// this is not necessarily to spec. but if there is only one id (root or reply) then assign it to both
|
|
17
|
+
// this handles the cases where a client only set a "reply" tag and no root
|
|
18
|
+
rootETag = replyETag = rootETag || replyETag;
|
|
19
|
+
}
|
|
20
|
+
if (!rootATag || !replyATag) {
|
|
21
|
+
rootATag = replyATag = rootATag || replyATag;
|
|
22
|
+
}
|
|
23
|
+
if (!rootETag && !replyETag) {
|
|
24
|
+
// legacy behavior
|
|
25
|
+
// https://github.com/nostr-protocol/nips/blob/master/10.md#positional-e-tags-deprecated
|
|
26
|
+
const legacyETags = eTags.filter((t) => {
|
|
27
|
+
// ignore it if there is a marker
|
|
28
|
+
if (t[3])
|
|
29
|
+
return false;
|
|
30
|
+
return true;
|
|
31
|
+
});
|
|
32
|
+
if (legacyETags.length >= 1) {
|
|
33
|
+
// first tag is the root
|
|
34
|
+
rootETag = legacyETags[0];
|
|
35
|
+
// last tag is reply
|
|
36
|
+
replyETag = legacyETags[legacyETags.length - 1] ?? rootETag;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
root: rootETag || rootATag ? { e: rootETag, a: rootATag } : undefined,
|
|
41
|
+
reply: replyETag || replyATag ? { e: replyETag, a: replyATag } : undefined,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Returns the parsed NIP-10 tags for an event */
|
|
45
|
+
export function getNip10References(event) {
|
|
46
|
+
return getOrComputeCachedValue(event, Nip10ThreadRefsSymbol, () => {
|
|
47
|
+
const tags = interpretThreadTags(event);
|
|
48
|
+
let root;
|
|
49
|
+
if (tags.root) {
|
|
50
|
+
try {
|
|
51
|
+
root = {
|
|
52
|
+
e: tags.root.e && getEventPointerFromTag(tags.root.e),
|
|
53
|
+
a: tags.root.a && getAddressPointerFromTag(tags.root.a),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (error) { }
|
|
57
|
+
}
|
|
58
|
+
let reply;
|
|
59
|
+
if (tags.reply) {
|
|
60
|
+
try {
|
|
61
|
+
reply = {
|
|
62
|
+
e: tags.reply.e && getEventPointerFromTag(tags.reply.e),
|
|
63
|
+
a: tags.reply.a && getAddressPointerFromTag(tags.reply.a),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) { }
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
root,
|
|
70
|
+
reply,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const convertToUrl: (url: string | URL) => URL;
|
|
2
|
+
export declare const getURLFilename: (url: URL) => string | undefined;
|
|
3
|
+
export declare const IMAGE_EXT: string[];
|
|
4
|
+
export declare const VIDEO_EXT: string[];
|
|
5
|
+
export declare const STREAM_EXT: string[];
|
|
6
|
+
export declare const AUDIO_EXT: string[];
|
|
7
|
+
export declare function isVisualMediaURL(url: string | URL): boolean;
|
|
8
|
+
export declare function isImageURL(url: string | URL): boolean;
|
|
9
|
+
export declare function isVideoURL(url: string | URL): boolean;
|
|
10
|
+
export declare function isStreamURL(url: string | URL): boolean;
|
|
11
|
+
export declare function isAudioURL(url: string | URL): boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const convertToUrl = (url) => (url instanceof URL ? url : new URL(url));
|
|
2
|
+
export const getURLFilename = (url) => url.pathname.split("/").pop()?.toLocaleLowerCase() || url.searchParams.get("filename")?.toLocaleLowerCase();
|
|
3
|
+
export const IMAGE_EXT = [".svg", ".gif", ".png", ".jpg", ".jpeg", ".webp", ".avif"];
|
|
4
|
+
export const VIDEO_EXT = [".mp4", ".mkv", ".webm", ".mov"];
|
|
5
|
+
export const STREAM_EXT = [".m3u8"];
|
|
6
|
+
export const AUDIO_EXT = [".mp3", ".wav", ".ogg", ".aac"];
|
|
7
|
+
export function isVisualMediaURL(url) {
|
|
8
|
+
return isImageURL(url) || isVideoURL(url) || isStreamURL(url);
|
|
9
|
+
}
|
|
10
|
+
export function isImageURL(url) {
|
|
11
|
+
url = convertToUrl(url);
|
|
12
|
+
const filename = getURLFilename(url);
|
|
13
|
+
return !!filename && IMAGE_EXT.some((ext) => filename.endsWith(ext));
|
|
14
|
+
}
|
|
15
|
+
export function isVideoURL(url) {
|
|
16
|
+
url = convertToUrl(url);
|
|
17
|
+
const filename = getURLFilename(url);
|
|
18
|
+
return !!filename && VIDEO_EXT.some((ext) => filename.endsWith(ext));
|
|
19
|
+
}
|
|
20
|
+
export function isStreamURL(url) {
|
|
21
|
+
url = convertToUrl(url);
|
|
22
|
+
const filename = getURLFilename(url);
|
|
23
|
+
return !!filename && STREAM_EXT.some((ext) => filename.endsWith(ext));
|
|
24
|
+
}
|
|
25
|
+
export function isAudioURL(url) {
|
|
26
|
+
url = convertToUrl(url);
|
|
27
|
+
const filename = getURLFilename(url);
|
|
28
|
+
return !!filename && AUDIO_EXT.some((ext) => filename.endsWith(ext));
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
|
+
export declare const ZapRequestSymbol: unique symbol;
|
|
3
|
+
export declare const ZapFromSymbol: unique symbol;
|
|
4
|
+
export declare const ZapInvoiceSymbol: unique symbol;
|
|
5
|
+
export declare const ZapEventPointerSymbol: unique symbol;
|
|
6
|
+
export declare function getZapSender(zap: NostrEvent): string;
|
|
7
|
+
export declare function getZapRecipient(zap: NostrEvent): string;
|
|
8
|
+
export declare function getZapPayment(zap: NostrEvent): import("./bolt11.js").ParsedInvoice | undefined;
|
|
9
|
+
export declare function getZapAddressPointer(zap: NostrEvent): import("nostr-tools/nip19").AddressPointer | null;
|
|
10
|
+
export declare function getZapEventPointer(zap: NostrEvent): import("nostr-tools/nip19").EventPointer | null;
|
|
11
|
+
export declare function getZapPreimage(zap: NostrEvent): string | undefined;
|
|
12
|
+
export declare function getZapRequest(zap: NostrEvent): import("nostr-tools").Event;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { nip57 } from "nostr-tools";
|
|
2
|
+
import { getOrComputeCachedValue } from "./cache.js";
|
|
3
|
+
import { getTagValue } from "./event.js";
|
|
4
|
+
import { isATag, isETag } from "./tags.js";
|
|
5
|
+
import { getAddressPointerFromTag, getEventPointerFromTag } from "./pointers.js";
|
|
6
|
+
import { parseBolt11 } from "./bolt11.js";
|
|
7
|
+
export const ZapRequestSymbol = Symbol.for("zap-request");
|
|
8
|
+
export const ZapFromSymbol = Symbol.for("zap-from");
|
|
9
|
+
export const ZapInvoiceSymbol = Symbol.for("zap-bolt11");
|
|
10
|
+
export const ZapEventPointerSymbol = Symbol.for("zap-event-pointer");
|
|
11
|
+
export function getZapSender(zap) {
|
|
12
|
+
return getTagValue(zap, "P") || getZapRequest(zap).pubkey;
|
|
13
|
+
}
|
|
14
|
+
export function getZapRecipient(zap) {
|
|
15
|
+
const recipient = getTagValue(zap, "p");
|
|
16
|
+
if (!recipient)
|
|
17
|
+
throw new Error("Missing recipient");
|
|
18
|
+
return recipient;
|
|
19
|
+
}
|
|
20
|
+
export function getZapPayment(zap) {
|
|
21
|
+
return getOrComputeCachedValue(zap, ZapInvoiceSymbol, () => {
|
|
22
|
+
const bolt11 = getTagValue(zap, "bolt11");
|
|
23
|
+
return bolt11 ? parseBolt11(bolt11) : undefined;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export function getZapAddressPointer(zap) {
|
|
27
|
+
return getOrComputeCachedValue(zap, ZapEventPointerSymbol, () => {
|
|
28
|
+
const a = zap.tags.find(isATag);
|
|
29
|
+
return a ? getAddressPointerFromTag(a) : null;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function getZapEventPointer(zap) {
|
|
33
|
+
return getOrComputeCachedValue(zap, ZapEventPointerSymbol, () => {
|
|
34
|
+
const e = zap.tags.find(isETag);
|
|
35
|
+
return e ? getEventPointerFromTag(e) : null;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function getZapPreimage(zap) {
|
|
39
|
+
return getTagValue(zap, "preimage");
|
|
40
|
+
}
|
|
41
|
+
export function getZapRequest(zap) {
|
|
42
|
+
return getOrComputeCachedValue(zap, ZapRequestSymbol, () => {
|
|
43
|
+
const description = getTagValue(zap, "description");
|
|
44
|
+
if (!description)
|
|
45
|
+
throw new Error("Missing description tag");
|
|
46
|
+
const error = nip57.validateZapRequest(description);
|
|
47
|
+
if (error)
|
|
48
|
+
throw new Error(error);
|
|
49
|
+
return JSON.parse(description);
|
|
50
|
+
});
|
|
51
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/logger.d.ts
ADDED
package/dist/logger.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
|
+
export function getValue(observable) {
|
|
3
|
+
if (observable instanceof BehaviorSubject)
|
|
4
|
+
return observable.value;
|
|
5
|
+
if (Reflect.has(observable, "value"))
|
|
6
|
+
return Reflect.get(observable, "value");
|
|
7
|
+
return new Promise((res) => {
|
|
8
|
+
const sub = observable.subscribe((v) => {
|
|
9
|
+
res(v);
|
|
10
|
+
sub.unsubscribe();
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Observable, ShareConfig } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Creates an operator that adds a 'value' property and multiplexes the source
|
|
4
|
+
* @param config Optional ShareConfig for customizing sharing behavior
|
|
5
|
+
*/
|
|
6
|
+
export declare function shareLatestValue<T>(config?: ShareConfig<T>): (source: Observable<T>) => Observable<T> & {
|
|
7
|
+
value: T | undefined;
|
|
8
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { share } from "rxjs";
|
|
2
|
+
import { tap } from "rxjs/operators";
|
|
3
|
+
/**
|
|
4
|
+
* Creates an operator that adds a 'value' property and multiplexes the source
|
|
5
|
+
* @param config Optional ShareConfig for customizing sharing behavior
|
|
6
|
+
*/
|
|
7
|
+
export function shareLatestValue(config = {}) {
|
|
8
|
+
return (source) => {
|
|
9
|
+
// Create storage for latest value
|
|
10
|
+
let latestValue = undefined;
|
|
11
|
+
// Create shared source with value tracking
|
|
12
|
+
const shared$ = source.pipe(tap((value) => {
|
|
13
|
+
latestValue = value;
|
|
14
|
+
}), share(config));
|
|
15
|
+
// Add value property
|
|
16
|
+
Object.defineProperty(shared$, "value", {
|
|
17
|
+
get: () => latestValue,
|
|
18
|
+
});
|
|
19
|
+
return shared$;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
export type StatefulObservable<T> = Observable<T> & {
|
|
3
|
+
_stateful?: true;
|
|
4
|
+
value?: T;
|
|
5
|
+
error?: Error;
|
|
6
|
+
complete?: boolean;
|
|
7
|
+
};
|
|
8
|
+
/** Wraps an {@link Observable} and makes it stateful */
|
|
9
|
+
export declare function stateful<T extends unknown>(observable: Observable<T>, cleanup?: boolean): StatefulObservable<T>;
|
|
10
|
+
export declare function isStateful<T extends unknown>(observable: Observable<T> | StatefulObservable<T>): observable is StatefulObservable<T>;
|