applesauce-relay 5.2.0 → 6.0.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/group.d.ts +29 -40
- package/dist/group.js +85 -56
- package/dist/liveness.d.ts +3 -3
- package/dist/operators/complete-on-eose.d.ts +4 -4
- package/dist/operators/complete-when.d.ts +7 -0
- package/dist/operators/complete-when.js +14 -0
- package/dist/operators/index.d.ts +0 -2
- package/dist/operators/index.js +0 -2
- package/dist/operators/only-events.d.ts +2 -2
- package/dist/operators/store-events.d.ts +2 -2
- package/dist/pool.d.ts +16 -16
- package/dist/pool.js +2 -2
- package/dist/relay.d.ts +35 -27
- package/dist/relay.js +214 -94
- package/dist/types.d.ts +102 -129
- package/package.json +3 -3
- package/dist/operators/mark-from-relay.d.ts +0 -4
- package/dist/operators/mark-from-relay.js +0 -9
- package/dist/operators/to-event-store.d.ts +0 -9
- package/dist/operators/to-event-store.js +0 -15
package/dist/group.d.ts
CHANGED
|
@@ -1,62 +1,51 @@
|
|
|
1
|
-
import { IAsyncEventStoreActions, IEventStoreActions } from "applesauce-core/event-store";
|
|
2
1
|
import type { Filter, NostrEvent } from "applesauce-core/helpers";
|
|
3
|
-
import { BehaviorSubject,
|
|
4
|
-
import {
|
|
5
|
-
import { SyncDirection } from "./relay.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/** Whether to sync in parallel (default true) */
|
|
10
|
-
parallel?: boolean;
|
|
11
|
-
};
|
|
12
|
-
/** Options for a subscription on a group of relays */
|
|
13
|
-
export type GroupSubscriptionOptions = SubscriptionOptions & {
|
|
14
|
-
/** Deduplicate events with an event store (default is a temporary instance of EventMemory), null will disable deduplication */
|
|
15
|
-
eventStore?: IEventStoreActions | IAsyncEventStoreActions | null;
|
|
16
|
-
};
|
|
17
|
-
/** Options for a request on a group of relays */
|
|
18
|
-
export type GroupRequestOptions = RequestOptions & {
|
|
19
|
-
/** Deduplicate events with an event store (default is a temporary instance of EventMemory), null will disable deduplication */
|
|
20
|
-
eventStore?: IEventStoreActions | IAsyncEventStoreActions | null;
|
|
21
|
-
};
|
|
22
|
-
export declare class RelayGroup implements IGroup {
|
|
23
|
-
protected relays$: BehaviorSubject<IRelay[]> | Observable<IRelay[]>;
|
|
2
|
+
import { BehaviorSubject, Observable } from "rxjs";
|
|
3
|
+
import { type ReconcileFunction } from "./negentropy.js";
|
|
4
|
+
import { Relay, SyncDirection } from "./relay.js";
|
|
5
|
+
import { FilterInput, GroupNegentropySyncOptions, GroupRelayInput, GroupReqMessage, GroupReqOptions, GroupRequestCompleteOperator, GroupRequestOptions, GroupSubscriptionOptions, NegentropyReadStore, NegentropySyncStore, PublishOptions, PublishResponse, RelayCountResponse, RelayReqMessage, RelayStatus } from "./types.js";
|
|
6
|
+
export declare class RelayGroup {
|
|
7
|
+
protected relays$: BehaviorSubject<Relay[]> | Observable<Relay[]>;
|
|
24
8
|
/** Observable of relay status for all relays in the group */
|
|
25
9
|
status$: Observable<Record<string, RelayStatus>>;
|
|
26
|
-
get relays():
|
|
27
|
-
constructor(relays:
|
|
10
|
+
get relays(): Relay[];
|
|
11
|
+
constructor(relays: GroupRelayInput);
|
|
28
12
|
/** Whether this group is controlled by an upstream observable */
|
|
29
13
|
private get controlled();
|
|
30
14
|
/** Check if a relay is in the group */
|
|
31
|
-
has(relay:
|
|
15
|
+
has(relay: Relay | string): boolean;
|
|
32
16
|
/** Add a relay to the group */
|
|
33
|
-
add(relay:
|
|
17
|
+
add(relay: Relay): void;
|
|
34
18
|
/** Remove a relay from the group */
|
|
35
|
-
remove(relay:
|
|
19
|
+
remove(relay: Relay): void;
|
|
36
20
|
/** Internal logic for handling requests to multiple relays */
|
|
37
|
-
protected internalSubscription(project: (relay:
|
|
21
|
+
protected internalSubscription(project: (relay: Relay) => Observable<RelayReqMessage>): Observable<GroupReqMessage>;
|
|
38
22
|
/** Internal logic for handling publishes to multiple relays */
|
|
39
|
-
protected internalPublish(project: (relay:
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
* @note This does not deduplicate events
|
|
43
|
-
*/
|
|
44
|
-
req(filters: FilterInput, id?: string, opts?: {
|
|
45
|
-
/** Deduplicate events with an event store (default is a temporary instance of EventMemory), null will disable deduplication */
|
|
46
|
-
eventStore?: IEventStoreActions | IAsyncEventStoreActions | null;
|
|
47
|
-
}): Observable<SubscriptionResponse>;
|
|
23
|
+
protected internalPublish(project: (relay: Relay) => Observable<PublishResponse>): Observable<PublishResponse>;
|
|
24
|
+
/** Send a REQ to all relays and returns all responses */
|
|
25
|
+
req(filters: FilterInput, opts?: GroupReqOptions): Observable<GroupReqMessage>;
|
|
48
26
|
/** Send an event to all relays */
|
|
49
27
|
event(event: NostrEvent): Observable<PublishResponse>;
|
|
50
28
|
/** Negentropy sync events with the relays and an event store */
|
|
51
29
|
negentropy(store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: GroupNegentropySyncOptions): Promise<boolean>;
|
|
52
30
|
/** Publish an event to all relays with retries ( default 3 retries ) */
|
|
53
31
|
publish(event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse[]>;
|
|
54
|
-
/** Request events from all relays and complete on
|
|
32
|
+
/** Request events from all relays and complete based on condition */
|
|
55
33
|
request(filters: FilterInput, opts?: GroupRequestOptions): Observable<NostrEvent>;
|
|
56
34
|
/** Open a subscription to all relays with retries ( default 3 retries ) */
|
|
57
|
-
subscription(filters: FilterInput, opts?: GroupSubscriptionOptions): Observable<
|
|
35
|
+
subscription(filters: FilterInput, opts?: GroupSubscriptionOptions): Observable<NostrEvent>;
|
|
58
36
|
/** Count events on all relays in the group */
|
|
59
|
-
count(filters: Filter | Filter[], id?: string): Observable<Record<string,
|
|
37
|
+
count(filters: Filter | Filter[], id?: string): Observable<Record<string, RelayCountResponse>>;
|
|
60
38
|
/** Negentropy sync events with the relays and an event store */
|
|
61
39
|
sync(store: NegentropySyncStore | NostrEvent[], filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
|
|
40
|
+
/**
|
|
41
|
+
* Creates a complete condition that waits for the first EOSE message from a relay and then starts a timeout for the remaining relays
|
|
42
|
+
* @param timeout - The timeout in milliseconds for the remaining relays
|
|
43
|
+
*/
|
|
44
|
+
static completeAfterFirstRelay(timeout?: number): GroupRequestCompleteOperator;
|
|
45
|
+
/** Creates a group request complete operator that waits for all relays to send an EOSE message or timeout */
|
|
46
|
+
static completeOnAllEose(): GroupRequestCompleteOperator;
|
|
47
|
+
/** A group request complete condition that completes when any of the provided conditions are truthy (OR) */
|
|
48
|
+
static completeOnAny(...conditions: GroupRequestCompleteOperator[]): GroupRequestCompleteOperator;
|
|
49
|
+
/** A group request complete condition that completes when any of the provided conditions are truthy (AND) */
|
|
50
|
+
static completeOnAll(...conditions: GroupRequestCompleteOperator[]): GroupRequestCompleteOperator;
|
|
62
51
|
}
|
package/dist/group.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { EventMemory } from "applesauce-core/event-store";
|
|
2
2
|
import { filterDuplicateEvents } from "applesauce-core/observable";
|
|
3
3
|
import { nanoid } from "nanoid";
|
|
4
|
-
import { BehaviorSubject, catchError, combineLatest, defaultIfEmpty, defer,
|
|
5
|
-
import {
|
|
6
|
-
import { onlyEvents } from "./operators/only-events.js";
|
|
4
|
+
import { BehaviorSubject, catchError, combineLatest, connect, defaultIfEmpty, defer, filter, from, identity, lastValueFrom, map, merge, of, scan, share, shareReplay, startWith, switchMap, take, timeout, timer, toArray, } from "rxjs";
|
|
5
|
+
import { completeWhen } from "./operators/complete-when.js";
|
|
7
6
|
import { reverseSwitchMap } from "./operators/reverse-switch-map.js";
|
|
8
7
|
/** Convert an error to a PublishResponse */
|
|
9
8
|
function errorToPublishResponse(relay) {
|
|
@@ -63,11 +62,11 @@ export class RelayGroup {
|
|
|
63
62
|
this.relays$.next(this.relays.filter((r) => r !== relay));
|
|
64
63
|
}
|
|
65
64
|
/** Internal logic for handling requests to multiple relays */
|
|
66
|
-
internalSubscription(project
|
|
65
|
+
internalSubscription(project) {
|
|
67
66
|
// Keep a cache of upstream observables for each relay
|
|
68
67
|
const upstream = new WeakMap();
|
|
69
68
|
// Subscribe to the group relays
|
|
70
|
-
const
|
|
69
|
+
const messages = this.relays$.pipe(
|
|
71
70
|
// Every time they change switch to a new observable
|
|
72
71
|
// Using reverseSwitchMap to subscribe to the new relays before unsubscribing from the old ones
|
|
73
72
|
// This avoids sending duplicate REQ messages to the relays
|
|
@@ -80,44 +79,17 @@ export class RelayGroup {
|
|
|
80
79
|
continue;
|
|
81
80
|
}
|
|
82
81
|
const observable = project(relay).pipe(
|
|
83
|
-
// Catch connection errors and return
|
|
84
|
-
catchError(() => of("
|
|
85
|
-
// Map values into tuple of relay and value
|
|
86
|
-
map((value) => [relay, value]));
|
|
82
|
+
// Catch connection errors and return ERROR
|
|
83
|
+
catchError((err) => of({ type: "ERROR", from: relay.url, error: err })));
|
|
87
84
|
observables.push(observable);
|
|
88
85
|
upstream.set(relay, observable);
|
|
89
86
|
}
|
|
90
87
|
return merge(...observables);
|
|
91
88
|
}),
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
// Create an observable that only emits the events from the relays
|
|
95
|
-
const events = main.pipe(
|
|
96
|
-
// Pick the value from the tuple
|
|
97
|
-
map(([_, value]) => value),
|
|
98
|
-
// Only return events
|
|
99
|
-
onlyEvents(),
|
|
100
|
-
// Add event operations
|
|
101
|
-
eventOperator);
|
|
102
|
-
// Create an observable that emits EOSE when all relays have sent EOSE
|
|
103
|
-
const eose = this.relays$.pipe(
|
|
104
|
-
// When the relays change, switch to a new observable
|
|
105
|
-
switchMap((relays) =>
|
|
106
|
-
// Subscribe to the events, and wait for EOSE from all relays
|
|
107
|
-
main.pipe(
|
|
108
|
-
// Only select EOSE messages
|
|
109
|
-
filter(([_, value]) => value === "EOSE"),
|
|
110
|
-
// Track the relays that have sent EOSE
|
|
111
|
-
scan((received, [relay]) => [...received, relay], []),
|
|
112
|
-
// Keep the observable open while there are relays that have not sent EOSE
|
|
113
|
-
takeWhile((received) => relays.some((r) => !received.includes(r))),
|
|
114
|
-
// Ignore all values
|
|
115
|
-
ignoreElements(),
|
|
116
|
-
// When all relays have sent EOSE, emit EOSE
|
|
117
|
-
endWith("EOSE"))));
|
|
118
|
-
return merge(events, eose).pipe(
|
|
119
|
-
// Ensure a single upstream
|
|
89
|
+
// Ensure a single upstream subscription
|
|
90
|
+
// NOTE: this is required because the complete operator will subscribe many times to this
|
|
120
91
|
share());
|
|
92
|
+
return messages;
|
|
121
93
|
}
|
|
122
94
|
/** Internal logic for handling publishes to multiple relays */
|
|
123
95
|
internalPublish(project) {
|
|
@@ -144,20 +116,17 @@ export class RelayGroup {
|
|
|
144
116
|
upstream.set(relay, observable);
|
|
145
117
|
}
|
|
146
118
|
return merge(...observables);
|
|
147
|
-
})
|
|
119
|
+
}),
|
|
120
|
+
// Ensure a single upstream publish
|
|
121
|
+
share());
|
|
148
122
|
}
|
|
149
|
-
/**
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
*/
|
|
153
|
-
req(filters, id = nanoid(), opts) {
|
|
154
|
-
return this.internalSubscription((relay) => relay.req(filters, id), opts?.eventStore ? filterDuplicateEvents(opts?.eventStore) : identity);
|
|
123
|
+
/** Send a REQ to all relays and returns all responses */
|
|
124
|
+
req(filters, opts) {
|
|
125
|
+
return this.internalSubscription((relay) => relay.req(filters, opts));
|
|
155
126
|
}
|
|
156
127
|
/** Send an event to all relays */
|
|
157
128
|
event(event) {
|
|
158
|
-
return this.internalPublish((relay) => relay.event(event))
|
|
159
|
-
// Ensure a single upstream subscription
|
|
160
|
-
share());
|
|
129
|
+
return this.internalPublish((relay) => relay.event(event));
|
|
161
130
|
}
|
|
162
131
|
/** Negentropy sync events with the relays and an event store */
|
|
163
132
|
async negentropy(store, filter, reconcile, opts) {
|
|
@@ -177,21 +146,42 @@ export class RelayGroup {
|
|
|
177
146
|
publish(event, opts) {
|
|
178
147
|
return lastValueFrom(this.internalPublish((relay) => from(relay.publish(event, opts))).pipe(toArray(), defaultIfEmpty([])));
|
|
179
148
|
}
|
|
180
|
-
/** Request events from all relays and complete on
|
|
149
|
+
/** Request events from all relays and complete based on condition */
|
|
181
150
|
request(filters, opts) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
151
|
+
const complete = opts?.complete ??
|
|
152
|
+
// Default complete condition is to wait for the first relay to send an EOSE message and then timeout or all relays to EOSE
|
|
153
|
+
RelayGroup.completeOnAny(RelayGroup.completeAfterFirstRelay(5_000), RelayGroup.completeOnAllEose());
|
|
154
|
+
return this.internalSubscription(
|
|
155
|
+
// NOTE: we need to use the .req() method here because it returns the full RelayReqResponse object
|
|
156
|
+
(relay) => relay.req(filters,
|
|
157
|
+
// Manually default to relays reconnect config
|
|
158
|
+
{ ...opts, reconnect: opts?.reconnect ?? relay.requestReconnect })).pipe(
|
|
159
|
+
// Add the completion condition if provided
|
|
160
|
+
complete ? completeWhen(complete) : identity,
|
|
161
|
+
// Add request timeout
|
|
162
|
+
timeout({ first: opts?.timeout ?? 30_000 }),
|
|
163
|
+
// Filter only for event messages
|
|
164
|
+
filter((message) => message.type === "EVENT"),
|
|
165
|
+
// Extract event messages
|
|
166
|
+
map((message) => message.event),
|
|
185
167
|
// If an event store is provided, filter duplicate events
|
|
186
|
-
opts?.eventStore
|
|
187
|
-
//
|
|
188
|
-
|
|
168
|
+
opts?.eventStore === null ? identity : filterDuplicateEvents(opts?.eventStore ?? new EventMemory()),
|
|
169
|
+
// Only create one upstream subscription
|
|
170
|
+
share());
|
|
189
171
|
}
|
|
190
172
|
/** Open a subscription to all relays with retries ( default 3 retries ) */
|
|
191
173
|
subscription(filters, opts) {
|
|
192
|
-
return this.internalSubscription(
|
|
174
|
+
return this.internalSubscription(
|
|
175
|
+
// NOTE: we need to use the .req() method here because it returns the full RelayReqResponse object
|
|
176
|
+
(relay) => relay.req(filters, { ...opts, reconnect: opts?.reconnect ?? relay.subscriptionReconnect })).pipe(
|
|
177
|
+
// Filter only for event messages
|
|
178
|
+
filter((message) => message.type === "EVENT"),
|
|
179
|
+
// Extract event messages
|
|
180
|
+
map((message) => message.event),
|
|
193
181
|
// If an event store is provided, filter duplicate events
|
|
194
|
-
opts?.eventStore
|
|
182
|
+
opts?.eventStore === null ? identity : filterDuplicateEvents(opts?.eventStore ?? new EventMemory()),
|
|
183
|
+
// Only create one upstream subscription
|
|
184
|
+
share());
|
|
195
185
|
}
|
|
196
186
|
/** Count events on all relays in the group */
|
|
197
187
|
count(filters, id = nanoid()) {
|
|
@@ -214,4 +204,43 @@ export class RelayGroup {
|
|
|
214
204
|
// Only create one upstream subscription
|
|
215
205
|
share());
|
|
216
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Creates a complete condition that waits for the first EOSE message from a relay and then starts a timeout for the remaining relays
|
|
209
|
+
* @param timeout - The timeout in milliseconds for the remaining relays
|
|
210
|
+
*/
|
|
211
|
+
static completeAfterFirstRelay(timeout = 5_000) {
|
|
212
|
+
return (source) =>
|
|
213
|
+
// Listen for first EOSE message from a relay
|
|
214
|
+
source.pipe(filter((m) => m.type === "EOSE")).pipe(
|
|
215
|
+
// Ignore all other EOSE messages
|
|
216
|
+
take(1),
|
|
217
|
+
// Start a timeout for the remaining relays
|
|
218
|
+
switchMap(() => timer(timeout)),
|
|
219
|
+
// Emit true when the timeout completes
|
|
220
|
+
map(() => true));
|
|
221
|
+
}
|
|
222
|
+
/** Creates a group request complete operator that waits for all relays to send an EOSE message or timeout */
|
|
223
|
+
static completeOnAllEose() {
|
|
224
|
+
return (source) => source.pipe(
|
|
225
|
+
// Filter for relay status messages
|
|
226
|
+
filter((message) => message.type === "OPEN" || message.type === "EOSE" || message.type === "ERROR"),
|
|
227
|
+
// Accumulate the relay status messages
|
|
228
|
+
scan((acc, message) => acc.set(message.from, message.type), new Map()),
|
|
229
|
+
// Emit true when all relays are no longer OPEN
|
|
230
|
+
map((all) => Array.from(all.values()).every((t) => t !== "OPEN")));
|
|
231
|
+
}
|
|
232
|
+
/** A group request complete condition that completes when any of the provided conditions are truthy (OR) */
|
|
233
|
+
static completeOnAny(...conditions) {
|
|
234
|
+
return connect((shared$) =>
|
|
235
|
+
// Merge all the conditions
|
|
236
|
+
merge(...conditions.map((condition) => shared$.pipe(condition))));
|
|
237
|
+
}
|
|
238
|
+
/** A group request complete condition that completes when any of the provided conditions are truthy (AND) */
|
|
239
|
+
static completeOnAll(...conditions) {
|
|
240
|
+
return connect((shared$) =>
|
|
241
|
+
// Get last state from all conditions
|
|
242
|
+
combineLatest(conditions.map((condition) => shared$.pipe(condition))).pipe(
|
|
243
|
+
// Check all values are truthy
|
|
244
|
+
map((all) => all.every((v) => !!v))));
|
|
245
|
+
}
|
|
217
246
|
}
|
package/dist/liveness.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Observable } from "rxjs";
|
|
2
|
-
import {
|
|
2
|
+
import type { RelayPool } from "./pool.js";
|
|
3
3
|
/** Relay health states for liveness tracking */
|
|
4
4
|
export type RelayHealthState = "online" | "offline" | "dead";
|
|
5
5
|
/** State information for a relay's health tracking */
|
|
@@ -114,9 +114,9 @@ export declare class RelayLiveness {
|
|
|
114
114
|
reset(relay?: string): void;
|
|
115
115
|
private connections;
|
|
116
116
|
/** Connect to a {@link RelayPool} instance and track relay connections */
|
|
117
|
-
connectToPool(pool:
|
|
117
|
+
connectToPool(pool: RelayPool): void;
|
|
118
118
|
/** Disconnect from a {@link RelayPool} instance */
|
|
119
|
-
disconnectFromPool(pool:
|
|
119
|
+
disconnectFromPool(pool: RelayPool): void;
|
|
120
120
|
private updateRelayState;
|
|
121
121
|
private saveKnownRelays;
|
|
122
122
|
private saveRelayState;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MonoTypeOperatorFunction, OperatorFunction } from "rxjs";
|
|
2
2
|
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
3
|
-
import {
|
|
4
|
-
export declare function completeOnEose(includeEose: true): MonoTypeOperatorFunction<
|
|
5
|
-
export declare function completeOnEose(): OperatorFunction<
|
|
6
|
-
export declare function completeOnEose(includeEose: false): OperatorFunction<
|
|
3
|
+
import { RelaySubscriptionResponse } from "../types.js";
|
|
4
|
+
export declare function completeOnEose(includeEose: true): MonoTypeOperatorFunction<RelaySubscriptionResponse>;
|
|
5
|
+
export declare function completeOnEose(): OperatorFunction<RelaySubscriptionResponse, NostrEvent>;
|
|
6
|
+
export declare function completeOnEose(includeEose: false): OperatorFunction<RelaySubscriptionResponse, NostrEvent>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MonoTypeOperatorFunction, OperatorFunction } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Complete an observable when an operator emits a value
|
|
4
|
+
* @param operator - The operator to apply to the source observable
|
|
5
|
+
* @param check - A method used to check value for completion, defaults to truthy
|
|
6
|
+
*/
|
|
7
|
+
export declare function completeWhen<T, U>(operator: OperatorFunction<T, U>, check?: ((v: U) => boolean) | null): MonoTypeOperatorFunction<T>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { connect, filter, take, takeUntil } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Complete an observable when an operator emits a value
|
|
4
|
+
* @param operator - The operator to apply to the source observable
|
|
5
|
+
* @param check - A method used to check value for completion, defaults to truthy
|
|
6
|
+
*/
|
|
7
|
+
export function completeWhen(operator, check = (v) => !!v) {
|
|
8
|
+
return connect((shared$) => {
|
|
9
|
+
const complete$ = check ? shared$.pipe(operator, filter(check), take(1)) : shared$.pipe(operator, take(1));
|
|
10
|
+
return shared$.pipe(
|
|
11
|
+
// Complete when the operator returns truthy value
|
|
12
|
+
takeUntil(complete$));
|
|
13
|
+
});
|
|
14
|
+
}
|
package/dist/operators/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OperatorFunction } from "rxjs";
|
|
2
2
|
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
3
|
-
import {
|
|
3
|
+
import { RelaySubscriptionResponse } from "../types.js";
|
|
4
4
|
/** Filter subscription responses and only return the events */
|
|
5
|
-
export declare function onlyEvents(): OperatorFunction<
|
|
5
|
+
export declare function onlyEvents(): OperatorFunction<RelaySubscriptionResponse, NostrEvent>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IEventStore } from "applesauce-core";
|
|
2
2
|
import { MonoTypeOperatorFunction } from "rxjs";
|
|
3
|
-
import {
|
|
3
|
+
import { RelaySubscriptionResponse } from "../types.js";
|
|
4
4
|
/** Sends all events to the event store but does not remove duplicates */
|
|
5
|
-
export declare function storeEvents(eventStore: IEventStore): MonoTypeOperatorFunction<
|
|
5
|
+
export declare function storeEvents(eventStore: IEventStore): MonoTypeOperatorFunction<RelaySubscriptionResponse>;
|
package/dist/pool.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import { BehaviorSubject, Observable, Subject } from "rxjs";
|
|
|
5
5
|
import { RelayGroup } from "./group.js";
|
|
6
6
|
import type { NegentropySyncOptions, ReconcileFunction } from "./negentropy.js";
|
|
7
7
|
import { Relay, SyncDirection, type RelayOptions } from "./relay.js";
|
|
8
|
-
import type {
|
|
9
|
-
export declare class RelayPool
|
|
8
|
+
import type { RelayCountResponse, FilterInput, GroupReqOptions, GroupReqMessage, PoolRelayInput, NegentropyReadStore, NegentropySyncStore, PublishResponse, RelayStatus } from "./types.js";
|
|
9
|
+
export declare class RelayPool {
|
|
10
10
|
options?: RelayOptions | undefined;
|
|
11
11
|
relays$: BehaviorSubject<Map<string, Relay>>;
|
|
12
12
|
get relays(): Map<string, Relay>;
|
|
@@ -15,34 +15,34 @@ export declare class RelayPool implements IPool {
|
|
|
15
15
|
/** Whether to ignore relays that are ready=false */
|
|
16
16
|
ignoreOffline: boolean;
|
|
17
17
|
/** A signal when a relay is added */
|
|
18
|
-
add$: Subject<
|
|
18
|
+
add$: Subject<Relay>;
|
|
19
19
|
/** A signal when a relay is removed */
|
|
20
|
-
remove$: Subject<
|
|
20
|
+
remove$: Subject<Relay>;
|
|
21
21
|
constructor(options?: RelayOptions | undefined);
|
|
22
22
|
/** Get or create a new relay connection */
|
|
23
23
|
relay(url: string): Relay;
|
|
24
24
|
/** Create a group of relays */
|
|
25
|
-
group(relays:
|
|
25
|
+
group(relays: PoolRelayInput, ignoreOffline?: boolean): RelayGroup;
|
|
26
26
|
/** Removes a relay from the pool and defaults to closing the connection */
|
|
27
|
-
remove(relay: string |
|
|
27
|
+
remove(relay: string | Relay, close?: boolean): void;
|
|
28
28
|
/** Make a REQ to multiple relays that does not deduplicate events */
|
|
29
|
-
req(relays:
|
|
29
|
+
req(relays: PoolRelayInput, filters: FilterInput, opts?: GroupReqOptions): Observable<GroupReqMessage>;
|
|
30
30
|
/** Send an EVENT message to multiple relays */
|
|
31
|
-
event(relays:
|
|
31
|
+
event(relays: PoolRelayInput, event: NostrEvent): Observable<PublishResponse>;
|
|
32
32
|
/** Negentropy sync event ids with the relays and an event store */
|
|
33
|
-
negentropy(relays:
|
|
33
|
+
negentropy(relays: PoolRelayInput, store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
|
|
34
34
|
/** Publish an event to multiple relays */
|
|
35
|
-
publish(relays:
|
|
35
|
+
publish(relays: PoolRelayInput, event: Parameters<RelayGroup["publish"]>[0], opts?: Parameters<RelayGroup["publish"]>[1]): Promise<PublishResponse[]>;
|
|
36
36
|
/** Request events from multiple relays */
|
|
37
|
-
request(relays:
|
|
37
|
+
request(relays: PoolRelayInput, filters: Parameters<RelayGroup["request"]>[0], opts?: Parameters<RelayGroup["request"]>[1]): Observable<NostrEvent>;
|
|
38
38
|
/** Open a subscription to multiple relays */
|
|
39
|
-
subscription(relays:
|
|
39
|
+
subscription(relays: PoolRelayInput, filters: Parameters<RelayGroup["subscription"]>[0], options?: Parameters<RelayGroup["subscription"]>[1]): Observable<NostrEvent>;
|
|
40
40
|
/** Open a subscription for a map of relays and filters */
|
|
41
|
-
subscriptionMap(relays: FilterMap | Observable<FilterMap>, options?: Parameters<RelayGroup["subscription"]>[1]): Observable<
|
|
41
|
+
subscriptionMap(relays: FilterMap | Observable<FilterMap>, options?: Parameters<RelayGroup["subscription"]>[1]): Observable<NostrEvent>;
|
|
42
42
|
/** Open a subscription for an {@link OutboxMap} and filter */
|
|
43
|
-
outboxSubscription(outboxes: OutboxMap | Observable<OutboxMap>, filter: Omit<Filter, "authors">, options?: Parameters<RelayGroup["subscription"]>[1]): Observable<
|
|
43
|
+
outboxSubscription(outboxes: OutboxMap | Observable<OutboxMap>, filter: Omit<Filter, "authors">, options?: Parameters<RelayGroup["subscription"]>[1]): Observable<NostrEvent>;
|
|
44
44
|
/** Count events on multiple relays */
|
|
45
|
-
count(relays:
|
|
45
|
+
count(relays: PoolRelayInput, filters: Filter | Filter[], id?: string): Observable<Record<string, RelayCountResponse>>;
|
|
46
46
|
/** Negentropy sync events with the relays and an event store */
|
|
47
|
-
sync(relays:
|
|
47
|
+
sync(relays: PoolRelayInput, store: NegentropySyncStore | NostrEvent[], filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
|
|
48
48
|
}
|
package/dist/pool.js
CHANGED
|
@@ -82,9 +82,9 @@ export class RelayPool {
|
|
|
82
82
|
this.remove$.next(instance);
|
|
83
83
|
}
|
|
84
84
|
/** Make a REQ to multiple relays that does not deduplicate events */
|
|
85
|
-
req(relays, filters,
|
|
85
|
+
req(relays, filters, opts) {
|
|
86
86
|
// Never filter out offline relays in manual methods
|
|
87
|
-
return this.group(relays, false).req(filters,
|
|
87
|
+
return this.group(relays, false).req(filters, opts);
|
|
88
88
|
}
|
|
89
89
|
/** Send an EVENT message to multiple relays */
|
|
90
90
|
event(relays, event) {
|
package/dist/relay.d.ts
CHANGED
|
@@ -4,21 +4,25 @@ import { Filter } from "applesauce-core/helpers/filter";
|
|
|
4
4
|
import { BehaviorSubject, MonoTypeOperatorFunction, Observable, RepeatConfig, RetryConfig, Subject } from "rxjs";
|
|
5
5
|
import { WebSocketSubject, WebSocketSubjectConfig } from "rxjs/webSocket";
|
|
6
6
|
import { type NegentropySyncOptions, type ReconcileFunction } from "./negentropy.js";
|
|
7
|
-
import { AuthSigner,
|
|
7
|
+
import { AuthSigner, FilterInput, NegentropyReadStore, NegentropySyncStore, PublishOptions, PublishResponse, RelayCountResponse, RelayInformation, RelayReqMessage, RelayReqOptions, RelayRequestCompleteOperator, RelayRequestOptions, RelayRequestResponse, RelayStatus, RelaySubscriptionOptions, RelaySubscriptionResponse } from "./types.js";
|
|
8
8
|
/** Flags for the negentropy sync type */
|
|
9
9
|
export declare enum SyncDirection {
|
|
10
10
|
RECEIVE = 1,
|
|
11
11
|
SEND = 2,
|
|
12
12
|
BOTH = 3
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
export declare class
|
|
14
|
+
/** Base error thrown when a relay closes a REQ or COUNT with a CLOSED message */
|
|
15
|
+
export declare class RelayClosedError extends Error {
|
|
16
|
+
readonly reason: string;
|
|
17
|
+
constructor(reason: string);
|
|
18
|
+
}
|
|
19
|
+
/** Thrown when the relay closes a subscription with an auth-required: prefix */
|
|
20
|
+
export declare class AuthRequiredError extends RelayClosedError {
|
|
21
|
+
constructor(reason: string);
|
|
16
22
|
}
|
|
17
23
|
export type RelayOptions = {
|
|
18
24
|
/** Custom WebSocket implementation */
|
|
19
25
|
WebSocket?: WebSocketSubjectConfig<any>["WebSocketCtor"];
|
|
20
|
-
/** How long to wait for an EOSE message (default 10s) */
|
|
21
|
-
eoseTimeout?: number;
|
|
22
26
|
/** How long to wait for an OK message from the relay (default 10s) */
|
|
23
27
|
eventTimeout?: number;
|
|
24
28
|
/** How long to wait for a publish to complete (default 30s) */
|
|
@@ -38,14 +42,14 @@ export type RelayOptions = {
|
|
|
38
42
|
now: number;
|
|
39
43
|
attempts: number;
|
|
40
44
|
}) => "reconnect" | "close" | "ignore";
|
|
41
|
-
/** Default
|
|
42
|
-
|
|
43
|
-
/** Default
|
|
44
|
-
|
|
45
|
+
/** Default reconnect config for subscription() method */
|
|
46
|
+
subscriptionReconnect?: RetryConfig;
|
|
47
|
+
/** Default reconnect config for request() method */
|
|
48
|
+
requestReconnect?: RetryConfig;
|
|
45
49
|
/** Default retry config for publish() method */
|
|
46
50
|
publishRetry?: RetryConfig;
|
|
47
51
|
};
|
|
48
|
-
export declare class Relay
|
|
52
|
+
export declare class Relay {
|
|
49
53
|
url: string;
|
|
50
54
|
protected log: typeof logger;
|
|
51
55
|
protected socket: WebSocketSubject<any>;
|
|
@@ -103,7 +107,7 @@ export declare class Relay implements IRelay {
|
|
|
103
107
|
open$: Subject<Event>;
|
|
104
108
|
/** An observable that emits when underlying websocket is closed */
|
|
105
109
|
close$: Subject<CloseEvent>;
|
|
106
|
-
/** An observable that emits when underlying websocket is closing due to
|
|
110
|
+
/** An observable that emits when underlying websocket is closing due to unsubscribe or complete */
|
|
107
111
|
closing$: Subject<void>;
|
|
108
112
|
/** Tracks active req() operations by subscription ID */
|
|
109
113
|
reqs$: BehaviorSubject<Record<string, Filter[]>>;
|
|
@@ -118,8 +122,6 @@ export declare class Relay implements IRelay {
|
|
|
118
122
|
get information(): RelayInformation | null;
|
|
119
123
|
get lastMessageAt(): number;
|
|
120
124
|
get reqs(): Record<string, Filter[]>;
|
|
121
|
-
/** If an EOSE message is not seen in this time, emit one locally (default 10s) */
|
|
122
|
-
eoseTimeout: number;
|
|
123
125
|
/** How long to wait for an OK message from the relay (default 10s) */
|
|
124
126
|
eventTimeout: number;
|
|
125
127
|
/** How long to wait for a publish to complete (default 30s) */
|
|
@@ -132,12 +134,12 @@ export declare class Relay implements IRelay {
|
|
|
132
134
|
pingFrequency: number;
|
|
133
135
|
/** How long to wait for EOSE response in milliseconds (default 20000) */
|
|
134
136
|
pingTimeout: number;
|
|
135
|
-
/** Default
|
|
136
|
-
|
|
137
|
-
/** Default
|
|
138
|
-
|
|
137
|
+
/** Default reconnect config for subscription() method */
|
|
138
|
+
subscriptionReconnect: RetryConfig;
|
|
139
|
+
/** Default reconnect config for request() method */
|
|
140
|
+
requestReconnect: RetryConfig;
|
|
139
141
|
/** Default retry config for publish() method */
|
|
140
|
-
|
|
142
|
+
publishRetry: RetryConfig;
|
|
141
143
|
/** Policy hook for unresponsive connections */
|
|
142
144
|
protected onUnresponsive?: RelayOptions["onUnresponsive"];
|
|
143
145
|
protected receivedAuthRequiredForReq: BehaviorSubject<boolean>;
|
|
@@ -158,9 +160,9 @@ export declare class Relay implements IRelay {
|
|
|
158
160
|
/** Send a message to the relay */
|
|
159
161
|
send(message: any): void;
|
|
160
162
|
/** Create a REQ observable that emits events or "EOSE" or errors */
|
|
161
|
-
req(filters: FilterInput,
|
|
163
|
+
req(filters: FilterInput, opts?: RelayReqOptions): Observable<RelayReqMessage>;
|
|
162
164
|
/** Create a COUNT observable that emits a single count response */
|
|
163
|
-
count(filters: Filter | Filter[], id?: string): Observable<
|
|
165
|
+
count(filters: Filter | Filter[], id?: string): Observable<RelayCountResponse>;
|
|
164
166
|
/** Send an EVENT or AUTH message and return an observable of PublishResponse that completes or errors */
|
|
165
167
|
event(event: NostrEvent, verb?: "EVENT" | "AUTH"): Observable<PublishResponse>;
|
|
166
168
|
/** send and AUTH message */
|
|
@@ -169,22 +171,20 @@ export declare class Relay implements IRelay {
|
|
|
169
171
|
negentropy(store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
|
|
170
172
|
/** Authenticate with the relay using a signer */
|
|
171
173
|
authenticate(signer: AuthSigner): Promise<PublishResponse>;
|
|
172
|
-
/** Internal operator for creating the retry() operator */
|
|
174
|
+
/** Internal operator for creating the retry() operator for reconnecting to the websocket */
|
|
173
175
|
protected customRetryOperator<T extends unknown = unknown>(times: undefined | boolean | number | RetryConfig, base?: RetryConfig): MonoTypeOperatorFunction<T>;
|
|
174
|
-
/** Internal operator for creating the repeat() operator */
|
|
176
|
+
/** Internal operator for creating the repeat() operator for resubscribing */
|
|
175
177
|
protected customRepeatOperator<T extends unknown = unknown>(times: undefined | boolean | number | RepeatConfig | undefined): MonoTypeOperatorFunction<T>;
|
|
176
178
|
/** Internal operator for creating the timeout() operator */
|
|
177
179
|
protected customTimeoutOperator<T extends unknown = unknown>(timeout: undefined | boolean | number, defaultTimeout: number): MonoTypeOperatorFunction<T>;
|
|
178
|
-
/** Internal operator for handling auth-required errors from REQ/COUNT operations */
|
|
179
|
-
protected handleAuthRequiredForReq(operation: "REQ" | "COUNT"): MonoTypeOperatorFunction<any>;
|
|
180
180
|
/** Creates a REQ that retries when relay errors ( default 3 retries ) */
|
|
181
|
-
subscription(filters: FilterInput, opts?:
|
|
181
|
+
subscription(filters: FilterInput, opts?: RelaySubscriptionOptions): Observable<RelaySubscriptionResponse>;
|
|
182
182
|
/** Makes a single request that retires on errors and completes on EOSE */
|
|
183
|
-
request(filters: FilterInput, opts?:
|
|
183
|
+
request(filters: FilterInput, opts?: RelayRequestOptions): Observable<RelayRequestResponse>;
|
|
184
184
|
/** Publishes an event to the relay and retries when relay errors or responds with auth-required ( default 3 retries ) */
|
|
185
185
|
publish(event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse>;
|
|
186
186
|
/** Negentropy sync events with the relay and an event store */
|
|
187
|
-
sync(store: NegentropySyncStore,
|
|
187
|
+
sync(store: NegentropySyncStore, filters: Filter, direction?: SyncDirection): Observable<NostrEvent>;
|
|
188
188
|
/** Force close the connection */
|
|
189
189
|
close(): void;
|
|
190
190
|
/** An async method that returns the NIP-11 information document for the relay */
|
|
@@ -197,4 +197,12 @@ export declare class Relay implements IRelay {
|
|
|
197
197
|
static fetchInformationDocument(url: string): Observable<RelayInformation | null>;
|
|
198
198
|
/** Static method to create a reconnection method for each relay */
|
|
199
199
|
static createReconnectTimer(_relay: string): (_error?: Error | CloseEvent, tries?: number) => Observable<0>;
|
|
200
|
+
/** A complete condition that waits for the subscription to open */
|
|
201
|
+
static afterOpen(condition: RelayRequestCompleteOperator): RelayRequestCompleteOperator;
|
|
202
|
+
/** An OR complete condition, that completes when either condition is truthy */
|
|
203
|
+
static completeOr(...conditions: RelayRequestCompleteOperator[]): RelayRequestCompleteOperator;
|
|
204
|
+
/** An AND complete condition, that completes when all conditions are truthy */
|
|
205
|
+
static completeAnd(...conditions: RelayRequestCompleteOperator[]): RelayRequestCompleteOperator;
|
|
206
|
+
/** A default complete condition that waits for the subscription to open and then completes after a timeout */
|
|
207
|
+
static defaultComplete(timeout: number, afterOpen?: number): RelayRequestCompleteOperator;
|
|
200
208
|
}
|