applesauce-relay 4.4.2 → 5.1.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/README.md CHANGED
@@ -22,7 +22,7 @@ npm install applesauce-relay
22
22
 
23
23
  ## Examples
24
24
 
25
- Read the [documentation](https://hzrd149.github.io/applesauce/overview/relays.html) for more detailed explanation of all methods
25
+ Read the [documentation](https://applesauce.build/overview/relays.html) for more detailed explanation of all methods
26
26
 
27
27
  ### Single Relay
28
28
 
package/dist/group.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import { type NostrEvent } from "nostr-tools";
1
+ import { IAsyncEventStoreActions, IEventStoreActions } from "applesauce-core/event-store";
2
+ import type { Filter, NostrEvent } from "applesauce-core/helpers";
2
3
  import { BehaviorSubject, MonoTypeOperatorFunction, Observable } from "rxjs";
3
- import { IAsyncEventStoreActions, IAsyncEventStoreRead, IEventStoreActions, IEventStoreRead } from "applesauce-core";
4
- import { type FilterWithAnd } from "applesauce-core/helpers";
5
4
  import { NegentropySyncOptions, type ReconcileFunction } from "./negentropy.js";
6
5
  import { SyncDirection } from "./relay.js";
7
- import { CountResponse, FilterInput, IGroup, IGroupRelayInput, IRelay, PublishOptions, PublishResponse, RequestOptions, SubscriptionOptions, SubscriptionResponse } from "./types.js";
6
+ import { CountResponse, FilterInput, IGroup, IGroupRelayInput, IRelay, NegentropyReadStore, NegentropySyncStore, PublishOptions, PublishResponse, RelayStatus, RequestOptions, SubscriptionOptions, SubscriptionResponse } from "./types.js";
8
7
  /** Options for negentropy sync on a group of relays */
9
8
  export type GroupNegentropySyncOptions = NegentropySyncOptions & {
10
9
  /** Whether to sync in parallel (default true) */
@@ -22,6 +21,8 @@ export type GroupRequestOptions = RequestOptions & {
22
21
  };
23
22
  export declare class RelayGroup implements IGroup {
24
23
  protected relays$: BehaviorSubject<IRelay[]> | Observable<IRelay[]>;
24
+ /** Observable of relay status for all relays in the group */
25
+ status$: Observable<Record<string, RelayStatus>>;
25
26
  get relays(): IRelay[];
26
27
  constructor(relays: IGroupRelayInput);
27
28
  /** Whether this group is controlled by an upstream observable */
@@ -47,7 +48,7 @@ export declare class RelayGroup implements IGroup {
47
48
  /** Send an event to all relays */
48
49
  event(event: NostrEvent): Observable<PublishResponse>;
49
50
  /** Negentropy sync events with the relays and an event store */
50
- negentropy(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, reconcile: ReconcileFunction, opts?: GroupNegentropySyncOptions): Promise<boolean>;
51
+ negentropy(store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: GroupNegentropySyncOptions): Promise<boolean>;
51
52
  /** Publish an event to all relays with retries ( default 3 retries ) */
52
53
  publish(event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse[]>;
53
54
  /** Request events from all relays and complete on EOSE */
@@ -55,7 +56,7 @@ export declare class RelayGroup implements IGroup {
55
56
  /** Open a subscription to all relays with retries ( default 3 retries ) */
56
57
  subscription(filters: FilterInput, opts?: GroupSubscriptionOptions): Observable<SubscriptionResponse>;
57
58
  /** Count events on all relays in the group */
58
- count(filters: FilterWithAnd | FilterWithAnd[], id?: string): Observable<Record<string, CountResponse>>;
59
+ count(filters: Filter | Filter[], id?: string): Observable<Record<string, CountResponse>>;
59
60
  /** Negentropy sync events with the relays and an event store */
60
- sync(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, direction?: SyncDirection): Observable<NostrEvent>;
61
+ sync(store: NegentropySyncStore | NostrEvent[], filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
61
62
  }
package/dist/group.js CHANGED
@@ -1,6 +1,7 @@
1
+ import { EventMemory } from "applesauce-core/event-store";
2
+ import { filterDuplicateEvents } from "applesauce-core/observable";
1
3
  import { nanoid } from "nanoid";
2
- import { BehaviorSubject, catchError, combineLatest, defaultIfEmpty, defer, endWith, filter, from, identity, ignoreElements, lastValueFrom, map, merge, of, scan, share, switchMap, take, takeWhile, toArray, } from "rxjs";
3
- import { EventMemory, filterDuplicateEvents, } from "applesauce-core";
4
+ import { BehaviorSubject, catchError, combineLatest, defaultIfEmpty, defer, endWith, filter, from, identity, ignoreElements, lastValueFrom, map, merge, of, scan, share, shareReplay, startWith, switchMap, take, takeWhile, toArray, } from "rxjs";
4
5
  import { completeOnEose } from "./operators/complete-on-eose.js";
5
6
  import { onlyEvents } from "./operators/only-events.js";
6
7
  import { reverseSwitchMap } from "./operators/reverse-switch-map.js";
@@ -10,6 +11,8 @@ function errorToPublishResponse(relay) {
10
11
  }
11
12
  export class RelayGroup {
12
13
  relays$ = new BehaviorSubject([]);
14
+ /** Observable of relay status for all relays in the group */
15
+ status$;
13
16
  get relays() {
14
17
  if (this.relays$ instanceof BehaviorSubject)
15
18
  return this.relays$.value;
@@ -17,6 +20,23 @@ export class RelayGroup {
17
20
  }
18
21
  constructor(relays) {
19
22
  this.relays$ = Array.isArray(relays) ? new BehaviorSubject(relays) : relays;
23
+ // Initialize status$ observable
24
+ this.status$ = this.relays$.pipe(switchMap((relays) => {
25
+ // If no relays, return empty record
26
+ if (relays.length === 0)
27
+ return of({});
28
+ // Merge all relay status streams
29
+ return merge(...relays.map((relay) => relay.status$)).pipe(
30
+ // Accumulate into a Record
31
+ scan((acc, status) => ({
32
+ ...acc,
33
+ [status.url]: status,
34
+ }), {}),
35
+ // Start with initial empty state
36
+ startWith({}));
37
+ }),
38
+ // Share the subscription
39
+ shareReplay(1));
20
40
  }
21
41
  /** Whether this group is controlled by an upstream observable */
22
42
  get controlled() {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./group.js";
2
2
  export * from "./liveness.js";
3
+ export * from "./management.js";
3
4
  export * from "./pool.js";
4
5
  export * from "./relay.js";
5
6
  export * from "./types.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./group.js";
2
2
  export * from "./liveness.js";
3
+ export * from "./management.js";
3
4
  export * from "./pool.js";
4
5
  export * from "./relay.js";
5
6
  export * from "./types.js";
@@ -1,6 +1,6 @@
1
1
  // (C) 2023 Doug Hoyte. MIT license
2
2
  // Modified by hzrd149 to be TypeScript and work without the window.cyrpto.subtle API
3
- import { sha256 } from "@noble/hashes/sha256";
3
+ import { sha256 } from "@noble/hashes/sha2";
4
4
  const PROTOCOL_VERSION = 0x61; // Version 1
5
5
  const ID_SIZE = 32;
6
6
  const FINGERPRINT_SIZE = 16;
@@ -0,0 +1,169 @@
1
+ import { logger } from "applesauce-core";
2
+ import { Observable } from "rxjs";
3
+ import { AuthSigner } from "./types.js";
4
+ import { Relay } from "./relay.js";
5
+ /** Base request structure for all NIP-86 requests */
6
+ export interface TRelayRequest<Method extends string, Params extends any[] = any[]> {
7
+ /** The method to call */
8
+ method: Method;
9
+ /** Parameters for the method */
10
+ params: Params;
11
+ }
12
+ /** Base response structure for all NIP-86 responses */
13
+ export type TRelayErrorResponse = {
14
+ /** Error message, non-null in case of error */
15
+ error: string;
16
+ result: null;
17
+ };
18
+ export type TRelaySuccessResponse<Result> = {
19
+ error: null;
20
+ /** Result object, null in case of error */
21
+ result: Result;
22
+ };
23
+ /** Merged relay management method and response types */
24
+ export type TRelayMethod<Method extends string = string, Params extends any[] = any[], Result extends any = any> = {
25
+ /** Method string */
26
+ method: Method;
27
+ /** Request type */
28
+ request: TRelayRequest<Method, Params>;
29
+ /** Response success type */
30
+ response: TRelaySuccessResponse<Result>;
31
+ /** Response error type */
32
+ error: TRelayErrorResponse;
33
+ };
34
+ export type SupportedMethodsMethod = TRelayMethod<"supportedmethods", [], string[]>;
35
+ export type BanPubkeyMethod = TRelayMethod<"banpubkey", [string, string?], true>;
36
+ export type ListBannedPubkeysMethod = TRelayMethod<"listbannedpubkeys", [], Array<{
37
+ pubkey: string;
38
+ reason?: string;
39
+ }>>;
40
+ export type AllowPubkeyMethod = TRelayMethod<"allowpubkey", [string, string?], true>;
41
+ export type ListAllowedPubkeysMethod = TRelayMethod<"listallowedpubkeys", [
42
+ ], Array<{
43
+ pubkey: string;
44
+ reason?: string;
45
+ }>>;
46
+ export type ListEventsNeedingModerationMethod = TRelayMethod<"listeventsneedingmoderation", [
47
+ ], Array<{
48
+ id: string;
49
+ reason?: string;
50
+ }>>;
51
+ export type AllowEventMethod = TRelayMethod<"allowevent", [string, string?], true>;
52
+ export type BanEventMethod = TRelayMethod<"banevent", [string, string?], true>;
53
+ export type ListBannedEventsMethod = TRelayMethod<"listbannedevents", [], Array<{
54
+ id: string;
55
+ reason?: string;
56
+ }>>;
57
+ export type ChangeRelayNameMethod = TRelayMethod<"changerelayname", [string], true>;
58
+ export type ChangeRelayDescriptionMethod = TRelayMethod<"changerelaydescription", [string], true>;
59
+ export type ChangeRelayIconMethod = TRelayMethod<"changerelayicon", [string], true>;
60
+ export type AllowKindMethod = TRelayMethod<"allowkind", [number], true>;
61
+ export type DisallowKindMethod = TRelayMethod<"disallowkind", [number], true>;
62
+ export type ListAllowedKindsMethod = TRelayMethod<"listallowedkinds", [], number[]>;
63
+ export type BlockIpMethod = TRelayMethod<"blockip", [string, string?], true>;
64
+ export type UnblockIpMethod = TRelayMethod<"unblockip", [string], true>;
65
+ export type ListBlockedIpsMethod = TRelayMethod<"listblockedips", [], Array<{
66
+ ip: string;
67
+ reason?: string;
68
+ }>>;
69
+ /** Union type for all relay management method definitions */
70
+ export type RelayManagementMethods = SupportedMethodsMethod | BanPubkeyMethod | ListBannedPubkeysMethod | AllowPubkeyMethod | ListAllowedPubkeysMethod | ListEventsNeedingModerationMethod | AllowEventMethod | BanEventMethod | ListBannedEventsMethod | ChangeRelayNameMethod | ChangeRelayDescriptionMethod | ChangeRelayIconMethod | AllowKindMethod | DisallowKindMethod | ListAllowedKindsMethod | BlockIpMethod | UnblockIpMethod | ListBlockedIpsMethod;
71
+ /** Custom error for relay management operations */
72
+ export declare class RelayManagementError extends Error {
73
+ readonly method?: string | undefined;
74
+ readonly statusCode?: number | undefined;
75
+ constructor(message: string, method?: string | undefined, statusCode?: number | undefined);
76
+ }
77
+ /** RelayManagement class for NIP-86 relay management API */
78
+ export declare class RelayManagement {
79
+ #private;
80
+ readonly relay: Relay;
81
+ readonly signer: AuthSigner;
82
+ protected log: typeof logger;
83
+ protected httpUrl: string;
84
+ constructor(relay: Relay, signer: AuthSigner);
85
+ /**
86
+ * Core request method that handles all RPC calls with NIP-98 authentication
87
+ */
88
+ request<Method extends RelayManagementMethods>(method: Method["method"], params: Method["request"]["params"]): Promise<Method["response"]["result"]>;
89
+ /** Get list of supported methods */
90
+ supportedMethods(): Promise<string[]>;
91
+ /** Ban a pubkey */
92
+ banPubkey(pubkey: string, reason?: string): Promise<true>;
93
+ /** List all banned pubkeys */
94
+ listBannedPubkeys(): Promise<Array<{
95
+ pubkey: string;
96
+ reason?: string;
97
+ }>>;
98
+ /** Allow a pubkey */
99
+ allowPubkey(pubkey: string, reason?: string): Promise<true>;
100
+ /** List all allowed pubkeys */
101
+ listAllowedPubkeys(): Promise<Array<{
102
+ pubkey: string;
103
+ reason?: string;
104
+ }>>;
105
+ /** List events needing moderation */
106
+ listEventsNeedingModeration(): Promise<Array<{
107
+ id: string;
108
+ reason?: string;
109
+ }>>;
110
+ /** Allow an event */
111
+ allowEvent(eventId: string, reason?: string): Promise<true>;
112
+ /** Ban an event */
113
+ banEvent(eventId: string, reason?: string): Promise<true>;
114
+ /** List all banned events */
115
+ listBannedEvents(): Promise<Array<{
116
+ id: string;
117
+ reason?: string;
118
+ }>>;
119
+ /** Change relay name */
120
+ changeRelayName(name: string): Promise<true>;
121
+ /** Change relay description */
122
+ changeRelayDescription(description: string): Promise<true>;
123
+ /** Change relay icon */
124
+ changeRelayIcon(iconUrl: string): Promise<true>;
125
+ /** Allow a kind */
126
+ allowKind(kind: number): Promise<true>;
127
+ /** Disallow a kind */
128
+ disallowKind(kind: number): Promise<true>;
129
+ /** List all allowed kinds */
130
+ listAllowedKinds(): Promise<number[]>;
131
+ /** Block an IP address */
132
+ blockIp(ip: string, reason?: string): Promise<true>;
133
+ /** Unblock an IP address */
134
+ unblockIp(ip: string): Promise<true>;
135
+ /** List all blocked IPs */
136
+ listBlockedIps(): Promise<Array<{
137
+ ip: string;
138
+ reason?: string;
139
+ }>>;
140
+ /** Observable that emits supported methods when subscribed */
141
+ supportMethods$: Observable<string[]>;
142
+ /** Observable that emits banned pubkeys when subscribed */
143
+ bannedPubkeys$: Observable<Array<{
144
+ pubkey: string;
145
+ reason?: string;
146
+ }>>;
147
+ /** Observable that emits allowed pubkeys when subscribed */
148
+ allowedPubkeys$: Observable<Array<{
149
+ pubkey: string;
150
+ reason?: string;
151
+ }>>;
152
+ /** Observable that emits events needing moderation when subscribed */
153
+ eventsNeedingModeration$: Observable<Array<{
154
+ id: string;
155
+ reason?: string;
156
+ }>>;
157
+ /** Observable that emits banned events when subscribed */
158
+ bannedEvents$: Observable<Array<{
159
+ id: string;
160
+ reason?: string;
161
+ }>>;
162
+ /** Observable that emits allowed kinds when subscribed */
163
+ allowedKinds$: Observable<number[]>;
164
+ /** Observable that emits blocked IPs when subscribed */
165
+ blockedIps$: Observable<Array<{
166
+ ip: string;
167
+ reason?: string;
168
+ }>>;
169
+ }
@@ -0,0 +1,202 @@
1
+ import { logger } from "applesauce-core";
2
+ import { ensureHttpURL } from "applesauce-core/helpers/url";
3
+ import { getToken } from "nostr-tools/nip98";
4
+ import { BehaviorSubject, from, shareReplay, throwError } from "rxjs";
5
+ import { catchError, switchMap } from "rxjs/operators";
6
+ /** Custom error for relay management operations */
7
+ export class RelayManagementError extends Error {
8
+ method;
9
+ statusCode;
10
+ constructor(message, method, statusCode) {
11
+ super(message);
12
+ this.method = method;
13
+ this.statusCode = statusCode;
14
+ this.name = "RelayManagementError";
15
+ }
16
+ }
17
+ /** RelayManagement class for NIP-86 relay management API */
18
+ export class RelayManagement {
19
+ relay;
20
+ signer;
21
+ log = logger.extend("RelayManagement");
22
+ httpUrl;
23
+ // Internal refresh triggers for observables
24
+ #refreshSupportMethods$ = new BehaviorSubject(undefined);
25
+ #refreshBannedPubkeys$ = new BehaviorSubject(undefined);
26
+ #refreshAllowedPubkeys$ = new BehaviorSubject(undefined);
27
+ #refreshEventsNeedingModeration$ = new BehaviorSubject(undefined);
28
+ #refreshBannedEvents$ = new BehaviorSubject(undefined);
29
+ #refreshAllowedKinds$ = new BehaviorSubject(undefined);
30
+ #refreshBlockedIps$ = new BehaviorSubject(undefined);
31
+ constructor(relay, signer) {
32
+ this.relay = relay;
33
+ this.signer = signer;
34
+ this.log = this.log.extend(relay.url);
35
+ this.httpUrl = ensureHttpURL(relay.url);
36
+ }
37
+ /**
38
+ * Core request method that handles all RPC calls with NIP-98 authentication
39
+ */
40
+ async request(method, params) {
41
+ const requestBody = {
42
+ method,
43
+ params,
44
+ };
45
+ const requestBodyString = JSON.stringify(requestBody);
46
+ // Generate NIP-98 token using getToken from nostr-tools
47
+ const authHeader = await getToken(this.httpUrl, "POST", (event) => this.signer.signEvent(event), true, // includeAuthorizationScheme - returns "Nostr <token>" format
48
+ requestBody);
49
+ // Make the HTTP request
50
+ const response = await fetch(this.httpUrl, {
51
+ method: "POST",
52
+ headers: {
53
+ "Content-Type": "application/nostr+json+rpc",
54
+ Authorization: authHeader,
55
+ },
56
+ body: requestBodyString,
57
+ });
58
+ // Handle HTTP errors
59
+ if (!response.ok) {
60
+ if (response.status === 401) {
61
+ throw new RelayManagementError("Unauthorized: Invalid or missing NIP-98 authentication", method, response.status);
62
+ }
63
+ const errorText = await response.text().catch(() => "Unknown error");
64
+ throw new RelayManagementError(`HTTP ${response.status}: ${errorText}`, method, response.status);
65
+ }
66
+ // Parse the response
67
+ const data = await response.json();
68
+ // Handle RPC errors
69
+ if (data.error) {
70
+ throw new RelayManagementError(`RPC error: ${data.error}`, method, response.status);
71
+ }
72
+ return data.result;
73
+ }
74
+ // Convenience methods for each RPC call
75
+ /** Get list of supported methods */
76
+ async supportedMethods() {
77
+ return this.request("supportedmethods", []);
78
+ }
79
+ /** Ban a pubkey */
80
+ async banPubkey(pubkey, reason) {
81
+ const result = await this.request("banpubkey", reason ? [pubkey, reason] : [pubkey]);
82
+ this.#refreshBannedPubkeys$.next();
83
+ return result;
84
+ }
85
+ /** List all banned pubkeys */
86
+ async listBannedPubkeys() {
87
+ return this.request("listbannedpubkeys", []);
88
+ }
89
+ /** Allow a pubkey */
90
+ async allowPubkey(pubkey, reason) {
91
+ const result = await this.request("allowpubkey", reason ? [pubkey, reason] : [pubkey]);
92
+ this.#refreshAllowedPubkeys$.next();
93
+ this.#refreshBannedPubkeys$.next(); // Also refresh banned list in case it was unbanned
94
+ return result;
95
+ }
96
+ /** List all allowed pubkeys */
97
+ async listAllowedPubkeys() {
98
+ return this.request("listallowedpubkeys", []);
99
+ }
100
+ /** List events needing moderation */
101
+ async listEventsNeedingModeration() {
102
+ return this.request("listeventsneedingmoderation", []);
103
+ }
104
+ /** Allow an event */
105
+ async allowEvent(eventId, reason) {
106
+ const result = await this.request("allowevent", reason ? [eventId, reason] : [eventId]);
107
+ this.#refreshBannedEvents$.next(); // Also refresh banned list in case it was unbanned
108
+ this.#refreshEventsNeedingModeration$.next();
109
+ return result;
110
+ }
111
+ /** Ban an event */
112
+ async banEvent(eventId, reason) {
113
+ const result = await this.request("banevent", reason ? [eventId, reason] : [eventId]);
114
+ this.#refreshBannedEvents$.next();
115
+ this.#refreshEventsNeedingModeration$.next();
116
+ return result;
117
+ }
118
+ /** List all banned events */
119
+ async listBannedEvents() {
120
+ return this.request("listbannedevents", []);
121
+ }
122
+ /** Change relay name */
123
+ async changeRelayName(name) {
124
+ return this.request("changerelayname", [name]);
125
+ }
126
+ /** Change relay description */
127
+ async changeRelayDescription(description) {
128
+ return this.request("changerelaydescription", [description]);
129
+ }
130
+ /** Change relay icon */
131
+ async changeRelayIcon(iconUrl) {
132
+ return this.request("changerelayicon", [iconUrl]);
133
+ }
134
+ /** Allow a kind */
135
+ async allowKind(kind) {
136
+ const result = await this.request("allowkind", [kind]);
137
+ this.#refreshAllowedKinds$.next();
138
+ return result;
139
+ }
140
+ /** Disallow a kind */
141
+ async disallowKind(kind) {
142
+ const result = await this.request("disallowkind", [kind]);
143
+ this.#refreshAllowedKinds$.next();
144
+ return result;
145
+ }
146
+ /** List all allowed kinds */
147
+ async listAllowedKinds() {
148
+ return this.request("listallowedkinds", []);
149
+ }
150
+ /** Block an IP address */
151
+ async blockIp(ip, reason) {
152
+ const result = await this.request("blockip", reason ? [ip, reason] : [ip]);
153
+ this.#refreshBlockedIps$.next();
154
+ return result;
155
+ }
156
+ /** Unblock an IP address */
157
+ async unblockIp(ip) {
158
+ const result = await this.request("unblockip", [ip]);
159
+ this.#refreshBlockedIps$.next();
160
+ return result;
161
+ }
162
+ /** List all blocked IPs */
163
+ async listBlockedIps() {
164
+ return this.request("listblockedips", []);
165
+ }
166
+ // Reactive observables for list methods
167
+ /** Observable that emits supported methods when subscribed */
168
+ supportMethods$ = this.#refreshSupportMethods$.pipe(switchMap(() => from(this.supportedMethods())), catchError((error) => {
169
+ this.log("Error fetching supported methods:", error);
170
+ return throwError(() => error);
171
+ }), shareReplay(1));
172
+ /** Observable that emits banned pubkeys when subscribed */
173
+ bannedPubkeys$ = this.#refreshBannedPubkeys$.pipe(switchMap(() => from(this.listBannedPubkeys())), catchError((error) => {
174
+ this.log("Error fetching banned pubkeys:", error);
175
+ return throwError(() => error);
176
+ }), shareReplay(1));
177
+ /** Observable that emits allowed pubkeys when subscribed */
178
+ allowedPubkeys$ = this.#refreshAllowedPubkeys$.pipe(switchMap(() => from(this.listAllowedPubkeys())), catchError((error) => {
179
+ this.log("Error fetching allowed pubkeys:", error);
180
+ return throwError(() => error);
181
+ }), shareReplay(1));
182
+ /** Observable that emits events needing moderation when subscribed */
183
+ eventsNeedingModeration$ = this.#refreshEventsNeedingModeration$.pipe(switchMap(() => from(this.listEventsNeedingModeration())), catchError((error) => {
184
+ this.log("Error fetching events needing moderation:", error);
185
+ return throwError(() => error);
186
+ }), shareReplay(1));
187
+ /** Observable that emits banned events when subscribed */
188
+ bannedEvents$ = this.#refreshBannedEvents$.pipe(switchMap(() => from(this.listBannedEvents())), catchError((error) => {
189
+ this.log("Error fetching banned events:", error);
190
+ return throwError(() => error);
191
+ }), shareReplay(1));
192
+ /** Observable that emits allowed kinds when subscribed */
193
+ allowedKinds$ = this.#refreshAllowedKinds$.pipe(switchMap(() => from(this.listAllowedKinds())), catchError((error) => {
194
+ this.log("Error fetching allowed kinds:", error);
195
+ return throwError(() => error);
196
+ }), shareReplay(1));
197
+ /** Observable that emits blocked IPs when subscribed */
198
+ blockedIps$ = this.#refreshBlockedIps$.pipe(switchMap(() => from(this.listBlockedIps())), catchError((error) => {
199
+ this.log("Error fetching blocked IPs:", error);
200
+ return throwError(() => error);
201
+ }), shareReplay(1));
202
+ }
@@ -1,5 +1,5 @@
1
1
  import { IAsyncEventStoreRead, IEventStoreRead } from "applesauce-core";
