applesauce-core 3.1.0 → 4.1.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/async-event-store.d.ts +136 -0
- package/dist/event-store/async-event-store.js +364 -0
- package/dist/event-store/{event-set.d.ts → event-memory.d.ts} +17 -25
- package/dist/event-store/{event-set.js → event-memory.js} +54 -53
- package/dist/event-store/event-store.d.ts +59 -63
- package/dist/event-store/event-store.js +126 -190
- package/dist/event-store/index.d.ts +2 -1
- package/dist/event-store/index.js +2 -1
- package/dist/event-store/interface.d.ts +115 -38
- package/dist/event-store/model-mixin.d.ts +59 -0
- package/dist/event-store/model-mixin.js +147 -0
- package/dist/helpers/app-data.d.ts +39 -0
- package/dist/helpers/app-data.js +68 -0
- package/dist/helpers/bookmarks.d.ts +11 -1
- package/dist/helpers/bookmarks.js +29 -4
- package/dist/helpers/comment.d.ts +13 -20
- package/dist/helpers/comment.js +16 -27
- package/dist/helpers/contacts.d.ts +10 -1
- package/dist/helpers/contacts.js +30 -3
- package/dist/helpers/encrypted-content-cache.js +7 -7
- package/dist/helpers/encrypted-content.d.ts +9 -2
- package/dist/helpers/encrypted-content.js +12 -9
- package/dist/helpers/event-cache.d.ts +3 -1
- package/dist/helpers/event-cache.js +3 -1
- package/dist/helpers/event-tags.d.ts +6 -0
- package/dist/helpers/event-tags.js +4 -0
- package/dist/helpers/event.d.ts +8 -1
- package/dist/helpers/event.js +6 -0
- package/dist/helpers/file-metadata.d.ts +4 -9
- package/dist/helpers/file-metadata.js +2 -10
- package/dist/helpers/filter.d.ts +4 -3
- package/dist/helpers/filter.js +3 -3
- package/dist/helpers/gift-wraps.d.ts +35 -14
- package/dist/helpers/gift-wraps.js +59 -50
- package/dist/helpers/groups.d.ts +2 -5
- package/dist/helpers/groups.js +2 -5
- package/dist/helpers/hidden-content.d.ts +14 -5
- package/dist/helpers/hidden-content.js +19 -8
- package/dist/helpers/hidden-tags.d.ts +16 -7
- package/dist/helpers/hidden-tags.js +47 -26
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/helpers/legacy-messages.d.ts +17 -13
- package/dist/helpers/legacy-messages.js +21 -19
- package/dist/helpers/lists.js +2 -1
- package/dist/helpers/lnurl.d.ts +4 -0
- package/dist/helpers/lnurl.js +7 -3
- package/dist/helpers/mailboxes.d.ts +2 -6
- package/dist/helpers/mailboxes.js +26 -20
- package/dist/helpers/mutes.d.ts +11 -1
- package/dist/helpers/mutes.js +30 -5
- package/dist/helpers/picture-post.d.ts +2 -1
- package/dist/helpers/pointers.d.ts +3 -1
- package/dist/helpers/pointers.js +4 -1
- package/dist/helpers/profile.d.ts +7 -3
- package/dist/helpers/profile.js +7 -8
- package/dist/helpers/relay-selection.d.ts +17 -0
- package/dist/helpers/relay-selection.js +102 -0
- package/dist/helpers/relays.d.ts +3 -1
- package/dist/helpers/relays.js +18 -2
- package/dist/helpers/url.js +3 -3
- package/dist/helpers/wrapped-messages.d.ts +5 -3
- package/dist/helpers/wrapped-messages.js +5 -3
- package/dist/helpers/zap.d.ts +18 -14
- package/dist/helpers/zap.js +26 -28
- package/dist/models/common.d.ts +4 -4
- package/dist/models/common.js +79 -40
- package/dist/models/gift-wrap.d.ts +1 -1
- package/dist/models/gift-wrap.js +4 -4
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +1 -0
- package/dist/models/legacy-messages.d.ts +2 -2
- package/dist/models/legacy-messages.js +13 -10
- package/dist/models/outbox.d.ts +13 -0
- package/dist/models/outbox.js +18 -0
- package/dist/models/profile.d.ts +1 -1
- package/dist/models/zaps.d.ts +5 -4
- package/dist/models/zaps.js +2 -2
- package/dist/observable/index.d.ts +4 -3
- package/dist/observable/index.js +5 -4
- package/dist/observable/map-events-to-store.d.ts +5 -3
- package/dist/observable/map-events-to-store.js +14 -3
- package/dist/observable/map-events-to-timeline.js +12 -0
- package/dist/observable/relay-selection.d.ts +9 -0
- package/dist/observable/relay-selection.js +43 -0
- package/package.json +5 -3
- package/dist/observable/map-events-timeline.js +0 -9
- /package/dist/observable/{map-events-timeline.d.ts → map-events-to-timeline.d.ts} +0 -0
|
@@ -1,28 +1,44 @@
|
|
|
1
1
|
import { Filter, NostrEvent } from "nostr-tools";
|
|
2
2
|
import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19";
|
|
3
3
|
import { Observable } from "rxjs";
|
|
4
|
-
import {
|
|
5
|
-
import { Mutes } from "../helpers/mutes.js";
|
|
4
|
+
import { AddressPointerWithoutD } from "../helpers/pointers.js";
|
|
6
5
|
import { ProfileContent } from "../helpers/profile.js";
|
|
6
|
+
import { Mutes } from "../helpers/mutes.js";
|
|
7
7
|
import { Thread } from "../models/thread.js";
|
|
8
|
-
import { AddressPointerWithoutD } from "../helpers/pointers.js";
|
|
9
8
|
/** The read interface for an event store */
|
|
10
9
|
export interface IEventStoreRead {
|
|
11
10
|
/** Check if the event store has an event with id */
|
|
12
11
|
hasEvent(id: string): boolean;
|
|
13
|
-
/** Check if the event store has a replaceable event */
|
|
14
|
-
hasReplaceable(kind: number, pubkey: string, identifier?: string): boolean;
|
|
15
12
|
/** Get an event by id */
|
|
16
13
|
getEvent(id: string): NostrEvent | undefined;
|
|
14
|
+
/** Check if the event store has a replaceable event */
|
|
15
|
+
hasReplaceable(kind: number, pubkey: string, identifier?: string): boolean;
|
|
17
16
|
/** Get a replaceable event */
|
|
18
17
|
getReplaceable(kind: number, pubkey: string, identifier?: string): NostrEvent | undefined;
|
|
19
18
|
/** Get the history of a replaceable event */
|
|
20
19
|
getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
|
|
21
20
|
/** Get all events that match the filters */
|
|
22
|
-
getByFilters(filters: Filter | Filter[]):
|
|
21
|
+
getByFilters(filters: Filter | Filter[]): NostrEvent[];
|
|
23
22
|
/** Get a timeline of events that match the filters */
|
|
24
23
|
getTimeline(filters: Filter | Filter[]): NostrEvent[];
|
|
25
24
|
}
|
|
25
|
+
/** The async read interface for an event store */
|
|
26
|
+
export interface IAsyncEventStoreRead {
|
|
27
|
+
/** Check if the event store has an event with id */
|
|
28
|
+
hasEvent(id: string): Promise<boolean>;
|
|
29
|
+
/** Get an event by id */
|
|
30
|
+
getEvent(id: string): Promise<NostrEvent | undefined>;
|
|
31
|
+
/** Check if the event store has a replaceable event */
|
|
32
|
+
hasReplaceable(kind: number, pubkey: string, identifier?: string): Promise<boolean>;
|
|
33
|
+
/** Get a replaceable event */
|
|
34
|
+
getReplaceable(kind: number, pubkey: string, identifier?: string): Promise<NostrEvent | undefined>;
|
|
35
|
+
/** Get the history of a replaceable event */
|
|
36
|
+
getReplaceableHistory(kind: number, pubkey: string, identifier?: string): Promise<NostrEvent[] | undefined>;
|
|
37
|
+
/** Get all events that match the filters */
|
|
38
|
+
getByFilters(filters: Filter | Filter[]): Promise<NostrEvent[]>;
|
|
39
|
+
/** Get a timeline of events that match the filters */
|
|
40
|
+
getTimeline(filters: Filter | Filter[]): Promise<NostrEvent[]>;
|
|
41
|
+
}
|
|
26
42
|
/** The stream interface for an event store */
|
|
27
43
|
export interface IEventStoreStreams {
|
|
28
44
|
/** A stream of new events added to the store */
|
|
@@ -41,8 +57,19 @@ export interface IEventStoreActions {
|
|
|
41
57
|
/** Notify the store that an event has updated */
|
|
42
58
|
update(event: NostrEvent): void;
|
|
43
59
|
}
|
|
60
|
+
/** The async actions for an event store */
|
|
61
|
+
export interface IAsyncEventStoreActions {
|
|
62
|
+
/** Add an event to the store */
|
|
63
|
+
add(event: NostrEvent): Promise<NostrEvent | null>;
|
|
64
|
+
/** Remove an event from the store */
|
|
65
|
+
remove(event: string | NostrEvent): Promise<boolean>;
|
|
66
|
+
/** Notify the store that an event has updated */
|
|
67
|
+
update(event: NostrEvent): Promise<void>;
|
|
68
|
+
}
|
|
44
69
|
/** The claim interface for an event store */
|
|
45
70
|
export interface IEventClaims {
|
|
71
|
+
/** Tell the store that this event was used */
|
|
72
|
+
touch(event: NostrEvent): void;
|
|
46
73
|
/** Sets the claim on the event and touches it */
|
|
47
74
|
claim(event: NostrEvent, claim: any): void;
|
|
48
75
|
/** Checks if an event is claimed by anything */
|
|
@@ -51,59 +78,109 @@ export interface IEventClaims {
|
|
|
51
78
|
removeClaim(event: NostrEvent, claim: any): void;
|
|
52
79
|
/** Removes all claims on an event */
|
|
53
80
|
clearClaim(event: NostrEvent): void;
|
|
81
|
+
/** Returns a generator of unclaimed events in order of least used */
|
|
82
|
+
unclaimed(): Generator<NostrEvent>;
|
|
54
83
|
}
|
|
55
84
|
/** An event store that can be subscribed to */
|
|
56
|
-
export interface
|
|
57
|
-
/**
|
|
85
|
+
export interface IEventSubscriptions {
|
|
86
|
+
/** Subscribe to an event by id */
|
|
58
87
|
event(id: string | EventPointer): Observable<NostrEvent | undefined>;
|
|
59
88
|
/** Subscribe to a replaceable event by pointer */
|
|
60
89
|
replaceable(pointer: AddressPointerWithoutD): Observable<NostrEvent | undefined>;
|
|
90
|
+
/** Subscribe to a replaceable event with legacy arguments */
|
|
91
|
+
replaceable(kind: number, pubkey: string, identifier?: string): Observable<NostrEvent | undefined>;
|
|
61
92
|
/** Subscribe to an addressable event by pointer */
|
|
62
93
|
addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
|
|
63
94
|
/** Subscribe to a batch of events that match the filters */
|
|
64
|
-
|
|
95
|
+
filters(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent>;
|
|
96
|
+
/** Subscribe to a sorted timeline of events that match the filters */
|
|
97
|
+
timeline(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent[]>;
|
|
98
|
+
}
|
|
99
|
+
/** @deprecated use {@link IEventSubscriptions} instead */
|
|
100
|
+
export interface IEventStoreSubscriptions extends IEventSubscriptions {
|
|
65
101
|
}
|
|
66
102
|
/** Methods for creating common models */
|
|
67
|
-
export interface
|
|
68
|
-
model<T extends unknown, Args extends Array<any>>(constructor: ModelConstructor<T, Args>, ...args: Args): Observable<T>;
|
|
69
|
-
|
|
70
|
-
replaceable(pointer: AddressPointerWithoutD): Observable<NostrEvent | undefined>;
|
|
71
|
-
addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
|
|
72
|
-
timeline(filters: Filter | Filter[], includeOldVersion?: boolean): Observable<NostrEvent[]>;
|
|
103
|
+
export interface IEventModelMixin<TStore extends IEventStore | IAsyncEventStore> {
|
|
104
|
+
model<T extends unknown, Args extends Array<any>>(constructor: ModelConstructor<T, Args, TStore>, ...args: Args): Observable<T>;
|
|
105
|
+
/** @deprecated use multiple {@link EventModel} instead */
|
|
73
106
|
events(ids: string[]): Observable<Record<string, NostrEvent | undefined>>;
|
|
107
|
+
/** @deprecated use multiple {@link ReplaceableModel} instead */
|
|
74
108
|
replaceableSet(pointers: (AddressPointer | AddressPointerWithoutD)[]): Observable<Record<string, NostrEvent | undefined>>;
|
|
75
109
|
}
|
|
76
|
-
/**
|
|
77
|
-
export
|
|
78
|
-
/**
|
|
79
|
-
export type ModelConstructor<T extends unknown, Args extends Array<any>> = ((...args: Args) => Model<T>) & {
|
|
80
|
-
getKey?: (...args: Args) => string;
|
|
81
|
-
};
|
|
82
|
-
/** The base interface for a set of events */
|
|
83
|
-
export interface IEventSet extends IEventStoreRead, IEventStoreStreams, IEventStoreActions, IEventClaims {
|
|
84
|
-
events: LRU<NostrEvent>;
|
|
85
|
-
}
|
|
86
|
-
export interface IEventStore extends IEventStoreRead, IEventStoreStreams, IEventStoreActions, IEventStoreModels, IEventClaims {
|
|
87
|
-
/** Enable this to keep old versions of replaceable events */
|
|
88
|
-
keepOldVersions: boolean;
|
|
89
|
-
/** Enable this to keep expired events */
|
|
90
|
-
keepExpired: boolean;
|
|
91
|
-
filters(filters: Filter | Filter[]): Observable<NostrEvent>;
|
|
92
|
-
updated(id: string | NostrEvent): Observable<NostrEvent>;
|
|
93
|
-
removed(id: string): Observable<never>;
|
|
94
|
-
replaceable(kind: number, pubkey: string, identifier?: string): Observable<NostrEvent | undefined>;
|
|
95
|
-
replaceable(pointer: AddressPointerWithoutD): Observable<NostrEvent | undefined>;
|
|
96
|
-
eventLoader?: (pointer: EventPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
|
|
97
|
-
replaceableLoader?: (pointer: AddressPointerWithoutD) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
|
|
98
|
-
addressableLoader?: (pointer: AddressPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
|
|
110
|
+
/** Methods for creating helpful models */
|
|
111
|
+
export interface IEventHelpfulSubscriptions {
|
|
112
|
+
/** Subscribe to a users profile */
|
|
99
113
|
profile(user: string | ProfilePointer): Observable<ProfileContent | undefined>;
|
|
114
|
+
/** Subscribe to a users contacts */
|
|
100
115
|
contacts(user: string | ProfilePointer): Observable<ProfilePointer[]>;
|
|
116
|
+
/** Subscribe to a users mutes */
|
|
101
117
|
mutes(user: string | ProfilePointer): Observable<Mutes | undefined>;
|
|
118
|
+
/** Subscribe to a users NIP-65 mailboxes */
|
|
102
119
|
mailboxes(user: string | ProfilePointer): Observable<{
|
|
103
120
|
inboxes: string[];
|
|
104
121
|
outboxes: string[];
|
|
105
122
|
} | undefined>;
|
|
123
|
+
/** Subscribe to a users blossom servers */
|
|
106
124
|
blossomServers(user: string | ProfilePointer): Observable<URL[]>;
|
|
125
|
+
/** Subscribe to an event's reactions */
|
|
107
126
|
reactions(event: NostrEvent): Observable<NostrEvent[]>;
|
|
127
|
+
/** Subscribe to a thread */
|
|
108
128
|
thread(root: string | EventPointer | AddressPointer): Observable<Thread>;
|
|
129
|
+
/** Subscribe to a event's comments */
|
|
130
|
+
comments(event: NostrEvent): Observable<NostrEvent[]>;
|
|
131
|
+
}
|
|
132
|
+
/** @deprecated use {@link IEventModelMixin} instead */
|
|
133
|
+
export interface IEventStoreModels extends IEventModelMixin<IEventStore> {
|
|
134
|
+
}
|
|
135
|
+
/** The interface that is passed to the model for creating subscriptions */
|
|
136
|
+
export type ModelEventStore<TStore extends IEventStore | IAsyncEventStore> = IEventStoreStreams & IEventSubscriptions & IEventModelMixin<TStore> & IEventFallbackLoaders & TStore;
|
|
137
|
+
/** A computed view of an event set or event store */
|
|
138
|
+
export type Model<T extends unknown, TStore extends IEventStore | IAsyncEventStore = IEventStore | IAsyncEventStore> = (events: ModelEventStore<TStore>) => Observable<T>;
|
|
139
|
+
/** A constructor for a {@link Model} */
|
|
140
|
+
export type ModelConstructor<T extends unknown, Args extends Array<any>, TStore extends IEventStore | IAsyncEventStore = IEventStore> = ((...args: Args) => Model<T, TStore>) & {
|
|
141
|
+
getKey?: (...args: Args) => string;
|
|
142
|
+
};
|
|
143
|
+
/** The base interface for a database of events */
|
|
144
|
+
export interface IEventDatabase extends IEventStoreRead {
|
|
145
|
+
/** Add an event to the database */
|
|
146
|
+
add(event: NostrEvent): NostrEvent;
|
|
147
|
+
/** Remove an event from the database */
|
|
148
|
+
remove(event: string | NostrEvent): boolean;
|
|
149
|
+
/** Remove multiple events that match the given filters */
|
|
150
|
+
removeByFilters(filters: Filter | Filter[]): number;
|
|
151
|
+
/** Notifies the database that an event has updated */
|
|
152
|
+
update?: (event: NostrEvent) => void;
|
|
153
|
+
}
|
|
154
|
+
/** The async base interface for a set of events */
|
|
155
|
+
export interface IAsyncEventDatabase extends IAsyncEventStoreRead {
|
|
156
|
+
/** Add an event to the database */
|
|
157
|
+
add(event: NostrEvent): Promise<NostrEvent>;
|
|
158
|
+
/** Remove an event from the database */
|
|
159
|
+
remove(event: string | NostrEvent): Promise<boolean>;
|
|
160
|
+
/** Remove multiple events that match the given filters */
|
|
161
|
+
removeByFilters(filters: Filter | Filter[]): Promise<number>;
|
|
162
|
+
/** Notifies the database that an event has updated */
|
|
163
|
+
update?: (event: NostrEvent) => void;
|
|
164
|
+
}
|
|
165
|
+
/** The base interface for the in-memory database of events */
|
|
166
|
+
export interface IEventMemory extends IEventStoreRead, IEventClaims {
|
|
167
|
+
/** Add an event to the store */
|
|
168
|
+
add(event: NostrEvent): NostrEvent;
|
|
169
|
+
/** Remove an event from the store */
|
|
170
|
+
remove(event: string | NostrEvent): boolean;
|
|
171
|
+
}
|
|
172
|
+
/** A set of methods that an event store will use to load single events it does not have */
|
|
173
|
+
export interface IEventFallbackLoaders {
|
|
174
|
+
/** A method that will be called when an event isn't found in the store */
|
|
175
|
+
eventLoader?: (pointer: EventPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
|
|
176
|
+
/** A method that will be called when a replaceable event isn't found in the store */
|
|
177
|
+
replaceableLoader?: (pointer: AddressPointerWithoutD) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
|
|
178
|
+
/** A method that will be called when an addressable event isn't found in the store */
|
|
179
|
+
addressableLoader?: (pointer: AddressPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
|
|
180
|
+
}
|
|
181
|
+
/** The async event store interface */
|
|
182
|
+
export interface IAsyncEventStore extends IAsyncEventStoreRead, IEventStoreStreams, IEventSubscriptions, IAsyncEventStoreActions, IEventModelMixin<IAsyncEventStore>, IEventHelpfulSubscriptions, IEventClaims, IEventFallbackLoaders {
|
|
183
|
+
}
|
|
184
|
+
/** The sync event store interface */
|
|
185
|
+
export interface IEventStore extends IEventStoreRead, IEventStoreStreams, IEventSubscriptions, IEventStoreActions, IEventModelMixin<IEventStore>, IEventHelpfulSubscriptions, IEventClaims, IEventFallbackLoaders {
|
|
109
186
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Filter, NostrEvent } from "nostr-tools";
|
|
2
|
+
import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19";
|
|
3
|
+
import { Observable } from "rxjs";
|
|
4
|
+
import { AddressPointerWithoutD } from "../helpers/pointers.js";
|
|
5
|
+
import { IAsyncEventStore, IEventStore, ModelConstructor } from "./interface.js";
|
|
6
|
+
/** A mixin that provides model functionality for both sync and async event stores */
|
|
7
|
+
export declare function EventStoreModelMixin<T extends new (...args: any[]) => any, TStore extends IEventStore | IAsyncEventStore>(Base: T): {
|
|
8
|
+
new (...args: any[]): {
|
|
9
|
+
[x: string]: any;
|
|
10
|
+
/** A directory of all active models */
|
|
11
|
+
models: Map<ModelConstructor<any, any[], TStore>, Map<string, Observable<any>>>;
|
|
12
|
+
/** How long a model should be kept "warm" while nothing is subscribed to it */
|
|
13
|
+
modelKeepWarm: number;
|
|
14
|
+
/** Get or create a model on the event store */
|
|
15
|
+
model<T_1 extends unknown, Args extends Array<any>>(constructor: ModelConstructor<T_1, Args, TStore>, ...args: Args): Observable<T_1>;
|
|
16
|
+
/**
|
|
17
|
+
* Creates an observable that streams all events that match the filter
|
|
18
|
+
* @param filters
|
|
19
|
+
* @param [onlyNew=false] Only subscribe to new events
|
|
20
|
+
*/
|
|
21
|
+
filters(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent>;
|
|
22
|
+
/** Creates a {@link EventModel} */
|
|
23
|
+
event(pointer: string | EventPointer): Observable<NostrEvent | undefined>;
|
|
24
|
+
/** Creates a {@link ReplaceableModel} */
|
|
25
|
+
replaceable(pointer: AddressPointer | AddressPointerWithoutD): Observable<NostrEvent | undefined>;
|
|
26
|
+
replaceable(kind: number, pubkey: string, identifier?: string): Observable<NostrEvent | undefined>;
|
|
27
|
+
/** Subscribe to an addressable event by pointer */
|
|
28
|
+
addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
|
|
29
|
+
/** Creates a {@link TimelineModel} */
|
|
30
|
+
timeline(filters: Filter | Filter[], includeOldVersion?: boolean): Observable<NostrEvent[]>;
|
|
31
|
+
/** Subscribe to a users profile */
|
|
32
|
+
profile(user: string | ProfilePointer): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
|
|
33
|
+
/** Subscribe to a users contacts */
|
|
34
|
+
contacts(user: string | ProfilePointer): Observable<ProfilePointer[]>;
|
|
35
|
+
/** Subscribe to a users mutes */
|
|
36
|
+
mutes(user: string | ProfilePointer): Observable<import("../helpers/mutes.js").Mutes | undefined>;
|
|
37
|
+
/** Subscribe to a users NIP-65 mailboxes */
|
|
38
|
+
mailboxes(user: string | ProfilePointer): Observable<{
|
|
39
|
+
inboxes: string[];
|
|
40
|
+
outboxes: string[];
|
|
41
|
+
} | undefined>;
|
|
42
|
+
/** Subscribe to a users blossom servers */
|
|
43
|
+
blossomServers(user: string | ProfilePointer): Observable<URL[]>;
|
|
44
|
+
/** Subscribe to an event's reactions */
|
|
45
|
+
reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
|
|
46
|
+
/** Subscribe to a thread */
|
|
47
|
+
thread(root: string | EventPointer | AddressPointer): Observable<import("../models/thread.js").Thread>;
|
|
48
|
+
/** Subscribe to a event's comments */
|
|
49
|
+
comments(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
|
|
50
|
+
/** @deprecated use multiple {@link EventModel} instead */
|
|
51
|
+
events(ids: string[]): Observable<Record<string, NostrEvent | undefined>>;
|
|
52
|
+
/** @deprecated use multiple {@link ReplaceableModel} instead */
|
|
53
|
+
replaceableSet(pointers: {
|
|
54
|
+
kind: number;
|
|
55
|
+
pubkey: string;
|
|
56
|
+
identifier?: string;
|
|
57
|
+
}[]): Observable<Record<string, NostrEvent | undefined>>;
|
|
58
|
+
};
|
|
59
|
+
} & T;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import hash_sum from "hash-sum";
|
|
2
|
+
import { EMPTY, ReplaySubject, filter, finalize, from, merge, mergeMap, share, timer } from "rxjs";
|
|
3
|
+
import { matchFilters } from "../helpers/filter.js";
|
|
4
|
+
// Model imports
|
|
5
|
+
import { UserBlossomServersModel } from "../models/blossom.js";
|
|
6
|
+
import { EventModel, EventsModel, ReplaceableModel, ReplaceableSetModel, TimelineModel } from "../models/common.js";
|
|
7
|
+
import { ContactsModel } from "../models/contacts.js";
|
|
8
|
+
import { CommentsModel, ThreadModel } from "../models/index.js";
|
|
9
|
+
import { MailboxesModel } from "../models/mailboxes.js";
|
|
10
|
+
import { MuteModel } from "../models/mutes.js";
|
|
11
|
+
import { ProfileModel } from "../models/profile.js";
|
|
12
|
+
import { ReactionsModel } from "../models/reactions.js";
|
|
13
|
+
/** A mixin that provides model functionality for both sync and async event stores */
|
|
14
|
+
export function EventStoreModelMixin(Base) {
|
|
15
|
+
return class extends Base {
|
|
16
|
+
/** A directory of all active models */
|
|
17
|
+
models = new Map();
|
|
18
|
+
/** How long a model should be kept "warm" while nothing is subscribed to it */
|
|
19
|
+
modelKeepWarm = 60_000;
|
|
20
|
+
/** Get or create a model on the event store */
|
|
21
|
+
model(constructor, ...args) {
|
|
22
|
+
let models = this.models.get(constructor);
|
|
23
|
+
if (!models) {
|
|
24
|
+
models = new Map();
|
|
25
|
+
this.models.set(constructor, models);
|
|
26
|
+
}
|
|
27
|
+
const key = constructor.getKey ? constructor.getKey(...args) : hash_sum(args);
|
|
28
|
+
let model = models.get(key);
|
|
29
|
+
// Create the model if it does not exist
|
|
30
|
+
if (!model) {
|
|
31
|
+
const cleanup = () => {
|
|
32
|
+
// Remove the model from the cache if its the same one
|
|
33
|
+
if (models.get(key) === model)
|
|
34
|
+
models.delete(key);
|
|
35
|
+
};
|
|
36
|
+
model = constructor(...args)(this).pipe(
|
|
37
|
+
// remove the model when its unsubscribed
|
|
38
|
+
finalize(cleanup),
|
|
39
|
+
// only subscribe to models once for all subscriptions
|
|
40
|
+
share({
|
|
41
|
+
connector: () => new ReplaySubject(1),
|
|
42
|
+
resetOnComplete: () => timer(this.modelKeepWarm),
|
|
43
|
+
resetOnRefCountZero: () => timer(this.modelKeepWarm),
|
|
44
|
+
}));
|
|
45
|
+
// Add the model to the cache
|
|
46
|
+
models.set(key, model);
|
|
47
|
+
}
|
|
48
|
+
return model;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Creates an observable that streams all events that match the filter
|
|
52
|
+
* @param filters
|
|
53
|
+
* @param [onlyNew=false] Only subscribe to new events
|
|
54
|
+
*/
|
|
55
|
+
filters(filters, onlyNew = false) {
|
|
56
|
+
filters = Array.isArray(filters) ? filters : [filters];
|
|
57
|
+
const getByFiltersResult = this.getByFilters(filters);
|
|
58
|
+
// Create the existing events observable
|
|
59
|
+
const existingEvents$ = onlyNew
|
|
60
|
+
? EMPTY
|
|
61
|
+
: // Check if result is a Promise (async) or direct Set (sync)
|
|
62
|
+
getByFiltersResult && typeof getByFiltersResult.then === "function"
|
|
63
|
+
? from(getByFiltersResult).pipe(mergeMap((events) => from(Array.from(events))))
|
|
64
|
+
: from(Array.from(getByFiltersResult));
|
|
65
|
+
// Create the new events observable
|
|
66
|
+
const newEvents$ = this.insert$.pipe(filter((e) => matchFilters(filters, e)));
|
|
67
|
+
return merge(existingEvents$, newEvents$);
|
|
68
|
+
}
|
|
69
|
+
// Helper methods for creating models
|
|
70
|
+
/** Creates a {@link EventModel} */
|
|
71
|
+
event(pointer) {
|
|
72
|
+
if (typeof pointer === "string")
|
|
73
|
+
pointer = { id: pointer };
|
|
74
|
+
return this.model(EventModel, pointer);
|
|
75
|
+
}
|
|
76
|
+
replaceable(...args) {
|
|
77
|
+
let pointer;
|
|
78
|
+
// Parse arguments
|
|
79
|
+
if (args.length === 1) {
|
|
80
|
+
pointer = args[0];
|
|
81
|
+
}
|
|
82
|
+
else if (args.length === 3 || args.length === 2) {
|
|
83
|
+
let [kind, pubkey, identifier] = args;
|
|
84
|
+
pointer = { kind, pubkey, identifier };
|
|
85
|
+
}
|
|
86
|
+
if (!pointer)
|
|
87
|
+
throw new Error("Invalid arguments, expected address pointer or kind, pubkey, identifier");
|
|
88
|
+
return this.model(ReplaceableModel, pointer);
|
|
89
|
+
}
|
|
90
|
+
/** Subscribe to an addressable event by pointer */
|
|
91
|
+
addressable(pointer) {
|
|
92
|
+
return this.model(ReplaceableModel, pointer);
|
|
93
|
+
}
|
|
94
|
+
/** Creates a {@link TimelineModel} */
|
|
95
|
+
timeline(filters, includeOldVersion = false) {
|
|
96
|
+
return this.model(TimelineModel, filters, includeOldVersion);
|
|
97
|
+
}
|
|
98
|
+
/** Subscribe to a users profile */
|
|
99
|
+
profile(user) {
|
|
100
|
+
return this.model(ProfileModel, user);
|
|
101
|
+
}
|
|
102
|
+
/** Subscribe to a users contacts */
|
|
103
|
+
contacts(user) {
|
|
104
|
+
if (typeof user === "string")
|
|
105
|
+
user = { pubkey: user };
|
|
106
|
+
return this.model(ContactsModel, user);
|
|
107
|
+
}
|
|
108
|
+
/** Subscribe to a users mutes */
|
|
109
|
+
mutes(user) {
|
|
110
|
+
if (typeof user === "string")
|
|
111
|
+
user = { pubkey: user };
|
|
112
|
+
return this.model(MuteModel, user);
|
|
113
|
+
}
|
|
114
|
+
/** Subscribe to a users NIP-65 mailboxes */
|
|
115
|
+
mailboxes(user) {
|
|
116
|
+
if (typeof user === "string")
|
|
117
|
+
user = { pubkey: user };
|
|
118
|
+
return this.model(MailboxesModel, user);
|
|
119
|
+
}
|
|
120
|
+
/** Subscribe to a users blossom servers */
|
|
121
|
+
blossomServers(user) {
|
|
122
|
+
if (typeof user === "string")
|
|
123
|
+
user = { pubkey: user };
|
|
124
|
+
return this.model(UserBlossomServersModel, user);
|
|
125
|
+
}
|
|
126
|
+
/** Subscribe to an event's reactions */
|
|
127
|
+
reactions(event) {
|
|
128
|
+
return this.model(ReactionsModel, event);
|
|
129
|
+
}
|
|
130
|
+
/** Subscribe to a thread */
|
|
131
|
+
thread(root) {
|
|
132
|
+
return this.model(ThreadModel, root);
|
|
133
|
+
}
|
|
134
|
+
/** Subscribe to a event's comments */
|
|
135
|
+
comments(event) {
|
|
136
|
+
return this.model(CommentsModel, event);
|
|
137
|
+
}
|
|
138
|
+
/** @deprecated use multiple {@link EventModel} instead */
|
|
139
|
+
events(ids) {
|
|
140
|
+
return this.model(EventsModel, ids);
|
|
141
|
+
}
|
|
142
|
+
/** @deprecated use multiple {@link ReplaceableModel} instead */
|
|
143
|
+
replaceableSet(pointers) {
|
|
144
|
+
return this.model(ReplaceableSetModel, pointers);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
|
+
import { EncryptionMethod } from "./encrypted-content.js";
|
|
3
|
+
import { HiddenContentSigner } from "./hidden-content.js";
|
|
4
|
+
export declare const APP_DATA_KIND = 30078;
|
|
5
|
+
/** A symbol used to cache the application data content */
|
|
6
|
+
export declare const AppDataContentSymbol: unique symbol;
|
|
7
|
+
/** Checks if an event has application data */
|
|
8
|
+
export declare function hasAppData<T extends {
|
|
9
|
+
kind: number;
|
|
10
|
+
content: string;
|
|
11
|
+
}>(event: T): boolean;
|
|
12
|
+
/** Checks if the application data is encrypted */
|
|
13
|
+
export declare function getAppDataEncryption<T extends {
|
|
14
|
+
kind: number;
|
|
15
|
+
content: string;
|
|
16
|
+
}>(event: T): EncryptionMethod | undefined;
|
|
17
|
+
/** Checks if the application data is locked (encrypted and not decrypted) */
|
|
18
|
+
export declare function isAppDataUnlocked<T extends {
|
|
19
|
+
kind: number;
|
|
20
|
+
}>(event: T): boolean;
|
|
21
|
+
/** Returns the parsed application data for an event if it's unlocked */
|
|
22
|
+
export declare function getAppDataContent<R extends unknown = unknown, T extends {
|
|
23
|
+
kind: number;
|
|
24
|
+
content: string;
|
|
25
|
+
} = NostrEvent>(event: T): R | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Unlocks the encrypted application data in the event
|
|
28
|
+
* @param event The event with encrypted content to decrypt
|
|
29
|
+
* @param signer A signer to use to decrypt the content
|
|
30
|
+
* @param override The encryption method to use instead of the default
|
|
31
|
+
* @returns The decrypted application data
|
|
32
|
+
*/
|
|
33
|
+
export declare function unlockAppData<R extends unknown = unknown, T extends {
|
|
34
|
+
kind: number;
|
|
35
|
+
pubkey: string;
|
|
36
|
+
content: string;
|
|
37
|
+
} = NostrEvent>(event: T, signer: HiddenContentSigner, override?: EncryptionMethod): Promise<R>;
|
|
38
|
+
/** Removes the unencrypted application data cache on an event */
|
|
39
|
+
export declare function lockAppData<T extends object>(event: T): void;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { isNIP04Encrypted } from "./encryption.js";
|
|
2
|
+
import { getHiddenContent, isHiddenContentUnlocked, lockHiddenContent, setHiddenContentEncryptionMethod, unlockHiddenContent, } from "./hidden-content.js";
|
|
3
|
+
import { safeParse } from "./json.js";
|
|
4
|
+
// NIP-78 Application Data event kind
|
|
5
|
+
export const APP_DATA_KIND = 30078;
|
|
6
|
+
/** A symbol used to cache the application data content */
|
|
7
|
+
export const AppDataContentSymbol = Symbol.for("app-data-content");
|
|
8
|
+
// Set the encryption method for app data events (default to nip44)
|
|
9
|
+
setHiddenContentEncryptionMethod(APP_DATA_KIND, "nip44");
|
|
10
|
+
/** Checks if an event has application data */
|
|
11
|
+
export function hasAppData(event) {
|
|
12
|
+
return event.kind === APP_DATA_KIND && event.content.length > 0;
|
|
13
|
+
}
|
|
14
|
+
/** Checks if the application data is encrypted */
|
|
15
|
+
export function getAppDataEncryption(event) {
|
|
16
|
+
// If content is empty, it can't be encrypted
|
|
17
|
+
if (event.content.length === 0)
|
|
18
|
+
return undefined;
|
|
19
|
+
// Try to parse as JSON - if it fails, it's likely encrypted
|
|
20
|
+
const parsed = safeParse(event.content);
|
|
21
|
+
if (parsed !== undefined)
|
|
22
|
+
return undefined;
|
|
23
|
+
return isNIP04Encrypted(event.content) ? "nip04" : "nip44";
|
|
24
|
+
}
|
|
25
|
+
/** Checks if the application data is locked (encrypted and not decrypted) */
|
|
26
|
+
export function isAppDataUnlocked(event) {
|
|
27
|
+
return isHiddenContentUnlocked(event);
|
|
28
|
+
}
|
|
29
|
+
/** Returns the parsed application data for an event if it's unlocked */
|
|
30
|
+
export function getAppDataContent(event) {
|
|
31
|
+
const cached = Reflect.get(event, AppDataContentSymbol);
|
|
32
|
+
if (cached)
|
|
33
|
+
return cached;
|
|
34
|
+
// If content is empty, return undefined
|
|
35
|
+
if (event.content.length === 0)
|
|
36
|
+
return undefined;
|
|
37
|
+
let data = getAppDataEncryption(event) ? undefined : safeParse(event.content);
|
|
38
|
+
if (!data) {
|
|
39
|
+
const decrypted = getHiddenContent(event);
|
|
40
|
+
if (decrypted)
|
|
41
|
+
data = safeParse(decrypted);
|
|
42
|
+
}
|
|
43
|
+
if (!data)
|
|
44
|
+
return undefined;
|
|
45
|
+
Reflect.set(event, AppDataContentSymbol, data);
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Unlocks the encrypted application data in the event
|
|
50
|
+
* @param event The event with encrypted content to decrypt
|
|
51
|
+
* @param signer A signer to use to decrypt the content
|
|
52
|
+
* @param override The encryption method to use instead of the default
|
|
53
|
+
* @returns The decrypted application data
|
|
54
|
+
*/
|
|
55
|
+
export async function unlockAppData(event, signer, override) {
|
|
56
|
+
if (!getAppDataEncryption(event))
|
|
57
|
+
return getAppDataContent(event);
|
|
58
|
+
const method = override ?? getAppDataEncryption(event);
|
|
59
|
+
const plaintext = await unlockHiddenContent(event, signer, method);
|
|
60
|
+
const parsed = safeParse(plaintext);
|
|
61
|
+
if (parsed === undefined)
|
|
62
|
+
throw new Error("Failed to parse decrypted application data as JSON");
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
/** Removes the unencrypted application data cache on an event */
|
|
66
|
+
export function lockAppData(event) {
|
|
67
|
+
lockHiddenContent(event);
|
|
68
|
+
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { NostrEvent } from "nostr-tools";
|
|
2
2
|
import { AddressPointer, EventPointer } from "nostr-tools/nip19";
|
|
3
|
+
import { HiddenContentSigner } from "./index.js";
|
|
3
4
|
export declare const BookmarkPublicSymbol: unique symbol;
|
|
4
5
|
export declare const BookmarkHiddenSymbol: unique symbol;
|
|
6
|
+
/** Type for unlocked bookmarks events */
|
|
7
|
+
export type UnlockedBookmarks = {
|
|
8
|
+
[BookmarkHiddenSymbol]: Bookmarks;
|
|
9
|
+
};
|
|
5
10
|
export type Bookmarks = {
|
|
6
11
|
notes: EventPointer[];
|
|
7
12
|
articles: AddressPointer[];
|
|
@@ -16,5 +21,10 @@ export declare function mergeBookmarks(...bookmarks: (Bookmarks | undefined)[]):
|
|
|
16
21
|
export declare function getBookmarks(bookmark: NostrEvent): Bookmarks;
|
|
17
22
|
/** Returns the public bookmarks of the event */
|
|
18
23
|
export declare function getPublicBookmarks(bookmark: NostrEvent): Bookmarks;
|
|
24
|
+
/** Checks if the hidden bookmarks are unlocked */
|
|
25
|
+
export declare function isHiddenBookmarksUnlocked<T extends NostrEvent>(bookmark: T): bookmark is T & UnlockedBookmarks;
|
|
19
26
|
/** Returns the bookmarks of the event if its unlocked */
|
|
20
|
-
export declare function getHiddenBookmarks(bookmark:
|
|
27
|
+
export declare function getHiddenBookmarks<T extends NostrEvent & UnlockedBookmarks>(bookmark: T): Bookmarks;
|
|
28
|
+
export declare function getHiddenBookmarks<T extends NostrEvent>(bookmark: T): Bookmarks | undefined;
|
|
29
|
+
/** Unlocks the hidden bookmarks on a bookmarks event */
|
|
30
|
+
export declare function unlockHiddenBookmarks(bookmark: NostrEvent, signer: HiddenContentSigner): Promise<Bookmarks>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { kinds } from "nostr-tools";
|
|
2
2
|
import { getOrComputeCachedValue } from "./cache.js";
|
|
3
|
-
import { getHiddenTags,
|
|
3
|
+
import { getHiddenTags, isHiddenTagsUnlocked, notifyEventUpdate, unlockHiddenTags, } from "./index.js";
|
|
4
4
|
import { getAddressPointerFromATag, getCoordinateFromAddressPointer, getEventPointerFromETag, mergeAddressPointers, mergeEventPointers, } from "./pointers.js";
|
|
5
5
|
export const BookmarkPublicSymbol = Symbol.for("bookmark-public");
|
|
6
6
|
export const BookmarkHiddenSymbol = Symbol.for("bookmark-hidden");
|
|
@@ -63,9 +63,34 @@ export function getBookmarks(bookmark) {
|
|
|
63
63
|
export function getPublicBookmarks(bookmark) {
|
|
64
64
|
return getOrComputeCachedValue(bookmark, BookmarkPublicSymbol, () => parseBookmarkTags(bookmark.tags));
|
|
65
65
|
}
|
|
66
|
-
/**
|
|
66
|
+
/** Checks if the hidden bookmarks are unlocked */
|
|
67
|
+
export function isHiddenBookmarksUnlocked(bookmark) {
|
|
68
|
+
return isHiddenTagsUnlocked(bookmark) && Reflect.has(bookmark, BookmarkHiddenSymbol);
|
|
69
|
+
}
|
|
67
70
|
export function getHiddenBookmarks(bookmark) {
|
|
68
|
-
if (
|
|
71
|
+
if (isHiddenBookmarksUnlocked(bookmark))
|
|
72
|
+
return bookmark[BookmarkHiddenSymbol];
|
|
73
|
+
//get hidden tags
|
|
74
|
+
const tags = getHiddenTags(bookmark);
|
|
75
|
+
if (!tags)
|
|
69
76
|
return undefined;
|
|
70
|
-
|
|
77
|
+
// parse bookmarks
|
|
78
|
+
const bookmarks = parseBookmarkTags(tags);
|
|
79
|
+
// set cached value
|
|
80
|
+
Reflect.set(bookmark, BookmarkHiddenSymbol, bookmarks);
|
|
81
|
+
return bookmarks;
|
|
82
|
+
}
|
|
83
|
+
/** Unlocks the hidden bookmarks on a bookmarks event */
|
|
84
|
+
export async function unlockHiddenBookmarks(bookmark, signer) {
|
|
85
|
+
if (isHiddenBookmarksUnlocked(bookmark))
|
|
86
|
+
return bookmark[BookmarkHiddenSymbol];
|
|
87
|
+
// unlock hidden tags
|
|
88
|
+
await unlockHiddenTags(bookmark, signer);
|
|
89
|
+
// get hidden bookmarks
|
|
90
|
+
const bookmarks = getHiddenBookmarks(bookmark);
|
|
91
|
+
if (!bookmarks)
|
|
92
|
+
throw new Error("Failed to unlock hidden bookmarks");
|
|
93
|
+
// notify event store
|
|
94
|
+
notifyEventUpdate(bookmark);
|
|
95
|
+
return bookmarks;
|
|
71
96
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { NostrEvent } from "nostr-tools";
|
|
2
2
|
import { ExternalPointer, ExternalIdentifiers } from "./external-id.js";
|
|
3
|
+
import { KnownEvent } from "./index.js";
|
|
3
4
|
export declare const COMMENT_KIND = 1111;
|
|
5
|
+
/** Type for validated comment events */
|
|
6
|
+
export type CommentEvent = KnownEvent<typeof COMMENT_KIND>;
|
|
4
7
|
export type CommentEventPointer = {
|
|
5
8
|
type: "event";
|
|
6
9
|
id: string;
|
|
@@ -22,30 +25,20 @@ export type CommentExternalPointer<T extends keyof ExternalIdentifiers> = Extern
|
|
|
22
25
|
export type CommentPointer = CommentEventPointer | CommentAddressPointer | CommentExternalPointer<keyof ExternalIdentifiers>;
|
|
23
26
|
export declare const CommentRootPointerSymbol: unique symbol;
|
|
24
27
|
export declare const CommentReplyPointerSymbol: unique symbol;
|
|
25
|
-
/**
|
|
26
|
-
* Gets the EventPointer from an array of tags
|
|
27
|
-
* @throws
|
|
28
|
-
*/
|
|
28
|
+
/** Gets the EventPointer from an array of tags */
|
|
29
29
|
export declare function getCommentEventPointer(tags: string[][], root?: boolean): CommentEventPointer | null;
|
|
30
|
-
/**
|
|
31
|
-
* Gets the AddressPointer from an array of tags
|
|
32
|
-
* @throws
|
|
33
|
-
*/
|
|
30
|
+
/** Gets the AddressPointer from an array of tags */
|
|
34
31
|
export declare function getCommentAddressPointer(tags: string[][], root?: boolean): CommentAddressPointer | null;
|
|
35
|
-
/**
|
|
36
|
-
* Gets the ExternalPointer from an array of tags
|
|
37
|
-
* @throws
|
|
38
|
-
*/
|
|
32
|
+
/** Gets the ExternalPointer from an array of tags */
|
|
39
33
|
export declare function getCommentExternalPointer(tags: string[][], root?: boolean): CommentExternalPointer<keyof ExternalIdentifiers> | null;
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
* @throws
|
|
43
|
-
*/
|
|
34
|
+
/** Returns the root pointer for a comment */
|
|
35
|
+
export declare function getCommentRootPointer(comment: CommentEvent): CommentPointer;
|
|
44
36
|
export declare function getCommentRootPointer(comment: NostrEvent): CommentPointer | null;
|
|
45
|
-
/**
|
|
46
|
-
* Returns the reply pointer for a comment
|
|
47
|
-
* @throws
|
|
48
|
-
*/
|
|
37
|
+
/** Returns the reply pointer for a comment */
|
|
49
38
|
export declare function getCommentReplyPointer(comment: NostrEvent): CommentPointer | null;
|
|
39
|
+
/** Checks if a pointer is a {@link CommentEventPointer} */
|
|
50
40
|
export declare function isCommentEventPointer(pointer: any): pointer is CommentEventPointer;
|
|
41
|
+
/** Checks if a pointer is a {@link CommentAddressPointer} */
|
|
51
42
|
export declare function isCommentAddressPointer(pointer: any): pointer is CommentAddressPointer;
|
|
43
|
+
/** Checks if a comment event is valid */
|
|
44
|
+
export declare function isValidComment(comment: NostrEvent): comment is CommentEvent;
|