applesauce-relay 0.0.0-next-20251231062646 → 0.0.0-next-20260116173453
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/relay.d.ts +14 -0
- package/dist/relay.js +58 -2
- package/package.json +3 -3
package/dist/relay.d.ts
CHANGED
|
@@ -25,6 +25,12 @@ export type RelayOptions = {
|
|
|
25
25
|
publishTimeout?: number;
|
|
26
26
|
/** How long to keep the connection alive after nothing is subscribed (default 30s) */
|
|
27
27
|
keepAlive?: number;
|
|
28
|
+
/** Enable/disable ping functionality (default false) */
|
|
29
|
+
enablePing?: boolean;
|
|
30
|
+
/** How often to send pings in milliseconds (default 29000) */
|
|
31
|
+
pingFrequency?: number;
|
|
32
|
+
/** How long to wait for EOSE response in milliseconds (default 20000) */
|
|
33
|
+
pingTimeout?: number;
|
|
28
34
|
/** Default retry config for subscription() method */
|
|
29
35
|
subscriptionRetry?: RetryConfig;
|
|
30
36
|
/** Default retry config for request() method */
|
|
@@ -66,6 +72,8 @@ export declare class Relay implements IRelay {
|
|
|
66
72
|
* @note Subscribing to this will not connect to the relay
|
|
67
73
|
*/
|
|
68
74
|
notice$: Observable<string>;
|
|
75
|
+
/** Timestamp of the last message received from the relay */
|
|
76
|
+
private lastMessageReceivedAt;
|
|
69
77
|
/** An observable that emits the NIP-11 information document for the relay */
|
|
70
78
|
information$: Observable<RelayInformation | null>;
|
|
71
79
|
protected _nip11: RelayInformation | null;
|
|
@@ -96,6 +104,12 @@ export declare class Relay implements IRelay {
|
|
|
96
104
|
publishTimeout: number;
|
|
97
105
|
/** How long to keep the connection alive after nothing is subscribed (default 30s) */
|
|
98
106
|
keepAlive: number;
|
|
107
|
+
/** Enable/disable ping functionality (default false) */
|
|
108
|
+
enablePing: boolean;
|
|
109
|
+
/** How often to send pings in milliseconds (default 29000) */
|
|
110
|
+
pingFrequency: number;
|
|
111
|
+
/** How long to wait for EOSE response in milliseconds (default 20000) */
|
|
112
|
+
pingTimeout: number;
|
|
99
113
|
/** Default retry config for subscription() method */
|
|
100
114
|
protected subscriptionReconnect: RetryConfig;
|
|
101
115
|
/** Default retry config for request() method */
|
package/dist/relay.js
CHANGED
|
@@ -24,6 +24,11 @@ export var SyncDirection;
|
|
|
24
24
|
/** An error that is thrown when a REQ is closed from the relay side */
|
|
25
25
|
export class ReqCloseError extends Error {
|
|
26
26
|
}
|
|
27
|
+
/** A dummy filter that will return empty results */
|
|
28
|
+
const PING_FILTER = {
|
|
29
|
+
ids: ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
|
|
30
|
+
limit: 0,
|
|
31
|
+
};
|
|
27
32
|
export class Relay {
|
|
28
33
|
url;
|
|
29
34
|
log = logger.extend("Relay");
|
|
@@ -58,6 +63,8 @@ export class Relay {
|
|
|
58
63
|
* @note Subscribing to this will not connect to the relay
|
|
59
64
|
*/
|
|
60
65
|
notice$;
|
|
66
|
+
/** Timestamp of the last message received from the relay */
|
|
67
|
+
lastMessageReceivedAt = 0;
|
|
61
68
|
/** An observable that emits the NIP-11 information document for the relay */
|
|
62
69
|
information$;
|
|
63
70
|
_nip11 = null;
|
|
@@ -103,6 +110,12 @@ export class Relay {
|
|
|
103
110
|
publishTimeout = 30_000;
|
|
104
111
|
/** How long to keep the connection alive after nothing is subscribed (default 30s) */
|
|
105
112
|
keepAlive = 30_000;
|
|
113
|
+
/** Enable/disable ping functionality (default false) */
|
|
114
|
+
enablePing = false;
|
|
115
|
+
/** How often to send pings in milliseconds (default 29000) */
|
|
116
|
+
pingFrequency = 29_000;
|
|
117
|
+
/** How long to wait for EOSE response in milliseconds (default 20000) */
|
|
118
|
+
pingTimeout = 20_000;
|
|
106
119
|
/** Default retry config for subscription() method */
|
|
107
120
|
subscriptionReconnect;
|
|
108
121
|
/** Default retry config for request() method */
|
|
@@ -142,6 +155,12 @@ export class Relay {
|
|
|
142
155
|
this.publishTimeout = opts.publishTimeout;
|
|
143
156
|
if (opts?.keepAlive !== undefined)
|
|
144
157
|
this.keepAlive = opts.keepAlive;
|
|
158
|
+
if (opts?.enablePing !== undefined)
|
|
159
|
+
this.enablePing = opts.enablePing;
|
|
160
|
+
if (opts?.pingFrequency !== undefined)
|
|
161
|
+
this.pingFrequency = opts.pingFrequency;
|
|
162
|
+
if (opts?.pingTimeout !== undefined)
|
|
163
|
+
this.pingTimeout = opts.pingTimeout;
|
|
145
164
|
// Set retry configs
|
|
146
165
|
this.subscriptionReconnect = { ...DEFAULT_RETRY_CONFIG, ...(opts?.subscriptionRetry ?? {}) };
|
|
147
166
|
this.requestReconnect = { ...DEFAULT_RETRY_CONFIG, ...(opts?.requestRetry ?? {}) };
|
|
@@ -228,7 +247,12 @@ export class Relay {
|
|
|
228
247
|
this.challenge$.next(challenge);
|
|
229
248
|
}));
|
|
230
249
|
const allMessagesSubject = new Subject();
|
|
231
|
-
const listenForAllMessages = this.socket.pipe(tap((message) =>
|
|
250
|
+
const listenForAllMessages = this.socket.pipe(tap((message) => {
|
|
251
|
+
// Update the last message received at timestamp
|
|
252
|
+
this.lastMessageReceivedAt = Date.now();
|
|
253
|
+
// Pass to the message subject
|
|
254
|
+
allMessagesSubject.next(message);
|
|
255
|
+
}));
|
|
232
256
|
// Create passive observables for messages and notices
|
|
233
257
|
this.message$ = allMessagesSubject.asObservable();
|
|
234
258
|
this.notice$ = this.message$.pipe(
|
|
@@ -236,12 +260,44 @@ export class Relay {
|
|
|
236
260
|
filter((m) => Array.isArray(m) && m[0] === "NOTICE"),
|
|
237
261
|
// pick the string out of the message
|
|
238
262
|
map((m) => m[1]));
|
|
263
|
+
// Create ping health check observable
|
|
264
|
+
const pingHealthCheck = this.connected$.pipe(
|
|
265
|
+
// Switch based on connection state
|
|
266
|
+
switchMap((connected) => {
|
|
267
|
+
// Only run when connected and ping is enabled
|
|
268
|
+
if (!connected || !this.enablePing)
|
|
269
|
+
return NEVER;
|
|
270
|
+
// Start timer that emits periodically
|
|
271
|
+
return timer(this.pingFrequency, this.pingFrequency).pipe(
|
|
272
|
+
// For each ping, create a dummy REQ and wait for EOSE
|
|
273
|
+
mergeMap(() => {
|
|
274
|
+
// Skip ping if we have received a message in the last pingFrequency milliseconds
|
|
275
|
+
if (Date.now() - this.lastMessageReceivedAt < this.pingFrequency)
|
|
276
|
+
return NEVER;
|
|
277
|
+
// Send a ping request
|
|
278
|
+
this.send(["REQ", "ping:" + nanoid(), PING_FILTER]);
|
|
279
|
+
// Wait for the EOSE response
|
|
280
|
+
return this.message$.pipe(
|
|
281
|
+
// Complete after first message received
|
|
282
|
+
take(1),
|
|
283
|
+
// Add timeout to detect unresponsive connections
|
|
284
|
+
timeout({
|
|
285
|
+
first: this.pingTimeout,
|
|
286
|
+
with: () => {
|
|
287
|
+
console.warn(`Relay connection has become unresponsive: ${this.url}`);
|
|
288
|
+
return NEVER;
|
|
289
|
+
},
|
|
290
|
+
}));
|
|
291
|
+
}));
|
|
292
|
+
}),
|
|
293
|
+
// Catch errors to prevent breaking the watchTower
|
|
294
|
+
catchError(() => NEVER));
|
|
239
295
|
// Merge all watchers
|
|
240
296
|
this.watchTower = this.ready$.pipe(switchMap((ready) => {
|
|
241
297
|
if (!ready)
|
|
242
298
|
return NEVER;
|
|
243
299
|
// Only start the watch tower if the relay is ready
|
|
244
|
-
return merge(listenForAllMessages, listenForNotice, ListenForChallenge, this.information
|
|
300
|
+
return merge(listenForAllMessages, listenForNotice, ListenForChallenge, this.information$, pingHealthCheck).pipe(
|
|
245
301
|
// Never emit any values
|
|
246
302
|
ignoreElements(),
|
|
247
303
|
// Start the reconnect timer if the connection has an error
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-relay",
|
|
3
|
-
"version": "0.0.0-next-
|
|
3
|
+
"version": "0.0.0-next-20260116173453",
|
|
4
4
|
"description": "nostr relay communication framework built on rxjs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -57,14 +57,14 @@
|
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@noble/hashes": "^1.7.1",
|
|
60
|
-
"applesauce-core": "0.0.0-next-
|
|
60
|
+
"applesauce-core": "0.0.0-next-20260116173453",
|
|
61
61
|
"nanoid": "^5.0.9",
|
|
62
62
|
"nostr-tools": "~2.19",
|
|
63
63
|
"rxjs": "^7.8.1"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@hirez_io/observer-spy": "^2.2.0",
|
|
67
|
-
"applesauce-signers": "
|
|
67
|
+
"applesauce-signers": "^5.0.0",
|
|
68
68
|
"rimraf": "^6.0.1",
|
|
69
69
|
"typescript": "^5.7.3",
|
|
70
70
|
"vitest": "^4.0.15",
|