applesauce-relay 4.0.0 → 4.2.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/dist/pool.js CHANGED
@@ -1,13 +1,9 @@
1
- import { normalizeURL } from "applesauce-core/helpers";
2
- import { BehaviorSubject, Subject } from "rxjs";
1
+ import { createFilterMap, isFilterEqual, normalizeURL } from "applesauce-core/helpers";
2
+ import { BehaviorSubject, distinctUntilChanged, isObservable, map, of, Subject } from "rxjs";
3
3
  import { RelayGroup } from "./group.js";
4
4
  import { Relay } from "./relay.js";
5
5
  export class RelayPool {
6
6
  options;
7
- groups$ = new BehaviorSubject(new Map());
8
- get groups() {
9
- return this.groups$.value;
10
- }
11
7
  relays$ = new BehaviorSubject(new Map());
12
8
  get relays() {
13
9
  return this.relays$.value;
@@ -16,34 +12,13 @@ export class RelayPool {
16
12
  add$ = new Subject();
17
13
  /** A signal when a relay is removed */
18
14
  remove$ = new Subject();
19
- /** An array of relays to never connect to */
20
- blacklist = new Set();
21
15
  constructor(options) {
22
16
  this.options = options;
23
- // Listen for relays being added and removed to emit connect / disconnect signals
24
- // const listeners = new Map<IRelay, Subscription>();
25
- // this.add$.subscribe((relay) =>
26
- // listeners.set(
27
- // relay,
28
- // relay.connected$.subscribe((conn) => (conn ? this.connect$.next(relay) : this.disconnect$.next(relay))),
29
- // ),
30
- // );
31
- // this.remove$.subscribe((relay) => {
32
- // const listener = listeners.get(relay);
33
- // if (listener) listener.unsubscribe();
34
- // listeners.delete(relay);
35
- // });
36
- }
37
- filterBlacklist(urls) {
38
- return urls.filter((url) => !this.blacklist.has(url));
39
17
  }
40
18
  /** Get or create a new relay connection */
41
19
  relay(url) {
42
20
  // Normalize the url
43
21
  url = normalizeURL(url);
44
- // Check if the url is blacklisted
45
- if (this.blacklist.has(url))
46
- throw new Error("Relay is on blacklist");
47
22
  // Check if the relay already exists
48
23
  let relay = this.relays.get(url);
49
24
  if (relay)
@@ -57,17 +32,9 @@ export class RelayPool {
57
32
  }
58
33
  /** Create a group of relays */
59
34
  group(relays) {
60
- // Normalize all urls
61
- relays = relays.map((url) => normalizeURL(url));
62
- // Filter out any blacklisted relays
63
- relays = this.filterBlacklist(relays);
64
- const key = relays.sort().join(",");
65
- let group = this.groups.get(key);
66
- if (group)
67
- return group;
68
- group = new RelayGroup(relays.map((url) => this.relay(url)));
69
- this.groups$.next(this.groups.set(key, group));
70
- return group;
35
+ return new RelayGroup(Array.isArray(relays)
36
+ ? relays.map((url) => this.relay(url))
37
+ : relays.pipe(map((urls) => urls.map((url) => this.relay(url)))));
71
38
  }
72
39
  /** Removes a relay from the pool and defaults to closing the connection */
73
40
  remove(relay, close = true) {
@@ -112,6 +79,34 @@ export class RelayPool {
112
79
  subscription(relays, filters, options) {
113
80
  return this.group(relays).subscription(filters, options);
114
81
  }
82
+ /** Open a subscription for a map of relays and filters */
83
+ subscriptionMap(relays, options) {
84
+ // Convert input to observable
85
+ const relays$ = isObservable(relays) ? relays : of(relays);
86
+ return this.group(
87
+ // Create a group with an observable of dynamic relay urls
88
+ relays$.pipe(map((dir) => Object.keys(dir)))).subscription((relay) => {
89
+ // Return observable to subscribe to the relays unique filters
90
+ return relays$.pipe(
91
+ // Select the relays filters
92
+ map((dir) => dir[relay.url]),
93
+ // Don't send duplicate filters
94
+ distinctUntilChanged(isFilterEqual));
95
+ }, options);
96
+ }
97
+ /** Open a subscription for an {@link OutboxMap} and filter */
98
+ outboxSubscription(outboxes, filter, options) {
99
+ const filterMap = isObservable(outboxes)
100
+ ? outboxes.pipe(
101
+ // Project outbox map to filter map
102
+ map((outboxes) => createFilterMap(outboxes, filter)))
103
+ : createFilterMap(outboxes, filter);
104
+ return this.subscriptionMap(filterMap, options);
105
+ }
106
+ /** Count events on multiple relays */
107
+ count(relays, filters, id) {
108
+ return this.group(relays).count(filters, id);
109
+ }
115
110
  /** Negentropy sync events with the relays and an event store */
116
111
  sync(relays, store, filter, direction) {
117
112
  return this.group(relays).sync(store, filter, direction);
package/dist/relay.d.ts CHANGED
@@ -4,7 +4,7 @@ import { RelayInformation } from "nostr-tools/nip11";
4
4
  import { BehaviorSubject, MonoTypeOperatorFunction, Observable, RepeatConfig, RetryConfig, Subject } from "rxjs";
5
5
  import { WebSocketSubject, WebSocketSubjectConfig } from "rxjs/webSocket";
6
6
  import { type NegentropySyncOptions, type ReconcileFunction } from "./negentropy.js";
7
- import { AuthSigner, FilterInput, IRelay, PublishOptions, PublishResponse, RequestOptions, SubscriptionOptions, SubscriptionResponse } from "./types.js";
7
+ import { AuthSigner, CountResponse, FilterInput, IRelay, PublishOptions, PublishResponse, RequestOptions, SubscriptionOptions, SubscriptionResponse } from "./types.js";
8
8
  /** Flags for the negentropy sync type */
9
9
  export declare enum SyncDirection {
10
10
  RECEIVE = 1,
@@ -104,6 +104,8 @@ export declare class Relay implements IRelay {
104
104
  send(message: any): void;
105
105
  /** Create a REQ observable that emits events or "EOSE" or errors */
106
106
  req(filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
107
+ /** Create a COUNT observable that emits a single count response */
108
+ count(filters: Filter | Filter[], id?: string): Observable<CountResponse>;
107
109
  /** Send an EVENT or AUTH message and return an observable of PublishResponse that completes or errors */
108
110
  event(event: NostrEvent, verb?: "EVENT" | "AUTH"): Observable<PublishResponse>;
109
111
  /** send and AUTH message */
@@ -118,10 +120,12 @@ export declare class Relay implements IRelay {
118
120
  protected customRepeatOperator<T extends unknown = unknown>(times: undefined | boolean | number | RepeatConfig | undefined): MonoTypeOperatorFunction<T>;
119
121
  /** Internal operator for creating the timeout() operator */
120
122
  protected customTimeoutOperator<T extends unknown = unknown>(timeout: undefined | boolean | number, defaultTimeout: number): MonoTypeOperatorFunction<T>;
123
+ /** Internal operator for handling auth-required errors from REQ/COUNT operations */
124
+ protected handleAuthRequiredForReq(operation: "REQ" | "COUNT"): MonoTypeOperatorFunction<any>;
121
125
  /** Creates a REQ that retries when relay errors ( default 3 retries ) */
122
- subscription(filters: Filter | Filter[], opts?: SubscriptionOptions): Observable<SubscriptionResponse>;
126
+ subscription(filters: FilterInput, opts?: SubscriptionOptions): Observable<SubscriptionResponse>;
123
127
  /** Makes a single request that retires on errors and completes on EOSE */
124
- request(filters: Filter | Filter[], opts?: RequestOptions): Observable<NostrEvent>;
128
+ request(filters: FilterInput, opts?: RequestOptions): Observable<NostrEvent>;
125
129
  /** Publishes an event to the relay and retries when relay errors or responds with auth-required ( default 3 retries ) */
126
130
  publish(event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse>;
127
131
  /** Negentropy sync events with the relay and an event store */
package/dist/relay.js CHANGED
@@ -7,6 +7,7 @@ import { BehaviorSubject, catchError, combineLatest, defer, endWith, filter, fin
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";
10
+ const AUTH_REQUIRED_PREFIX = "auth-required:";
10
11
  const DEFAULT_RETRY_CONFIG = { count: 10, delay: 1000, resetOnSuccess: true };
11
12
  /** Flags for the negentropy sync type */
12
13
  export var SyncDirection;
@@ -264,7 +265,15 @@ export class Relay {
264
265
  /** Create a REQ observable that emits events or "EOSE" or errors */
265
266
  req(filters, id = nanoid()) {
266
267
  // Convert filters input into an observable, if its a normal value merge it with NEVER so it never completes
267
- const input = isObservable(filters) ? filters : merge(of(filters), NEVER);
268
+ let input;
269
+ // Create input from filters input
270
+ if (typeof filters === "function") {
271
+ const result = filters(this);
272
+ input = (isObservable(result) ? result : merge(of(result), NEVER)).pipe(map((f) => (Array.isArray(f) ? f : [f])));
273
+ }
274
+ else {
275
+ input = (isObservable(filters) ? filters : merge(of(filters), NEVER)).pipe(map((f) => (Array.isArray(f) ? f : [f])));
276
+ }
268
277
  // Create an observable that completes when the upstream observable completes
269
278
  const filtersComplete = input.pipe(ignoreElements(), endWith(null));
270
279
  // Create an observable that filters responses from the relay to just the ones for this REQ
@@ -274,7 +283,7 @@ export class Relay {
274
283
  // Create an observable that controls sending the filters and closing the REQ
275
284
  const control = input.pipe(
276
285
  // Send the filters when they change
277
- tap((filters) => this.socket.next(Array.isArray(filters) ? ["REQ", id, ...filters] : ["REQ", id, filters])),
286
+ tap((filters) => this.socket.next(["REQ", id, ...filters])),
278
287
  // Send the CLOSE message when unsubscribed or input completes
279
288
  finalize(() => this.socket.next(["CLOSE", id])),
280
289
  // Once filters have been sent, switch to listening for messages
@@ -294,21 +303,11 @@ export class Relay {
294
303
  throw new ReqCloseError(message[2]);
295
304
  else
296
305
  return message[2];
297
- }), catchError((error) => {
298
- // Set REQ auth required if the REQ is closed with auth-required
299
- if (error instanceof ReqCloseError &&
300
- error.message.startsWith("auth-required") &&
301
- !this.receivedAuthRequiredForReq.value) {
302
- this.log("Auth required for REQ");
303
- this.receivedAuthRequiredForReq.next(true);
304
- }
305
- // Pass the error through
306
- return throwError(() => error);
307
- }),
306
+ }), this.handleAuthRequiredForReq("REQ"),
308
307
  // mark events as from relays
309
308
  markFromRelay(this.url),
310
309
  // if no events are seen in 10s, emit EOSE
311
- // TODO: this should emit EOSE event if events are seen, the timeout should be for only the EOSE message
310
+ // TODO: this timeout should only emit EOSE after the last event is seen and no EOSE has been sent in (timeout ms)
312
311
  timeout({
313
312
  first: this.eoseTimeout,
314
313
  with: () => merge(of("EOSE"), NEVER),
@@ -318,6 +317,38 @@ export class Relay {
318
317
  // Wait for auth if required and make sure to start the watch tower
319
318
  return this.waitForReady(this.waitForAuth(this.authRequiredForRead$, observable));
320
319
  }
320
+ /** Create a COUNT observable that emits a single count response */
321
+ count(filters, id = nanoid()) {
322
+ // Create an observable that filters responses from the relay to just the ones for this COUNT
323
+ const messages = this.socket.pipe(filter((m) => Array.isArray(m) && (m[0] === "COUNT" || m[0] === "CLOSED") && m[1] === id),
324
+ // Singleton (prevents duplicate subscriptions)
325
+ share());
326
+ // Send the COUNT message and listen for response
327
+ const observable = defer(() => {
328
+ // Send the COUNT message when subscription starts
329
+ this.socket.next(Array.isArray(filters) ? ["COUNT", id, ...filters] : ["COUNT", id, filters]);
330
+ // Merge with watch tower to keep connection alive
331
+ return merge(this.watchTower, messages);
332
+ }).pipe(
333
+ // Map the messages to count responses or throw an error
334
+ map((message) => {
335
+ if (message[0] === "COUNT")
336
+ return message[2];
337
+ else
338
+ throw new ReqCloseError(message[2]);
339
+ }), this.handleAuthRequiredForReq("COUNT"),
340
+ // Complete on first value (COUNT responses are single-shot)
341
+ take(1),
342
+ // Add timeout
343
+ timeout({
344
+ first: this.eoseTimeout,
345
+ with: () => throwError(() => new Error("COUNT timeout")),
346
+ }),
347
+ // Only create one upstream subscription
348
+ share());
349
+ // Start the watch tower and wait for auth if required
350
+ return this.waitForReady(this.waitForAuth(this.authRequiredForRead$, observable));
351
+ }
321
352
  /** Send an EVENT or AUTH message and return an observable of PublishResponse that completes or errors */
322
353
  event(event, verb = "EVENT") {
323
354
  const messages = defer(() => {
@@ -338,7 +369,7 @@ export class Relay {
338
369
  take(1),
339
370
  // listen for OK auth-required
340
371
  tap(({ ok, message }) => {
341
- if (ok === false && message?.startsWith("auth-required") && !this.receivedAuthRequiredForEvent.value) {
372
+ if (ok === false && message?.startsWith(AUTH_REQUIRED_PREFIX) && !this.receivedAuthRequiredForEvent.value) {
342
373
  this.log("Auth required for publish");
343
374
  this.receivedAuthRequiredForEvent.next(true);
344
375
  }
@@ -414,6 +445,20 @@ export class Relay {
414
445
  else
415
446
  return simpleTimeout(timeout ?? defaultTimeout);
416
447
  }
448
+ /** Internal operator for handling auth-required errors from REQ/COUNT operations */
449
+ handleAuthRequiredForReq(operation) {
450
+ return catchError((error) => {
451
+ // Set auth required if the operation is closed with auth-required
452
+ if (error instanceof ReqCloseError &&
453
+ error.message.startsWith(AUTH_REQUIRED_PREFIX) &&
454
+ !this.receivedAuthRequiredForReq.value) {
455
+ this.log(`Auth required for ${operation}`);
456
+ this.receivedAuthRequiredForReq.next(true);
457
+ }
458
+ // Pass the error through
459
+ return throwError(() => error);
460
+ });
461
+ }
417
462
  /** Creates a REQ that retries when relay errors ( default 3 retries ) */
418
463
  subscription(filters, opts) {
419
464
  return this.req(filters, opts?.id).pipe(
@@ -440,7 +485,7 @@ export class Relay {
440
485
  publish(event, opts) {
441
486
  return lastValueFrom(this.event(event).pipe(mergeMap((result) => {
442
487
  // If the relay responds with auth-required, throw an error for the retry operator to handle
443
- if (result.ok === false && result.message?.startsWith("auth-required:"))
488
+ if (result.ok === false && result.message?.startsWith(AUTH_REQUIRED_PREFIX))
444
489
  return throwError(() => new Error(result.message));
445
490
  return of(result);
446
491
  }),
@@ -461,6 +506,14 @@ export class Relay {
461
506
  };
462
507
  return new Observable((observer) => {
463
508
  const controller = new AbortController();
509
+ let cleanupCalled = false;
510
+ // Store reference to cleanup the negentropy properly
511
+ const cleanup = () => {
512
+ if (!cleanupCalled) {
513
+ cleanupCalled = true;
514
+ controller.abort();
515
+ }
516
+ };
464
517
  this.negentropy(store, filter, async (have, need) => {
465
518
  // NOTE: it may be more efficient to sync all the events later in a single batch
466
519
  // Send missing events to the relay
@@ -475,11 +528,17 @@ export class Relay {
475
528
  }
476
529
  }, { signal: controller.signal })
477
530
  // Complete the observable when the sync is complete
478
- .then(() => observer.complete())
531
+ .then(() => {
532
+ if (!cleanupCalled)
533
+ observer.complete();
534
+ })
479
535
  // Error the observable when the sync fails
480
- .catch((err) => observer.error(err));
536
+ .catch((err) => {
537
+ if (!cleanupCalled)
538
+ observer.error(err);
539
+ });
481
540
  // Cancel the sync when the observable is unsubscribed
482
- return () => controller.abort();
541
+ return cleanup;
483
542
  }).pipe(
484
543
  // Only create one upstream subscription
485
544
  share());
package/dist/types.d.ts CHANGED
@@ -12,6 +12,9 @@ export type PublishResponse = {
12
12
  message?: string;
13
13
  from: string;
14
14
  };
15
+ export type CountResponse = {
16
+ count: number;
17
+ };
15
18
  export type MultiplexWebSocket<T = any> = Pick<WebSocketSubject<T>, "multiplex">;
16
19
  /** Options for the publish method on the pool and relay */
17
20
  export type PublishOptions = {
@@ -55,8 +58,8 @@ export type SubscriptionOptions = {
55
58
  export type AuthSigner = {
56
59
  signEvent: (event: EventTemplate) => NostrEvent | Promise<NostrEvent>;
57
60
  };
58
- /** The type of input the REQ method accepts */
59
- export type FilterInput = Filter | Filter[] | Observable<Filter | Filter[]>;
61
+ /** Filters that can be passed to request methods on the pool or relay */
62
+ export type FilterInput = Filter | Filter[] | Observable<Filter | Filter[]> | ((relay: IRelay) => Filter | Filter[] | Observable<Filter | Filter[]>);
60
63
  export interface IRelay extends MultiplexWebSocket {
61
64
  url: string;
62
65
  message$: Observable<any>;
@@ -65,6 +68,9 @@ export interface IRelay extends MultiplexWebSocket {
65
68
  challenge$: Observable<string | null>;
66
69
  authenticated$: Observable<boolean>;
67
70
  notices$: Observable<string[]>;
71
+ open$: Observable<Event>;
72
+ close$: Observable<CloseEvent>;
73
+ closing$: Observable<void>;
68
74
  error$: Observable<Error | null>;
69
75
  readonly connected: boolean;
70
76
  readonly authenticated: boolean;
@@ -74,6 +80,8 @@ export interface IRelay extends MultiplexWebSocket {
74
80
  close(): void;
75
81
  /** Send a REQ message */
76
82
  req(filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
83
+ /** Send a COUNT message */
84
+ count(filters: Filter | Filter[], id?: string): Observable<CountResponse>;
77
85
  /** Send an EVENT message */
78
86
  event(event: NostrEvent): Observable<PublishResponse>;
79
87
  /** Send an AUTH message */
@@ -97,19 +105,28 @@ export interface IRelay extends MultiplexWebSocket {
97
105
  /** Get the supported NIPs for the relay */
98
106
  getSupported(): Promise<number[] | null>;
99
107
  }
108
+ export type IGroupRelayInput = IRelay[] | Observable<IRelay[]>;
100
109
  export interface IGroup {
101
110
  /** Send a REQ message */
102
- req(filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
111
+ req(filters: Parameters<IRelay["req"]>[0], id?: string): Observable<SubscriptionResponse>;
103
112
  /** Send an EVENT message */
104
- event(event: NostrEvent): Observable<PublishResponse>;
113
+ event(event: Parameters<IRelay["event"]>[0]): Observable<PublishResponse>;
105
114
  /** Negentropy sync event ids with the relays and an event store */
106
115
  negentropy(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: Filter, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
116
+ /** Add a relay to the group */
117
+ add(relay: IRelay): void;
118
+ /** Remove a relay from the group */
119
+ remove(relay: IRelay): void;
120
+ /** Check if a relay is in the group */
121
+ has(relay: IRelay | string): boolean;
107
122
  /** Send an EVENT message with retries */
108
- publish(event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse[]>;
123
+ publish(event: Parameters<IRelay["event"]>[0], opts?: PublishOptions): Promise<PublishResponse[]>;
109
124
  /** Send a REQ message with retries */
110
- request(filters: FilterInput, opts?: GroupRequestOptions): Observable<NostrEvent>;
125
+ request(filters: Parameters<IRelay["request"]>[0], opts?: GroupRequestOptions): Observable<NostrEvent>;
111
126
  /** Open a subscription with retries */
112
- subscription(filters: FilterInput, opts?: GroupSubscriptionOptions): Observable<SubscriptionResponse>;
127
+ subscription(filters: Parameters<IRelay["subscription"]>[0], opts?: GroupSubscriptionOptions): Observable<SubscriptionResponse>;
128
+ /** Count events on the relays and an event store */
129
+ count(filters: Filter | Filter[], id?: string): Observable<Record<string, CountResponse>>;
113
130
  /** Negentropy sync events with the relay and an event store */
114
131
  sync(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
115
132
  }
@@ -118,25 +135,28 @@ export interface IPoolSignals {
118
135
  add$: Observable<IRelay>;
119
136
  remove$: Observable<IRelay>;
120
137
  }
138
+ export type IPoolRelayInput = string[] | Observable<string[]>;
121
139
  export interface IPool extends IPoolSignals {
122
140
  /** Get or create a relay */
123
141
  relay(url: string): IRelay;
124
142
  /** Create a relay group */
125
- group(relays: string[]): IGroup;
143
+ group(relays: IPoolRelayInput): IGroup;
126
144
  /** Removes a relay from the pool and defaults to closing the connection */
127
145
  remove(relay: string | IRelay, close?: boolean): void;
128
146
  /** Send a REQ message */
129
- req(relays: string[], filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
147
+ req(relays: IPoolRelayInput, filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
130
148
  /** Send an EVENT message */
131
- event(relays: string[], event: NostrEvent): Observable<PublishResponse>;
149
+ event(relays: IPoolRelayInput, event: NostrEvent): Observable<PublishResponse>;
132
150
  /** Negentropy sync event ids with the relays and an event store */
133
- negentropy(relays: string[], store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: Filter, reconcile: ReconcileFunction, opts?: GroupNegentropySyncOptions): Promise<boolean>;
151
+ negentropy(relays: IPoolRelayInput, store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: Filter, reconcile: ReconcileFunction, opts?: GroupNegentropySyncOptions): Promise<boolean>;
134
152
  /** Send an EVENT message to relays with retries */
135
- publish(relays: string[], event: Parameters<IGroup["publish"]>[0], opts?: Parameters<IGroup["publish"]>[1]): Promise<PublishResponse[]>;
153
+ publish(relays: IPoolRelayInput, event: Parameters<IGroup["publish"]>[0], opts?: Parameters<IGroup["publish"]>[1]): Promise<PublishResponse[]>;
136
154
  /** Send a REQ message to relays with retries */
137
- request(relays: string[], filters: Parameters<IGroup["request"]>[0], opts?: Parameters<IGroup["request"]>[1]): Observable<NostrEvent>;
155
+ request(relays: IPoolRelayInput, filters: Parameters<IGroup["request"]>[0], opts?: Parameters<IGroup["request"]>[1]): Observable<NostrEvent>;
138
156
  /** Open a subscription to relays with retries */
139
- subscription(relays: string[], filters: Parameters<IGroup["subscription"]>[0], opts?: Parameters<IGroup["subscription"]>[1]): Observable<SubscriptionResponse>;
157
+ subscription(relays: IPoolRelayInput, filters: Parameters<IGroup["subscription"]>[0], opts?: Parameters<IGroup["subscription"]>[1]): Observable<SubscriptionResponse>;
158
+ /** Count events on the relays and an event store */
159
+ count(relays: IPoolRelayInput, filters: Filter | Filter[], id?: string): Observable<Record<string, CountResponse>>;
140
160
  /** Negentropy sync events with the relay and an event store */
141
- sync(relays: string[], store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
161
+ sync(relays: IPoolRelayInput, store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
142
162
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-relay",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "description": "nostr relay communication framework built on rxjs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -52,14 +52,14 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@noble/hashes": "^1.7.1",
55
- "applesauce-core": "^4.0.0",
55
+ "applesauce-core": "^4.2.0",
56
56
  "nanoid": "^5.0.9",
57
57
  "nostr-tools": "~2.17",
58
58
  "rxjs": "^7.8.1"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@hirez_io/observer-spy": "^2.2.0",
62
- "applesauce-signers": "^4.0.0",
62
+ "applesauce-signers": "^4.2.0",
63
63
  "rimraf": "^6.0.1",
64
64
  "typescript": "^5.7.3",
65
65
  "vitest": "^3.2.4",