applesauce-core 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/event-store/database.d.ts +4 -41
- package/dist/event-store/database.js +15 -12
- package/dist/event-store/event-store.d.ts +1 -4
- package/dist/event-store/event-store.js +1 -10
- package/dist/helpers/bolt11.d.ts +8 -0
- package/dist/helpers/bolt11.js +14 -0
- package/dist/helpers/cache.js +9 -9
- package/dist/helpers/event.d.ts +9 -1
- package/dist/helpers/event.js +14 -1
- package/dist/helpers/filter.d.ts +4 -0
- package/dist/helpers/filter.js +9 -0
- package/dist/helpers/index.d.ts +2 -0
- package/dist/helpers/index.js +2 -0
- package/dist/helpers/mailboxes.d.ts +2 -8
- package/dist/helpers/mailboxes.js +13 -14
- package/dist/helpers/pointers.d.ts +1 -1
- package/dist/helpers/pointers.js +1 -3
- package/dist/helpers/profile.d.ts +2 -7
- package/dist/helpers/profile.js +26 -23
- package/dist/helpers/threading.d.ts +5 -5
- package/dist/helpers/threading.js +27 -15
- package/dist/helpers/zap.d.ts +14 -0
- package/dist/helpers/zap.js +66 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/observable/getValue.d.ts +1 -1
- package/dist/observable/getValue.js +4 -2
- package/dist/observable/index.d.ts +1 -2
- package/dist/observable/index.js +1 -2
- package/dist/observable/share-latest-value.d.ts +8 -0
- package/dist/observable/share-latest-value.js +21 -0
- package/dist/queries/index.d.ts +1 -0
- package/dist/queries/index.js +1 -0
- package/dist/queries/mailboxes.d.ts +2 -2
- package/dist/queries/mailboxes.js +3 -2
- package/dist/queries/profile.js +3 -2
- package/dist/queries/thread.js +3 -2
- package/dist/queries/zaps.d.ts +4 -0
- package/dist/queries/zaps.js +20 -0
- package/dist/query-store/index.d.ts +4 -5
- package/dist/query-store/index.js +2 -2
- package/package.json +10 -7
- package/dist/observable/stateful.d.ts +0 -10
- package/dist/observable/stateful.js +0 -60
- package/dist/observable/throttle.d.ts +0 -3
- package/dist/observable/throttle.js +0 -23
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
/// <reference types="debug" />
|
|
2
|
-
/// <reference types="zen-observable" />
|
|
3
1
|
import { Filter, NostrEvent } from "nostr-tools";
|
|
2
|
+
import { Subject } from "rxjs";
|
|
4
3
|
import { LRU } from "../helpers/lru.js";
|
|
5
4
|
/**
|
|
6
5
|
* An in-memory database for nostr events
|
|
@@ -14,48 +13,12 @@ export declare class Database {
|
|
|
14
13
|
protected created_at: NostrEvent[];
|
|
15
14
|
/** LRU cache of last events touched */
|
|
16
15
|
events: LRU<import("nostr-tools").Event>;
|
|
17
|
-
private insertedSignal;
|
|
18
|
-
private updatedSignal;
|
|
19
|
-
private deletedSignal;
|
|
20
16
|
/** A stream of events inserted into the database */
|
|
21
|
-
inserted:
|
|
22
|
-
subscribe(observer: ZenObservable.Observer<import("nostr-tools").Event>): ZenObservable.Subscription;
|
|
23
|
-
subscribe(onNext: (value: import("nostr-tools").Event) => void, onError?: ((error: any) => void) | undefined, onComplete?: (() => void) | undefined): ZenObservable.Subscription;
|
|
24
|
-
forEach(callback: (value: import("nostr-tools").Event) => void): Promise<void>;
|
|
25
|
-
map<R>(callback: (value: import("nostr-tools").Event) => R): import("zen-observable")<R>;
|
|
26
|
-
filter<S extends import("nostr-tools").Event>(callback: (value: import("nostr-tools").Event) => value is S): import("zen-observable")<S>;
|
|
27
|
-
filter(callback: (value: import("nostr-tools").Event) => boolean): import("zen-observable")<import("nostr-tools").Event>;
|
|
28
|
-
reduce(callback: (previousValue: import("nostr-tools").Event, currentValue: import("nostr-tools").Event) => import("nostr-tools").Event, initialValue?: import("nostr-tools").Event | undefined): import("zen-observable")<import("nostr-tools").Event>;
|
|
29
|
-
reduce<R_1>(callback: (previousValue: R_1, currentValue: import("nostr-tools").Event) => R_1, initialValue?: R_1 | undefined): import("zen-observable")<R_1>;
|
|
30
|
-
flatMap<R_2>(callback: (value: import("nostr-tools").Event) => ZenObservable.ObservableLike<R_2>): import("zen-observable")<R_2>;
|
|
31
|
-
concat<R_3>(...observable: import("zen-observable")<R_3>[]): import("zen-observable")<R_3>;
|
|
32
|
-
};
|
|
17
|
+
inserted: Subject<import("nostr-tools").Event>;
|
|
33
18
|
/** A stream of events that have been updated */
|
|
34
|
-
updated:
|
|
35
|
-
subscribe(observer: ZenObservable.Observer<import("nostr-tools").Event>): ZenObservable.Subscription;
|
|
36
|
-
subscribe(onNext: (value: import("nostr-tools").Event) => void, onError?: ((error: any) => void) | undefined, onComplete?: (() => void) | undefined): ZenObservable.Subscription;
|
|
37
|
-
forEach(callback: (value: import("nostr-tools").Event) => void): Promise<void>;
|
|
38
|
-
map<R>(callback: (value: import("nostr-tools").Event) => R): import("zen-observable")<R>;
|
|
39
|
-
filter<S extends import("nostr-tools").Event>(callback: (value: import("nostr-tools").Event) => value is S): import("zen-observable")<S>;
|
|
40
|
-
filter(callback: (value: import("nostr-tools").Event) => boolean): import("zen-observable")<import("nostr-tools").Event>;
|
|
41
|
-
reduce(callback: (previousValue: import("nostr-tools").Event, currentValue: import("nostr-tools").Event) => import("nostr-tools").Event, initialValue?: import("nostr-tools").Event | undefined): import("zen-observable")<import("nostr-tools").Event>;
|
|
42
|
-
reduce<R_1>(callback: (previousValue: R_1, currentValue: import("nostr-tools").Event) => R_1, initialValue?: R_1 | undefined): import("zen-observable")<R_1>;
|
|
43
|
-
flatMap<R_2>(callback: (value: import("nostr-tools").Event) => ZenObservable.ObservableLike<R_2>): import("zen-observable")<R_2>;
|
|
44
|
-
concat<R_3>(...observable: import("zen-observable")<R_3>[]): import("zen-observable")<R_3>;
|
|
45
|
-
};
|
|
19
|
+
updated: Subject<import("nostr-tools").Event>;
|
|
46
20
|
/** A stream of events removed of the database */
|
|
47
|
-
deleted:
|
|
48
|
-
subscribe(observer: ZenObservable.Observer<import("nostr-tools").Event>): ZenObservable.Subscription;
|
|
49
|
-
subscribe(onNext: (value: import("nostr-tools").Event) => void, onError?: ((error: any) => void) | undefined, onComplete?: (() => void) | undefined): ZenObservable.Subscription;
|
|
50
|
-
forEach(callback: (value: import("nostr-tools").Event) => void): Promise<void>;
|
|
51
|
-
map<R>(callback: (value: import("nostr-tools").Event) => R): import("zen-observable")<R>;
|
|
52
|
-
filter<S extends import("nostr-tools").Event>(callback: (value: import("nostr-tools").Event) => value is S): import("zen-observable")<S>;
|
|
53
|
-
filter(callback: (value: import("nostr-tools").Event) => boolean): import("zen-observable")<import("nostr-tools").Event>;
|
|
54
|
-
reduce(callback: (previousValue: import("nostr-tools").Event, currentValue: import("nostr-tools").Event) => import("nostr-tools").Event, initialValue?: import("nostr-tools").Event | undefined): import("zen-observable")<import("nostr-tools").Event>;
|
|
55
|
-
reduce<R_1>(callback: (previousValue: R_1, currentValue: import("nostr-tools").Event) => R_1, initialValue?: R_1 | undefined): import("zen-observable")<R_1>;
|
|
56
|
-
flatMap<R_2>(callback: (value: import("nostr-tools").Event) => ZenObservable.ObservableLike<R_2>): import("zen-observable")<R_2>;
|
|
57
|
-
concat<R_3>(...observable: import("zen-observable")<R_3>[]): import("zen-observable")<R_3>;
|
|
58
|
-
};
|
|
21
|
+
deleted: Subject<import("nostr-tools").Event>;
|
|
59
22
|
get size(): number;
|
|
60
23
|
protected claims: WeakMap<import("nostr-tools").Event, any>;
|
|
61
24
|
/** Index helper methods */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { binarySearch, insertEventIntoDescendingList } from "nostr-tools/utils";
|
|
2
|
-
import
|
|
3
|
-
import { getEventUID, getIndexableTags, getReplaceableUID } from "../helpers/event.js";
|
|
2
|
+
import { Subject } from "rxjs";
|
|
3
|
+
import { FromCacheSymbol, getEventUID, getIndexableTags, getReplaceableUID } from "../helpers/event.js";
|
|
4
4
|
import { INDEXABLE_TAGS } from "./common.js";
|
|
5
5
|
import { logger } from "../logger.js";
|
|
6
6
|
import { LRU } from "../helpers/lru.js";
|
|
@@ -16,15 +16,12 @@ export class Database {
|
|
|
16
16
|
created_at = [];
|
|
17
17
|
/** LRU cache of last events touched */
|
|
18
18
|
events = new LRU();
|
|
19
|
-
insertedSignal = new PushStream();
|
|
20
|
-
updatedSignal = new PushStream();
|
|
21
|
-
deletedSignal = new PushStream();
|
|
22
19
|
/** A stream of events inserted into the database */
|
|
23
|
-
inserted =
|
|
20
|
+
inserted = new Subject();
|
|
24
21
|
/** A stream of events that have been updated */
|
|
25
|
-
updated =
|
|
22
|
+
updated = new Subject();
|
|
26
23
|
/** A stream of events removed of the database */
|
|
27
|
-
deleted =
|
|
24
|
+
deleted = new Subject();
|
|
28
25
|
get size() {
|
|
29
26
|
return this.events.size;
|
|
30
27
|
}
|
|
@@ -79,8 +76,14 @@ export class Database {
|
|
|
79
76
|
addEvent(event) {
|
|
80
77
|
const uid = getEventUID(event);
|
|
81
78
|
const current = this.events.get(uid);
|
|
82
|
-
if (current && event.created_at <= current.created_at)
|
|
79
|
+
if (current && event.created_at <= current.created_at) {
|
|
80
|
+
// if this is a duplicate event, transfer some import symbols
|
|
81
|
+
if (current.id === event.id) {
|
|
82
|
+
if (event[FromCacheSymbol])
|
|
83
|
+
current[FromCacheSymbol] = event[FromCacheSymbol];
|
|
84
|
+
}
|
|
83
85
|
return current;
|
|
86
|
+
}
|
|
84
87
|
this.events.set(uid, event);
|
|
85
88
|
this.getKindIndex(event.kind).add(event);
|
|
86
89
|
this.getAuthorsIndex(event.pubkey).add(event);
|
|
@@ -90,13 +93,13 @@ export class Database {
|
|
|
90
93
|
}
|
|
91
94
|
}
|
|
92
95
|
insertEventIntoDescendingList(this.created_at, event);
|
|
93
|
-
this.
|
|
96
|
+
this.inserted.next(event);
|
|
94
97
|
return event;
|
|
95
98
|
}
|
|
96
99
|
/** Inserts and event into the database and notifies all subscriptions that the event has updated */
|
|
97
100
|
updateEvent(event) {
|
|
98
101
|
const inserted = this.addEvent(event);
|
|
99
|
-
this.
|
|
102
|
+
this.updated.next(inserted);
|
|
100
103
|
return inserted;
|
|
101
104
|
}
|
|
102
105
|
/** Deletes an event from the database and notifies all subscriptions */
|
|
@@ -119,7 +122,7 @@ export class Database {
|
|
|
119
122
|
const i = this.created_at.indexOf(event);
|
|
120
123
|
this.created_at.splice(i, 1);
|
|
121
124
|
this.events.delete(uid);
|
|
122
|
-
this.
|
|
125
|
+
this.deleted.next(event);
|
|
123
126
|
return true;
|
|
124
127
|
}
|
|
125
128
|
/** Sets the claim on the event and touches it */
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { Filter, NostrEvent } from "nostr-tools";
|
|
2
|
-
import Observable from "
|
|
2
|
+
import { Observable } from "rxjs";
|
|
3
3
|
import { Database } from "./database.js";
|
|
4
4
|
export declare class EventStore {
|
|
5
5
|
database: Database;
|
|
6
|
-
private singles;
|
|
7
|
-
private streams;
|
|
8
|
-
private timelines;
|
|
9
6
|
constructor();
|
|
10
7
|
/** Adds an event to the database */
|
|
11
8
|
add(event: NostrEvent, fromRelay?: string): import("nostr-tools").Event;
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { insertEventIntoDescendingList } from "nostr-tools/utils";
|
|
2
|
-
import Observable from "
|
|
2
|
+
import { Observable } from "rxjs";
|
|
3
3
|
import { Database } from "./database.js";
|
|
4
4
|
import { getEventUID, getReplaceableUID } from "../helpers/event.js";
|
|
5
5
|
import { matchFilters } from "../helpers/filter.js";
|
|
6
6
|
import { addSeenRelay } from "../helpers/relays.js";
|
|
7
7
|
export class EventStore {
|
|
8
8
|
database;
|
|
9
|
-
singles = new Map();
|
|
10
|
-
streams = new Map();
|
|
11
|
-
timelines = new Map();
|
|
12
9
|
constructor() {
|
|
13
10
|
this.database = new Database();
|
|
14
11
|
}
|
|
@@ -71,12 +68,10 @@ export class EventStore {
|
|
|
71
68
|
observer.next(undefined);
|
|
72
69
|
}
|
|
73
70
|
});
|
|
74
|
-
this.singles.set(observer, uid);
|
|
75
71
|
return () => {
|
|
76
72
|
inserted.unsubscribe();
|
|
77
73
|
deleted.unsubscribe();
|
|
78
74
|
updated.unsubscribe();
|
|
79
|
-
this.singles.delete(observer);
|
|
80
75
|
if (current)
|
|
81
76
|
this.database.removeClaim(current, observer);
|
|
82
77
|
};
|
|
@@ -160,10 +155,8 @@ export class EventStore {
|
|
|
160
155
|
claimed.add(event);
|
|
161
156
|
}
|
|
162
157
|
});
|
|
163
|
-
this.streams.set(observer, filters);
|
|
164
158
|
return () => {
|
|
165
159
|
sub.unsubscribe();
|
|
166
|
-
this.streams.delete(observer);
|
|
167
160
|
// remove all claims
|
|
168
161
|
for (const event of claimed)
|
|
169
162
|
this.database.removeClaim(event, observer);
|
|
@@ -228,9 +221,7 @@ export class EventStore {
|
|
|
228
221
|
this.database.removeClaim(current, observer);
|
|
229
222
|
}
|
|
230
223
|
});
|
|
231
|
-
this.timelines.set(observer, filters);
|
|
232
224
|
return () => {
|
|
233
|
-
this.timelines.delete(observer);
|
|
234
225
|
inserted.unsubscribe();
|
|
235
226
|
deleted.unsubscribe();
|
|
236
227
|
updated.unsubscribe();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { decode } from "light-bolt11-decoder";
|
|
2
|
+
export function parseBolt11(paymentRequest) {
|
|
3
|
+
const decoded = decode(paymentRequest);
|
|
4
|
+
const timestamp = decoded.sections.find((s) => s.name === "timestamp")?.value ?? 0;
|
|
5
|
+
const description = decoded.sections.find((s) => s.name === "description")?.value ?? "";
|
|
6
|
+
const amount = parseInt(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
|
|
7
|
+
return {
|
|
8
|
+
paymentRequest: decoded.paymentRequest,
|
|
9
|
+
description: description,
|
|
10
|
+
amount: amount,
|
|
11
|
+
timestamp: timestamp,
|
|
12
|
+
expiry: timestamp + decoded.expiry,
|
|
13
|
+
};
|
|
14
|
+
}
|
package/dist/helpers/cache.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
export function getCachedValue(event, symbol) {
|
|
2
|
-
|
|
3
|
-
return event[symbol];
|
|
2
|
+
return Reflect.get(event, symbol);
|
|
4
3
|
}
|
|
5
4
|
export function setCachedValue(event, symbol, value) {
|
|
6
|
-
|
|
7
|
-
event[symbol] = value;
|
|
5
|
+
Reflect.set(event, symbol, value);
|
|
8
6
|
}
|
|
9
7
|
/** Internal method used to cache computed values on events */
|
|
10
8
|
export function getOrComputeCachedValue(event, symbol, compute) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
if (Reflect.has(event, symbol)) {
|
|
10
|
+
return Reflect.get(event, symbol);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
const value = compute(event);
|
|
14
|
+
Reflect.set(event, symbol, value);
|
|
15
|
+
return value;
|
|
15
16
|
}
|
|
16
|
-
return cached;
|
|
17
17
|
}
|
package/dist/helpers/event.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { NostrEvent } from "nostr-tools";
|
|
1
|
+
import { NostrEvent, VerifiedEvent } from "nostr-tools";
|
|
2
2
|
export declare const EventUIDSymbol: unique symbol;
|
|
3
3
|
export declare const EventIndexableTagsSymbol: unique symbol;
|
|
4
|
+
export declare const FromCacheSymbol: unique symbol;
|
|
4
5
|
declare module "nostr-tools" {
|
|
5
6
|
interface Event {
|
|
6
7
|
[EventUIDSymbol]?: string;
|
|
7
8
|
[EventIndexableTagsSymbol]?: Set<string>;
|
|
9
|
+
[FromCacheSymbol]?: boolean;
|
|
8
10
|
}
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
@@ -24,3 +26,9 @@ export declare function getReplaceableUID(kind: number, pubkey: string, d?: stri
|
|
|
24
26
|
export declare function getIndexableTags(event: NostrEvent): Set<string>;
|
|
25
27
|
/** Returns the second index ( tag[1] ) of the first tag that matches the name */
|
|
26
28
|
export declare function getTagValue(event: NostrEvent, name: string): string | undefined;
|
|
29
|
+
/** Sets events verified flag without checking anything */
|
|
30
|
+
export declare function fakeVerifyEvent(event: NostrEvent): event is VerifiedEvent;
|
|
31
|
+
/** Marks an event as being from a cache */
|
|
32
|
+
export declare function markFromCache(event: NostrEvent): void;
|
|
33
|
+
/** Returns if an event was from a cache */
|
|
34
|
+
export declare function isFromCache(event: NostrEvent): boolean;
|
package/dist/helpers/event.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { kinds } from "nostr-tools";
|
|
1
|
+
import { kinds, verifiedSymbol } from "nostr-tools";
|
|
2
2
|
import { INDEXABLE_TAGS } from "../event-store/common.js";
|
|
3
3
|
export const EventUIDSymbol = Symbol.for("event-uid");
|
|
4
4
|
export const EventIndexableTagsSymbol = Symbol.for("indexable-tags");
|
|
5
|
+
export const FromCacheSymbol = Symbol.for("from-cache");
|
|
5
6
|
/**
|
|
6
7
|
* Returns if a kind is replaceable ( 10000 <= n < 20000 || n == 0 || n == 3 )
|
|
7
8
|
* or parameterized replaceable ( 30000 <= n < 40000 )
|
|
@@ -49,3 +50,15 @@ export function getIndexableTags(event) {
|
|
|
49
50
|
export function getTagValue(event, name) {
|
|
50
51
|
return event.tags.find((t) => t[0] === name)?.[1];
|
|
51
52
|
}
|
|
53
|
+
/** Sets events verified flag without checking anything */
|
|
54
|
+
export function fakeVerifyEvent(event) {
|
|
55
|
+
return (event[verifiedSymbol] = true);
|
|
56
|
+
}
|
|
57
|
+
/** Marks an event as being from a cache */
|
|
58
|
+
export function markFromCache(event) {
|
|
59
|
+
event[FromCacheSymbol] = true;
|
|
60
|
+
}
|
|
61
|
+
/** Returns if an event was from a cache */
|
|
62
|
+
export function isFromCache(event) {
|
|
63
|
+
return !!event[FromCacheSymbol];
|
|
64
|
+
}
|
package/dist/helpers/filter.d.ts
CHANGED
|
@@ -6,3 +6,7 @@ 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
|
+
/** Stringify filters in a predictable way */
|
|
10
|
+
export declare function stringifyFilter(filter: Filter | Filter[]): string;
|
|
11
|
+
/** Check if two filters are equal */
|
|
12
|
+
export declare function isFilterEqual(a: Filter | Filter[], b: Filter | Filter[]): boolean;
|
package/dist/helpers/filter.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getIndexableTags } from "./event.js";
|
|
2
|
+
import stringify from "json-stringify-deterministic";
|
|
2
3
|
/**
|
|
3
4
|
* Copied from nostr-tools and modified to use getIndexableTags
|
|
4
5
|
* @see https://github.com/nbd-wtf/nostr-tools/blob/a61cde77eacc9518001f11d7f67f1a50ae05fd80/filter.ts
|
|
@@ -39,3 +40,11 @@ export function matchFilters(filters, event) {
|
|
|
39
40
|
}
|
|
40
41
|
return false;
|
|
41
42
|
}
|
|
43
|
+
/** Stringify filters in a predictable way */
|
|
44
|
+
export function stringifyFilter(filter) {
|
|
45
|
+
return stringify(filter);
|
|
46
|
+
}
|
|
47
|
+
/** Check if two filters are equal */
|
|
48
|
+
export function isFilterEqual(a, b) {
|
|
49
|
+
return stringifyFilter(a) === stringifyFilter(b);
|
|
50
|
+
}
|
package/dist/helpers/index.d.ts
CHANGED
package/dist/helpers/index.js
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import { NostrEvent } from "nostr-tools";
|
|
2
2
|
export declare const MailboxesInboxesSymbol: unique symbol;
|
|
3
3
|
export declare const MailboxesOutboxesSymbol: unique symbol;
|
|
4
|
-
declare module "nostr-tools" {
|
|
5
|
-
interface Event {
|
|
6
|
-
[MailboxesInboxesSymbol]?: Set<string>;
|
|
7
|
-
[MailboxesOutboxesSymbol]?: Set<string>;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
4
|
/**
|
|
11
5
|
* Parses a 10002 event and stores the inboxes in the event using the {@link MailboxesInboxesSymbol} symbol
|
|
12
6
|
*/
|
|
13
|
-
export declare function getInboxes(event: NostrEvent):
|
|
7
|
+
export declare function getInboxes(event: NostrEvent): string[];
|
|
14
8
|
/**
|
|
15
9
|
* Parses a 10002 event and stores the outboxes in the event using the {@link MailboxesOutboxesSymbol} symbol
|
|
16
10
|
*/
|
|
17
|
-
export declare function getOutboxes(event: NostrEvent):
|
|
11
|
+
export declare function getOutboxes(event: NostrEvent): string[];
|
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
import { safeRelayUrl } from "./relays.js";
|
|
2
|
+
import { getOrComputeCachedValue } from "./cache.js";
|
|
2
3
|
export const MailboxesInboxesSymbol = Symbol.for("mailboxes-inboxes");
|
|
3
4
|
export const MailboxesOutboxesSymbol = Symbol.for("mailboxes-outboxes");
|
|
4
5
|
/**
|
|
5
6
|
* Parses a 10002 event and stores the inboxes in the event using the {@link MailboxesInboxesSymbol} symbol
|
|
6
7
|
*/
|
|
7
8
|
export function getInboxes(event) {
|
|
8
|
-
|
|
9
|
-
const inboxes =
|
|
9
|
+
return getOrComputeCachedValue(event, MailboxesInboxesSymbol, () => {
|
|
10
|
+
const inboxes = [];
|
|
10
11
|
for (const tag of event.tags) {
|
|
11
12
|
if (tag[0] === "r" && tag[1] && (tag[2] === "read" || tag[2] === undefined)) {
|
|
12
13
|
const url = safeRelayUrl(tag[1]);
|
|
13
|
-
if (url)
|
|
14
|
-
inboxes.
|
|
14
|
+
if (url && !inboxes.includes(url))
|
|
15
|
+
inboxes.push(url);
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
return event[MailboxesInboxesSymbol];
|
|
18
|
+
return inboxes;
|
|
19
|
+
});
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Parses a 10002 event and stores the outboxes in the event using the {@link MailboxesOutboxesSymbol} symbol
|
|
23
23
|
*/
|
|
24
24
|
export function getOutboxes(event) {
|
|
25
|
-
|
|
26
|
-
const outboxes =
|
|
25
|
+
return getOrComputeCachedValue(event, MailboxesOutboxesSymbol, () => {
|
|
26
|
+
const outboxes = [];
|
|
27
27
|
for (const tag of event.tags) {
|
|
28
28
|
if (tag[0] === "r" && tag[1] && (tag[2] === "write" || tag[2] === undefined)) {
|
|
29
29
|
const url = safeRelayUrl(tag[1]);
|
|
30
|
-
if (url)
|
|
31
|
-
outboxes.
|
|
30
|
+
if (url && !outboxes.includes(url))
|
|
31
|
+
outboxes.push(url);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
return event[MailboxesOutboxesSymbol];
|
|
34
|
+
return outboxes;
|
|
35
|
+
});
|
|
37
36
|
}
|
|
@@ -10,7 +10,7 @@ export declare function parseCoordinate(a: string, requireD: true, silent: false
|
|
|
10
10
|
export declare function parseCoordinate(a: string, requireD: true, silent: true): AddressPointer | null;
|
|
11
11
|
export declare function parseCoordinate(a: string, requireD: false, silent: true): AddressPointerWithoutD | null;
|
|
12
12
|
export declare function getPubkeyFromDecodeResult(result?: DecodeResult): string | undefined;
|
|
13
|
-
export declare function encodeDecodeResult(result: DecodeResult): "" | `
|
|
13
|
+
export declare function encodeDecodeResult(result: DecodeResult): "" | `nprofile1${string}` | `nevent1${string}` | `naddr1${string}` | `nsec1${string}` | `npub1${string}` | `note1${string}`;
|
|
14
14
|
export declare function getEventPointerFromTag(tag: string[]): EventPointer;
|
|
15
15
|
export declare function getAddressPointerFromTag(tag: string[]): AddressPointer;
|
|
16
16
|
export declare function getProfilePointerFromTag(tag: string[]): ProfilePointer;
|
package/dist/helpers/pointers.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { naddrEncode, neventEncode, noteEncode, nprofileEncode, npubEncode,
|
|
1
|
+
import { naddrEncode, neventEncode, noteEncode, nprofileEncode, npubEncode, nsecEncode, } from "nostr-tools/nip19";
|
|
2
2
|
import { getPublicKey } from "nostr-tools";
|
|
3
3
|
import { safeRelayUrls } from "./relays.js";
|
|
4
4
|
export function parseCoordinate(a, requireD = false, silent = true) {
|
|
@@ -53,8 +53,6 @@ export function encodeDecodeResult(result) {
|
|
|
53
53
|
return nprofileEncode(result.data);
|
|
54
54
|
case "nevent":
|
|
55
55
|
return neventEncode(result.data);
|
|
56
|
-
case "nrelay":
|
|
57
|
-
return nrelayEncode(result.data);
|
|
58
56
|
case "nsec":
|
|
59
57
|
return nsecEncode(result.data);
|
|
60
58
|
case "npub":
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { NostrEvent } from "nostr-tools";
|
|
2
2
|
export declare const ProfileContentSymbol: unique symbol;
|
|
3
|
-
declare module "nostr-tools" {
|
|
4
|
-
interface Event {
|
|
5
|
-
[ProfileContentSymbol]?: ProfileContent | Error;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
3
|
export type ProfileContent = {
|
|
9
4
|
name?: string;
|
|
10
5
|
display_name?: string;
|
|
@@ -21,5 +16,5 @@ export type ProfileContent = {
|
|
|
21
16
|
};
|
|
22
17
|
/** Returns the parsed profile content for a kind 0 event */
|
|
23
18
|
export declare function getProfileContent(event: NostrEvent): ProfileContent;
|
|
24
|
-
|
|
25
|
-
export declare function
|
|
19
|
+
/** Checks if the content of the kind 0 event is valid JSON */
|
|
20
|
+
export declare function isValidProfile(profile?: NostrEvent): boolean;
|
package/dist/helpers/profile.js
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
|
+
import { kinds } from "nostr-tools";
|
|
2
|
+
import { getOrComputeCachedValue } from "./cache.js";
|
|
1
3
|
export const ProfileContentSymbol = Symbol.for("profile-content");
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
/** Returns the parsed profile content for a kind 0 event */
|
|
5
|
+
export function getProfileContent(event) {
|
|
6
|
+
return getOrComputeCachedValue(event, ProfileContentSymbol, () => {
|
|
7
|
+
const profile = JSON.parse(event.content);
|
|
8
|
+
// ensure nip05 is a string
|
|
9
|
+
if (profile.nip05 && typeof profile.nip05 !== "string")
|
|
10
|
+
profile.nip05 = String(profile.nip05);
|
|
11
|
+
// add missing protocol to website
|
|
12
|
+
if (profile.website?.startsWith("http") === false) {
|
|
13
|
+
profile.website = "https://" + profile.website;
|
|
11
14
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
|
|
15
|
+
return profile;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/** Checks if the content of the kind 0 event is valid JSON */
|
|
19
|
+
export function isValidProfile(profile) {
|
|
20
|
+
if (!profile)
|
|
21
|
+
return false;
|
|
22
|
+
if (profile.kind !== kinds.Metadata)
|
|
23
|
+
return false;
|
|
24
|
+
try {
|
|
25
|
+
getProfileContent(profile);
|
|
26
|
+
return true;
|
|
19
27
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
throw cached;
|
|
23
|
-
else
|
|
24
|
-
return cached;
|
|
28
|
+
catch (error) {
|
|
29
|
+
return false;
|
|
25
30
|
}
|
|
26
|
-
else
|
|
27
|
-
return cached;
|
|
28
31
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NostrEvent } from "nostr-tools";
|
|
1
|
+
import { EventTemplate, NostrEvent } from "nostr-tools";
|
|
2
2
|
import { AddressPointer, EventPointer } from "nostr-tools/nip19";
|
|
3
3
|
export type ThreadReferences = {
|
|
4
4
|
root?: {
|
|
@@ -29,7 +29,7 @@ declare module "nostr-tools" {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
/** Parses NIP-10 tags and handles legacy behavior */
|
|
32
|
-
export declare function interpretThreadTags(event: NostrEvent): {
|
|
32
|
+
export declare function interpretThreadTags(event: NostrEvent | EventTemplate): {
|
|
33
33
|
root?: {
|
|
34
34
|
e: string[];
|
|
35
35
|
a: undefined;
|
|
@@ -39,7 +39,7 @@ export declare function interpretThreadTags(event: NostrEvent): {
|
|
|
39
39
|
} | {
|
|
40
40
|
e: string[];
|
|
41
41
|
a: string[];
|
|
42
|
-
}
|
|
42
|
+
};
|
|
43
43
|
reply?: {
|
|
44
44
|
e: string[];
|
|
45
45
|
a: undefined;
|
|
@@ -49,7 +49,7 @@ export declare function interpretThreadTags(event: NostrEvent): {
|
|
|
49
49
|
} | {
|
|
50
50
|
e: string[];
|
|
51
51
|
a: string[];
|
|
52
|
-
}
|
|
52
|
+
};
|
|
53
53
|
};
|
|
54
54
|
/** Returns the parsed NIP-10 tags for an event */
|
|
55
|
-
export declare function getNip10References(event: NostrEvent): ThreadReferences;
|
|
55
|
+
export declare function getNip10References(event: NostrEvent | EventTemplate): ThreadReferences;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getAddressPointerFromTag, getEventPointerFromTag } from "./pointers.js";
|
|
2
|
+
import { getOrComputeCachedValue } from "./cache.js";
|
|
2
3
|
export const Nip10ThreadRefsSymbol = Symbol.for("nip10-thread-refs");
|
|
3
4
|
/** Parses NIP-10 tags and handles legacy behavior */
|
|
4
5
|
export function interpretThreadTags(event) {
|
|
@@ -42,20 +43,31 @@ export function interpretThreadTags(event) {
|
|
|
42
43
|
}
|
|
43
44
|
/** Returns the parsed NIP-10 tags for an event */
|
|
44
45
|
export function getNip10References(event) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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,
|
|
58
71
|
};
|
|
59
|
-
}
|
|
60
|
-
return refs;
|
|
72
|
+
});
|
|
61
73
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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 const ZapAddressPointerSymbol: unique symbol;
|
|
7
|
+
export declare function getZapSender(zap: NostrEvent): string;
|
|
8
|
+
export declare function getZapRecipient(zap: NostrEvent): string;
|
|
9
|
+
export declare function getZapPayment(zap: NostrEvent): import("./bolt11.js").ParsedInvoice | undefined;
|
|
10
|
+
export declare function getZapAddressPointer(zap: NostrEvent): import("nostr-tools/nip19").AddressPointer | null;
|
|
11
|
+
export declare function getZapEventPointer(zap: NostrEvent): import("nostr-tools/nip19").EventPointer | null;
|
|
12
|
+
export declare function getZapPreimage(zap: NostrEvent): string | undefined;
|
|
13
|
+
export declare function getZapRequest(zap: NostrEvent): import("nostr-tools").Event;
|
|
14
|
+
export declare function isValidZap(zap?: NostrEvent): boolean;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { kinds, 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 const ZapAddressPointerSymbol = Symbol.for("zap-address-pointer");
|
|
12
|
+
export function getZapSender(zap) {
|
|
13
|
+
return getTagValue(zap, "P") || getZapRequest(zap).pubkey;
|
|
14
|
+
}
|
|
15
|
+
export function getZapRecipient(zap) {
|
|
16
|
+
const recipient = getTagValue(zap, "p");
|
|
17
|
+
if (!recipient)
|
|
18
|
+
throw new Error("Missing recipient");
|
|
19
|
+
return recipient;
|
|
20
|
+
}
|
|
21
|
+
export function getZapPayment(zap) {
|
|
22
|
+
return getOrComputeCachedValue(zap, ZapInvoiceSymbol, () => {
|
|
23
|
+
const bolt11 = getTagValue(zap, "bolt11");
|
|
24
|
+
return bolt11 ? parseBolt11(bolt11) : undefined;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function getZapAddressPointer(zap) {
|
|
28
|
+
return getOrComputeCachedValue(zap, ZapAddressPointerSymbol, () => {
|
|
29
|
+
const a = zap.tags.find(isATag);
|
|
30
|
+
return a ? getAddressPointerFromTag(a) : null;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function getZapEventPointer(zap) {
|
|
34
|
+
return getOrComputeCachedValue(zap, ZapEventPointerSymbol, () => {
|
|
35
|
+
const e = zap.tags.find(isETag);
|
|
36
|
+
return e ? getEventPointerFromTag(e) : null;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export function getZapPreimage(zap) {
|
|
40
|
+
return getTagValue(zap, "preimage");
|
|
41
|
+
}
|
|
42
|
+
export function getZapRequest(zap) {
|
|
43
|
+
return getOrComputeCachedValue(zap, ZapRequestSymbol, () => {
|
|
44
|
+
const description = getTagValue(zap, "description");
|
|
45
|
+
if (!description)
|
|
46
|
+
throw new Error("Missing description tag");
|
|
47
|
+
const error = nip57.validateZapRequest(description);
|
|
48
|
+
if (error)
|
|
49
|
+
throw new Error(error);
|
|
50
|
+
return JSON.parse(description);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export function isValidZap(zap) {
|
|
54
|
+
if (!zap)
|
|
55
|
+
return false;
|
|
56
|
+
if (zap.kind !== kinds.Zap)
|
|
57
|
+
return false;
|
|
58
|
+
try {
|
|
59
|
+
getZapRequest(zap);
|
|
60
|
+
getZapRecipient(zap);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import Observable from "
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
2
|
export declare function getValue<T>(observable: Observable<T>): T | Promise<T>;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
2
|
export function getValue(observable) {
|
|
3
|
-
if (
|
|
3
|
+
if (observable instanceof BehaviorSubject)
|
|
4
4
|
return observable.value;
|
|
5
|
+
if (Reflect.has(observable, "value"))
|
|
6
|
+
return Reflect.get(observable, "value");
|
|
5
7
|
return new Promise((res) => {
|
|
6
8
|
const sub = observable.subscribe((v) => {
|
|
7
9
|
res(v);
|
package/dist/observable/index.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/queries/index.d.ts
CHANGED
package/dist/queries/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { kinds } from "nostr-tools";
|
|
2
|
+
import { map } from "rxjs/operators";
|
|
2
3
|
import { getInboxes, getOutboxes } from "../helpers/mailboxes.js";
|
|
3
4
|
export function MailboxesQuery(pubkey) {
|
|
4
5
|
return {
|
|
5
6
|
key: pubkey,
|
|
6
|
-
run: (events) => events.replaceable(kinds.RelayList, pubkey).map((event) => event && {
|
|
7
|
+
run: (events) => events.replaceable(kinds.RelayList, pubkey).pipe(map((event) => event && {
|
|
7
8
|
inboxes: getInboxes(event),
|
|
8
9
|
outboxes: getOutboxes(event),
|
|
9
|
-
}),
|
|
10
|
+
})),
|
|
10
11
|
};
|
|
11
12
|
}
|
package/dist/queries/profile.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { kinds } from "nostr-tools";
|
|
2
|
-
import {
|
|
2
|
+
import { filter, map } from "rxjs/operators";
|
|
3
|
+
import { getProfileContent, isValidProfile } from "../helpers/profile.js";
|
|
3
4
|
export function ProfileQuery(pubkey) {
|
|
4
5
|
return {
|
|
5
6
|
key: pubkey,
|
|
6
7
|
run: (events) => {
|
|
7
|
-
return events.replaceable(kinds.Metadata, pubkey).map((event) => event && getProfileContent(event));
|
|
8
|
+
return events.replaceable(kinds.Metadata, pubkey).pipe(filter(isValidProfile), map((event) => event && getProfileContent(event)));
|
|
8
9
|
},
|
|
9
10
|
};
|
|
10
11
|
}
|
package/dist/queries/thread.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { kinds } from "nostr-tools";
|
|
2
|
+
import { map } from "rxjs/operators";
|
|
2
3
|
import { getNip10References } from "../helpers/threading.js";
|
|
3
4
|
import { getCoordinateFromAddressPointer, isAddressPointer } from "../helpers/pointers.js";
|
|
4
5
|
import { getEventUID } from "../helpers/event.js";
|
|
@@ -31,7 +32,7 @@ export function ThreadQuery(root, opts) {
|
|
|
31
32
|
}
|
|
32
33
|
return {
|
|
33
34
|
key: `${rootUID}-${kinds.join(",")}`,
|
|
34
|
-
run: (events) => events.stream([rootFilter, replyFilter]).map((event) => {
|
|
35
|
+
run: (events) => events.stream([rootFilter, replyFilter]).pipe(map((event) => {
|
|
35
36
|
if (!items.has(getEventUID(event))) {
|
|
36
37
|
const refs = getNip10References(event);
|
|
37
38
|
const replies = parentReferences.get(getEventUID(event)) || new Set();
|
|
@@ -60,6 +61,6 @@ export function ThreadQuery(root, opts) {
|
|
|
60
61
|
items.set(getEventUID(event), item);
|
|
61
62
|
}
|
|
62
63
|
return { root: items.get(rootUID), all: items };
|
|
63
|
-
}),
|
|
64
|
+
})),
|
|
64
65
|
};
|
|
65
66
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { map } from "rxjs";
|
|
2
|
+
import { kinds } from "nostr-tools";
|
|
3
|
+
import { getCoordinateFromAddressPointer, isAddressPointer } from "../helpers/pointers.js";
|
|
4
|
+
import { isValidZap } from "../helpers/zap.js";
|
|
5
|
+
export function EventZapsQuery(id) {
|
|
6
|
+
return {
|
|
7
|
+
key: JSON.stringify(id),
|
|
8
|
+
run: (events) => {
|
|
9
|
+
if (isAddressPointer(id)) {
|
|
10
|
+
return events
|
|
11
|
+
.timeline([{ kinds: [kinds.Zap], "#a": [getCoordinateFromAddressPointer(id)] }])
|
|
12
|
+
.pipe(map((events) => events.filter(isValidZap)));
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
id = typeof id === "string" ? id : id.id;
|
|
16
|
+
return events.timeline([{ kinds: [kinds.Zap], "#e": [id] }]).pipe(map((events) => events.filter(isValidZap)));
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import Observable from "
|
|
1
|
+
import { BehaviorSubject, Observable } from "rxjs";
|
|
2
2
|
import { Filter, NostrEvent } from "nostr-tools";
|
|
3
3
|
import { EventStore } from "../event-store/event-store.js";
|
|
4
|
-
import { StatefulObservable } from "../observable/stateful.js";
|
|
5
4
|
import { LRU } from "../helpers/lru.js";
|
|
6
5
|
import * as Queries from "../queries/index.js";
|
|
7
6
|
import { AddressPointer, EventPointer } from "nostr-tools/nip19";
|
|
@@ -14,7 +13,7 @@ export declare class QueryStore {
|
|
|
14
13
|
static Queries: typeof Queries;
|
|
15
14
|
store: EventStore;
|
|
16
15
|
constructor(store: EventStore);
|
|
17
|
-
queries: LRU<
|
|
16
|
+
queries: LRU<Observable<any> | BehaviorSubject<any>>;
|
|
18
17
|
/** Creates a cached query */
|
|
19
18
|
runQuery<T extends unknown, Args extends Array<any>>(queryConstructor: (...args: Args) => {
|
|
20
19
|
key: string;
|
|
@@ -40,8 +39,8 @@ export declare class QueryStore {
|
|
|
40
39
|
reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
|
|
41
40
|
/** Returns the parsed relay list (10002) for the pubkey */
|
|
42
41
|
mailboxes(pubkey: string): Observable<{
|
|
43
|
-
inboxes:
|
|
44
|
-
outboxes:
|
|
42
|
+
inboxes: string[];
|
|
43
|
+
outboxes: string[];
|
|
45
44
|
} | undefined>;
|
|
46
45
|
thread(root: string | EventPointer | AddressPointer): Observable<Queries.Thread>;
|
|
47
46
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { stateful } from "../observable/stateful.js";
|
|
2
1
|
import { LRU } from "../helpers/lru.js";
|
|
3
2
|
import * as Queries from "../queries/index.js";
|
|
3
|
+
import { shareLatestValue } from "../observable/share-latest-value.js";
|
|
4
4
|
export class QueryStore {
|
|
5
5
|
static Queries = Queries;
|
|
6
6
|
store;
|
|
@@ -14,7 +14,7 @@ export class QueryStore {
|
|
|
14
14
|
const query = queryConstructor(...args);
|
|
15
15
|
const key = `${queryConstructor.name}|${query.key}`;
|
|
16
16
|
if (!this.queries.has(key)) {
|
|
17
|
-
const observable =
|
|
17
|
+
const observable = query.run(this.store, this).pipe(shareLatestValue());
|
|
18
18
|
this.queries.set(key, observable);
|
|
19
19
|
return observable;
|
|
20
20
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -54,19 +54,18 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"debug": "^4.3.7",
|
|
56
56
|
"json-stringify-deterministic": "^1.0.12",
|
|
57
|
+
"light-bolt11-decoder": "^3.2.0",
|
|
57
58
|
"nanoid": "^5.0.7",
|
|
58
|
-
"nostr-tools": "^2.
|
|
59
|
-
"
|
|
60
|
-
"zen-observable": "^0.10.0"
|
|
59
|
+
"nostr-tools": "^2.10.1",
|
|
60
|
+
"rxjs": "^7.8.1"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@types/zen-push": "^0.1.4",
|
|
64
63
|
"@jest/globals": "^29.7.0",
|
|
65
64
|
"@types/debug": "^4.1.12",
|
|
66
65
|
"@types/jest": "^29.5.13",
|
|
67
|
-
"@types/zen-observable": "^0.8.7",
|
|
68
66
|
"jest": "^29.7.0",
|
|
69
|
-
"jest-extended": "^4.0.2"
|
|
67
|
+
"jest-extended": "^4.0.2",
|
|
68
|
+
"typescript": "^5.6.3"
|
|
70
69
|
},
|
|
71
70
|
"jest": {
|
|
72
71
|
"roots": [
|
|
@@ -76,6 +75,10 @@
|
|
|
76
75
|
"jest-extended/all"
|
|
77
76
|
]
|
|
78
77
|
},
|
|
78
|
+
"funding": {
|
|
79
|
+
"type": "lightning",
|
|
80
|
+
"url": "lightning:nostrudel@geyser.fund"
|
|
81
|
+
},
|
|
79
82
|
"scripts": {
|
|
80
83
|
"build": "tsc",
|
|
81
84
|
"watch:build": "tsc --watch > /dev/null",
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import Observable from "zen-observable";
|
|
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>;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import Observable from "zen-observable";
|
|
2
|
-
/** Wraps an {@link Observable} and makes it stateful */
|
|
3
|
-
export function stateful(observable, cleanup = false) {
|
|
4
|
-
let subscription = undefined;
|
|
5
|
-
let observers = [];
|
|
6
|
-
const self = new Observable((observer) => {
|
|
7
|
-
// add observer to list
|
|
8
|
-
observers.push(observer);
|
|
9
|
-
// pass any cached values
|
|
10
|
-
if (self.value)
|
|
11
|
-
observer.next(self.value);
|
|
12
|
-
if (self.error)
|
|
13
|
-
observer.error(self.error);
|
|
14
|
-
if (self.complete)
|
|
15
|
-
observer.complete();
|
|
16
|
-
// subscribe if not already
|
|
17
|
-
if (!subscription) {
|
|
18
|
-
subscription = observable.subscribe({
|
|
19
|
-
next: (v) => {
|
|
20
|
-
self.value = v;
|
|
21
|
-
for (const observer of observers)
|
|
22
|
-
observer.next(v);
|
|
23
|
-
},
|
|
24
|
-
error: (err) => {
|
|
25
|
-
self.error = err;
|
|
26
|
-
for (const observer of observers)
|
|
27
|
-
observer.error(err);
|
|
28
|
-
},
|
|
29
|
-
complete: () => {
|
|
30
|
-
self.complete = true;
|
|
31
|
-
for (const observer of observers)
|
|
32
|
-
observer.complete();
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
return () => {
|
|
37
|
-
let i = observers.indexOf(observer);
|
|
38
|
-
if (i !== -1) {
|
|
39
|
-
// remove observer from list
|
|
40
|
-
observers.splice(i, 1);
|
|
41
|
-
if (subscription && observers.length === 0) {
|
|
42
|
-
subscription.unsubscribe();
|
|
43
|
-
subscription = undefined;
|
|
44
|
-
// reset cached values
|
|
45
|
-
if (cleanup) {
|
|
46
|
-
delete self.value;
|
|
47
|
-
delete self.error;
|
|
48
|
-
delete self.complete;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
});
|
|
54
|
-
self._stateful = true;
|
|
55
|
-
return self;
|
|
56
|
-
}
|
|
57
|
-
export function isStateful(observable) {
|
|
58
|
-
// @ts-expect-error
|
|
59
|
-
return observable._stateful;
|
|
60
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import Observable from "zen-observable";
|
|
2
|
-
/** Throttles an {@link Observable} */
|
|
3
|
-
export function throttle(source, interval) {
|
|
4
|
-
return new Observable((observer) => {
|
|
5
|
-
let lastEmissionTime = 0;
|
|
6
|
-
let subscription = source.subscribe({
|
|
7
|
-
next(value) {
|
|
8
|
-
const currentTime = Date.now();
|
|
9
|
-
if (currentTime - lastEmissionTime >= interval) {
|
|
10
|
-
lastEmissionTime = currentTime;
|
|
11
|
-
observer.next(value);
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
error(err) {
|
|
15
|
-
observer.error(err);
|
|
16
|
-
},
|
|
17
|
-
complete() {
|
|
18
|
-
observer.complete();
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
return () => subscription.unsubscribe();
|
|
22
|
-
});
|
|
23
|
-
}
|