applesauce-relay 0.0.0-next-20250404075518 → 0.0.0-next-20250404095409

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.js CHANGED
@@ -1,13 +1,18 @@
1
- import { combineLatest, filter, map, merge } from "rxjs";
2
- import { onlyEvents } from "./operators/only-events.js";
1
+ import { catchError, combineLatest, EMPTY, filter, map, merge, of } from "rxjs";
3
2
  import { nanoid } from "nanoid";
3
+ import { onlyEvents } from "./operators/only-events.js";
4
4
  export class RelayGroup {
5
5
  relays;
6
6
  constructor(relays) {
7
7
  this.relays = relays;
8
8
  }
9
- req(filters, id = nanoid()) {
10
- const requests = this.relays.reduce((acc, r) => ({ ...acc, [r.url]: r.req(filters, id) }), {});
9
+ req(filters, id = nanoid(8)) {
10
+ const requests = this.relays.reduce((acc, relay) => ({
11
+ ...acc,
12
+ [relay.url]: relay.req(filters, id).pipe(
13
+ // Ignore connection errors
14
+ catchError(() => EMPTY)),
15
+ }), {});
11
16
  // Create stream of events only
12
17
  const events = merge(...Object.values(requests)).pipe(onlyEvents());
13
18
  // Create stream that emits EOSE when all relays have sent EOSE
@@ -18,6 +23,8 @@ export class RelayGroup {
18
23
  return merge(events, eose);
19
24
  }
20
25
  event(event) {
21
- return merge(...this.relays.map((r) => r.event(event)));
26
+ return merge(...this.relays.map((relay) => relay.event(event).pipe(
27
+ // Catch error and return as PublishResponse
28
+ catchError((err) => of({ ok: false, from: relay.url, message: err?.message || "Unknown error" })))));
22
29
  }
23
30
  }
package/dist/relay.d.ts CHANGED
@@ -30,9 +30,11 @@ export declare class Relay implements IRelay {
30
30
  constructor(url: string, opts?: RelayOptions);
31
31
  protected waitForAuth<T extends unknown = unknown>(requireAuth: BehaviorSubject<boolean>, observable: Observable<T>): Observable<T>;
32
32
  multiplex<T>(open: () => any, close: () => any, filter: (message: any) => boolean): Observable<T>;
33
+ /** Send a message to the relay */
33
34
  next(message: any): void;
35
+ /** Create a REQ observable that emits events | "EOSE" or errors */
34
36
  req(filters: Filter | Filter[], id?: string): Observable<SubscriptionResponse>;
35
- /** send an Event message */
37
+ /** send an Event message and always return an observable of PublishResponse that completes or errors */
36
38
  event(event: NostrEvent, verb?: "EVENT" | "AUTH"): Observable<PublishResponse>;
37
39
  /** send and AUTH message */
38
40
  auth(event: NostrEvent): Observable<{
package/dist/relay.js CHANGED
@@ -1,4 +1,4 @@
1
- import { BehaviorSubject, combineLatest, filter, ignoreElements, map, merge, NEVER, of, scan, share, switchMap, take, takeWhile, tap, timeout, } from "rxjs";
1
+ import { BehaviorSubject, combineLatest, defer, filter, ignoreElements, map, merge, NEVER, of, scan, share, switchMap, take, takeWhile, tap, timeout, } from "rxjs";
2
2
  import { webSocket } from "rxjs/webSocket";
3
3
  import { nanoid } from "nanoid";
4
4
  import { logger } from "applesauce-core";
@@ -97,7 +97,7 @@ export class Relay {
97
97
  share());
98
98
  }
99
99
  waitForAuth(
100
- // require BehaviorSubject so it always has a value
100
+ // NOTE: require BehaviorSubject so it always has a value
101
101
  requireAuth, observable) {
102
102
  return combineLatest([requireAuth, this.authenticated$]).pipe(
103
103
  // wait for auth not required or authenticated
@@ -110,9 +110,11 @@ export class Relay {
110
110
  multiplex(open, close, filter) {
111
111
  return this.socket.multiplex(open, close, filter);
112
112
  }
113
+ /** Send a message to the relay */
113
114
  next(message) {
114
115
  this.socket.next(message);
115
116
  }
117
+ /** Create a REQ observable that emits events | "EOSE" or errors */
116
118
  req(filters, id = nanoid()) {
117
119
  const request = this.socket
118
120
  .multiplex(() => (Array.isArray(filters) ? ["REQ", id, ...filters] : ["REQ", id, filters]), () => ["CLOSE", id], (message) => (message[0] === "EVENT" || message[0] === "CLOSE" || message[0] === "EOSE") && message[1] === id)
@@ -144,22 +146,24 @@ export class Relay {
144
146
  // Wait for auth if required and make sure to start the watch tower
145
147
  return this.waitForAuth(this.authRequiredForReq, merge(this.watchTower, request));
146
148
  }
147
- /** send an Event message */
149
+ /** send an Event message and always return an observable of PublishResponse that completes or errors */
148
150
  event(event, verb = "EVENT") {
149
- const base = this.socket
150
- .multiplex(() => [verb, event], () => void 0, (m) => m[0] === "OK" && m[1] === event.id)
151
- .pipe(
152
- // format OK message
153
- map((m) => ({ ok: m[2], message: m[3], from: this.url })));
151
+ const base = defer(() => {
152
+ // Send event when subscription starts
153
+ this.socket.next([verb, event]);
154
+ return this.socket.pipe(filter((m) => m[0] === "OK" && m[1] === event.id),
155
+ // format OK message
156
+ map((m) => ({ ok: m[2], message: m[3], from: this.url })));
157
+ });
154
158
  // Start the watch tower with the observable
155
159
  const withWatchTower = merge(this.watchTower, base);
156
- // A complete operators
160
+ // Add complete operators
157
161
  const observable = withWatchTower.pipe(
158
162
  // complete on first value
159
163
  take(1),
160
164
  // listen for OK auth-required
161
165
  tap(({ ok, message }) => {
162
- if (ok === false && message.startsWith("auth-required") && !this.authRequiredForPublish.value) {
166
+ if (ok === false && message?.startsWith("auth-required") && !this.authRequiredForPublish.value) {
163
167
  this.log("Auth required for publish");
164
168
  this.authRequiredForPublish.next(true);
165
169
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-relay",
3
- "version": "0.0.0-next-20250404075518",
3
+ "version": "0.0.0-next-20250404095409",
4
4
  "description": "nostr relay communication framework built on rxjs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -54,7 +54,7 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@noble/hashes": "^1.7.1",
57
- "applesauce-core": "0.0.0-next-20250404075518",
57
+ "applesauce-core": "0.0.0-next-20250404095409",
58
58
  "nanoid": "^5.0.9",
59
59
  "nostr-tools": "^2.10.4",
60
60
  "rxjs": "^7.8.1"