2
- import { type FilterWithAnd } from "applesauce-core/helpers";
2
+ import { type Filter } from "applesauce-core/helpers";
3
3
  import { NegentropyStorageVector } from "./lib/negentropy.js";
4
4
  import { MultiplexWebSocket } from "./types.js";
5
5
  /**
@@ -15,7 +15,7 @@ export type NegentropySyncOptions = {
15
15
  signal?: AbortSignal;
16
16
  };
17
17
  /** Creates a NegentropyStorageVector from an event store and filter */
18
- export declare function buildStorageFromFilter(store: IEventStoreRead | IAsyncEventStoreRead, filter: FilterWithAnd): Promise<NegentropyStorageVector>;
18
+ export declare function buildStorageFromFilter(store: IEventStoreRead | IAsyncEventStoreRead, filter: Filter): Promise<NegentropyStorageVector>;
19
19
  /** Creates a NegentropyStorageVector from an array of items */
20
20
  export declare function buildStorageVector(items: {
21
21
  id: string;
@@ -28,4 +28,4 @@ export declare function buildStorageVector(items: {
28
28
  */
29
29
  export declare function negentropySync(storage: NegentropyStorageVector, socket: MultiplexWebSocket & {
30
30
  next: (msg: any) => void;
31
- }, filter: FilterWithAnd, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
31
+ }, filter: Filter, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
@@ -1,5 +1,5 @@
1
1
  import { MonoTypeOperatorFunction, OperatorFunction } from "rxjs";
2
- import { NostrEvent } from "nostr-tools";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
3
  import { SubscriptionResponse } from "../types.js";
4
4
  export declare function completeOnEose(includeEose: true): MonoTypeOperatorFunction<SubscriptionResponse>;
5
5
  export declare function completeOnEose(): OperatorFunction<SubscriptionResponse, NostrEvent>;
@@ -1,5 +1,5 @@
1
1
  import { OperatorFunction } from "rxjs";
2
- import { NostrEvent } from "nostr-tools";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
3
  import { SubscriptionResponse } from "../types.js";
4
4
  /** Filter subscription responses and only return the events */
5
5
  export declare function onlyEvents(): OperatorFunction<SubscriptionResponse, NostrEvent>;
@@ -1,6 +1,6 @@
1
1
  import { OperatorFunction } from "rxjs";
2
2
  import { IEventStore } from "applesauce-core";
3
- import { NostrEvent } from "nostr-tools";
3
+ import { NostrEvent } from "applesauce-core/helpers/event";
4
4
  import { SubscriptionResponse } from "../types.js";
5
5
  /**
6
6
  * Adds all events to event store and returns a deduplicated timeline when EOSE is received
package/dist/pool.d.ts CHANGED
@@ -1,15 +1,19 @@
1
- import type { IAsyncEventStoreRead, IEventStoreRead } from "applesauce-core";
2
- import { FilterMap, OutboxMap, type FilterWithAnd } from "applesauce-core/helpers";
3
- import { Filter, type NostrEvent } from "nostr-tools";
1
+ import type { NostrEvent } from "applesauce-core/helpers/event";
2
+ import { Filter } from "applesauce-core/helpers/filter";
3
+ import { FilterMap, OutboxMap } from "applesauce-core/helpers/relay-selection";
4
4
  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 { CountResponse, FilterInput, IPool, IPoolRelayInput, IRelay, PublishResponse, SubscriptionResponse } from "./types.js";
8
+ import type { CountResponse, FilterInput, IPool, IPoolRelayInput, IRelay, NegentropyReadStore, NegentropySyncStore, PublishResponse, RelayStatus, SubscriptionResponse } from "./types.js";
9
9
  export declare class RelayPool implements IPool {
10
10
  options?: RelayOptions | undefined;
11
11
  relays$: BehaviorSubject<Map<string, Relay>>;
12
12
  get relays(): Map<string, Relay>;
13
+ /** Observable of relay status for all relays in the pool */
14
+ status$: Observable<Record<string, RelayStatus>>;
15
+ /** Whether to ignore relays that are ready=false */
16
+ ignoreOffline: boolean;
13
17
  /** A signal when a relay is added */
14
18
  add$: Subject<IRelay>;
15
19
  /** A signal when a relay is removed */
@@ -18,7 +22,7 @@ export declare class RelayPool implements IPool {
18
22
  /** Get or create a new relay connection */
19
23
  relay(url: string): Relay;
20
24
  /** Create a group of relays */
21
- group(relays: IPoolRelayInput): RelayGroup;
25
+ group(relays: IPoolRelayInput, ignoreOffline?: boolean): RelayGroup;
22
26
  /** Removes a relay from the pool and defaults to closing the connection */
23
27
  remove(relay: string | IRelay, close?: boolean): void;
24
28
  /** Make a REQ to multiple relays that does not deduplicate events */
@@ -26,7 +30,7 @@ export declare class RelayPool implements IPool {
26
30
  /** Send an EVENT message to multiple relays */
27
31
  event(relays: IPoolRelayInput, event: NostrEvent): Observable<PublishResponse>;
28
32
  /** Negentropy sync event ids with the relays and an event store */
29
- negentropy(relays: IPoolRelayInput, store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
33
+ negentropy(relays: IPoolRelayInput, store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
30
34
  /** Publish an event to multiple relays */
31
35
  publish(relays: IPoolRelayInput, event: Parameters<RelayGroup["publish"]>[0], opts?: Parameters<RelayGroup["publish"]>[1]): Promise<PublishResponse[]>;
32
36
  /** Request events from multiple relays */
@@ -38,7 +42,7 @@ export declare class RelayPool implements IPool {
38
42
  /** Open a subscription for an {@link OutboxMap} and filter */
39
43
  outboxSubscription(outboxes: OutboxMap | Observable<OutboxMap>, filter: Omit<Filter, "authors">, options?: Parameters<RelayGroup["subscription"]>[1]): Observable<SubscriptionResponse>;
40
44
  /** Count events on multiple relays */
41
- count(relays: IPoolRelayInput, filters: FilterWithAnd | FilterWithAnd[], id?: string): Observable<Record<string, CountResponse>>;
45
+ count(relays: IPoolRelayInput, filters: Filter | Filter[], id?: string): Observable<Record<string, CountResponse>>;
42
46
  /** Negentropy sync events with the relays and an event store */
43
- sync(relays: IPoolRelayInput, store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, direction?: SyncDirection): Observable<NostrEvent>;
47
+ sync(relays: IPoolRelayInput, store: NegentropySyncStore | NostrEvent[], filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
44
48
  }