applesauce-relay 0.0.0-next-20250716144720 → 0.0.0-next-20250718173521
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/README.md +6 -5
- package/dist/__tests__/relay.test.js +2 -12
- package/dist/group.d.ts +1 -1
- package/dist/group.js +2 -2
- package/dist/pool.d.ts +1 -1
- package/dist/pool.js +2 -2
- package/dist/relay.d.ts +3 -1
- package/dist/relay.js +21 -13
- package/dist/types.d.ts +3 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -56,9 +56,8 @@ const event = {
|
|
|
56
56
|
// ... other required fields
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
relay.
|
|
60
|
-
|
|
61
|
-
});
|
|
59
|
+
const response = await relay.publish(event);
|
|
60
|
+
console.log(`Published:`, response.ok);
|
|
62
61
|
```
|
|
63
62
|
|
|
64
63
|
### Relay Pool
|
|
@@ -85,7 +84,8 @@ pool
|
|
|
85
84
|
});
|
|
86
85
|
|
|
87
86
|
// Publish to multiple relays
|
|
88
|
-
pool.
|
|
87
|
+
const responses = await pool.publish(relays, event);
|
|
88
|
+
responses.forEach((response) => {
|
|
89
89
|
console.log(`Published to ${response.from}:`, response.ok);
|
|
90
90
|
});
|
|
91
91
|
```
|
|
@@ -112,7 +112,8 @@ group
|
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
// Publish to all relays in group
|
|
115
|
-
group.
|
|
115
|
+
const responses = await group.publish(event);
|
|
116
|
+
responses.forEach((response) => {
|
|
116
117
|
console.log(`Published to ${response.from}:`, response.ok);
|
|
117
118
|
});
|
|
118
119
|
```
|
|
@@ -144,7 +144,7 @@ describe("req", () => {
|
|
|
144
144
|
// Verify the second subscription received the event and EOSE
|
|
145
145
|
expect(secondSub.getValues()).toEqual([expect.objectContaining(mockEvent), "EOSE"]);
|
|
146
146
|
});
|
|
147
|
-
it("should
|
|
147
|
+
it("should wait for authentication if relay info document has limitations.auth_required = true", async () => {
|
|
148
148
|
// Mock the fetchInformationDocument method to return a document with auth_required = true
|
|
149
149
|
vi.spyOn(Relay, "fetchInformationDocument").mockImplementation(() => of({
|
|
150
150
|
name: "Auth Required Relay",
|
|
@@ -162,20 +162,10 @@ describe("req", () => {
|
|
|
162
162
|
const sub = subscribeSpyTo(relay.req([{ kinds: [1] }], "sub1"));
|
|
163
163
|
// Wait 10ms to ensure the information document is fetched
|
|
164
164
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
165
|
-
// Wait for connection
|
|
166
|
-
await server.connected;
|
|
167
165
|
// Verify no REQ message was sent yet (waiting for auth)
|
|
168
166
|
expect(server).not.toHaveReceivedMessages(["REQ", "sub1", { kinds: [1] }]);
|
|
169
|
-
// Send AUTH challenge
|
|
170
|
-
server.send(["AUTH", "challenge"]);
|
|
171
|
-
// Send auth response
|
|
172
|
-
subscribeSpyTo(relay.auth(mockEvent));
|
|
173
|
-
// Verify the auth event was sent
|
|
174
|
-
await expect(server.nextMessage).resolves.toEqual(["AUTH", mockEvent]);
|
|
175
|
-
// Accept auth
|
|
176
|
-
server.send(["OK", mockEvent.id, true, ""]);
|
|
177
167
|
// Simulate successful authentication
|
|
178
|
-
|
|
168
|
+
relay.authenticated$.next(true);
|
|
179
169
|
// Now the REQ should be sent
|
|
180
170
|
await expect(server).toReceiveMessage(["REQ", "sub1", { kinds: [1] }]);
|
|
181
171
|
// Send EVENT and EOSE to complete the subscription
|
package/dist/group.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare class RelayGroup implements IGroup {
|
|
|
9
9
|
/** Make a request to all relays */
|
|
10
10
|
req(filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
|
|
11
11
|
/** Send an event to all relays */
|
|
12
|
-
event(event: NostrEvent): Observable<PublishResponse>;
|
|
12
|
+
event(event: NostrEvent, opts?: PublishOptions): Observable<PublishResponse>;
|
|
13
13
|
/** Publish an event to all relays with retries ( default 3 retries ) */
|
|
14
14
|
publish(event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse[]>;
|
|
15
15
|
/** Request events from all relays with retries ( default 3 retries ) */
|
package/dist/group.js
CHANGED
|
@@ -28,8 +28,8 @@ export class RelayGroup {
|
|
|
28
28
|
return this.mergeEOSE(...requests);
|
|
29
29
|
}
|
|
30
30
|
/** Send an event to all relays */
|
|
31
|
-
event(event) {
|
|
32
|
-
return merge(...this.relays.map((relay) => relay.event(event).pipe(
|
|
31
|
+
event(event, opts) {
|
|
32
|
+
return merge(...this.relays.map((relay) => relay.event(event, opts).pipe(
|
|
33
33
|
// Catch error and return as PublishResponse
|
|
34
34
|
catchError((err) => of({ ok: false, from: relay.url, message: err?.message || "Unknown error" })))));
|
|
35
35
|
}
|
package/dist/pool.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export declare class RelayPool implements IPool {
|
|
|
20
20
|
/** Make a REQ to multiple relays that does not deduplicate events */
|
|
21
21
|
req(relays: string[], filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
|
|
22
22
|
/** Send an EVENT message to multiple relays */
|
|
23
|
-
event(relays: string[], event: NostrEvent): Observable<PublishResponse>;
|
|
23
|
+
event(relays: string[], event: NostrEvent, opts?: PublishOptions): Observable<PublishResponse>;
|
|
24
24
|
/** Publish an event to multiple relays */
|
|
25
25
|
publish(relays: string[], event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse[]>;
|
|
26
26
|
/** Request events from multiple relays */
|
package/dist/pool.js
CHANGED
|
@@ -55,8 +55,8 @@ export class RelayPool {
|
|
|
55
55
|
return this.group(relays).req(filters, id);
|
|
56
56
|
}
|
|
57
57
|
/** Send an EVENT message to multiple relays */
|
|
58
|
-
event(relays, event) {
|
|
59
|
-
return this.group(relays).event(event);
|
|
58
|
+
event(relays, event, opts) {
|
|
59
|
+
return this.group(relays).event(event, opts);
|
|
60
60
|
}
|
|
61
61
|
/** Publish an event to multiple relays */
|
|
62
62
|
publish(relays, event, opts) {
|
package/dist/relay.d.ts
CHANGED
|
@@ -79,7 +79,9 @@ export declare class Relay implements IRelay {
|
|
|
79
79
|
/** Create a REQ observable that emits events or "EOSE" or errors */
|
|
80
80
|
req(filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
|
|
81
81
|
/** Send an EVENT or AUTH message and return an observable of PublishResponse that completes or errors */
|
|
82
|
-
event(event: NostrEvent,
|
|
82
|
+
event(event: NostrEvent, opts?: PublishOptions & {
|
|
83
|
+
verb?: "EVENT" | "AUTH";
|
|
84
|
+
}): Observable<PublishResponse>;
|
|
83
85
|
/** send and AUTH message */
|
|
84
86
|
auth(event: NostrEvent): Promise<PublishResponse>;
|
|
85
87
|
/** Authenticate with the relay using a signer */
|
package/dist/relay.js
CHANGED
|
@@ -2,7 +2,7 @@ import { logger } from "applesauce-core";
|
|
|
2
2
|
import { simpleTimeout } from "applesauce-core/observable";
|
|
3
3
|
import { nanoid } from "nanoid";
|
|
4
4
|
import { nip42 } from "nostr-tools";
|
|
5
|
-
import { BehaviorSubject, catchError, combineLatest, defer, endWith, filter, finalize, from, ignoreElements, isObservable, lastValueFrom, map, merge, mergeMap, mergeWith, NEVER, of, retry, scan, share, shareReplay, Subject, switchMap, take, takeUntil, tap, throwError, timeout, timer, } from "rxjs";
|
|
5
|
+
import { BehaviorSubject, catchError, combineLatest, defer, endWith, filter, finalize, from, identity, ignoreElements, isObservable, lastValueFrom, map, merge, mergeMap, mergeWith, NEVER, of, pipe, retry, scan, share, shareReplay, Subject, switchMap, take, takeUntil, tap, throwError, timeout, timer, } from "rxjs";
|
|
6
6
|
import { webSocket } from "rxjs/webSocket";
|
|
7
7
|
import { ensureHttpURL } from "applesauce-core/helpers";
|
|
8
8
|
import { completeOnEose } from "./operators/complete-on-eose.js";
|
|
@@ -286,7 +286,8 @@ export class Relay {
|
|
|
286
286
|
return this.waitForReady(this.waitForAuth(this.authRequiredForRead$, observable));
|
|
287
287
|
}
|
|
288
288
|
/** Send an EVENT or AUTH message and return an observable of PublishResponse that completes or errors */
|
|
289
|
-
event(event,
|
|
289
|
+
event(event, opts) {
|
|
290
|
+
const verb = opts?.verb || "EVENT";
|
|
290
291
|
const base = defer(() => {
|
|
291
292
|
// Send event when subscription starts
|
|
292
293
|
this.socket.next([verb, event]);
|
|
@@ -316,13 +317,27 @@ export class Relay {
|
|
|
316
317
|
if (verb === "AUTH")
|
|
317
318
|
return this.waitForReady(observable);
|
|
318
319
|
else
|
|
319
|
-
return this.waitForReady(this.waitForAuth(this.authRequiredForPublish$, observable))
|
|
320
|
+
return this.waitForReady(this.waitForAuth(this.authRequiredForPublish$, observable)).pipe(
|
|
321
|
+
// If retry is set, add a pipeline for retrying the event
|
|
322
|
+
opts?.retries
|
|
323
|
+
? pipe(
|
|
324
|
+
// If the relay responds with auth-required, throw an error for the retry operator to handle
|
|
325
|
+
mergeMap((result) => {
|
|
326
|
+
if (result.ok === false && result.message?.startsWith("auth-required:"))
|
|
327
|
+
return throwError(() => new Error(result.message));
|
|
328
|
+
return of(result);
|
|
329
|
+
}),
|
|
330
|
+
// Retry the event until it succeeds or the number of retries is reached
|
|
331
|
+
retry(opts.retries))
|
|
332
|
+
: identity);
|
|
320
333
|
}
|
|
321
334
|
/** send and AUTH message */
|
|
322
335
|
auth(event) {
|
|
323
|
-
return lastValueFrom(this.event(event, "AUTH").pipe(
|
|
336
|
+
return lastValueFrom(this.event(event, { verb: "AUTH" }).pipe(
|
|
324
337
|
// update authenticated
|
|
325
|
-
tap((result) =>
|
|
338
|
+
tap((result) => {
|
|
339
|
+
this.authenticationResponse$.next(result);
|
|
340
|
+
})));
|
|
326
341
|
}
|
|
327
342
|
/** Authenticate with the relay using a signer */
|
|
328
343
|
authenticate(signer) {
|
|
@@ -348,14 +363,7 @@ export class Relay {
|
|
|
348
363
|
}
|
|
349
364
|
/** Publishes an event to the relay and retries when relay errors or responds with auth-required ( default 3 retries ) */
|
|
350
365
|
publish(event, opts) {
|
|
351
|
-
return lastValueFrom(this.event(event)
|
|
352
|
-
// If the relay responds with auth-required, throw an error for the retry operator to handle
|
|
353
|
-
if (result.ok === false && result.message?.startsWith("auth-required:"))
|
|
354
|
-
return throwError(() => new Error(result.message));
|
|
355
|
-
return of(result);
|
|
356
|
-
}),
|
|
357
|
-
// Retry the publish until it succeeds or the number of retries is reached
|
|
358
|
-
retry(opts?.retries ?? 3)));
|
|
366
|
+
return lastValueFrom(this.event(event, { ...opts, verb: "EVENT" }));
|
|
359
367
|
}
|
|
360
368
|
/** Static method to fetch the NIP-11 information document for a relay */
|
|
361
369
|
static fetchInformationDocument(url) {
|
package/dist/types.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export interface IRelay extends MultiplexWebSocket {
|
|
|
39
39
|
/** Send a REQ message */
|
|
40
40
|
req(filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
|
|
41
41
|
/** Send an EVENT message */
|
|
42
|
-
event(event: NostrEvent): Observable<PublishResponse>;
|
|
42
|
+
event(event: NostrEvent, opts?: PublishOptions): Observable<PublishResponse>;
|
|
43
43
|
/** Send an AUTH message */
|
|
44
44
|
auth(event: NostrEvent): Promise<PublishResponse>;
|
|
45
45
|
/** Authenticate with the relay using a signer */
|
|
@@ -63,7 +63,7 @@ export interface IGroup {
|
|
|
63
63
|
/** Send a REQ message */
|
|
64
64
|
req(filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
|
|
65
65
|
/** Send an EVENT message */
|
|
66
|
-
event(event: NostrEvent): Observable<PublishResponse>;
|
|
66
|
+
event(event: NostrEvent, opts?: PublishOptions): Observable<PublishResponse>;
|
|
67
67
|
/** Send an EVENT message with retries */
|
|
68
68
|
publish(event: NostrEvent, opts?: {
|
|
69
69
|
retries?: number;
|
|
@@ -87,7 +87,7 @@ export interface IPool {
|
|
|
87
87
|
/** Send a REQ message */
|
|
88
88
|
req(relays: string[], filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
|
|
89
89
|
/** Send an EVENT message */
|
|
90
|
-
event(relays: string[], event: NostrEvent): Observable<PublishResponse>;
|
|
90
|
+
event(relays: string[], event: NostrEvent, opts?: PublishOptions): Observable<PublishResponse>;
|
|
91
91
|
/** Send an EVENT message to relays with retries */
|
|
92
92
|
publish(relays: string[], event: NostrEvent, opts?: {
|
|
93
93
|
retries?: number;
|
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-20250718173521",
|
|
4
4
|
"description": "nostr relay communication framework built on rxjs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@hirez_io/observer-spy": "^2.2.0",
|
|
64
|
-
"applesauce-signers": "
|
|
64
|
+
"applesauce-signers": "0.0.0-next-20250718173521",
|
|
65
65
|
"@vitest/expect": "^3.1.1",
|
|
66
66
|
"typescript": "^5.7.3",
|
|
67
67
|
"vitest": "^3.2.3",
|