applesauce-relay 0.0.0-next-20250828145754 → 0.0.0-next-20250912090040

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 CHANGED
@@ -1,19 +1,27 @@
1
1
  import { type NostrEvent } from "nostr-tools";
2
2
  import { Observable } from "rxjs";
3
+ import { IEventStoreActions } from "applesauce-core";
3
4
  import { FilterInput, IGroup, IRelay, PublishOptions, PublishResponse, RequestOptions, SubscriptionOptions, SubscriptionResponse } from "./types.js";
4
5
  export declare class RelayGroup implements IGroup {
5
6
  relays: IRelay[];
6
7
  constructor(relays: IRelay[]);
7
8
  /** Takes an array of observables and only emits EOSE when all observables have emitted EOSE */
8
- protected mergeEOSE(...requests: Observable<SubscriptionResponse>[]): Observable<import("nostr-tools").Event | "EOSE">;
9
- /** Make a request to all relays */
9
+ protected mergeEOSE(requests: Observable<SubscriptionResponse>[], eventStore?: IEventStoreActions): Observable<import("nostr-tools").Event | "EOSE">;
10
+ /**
11
+ * Make a request to all relays
12
+ * @note This does not deduplicate events
13
+ */
10
14
  req(filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
11
15
  /** Send an event to all relays */
12
16
  event(event: NostrEvent): Observable<PublishResponse>;
13
17
  /** Publish an event to all relays with retries ( default 3 retries ) */
14
18
  publish(event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse[]>;
15
19
  /** Request events from all relays with retries ( default 3 retries ) */
16
- request(filters: FilterInput, opts?: RequestOptions): Observable<NostrEvent>;
20
+ request(filters: FilterInput, opts?: RequestOptions & {
21
+ eventStore?: IEventStoreActions;
22
+ }): Observable<NostrEvent>;
17
23
  /** Open a subscription to all relays with retries ( default 3 retries ) */
18
- subscription(filters: FilterInput, opts?: SubscriptionOptions): Observable<SubscriptionResponse>;
24
+ subscription(filters: FilterInput, opts?: SubscriptionOptions & {
25
+ eventStore?: IEventStoreActions;
26
+ }): Observable<SubscriptionResponse>;
19
27
  }
package/dist/group.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { nanoid } from "nanoid";
2
- import { catchError, EMPTY, endWith, ignoreElements, merge, of } from "rxjs";
2
+ import { catchError, EMPTY, endWith, identity, ignoreElements, merge, of } from "rxjs";
3
+ import { filterDuplicateEvents } from "applesauce-core";
3
4
  import { completeOnEose } from "./operators/complete-on-eose.js";
4
5
  import { onlyEvents } from "./operators/only-events.js";
5
6
  export class RelayGroup {
@@ -8,9 +9,13 @@ export class RelayGroup {
8
9
  this.relays = relays;
9
10
  }
10
11
  /** Takes an array of observables and only emits EOSE when all observables have emitted EOSE */
11
- mergeEOSE(...requests) {
12
+ mergeEOSE(requests, eventStore) {
12
13
  // Create stream of events only
13
- const events = merge(...requests).pipe(onlyEvents());
14
+ const events = merge(...requests).pipe(
15
+ // Ignore non event responses
16
+ onlyEvents(),
17
+ // If an event store is provided, filter duplicate events
18
+ eventStore ? filterDuplicateEvents(eventStore) : identity);
14
19
  // Create stream that emits EOSE when all relays have sent EOSE
15
20
  const eose = merge(
16
21
  // Create a new map of requests that only emits EOSE
@@ -19,13 +24,16 @@ export class RelayGroup {
19
24
  endWith("EOSE"));
20
25
  return merge(events, eose);
21
26
  }
22
- /** Make a request to all relays */
27
+ /**
28
+ * Make a request to all relays
29
+ * @note This does not deduplicate events
30
+ */
23
31
  req(filters, id = nanoid(8)) {
24
32
  const requests = this.relays.map((relay) => relay.req(filters, id).pipe(
25
33
  // Ignore connection errors
26
34
  catchError(() => of("EOSE"))));
27
35
  // Merge events and the single EOSE stream
28
- return this.mergeEOSE(...requests);
36
+ return this.mergeEOSE(requests);
29
37
  }
30
38
  /** Send an event to all relays */
31
39
  event(event) {
@@ -43,12 +51,16 @@ export class RelayGroup {
43
51
  request(filters, opts) {
44
52
  return merge(...this.relays.map((relay) => relay.request(filters, opts).pipe(
45
53
  // Ignore individual connection errors
46
- catchError(() => EMPTY))));
54
+ catchError(() => EMPTY)))).pipe(
55
+ // If an event store is provided, filter duplicate events
56
+ opts?.eventStore ? filterDuplicateEvents(opts.eventStore) : identity);
47
57
  }
48
58
  /** Open a subscription to all relays with retries ( default 3 retries ) */
49
59
  subscription(filters, opts) {
50
- return this.mergeEOSE(...this.relays.map((relay) => relay.subscription(filters, opts).pipe(
60
+ return this.mergeEOSE(this.relays.map((relay) => relay.subscription(filters, opts).pipe(
51
61
  // Ignore individual connection errors
52
- catchError(() => EMPTY))));
62
+ catchError(() => EMPTY))),
63
+ // Pass event store so that duplicate events are removed
64
+ opts?.eventStore);
53
65
  }
54
66
  }
package/dist/pool.d.ts CHANGED
@@ -2,7 +2,7 @@ import { type NostrEvent } from "nostr-tools";
2
2
  import { BehaviorSubject, Observable } from "rxjs";
3
3
  import { RelayGroup } from "./group.js";
4
4
  import { Relay, RelayOptions } from "./relay.js";
5
- import { IPool, PublishResponse, PublishOptions, RequestOptions, SubscriptionOptions, SubscriptionResponse, FilterInput, IRelay } from "./types.js";
5
+ import { FilterInput, IPool, IRelay, PublishResponse, SubscriptionResponse } from "./types.js";
6
6
  export declare class RelayPool implements IPool {
7
7
  options?: RelayOptions | undefined;
8
8
  groups$: BehaviorSubject<Map<string, RelayGroup>>;
@@ -24,9 +24,9 @@ export declare class RelayPool implements IPool {
24
24
  /** Send an EVENT message to multiple relays */
25
25
  event(relays: string[], event: NostrEvent): Observable<PublishResponse>;
26
26
  /** Publish an event to multiple relays */
27
- publish(relays: string[], event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse[]>;
27
+ publish(relays: string[], event: Parameters<RelayGroup["publish"]>[0], opts?: Parameters<RelayGroup["publish"]>[1]): Promise<PublishResponse[]>;
28
28
  /** Request events from multiple relays */
29
- request(relays: string[], filters: FilterInput, opts?: RequestOptions): Observable<NostrEvent>;
29
+ request(relays: string[], filters: Parameters<RelayGroup["request"]>[0], opts?: Parameters<RelayGroup["request"]>[1]): Observable<NostrEvent>;
30
30
  /** Open a subscription to multiple relays */
31
- subscription(relays: string[], filters: FilterInput, opts?: SubscriptionOptions): Observable<SubscriptionResponse>;
31
+ subscription(relays: string[], filters: Parameters<RelayGroup["subscription"]>[0], opts?: Parameters<RelayGroup["subscription"]>[1]): Observable<SubscriptionResponse>;
32
32
  }
package/dist/pool.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { BehaviorSubject } from "rxjs";
2
+ import { normalizeURL } from "applesauce-core/helpers";
2
3
  import { RelayGroup } from "./group.js";
3
4
  import { Relay } from "./relay.js";
4
- import { normalizeURL } from "applesauce-core/helpers";
5
5
  export class RelayPool {
6
6
  options;
7
7
  groups$ = new BehaviorSubject(new Map());
package/dist/relay.d.ts CHANGED
@@ -109,6 +109,10 @@ export declare class Relay implements IRelay {
109
109
  publish(event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse>;
110
110
  /** Force close the connection */
111
111
  close(): void;
112
+ /** An async method that returns the NIP-11 information document for the relay */
113
+ getInformation(): Promise<RelayInformation | null>;
114
+ /** An async method that returns the NIP-11 limitations for the relay */
115
+ getLimitations(): Promise<RelayInformation["limitation"] | null>;
112
116
  /** Static method to fetch the NIP-11 information document for a relay */
113
117
  static fetchInformationDocument(url: string): Observable<RelayInformation | null>;
114
118
  /** Static method to create a reconnection method for each relay */
package/dist/relay.js CHANGED
@@ -3,7 +3,7 @@ import { ensureHttpURL } from "applesauce-core/helpers";
3
3
  import { simpleTimeout } from "applesauce-core/observable";
4
4
  import { nanoid } from "nanoid";
5
5
  import { nip42 } from "nostr-tools";
6
- import { BehaviorSubject, catchError, combineLatest, defer, endWith, filter, finalize, from, identity, ignoreElements, isObservable, lastValueFrom, map, merge, mergeMap, mergeWith, NEVER, of, repeat, retry, scan, share, shareReplay, Subject, switchMap, take, takeUntil, tap, throwError, timeout, timer, } from "rxjs";
6
+ import { BehaviorSubject, catchError, combineLatest, defer, endWith, filter, finalize, firstValueFrom, from, identity, ignoreElements, isObservable, lastValueFrom, map, merge, mergeMap, mergeWith, NEVER, of, repeat, retry, scan, share, shareReplay, Subject, switchMap, take, takeUntil, tap, throwError, timeout, timer, } from "rxjs";
7
7
  import { webSocket } from "rxjs/webSocket";
8
8
  import { completeOnEose } from "./operators/complete-on-eose.js";
9
9
  import { markFromRelay } from "./operators/mark-from-relay.js";
@@ -145,10 +145,10 @@ export class Relay {
145
145
  }).pipe(
146
146
  // if the fetch fails, return null
147
147
  catchError(() => of(null)),
148
- // cache the result
149
- shareReplay(1),
150
148
  // update the internal state
151
- tap((info) => (this._nip11 = info)));
149
+ tap((info) => (this._nip11 = info)),
150
+ // cache the result
151
+ shareReplay(1));
152
152
  this.limitations$ = this.information$.pipe(map((info) => info?.limitation));
153
153
  // Create observables that track if auth is required for REQ or EVENT
154
154
  this.authRequiredForRead$ = this.receivedAuthRequiredForReq.pipe(tap((required) => required && this.log("Auth required for REQ")), shareReplay(1));
@@ -417,6 +417,14 @@ export class Relay {
417
417
  close() {
418
418
  this.socket.unsubscribe();
419
419
  }
420
+ /** An async method that returns the NIP-11 information document for the relay */
421
+ async getInformation() {
422
+ return firstValueFrom(this.information$);
423
+ }
424
+ /** An async method that returns the NIP-11 limitations for the relay */
425
+ async getLimitations() {
426
+ return firstValueFrom(this.limitations$);
427
+ }
420
428
  /** Static method to fetch the NIP-11 information document for a relay */
421
429
  static fetchInformationDocument(url) {
422
430
  return from(fetch(ensureHttpURL(url), { headers: { Accept: "application/nostr+json" } }).then((res) => res.json())).pipe(
package/dist/types.d.ts CHANGED
@@ -103,9 +103,9 @@ export interface IPool {
103
103
  /** Send an EVENT message */
104
104
  event(relays: string[], event: NostrEvent): Observable<PublishResponse>;
105
105
  /** Send an EVENT message to relays with retries */
106
- publish(relays: string[], event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse[]>;
106
+ publish(relays: string[], event: Parameters<IGroup["publish"]>[0], opts?: Parameters<IGroup["publish"]>[1]): Promise<PublishResponse[]>;
107
107
  /** Send a REQ message to relays with retries */
108
- request(relays: string[], filters: FilterInput, opts?: RequestOptions): Observable<NostrEvent>;
108
+ request(relays: string[], filters: Parameters<IGroup["request"]>[0], opts?: Parameters<IGroup["request"]>[1]): Observable<NostrEvent>;
109
109
  /** Open a subscription to relays with retries */
110
- subscription(relays: string[], filters: FilterInput, opts?: SubscriptionOptions): Observable<SubscriptionResponse>;
110
+ subscription(relays: string[], filters: Parameters<IGroup["subscription"]>[0], opts?: Parameters<IGroup["subscription"]>[1]): Observable<SubscriptionResponse>;
111
111
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-relay",
3
- "version": "0.0.0-next-20250828145754",
3
+ "version": "0.0.0-next-20250912090040",
4
4
  "description": "nostr relay communication framework built on rxjs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -54,17 +54,17 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@noble/hashes": "^1.7.1",
57
- "applesauce-core": "0.0.0-next-20250828145754",
57
+ "applesauce-core": "0.0.0-next-20250912090040",
58
58
  "nanoid": "^5.0.9",
59
59
  "nostr-tools": "~2.15",
60
60
  "rxjs": "^7.8.1"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@hirez_io/observer-spy": "^2.2.0",
64
- "applesauce-signers": "0.0.0-next-20250828145754",
64
+ "applesauce-signers": "^3.1.0",
65
65
  "@vitest/expect": "^3.1.1",
66
66
  "typescript": "^5.7.3",
67
- "vitest": "^3.2.3",
67
+ "vitest": "^3.2.4",
68
68
  "vitest-websocket-mock": "^0.5.0"
69
69
  },
70
70
  "funding": {