applesauce-relay 0.0.0-next-20251117143754 → 0.0.0-next-20251203172109

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,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, 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) */
@@ -47,7 +46,7 @@ export declare class RelayGroup implements IGroup {
47
46
  /** Send an event to all relays */
48
47
  event(event: NostrEvent): Observable<PublishResponse>;
49
48
  /** Negentropy sync events with the relays and an event store */
50
- negentropy(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, reconcile: ReconcileFunction, opts?: GroupNegentropySyncOptions): Promise<boolean>;
49
+ negentropy(store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: GroupNegentropySyncOptions): Promise<boolean>;
51
50
  /** Publish an event to all relays with retries ( default 3 retries ) */
52
51
  publish(event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse[]>;
53
52
  /** Request events from all relays and complete on EOSE */
@@ -55,7 +54,7 @@ export declare class RelayGroup implements IGroup {
55
54
  /** Open a subscription to all relays with retries ( default 3 retries ) */
56
55
  subscription(filters: FilterInput, opts?: GroupSubscriptionOptions): Observable<SubscriptionResponse>;
57
56
  /** Count events on all relays in the group */
58
- count(filters: FilterWithAnd | FilterWithAnd[], id?: string): Observable<Record<string, CountResponse>>;
57
+ count(filters: Filter | Filter[], id?: string): Observable<Record<string, CountResponse>>;
59
58
  /** Negentropy sync events with the relays and an event store */
60
- sync(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, direction?: SyncDirection): Observable<NostrEvent>;
59
+ sync(store: NegentropySyncStore | NostrEvent[], filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
61
60
  }
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
4
  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
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";
@@ -124,9 +125,7 @@ export class RelayGroup {
124
125
  upstream.set(relay, observable);
125
126
  }
126
127
  return merge(...observables);
127
- }),
128
- // Ensure a single upstream
129
- share());
128
+ }));
130
129
  }
131
130
  /**
132
131
  * Make a request to all relays
@@ -137,7 +136,9 @@ export class RelayGroup {
137
136
  }
138
137
  /** Send an event to all relays */
139
138
  event(event) {
140
- return this.internalPublish((relay) => relay.event(event));
139
+ return this.internalPublish((relay) => relay.event(event)).pipe(
140
+ // Ensure a single upstream subscription
141
+ share());
141
142
  }
142
143
  /** Negentropy sync events with the relays and an event store */
143
144
  async negentropy(store, filter, reconcile, opts) {
@@ -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;
@@ -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,11 +1,11 @@
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, SubscriptionResponse } from "./types.js";
9
9
  export declare class RelayPool implements IPool {
10
10
  options?: RelayOptions | undefined;
11
11
  relays$: BehaviorSubject<Map<string, Relay>>;
@@ -26,7 +26,7 @@ export declare class RelayPool implements IPool {
26
26
  /** Send an EVENT message to multiple relays */
27
27
  event(relays: IPoolRelayInput, event: NostrEvent): Observable<PublishResponse>;
28
28
  /** 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>;
29
+ negentropy(relays: IPoolRelayInput, store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
30
30
  /** Publish an event to multiple relays */
31
31
  publish(relays: IPoolRelayInput, event: Parameters<RelayGroup["publish"]>[0], opts?: Parameters<RelayGroup["publish"]>[1]): Promise<PublishResponse[]>;
32
32
  /** Request events from multiple relays */
@@ -38,7 +38,7 @@ export declare class RelayPool implements IPool {
38
38
  /** Open a subscription for an {@link OutboxMap} and filter */
39
39
  outboxSubscription(outboxes: OutboxMap | Observable<OutboxMap>, filter: Omit<Filter, "authors">, options?: Parameters<RelayGroup["subscription"]>[1]): Observable<SubscriptionResponse>;
40
40
  /** Count events on multiple relays */
41
- count(relays: IPoolRelayInput, filters: FilterWithAnd | FilterWithAnd[], id?: string): Observable<Record<string, CountResponse>>;
41
+ count(relays: IPoolRelayInput, filters: Filter | Filter[], id?: string): Observable<Record<string, CountResponse>>;
42
42
  /** Negentropy sync events with the relays and an event store */
43
- sync(relays: IPoolRelayInput, store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, direction?: SyncDirection): Observable<NostrEvent>;
43
+ sync(relays: IPoolRelayInput, store: NegentropySyncStore | NostrEvent[], filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
44
44
  }
package/dist/pool.js CHANGED
@@ -1,4 +1,6 @@
1
- import { createFilterMap, isFilterEqual, normalizeURL, } from "applesauce-core/helpers";
1
+ import { isFilterEqual } from "applesauce-core/helpers/filter";
2
+ import { createFilterMap } from "applesauce-core/helpers/relay-selection";
3
+ import { normalizeURL } from "applesauce-core/helpers/url";
2
4
  import { BehaviorSubject, distinctUntilChanged, isObservable, map, of, Subject } from "rxjs";
3
5
  import { RelayGroup } from "./group.js";
4
6
  import { Relay } from "./relay.js";
package/dist/relay.d.ts CHANGED
@@ -1,11 +1,10 @@
1
- import { IAsyncEventStoreRead, IEventStoreRead, logger } from "applesauce-core";
2
- import { type FilterWithAnd } from "applesauce-core/helpers";
3
- import { type NostrEvent } from "nostr-tools";
4
- import { RelayInformation } from "nostr-tools/nip11";
1
+ import { logger } from "applesauce-core";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
+ import { Filter } from "applesauce-core/helpers/filter";
5
4
  import { BehaviorSubject, MonoTypeOperatorFunction, Observable, RepeatConfig, RetryConfig, Subject } from "rxjs";
6
5
  import { WebSocketSubject, WebSocketSubjectConfig } from "rxjs/webSocket";
7
6
  import { type NegentropySyncOptions, type ReconcileFunction } from "./negentropy.js";
8
- import { AuthSigner, CountResponse, FilterInput, IRelay, PublishOptions, PublishResponse, RequestOptions, SubscriptionOptions, SubscriptionResponse } from "./types.js";
7
+ import { AuthSigner, CountResponse, FilterInput, IRelay, NegentropyReadStore, NegentropySyncStore, PublishOptions, PublishResponse, RelayInformation, RequestOptions, SubscriptionOptions, SubscriptionResponse } from "./types.js";
9
8
  /** Flags for the negentropy sync type */
10
9
  export declare enum SyncDirection {
11
10
  RECEIVE = 1,
@@ -106,13 +105,13 @@ export declare class Relay implements IRelay {
106
105
  /** Create a REQ observable that emits events or "EOSE" or errors */
107
106
  req(filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
108
107
  /** Create a COUNT observable that emits a single count response */
109
- count(filters: FilterWithAnd | FilterWithAnd[], id?: string): Observable<CountResponse>;
108
+ count(filters: Filter | Filter[], id?: string): Observable<CountResponse>;
110
109
  /** Send an EVENT or AUTH message and return an observable of PublishResponse that completes or errors */
111
110
  event(event: NostrEvent, verb?: "EVENT" | "AUTH"): Observable<PublishResponse>;
112
111
  /** send and AUTH message */
113
112
  auth(event: NostrEvent): Promise<PublishResponse>;
114
113
  /** Negentropy sync event ids with the relay and an event store */
115
- negentropy(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
114
+ negentropy(store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
116
115
  /** Authenticate with the relay using a signer */
117
116
  authenticate(signer: AuthSigner): Promise<PublishResponse>;
118
117
  /** Internal operator for creating the retry() operator */
@@ -130,7 +129,7 @@ export declare class Relay implements IRelay {
130
129
  /** Publishes an event to the relay and retries when relay errors or responds with auth-required ( default 3 retries ) */
131
130
  publish(event: NostrEvent, opts?: PublishOptions): Promise<PublishResponse>;
132
131
  /** Negentropy sync events with the relay and an event store */
133
- sync(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, direction?: SyncDirection): Observable<NostrEvent>;
132
+ sync(store: NegentropySyncStore, filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
134
133
  /** Force close the connection */
135
134
  close(): void;
136
135
  /** An async method that returns the NIP-11 information document for the relay */
package/dist/relay.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { logger } from "applesauce-core";
2
- import { ensureHttpURL } from "applesauce-core/helpers";
3
- import { simpleTimeout } from "applesauce-core/observable";
2
+ import { ensureHttpURL } from "applesauce-core/helpers/url";
3
+ import { mapEventsToStore, simpleTimeout } from "applesauce-core/observable";
4
4
  import { nanoid } from "nanoid";
5
- import { nip42 } from "nostr-tools";
5
+ import { makeAuthEvent } from "nostr-tools/nip42";
6
6
  import { BehaviorSubject, catchError, combineLatest, defer, endWith, filter, finalize, firstValueFrom, from, identity, ignoreElements, isObservable, lastValueFrom, map, merge, mergeMap, mergeWith, NEVER, Observable, 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";
@@ -166,8 +166,15 @@ export class Relay {
166
166
  this.limitations$ = this.information$.pipe(map((info) => (info ? info.limitation : null)));
167
167
  this.supported$ = this.information$.pipe(map((info) => info && Array.isArray(info.supported_nips) ? info.supported_nips.filter((n) => typeof n === "number") : null));
168
168
  // Create observables that track if auth is required for REQ or EVENT
169
- this.authRequiredForRead$ = this.receivedAuthRequiredForReq.pipe(tap((required) => required && this.log("Auth required for REQ")), shareReplay(1));
170
- this.authRequiredForPublish$ = this.receivedAuthRequiredForEvent.pipe(tap((required) => required && this.log("Auth required for EVENT")), shareReplay(1));
169
+ this.authRequiredForRead$ = this.receivedAuthRequiredForReq;
170
+ this.authRequiredForPublish$ = this.receivedAuthRequiredForEvent;
171
+ // Log when auth is required
172
+ this.authRequiredForRead$
173
+ .pipe(filter((r) => r === true), take(1))
174
+ .subscribe(() => this.log("Auth required for REQ"));
175
+ this.authRequiredForPublish$
176
+ .pipe(filter((r) => r === true), take(1))
177
+ .subscribe(() => this.log("Auth required for EVENT"));
171
178
  // Update the notices state
172
179
  const listenForNotice = this.socket.pipe(
173
180
  // listen for NOTICE messages
@@ -320,9 +327,7 @@ export class Relay {
320
327
  /** Create a COUNT observable that emits a single count response */
321
328
  count(filters, id = nanoid()) {
322
329
  // 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());
330
+ const messages = this.socket.pipe(filter((m) => Array.isArray(m) && (m[0] === "COUNT" || m[0] === "CLOSED") && m[1] === id));
326
331
  // Send the COUNT message and listen for response
327
332
  const observable = defer(() => {
328
333
  // Send the COUNT message when subscription starts
@@ -343,11 +348,10 @@ export class Relay {
343
348
  timeout({
344
349
  first: this.eoseTimeout,
345
350
  with: () => throwError(() => new Error("COUNT timeout")),
346
- }),
347
- // Only create one upstream subscription
348
- share());
351
+ }));
349
352
  // Start the watch tower and wait for auth if required
350
- return this.waitForReady(this.waitForAuth(this.authRequiredForRead$, observable));
353
+ // Use share() to prevent multiple subscriptions from creating duplicate COUNT messages
354
+ return this.waitForReady(this.waitForAuth(this.authRequiredForRead$, observable)).pipe(share());
351
355
  }
352
356
  /** Send an EVENT or AUTH message and return an observable of PublishResponse that completes or errors */
353
357
  event(event, verb = "EVENT") {
@@ -378,14 +382,13 @@ export class Relay {
378
382
  timeout({
379
383
  first: this.eventTimeout,
380
384
  with: () => of({ ok: false, from: this.url, message: "Timeout" }),
381
- }),
382
- // Only create one upstream subscription
383
- share());
385
+ }));
384
386
  // skip wait for auth if verb is AUTH
387
+ // Use share() to prevent multiple subscriptions from creating duplicate EVENT messages
385
388
  if (verb === "AUTH")
386
- return this.waitForReady(observable);
389
+ return this.waitForReady(observable).pipe(share());
387
390
  else
388
- return this.waitForReady(this.waitForAuth(this.authRequiredForPublish$, observable));
391
+ return this.waitForReady(this.waitForAuth(this.authRequiredForPublish$, observable)).pipe(share());
389
392
  }
390
393
  /** send and AUTH message */
391
394
  auth(event) {
@@ -407,7 +410,7 @@ export class Relay {
407
410
  authenticate(signer) {
408
411
  if (!this.challenge)
409
412
  throw new Error("Have not received authentication challenge");
410
- const p = signer.signEvent(nip42.makeAuthEvent(this.url, this.challenge));
413
+ const p = signer.signEvent(makeAuthEvent(this.url, this.challenge));
411
414
  const start = p instanceof Promise ? from(p) : of(p);
412
415
  return lastValueFrom(start.pipe(switchMap((event) => this.auth(event))));
413
416
  }
@@ -492,9 +495,7 @@ export class Relay {
492
495
  // Retry the publish until it succeeds or the number of retries is reached
493
496
  this.customRetryOperator(opts?.retries ?? opts?.reconnect ?? true, DEFAULT_RETRY_CONFIG),
494
497
  // Add timeout for publishing
495
- this.customTimeoutOperator(opts?.timeout, this.publishTimeout),
496
- // Single subscription
497
- share()));
498
+ this.customTimeoutOperator(opts?.timeout, this.publishTimeout)));
498
499
  }
499
500
  /** Negentropy sync events with the relay and an event store */
500
501
  sync(store, filter, direction = SyncDirection.RECEIVE) {
@@ -524,7 +525,15 @@ export class Relay {
524
525
  }
525
526
  // Fetch missing events from the relay
526
527
  if (direction & SyncDirection.RECEIVE && need.length > 0) {
527
- await lastValueFrom(this.req({ ids: need }).pipe(completeOnEose(), tap((event) => observer.next(event))));
528
+ await lastValueFrom(this.req({ ids: need }).pipe(
529
+ // Complete when EOSE is received
530
+ completeOnEose(),
531
+ // Add events to the store if its writable
532
+ Reflect.has(store, "add")
533
+ ? mapEventsToStore(store)
534
+ : identity,
535
+ // Pass events to observer
536
+ tap((event) => observer.next(event))));
528
537
  }
529
538
  }, { signal: controller.signal })
530
539
  // Complete the observable when the sync is complete
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { IAsyncEventStoreRead, IEventStoreRead } from "applesauce-core";
2
- import type { FilterWithAnd } from "applesauce-core/helpers";
3
- import type { EventTemplate, NostrEvent } from "nostr-tools";
4
- import type { RelayInformation } from "nostr-tools/nip11";
1
+ import type { IAsyncEventStoreActions, IAsyncEventStoreRead, IEventStoreRead } from "applesauce-core/event-store";
2
+ import type { Filter } from "applesauce-core/helpers/filter";
3
+ import type { EventTemplate, NostrEvent } from "applesauce-core/helpers/event";
4
+ import type { RelayInformation as CoreRelayInformation } from "nostr-tools/nip11";
5
5
  import type { Observable, repeat, retry } from "rxjs";
6
6
  import type { WebSocketSubject } from "rxjs/webSocket";
7
7
  import type { GroupNegentropySyncOptions, GroupRequestOptions, GroupSubscriptionOptions } from "./group.js";
@@ -60,7 +60,17 @@ export type AuthSigner = {
60
60
  signEvent: (event: EventTemplate) => NostrEvent | Promise<NostrEvent>;
61
61
  };
62
62
  /** Filters that can be passed to request methods on the pool or relay */
63
- export type FilterInput = FilterWithAnd | FilterWithAnd[] | Observable<FilterWithAnd | FilterWithAnd[]> | ((relay: IRelay) => FilterWithAnd | FilterWithAnd[] | Observable<FilterWithAnd | FilterWithAnd[]>);
63
+ export type FilterInput = Filter | Filter[] | Observable<Filter | Filter[]> | ((relay: IRelay) => Filter | Filter[] | Observable<Filter | Filter[]>);
64
+ export type RelayInformation = CoreRelayInformation & {
65
+ /** An array of attributes that describe the relay type/characteristics */
66
+ attributes?: string[];
67
+ };
68
+ /** A read only event store for negentropy sync */
69
+ export type NegentropyReadStore = IEventStoreRead | IAsyncEventStoreRead | NostrEvent[];
70
+ /** A writeable event store for negentropy sync */
71
+ export type NegentropyWriteStore = (IAsyncEventStoreRead & IAsyncEventStoreActions) | (IEventStoreRead & IAsyncEventStoreActions);
72
+ /** An event store that can be used for negentropy sync */
73
+ export type NegentropySyncStore = NegentropyReadStore | NegentropyWriteStore;
64
74
  export interface IRelay extends MultiplexWebSocket {
65
75
  url: string;
66
76
  message$: Observable<any>;
@@ -82,13 +92,13 @@ export interface IRelay extends MultiplexWebSocket {
82
92
  /** Send a REQ message */
83
93
  req(filters: FilterInput, id?: string): Observable<SubscriptionResponse>;
84
94
  /** Send a COUNT message */
85
- count(filters: FilterWithAnd | FilterWithAnd[], id?: string): Observable<CountResponse>;
95
+ count(filters: Filter | Filter[], id?: string): Observable<CountResponse>;
86
96
  /** Send an EVENT message */
87
97
  event(event: NostrEvent): Observable<PublishResponse>;
88
98
  /** Send an AUTH message */
89
99
  auth(event: NostrEvent): Promise<PublishResponse>;
90
100
  /** Negentropy sync event ids with the relay and an event store */
91
- negentropy(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
101
+ negentropy(store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
92
102
  /** Authenticate with the relay using a signer */
93
103
  authenticate(signer: AuthSigner): Promise<PublishResponse>;
94
104
  /** Send an EVENT message with retries */
@@ -98,7 +108,7 @@ export interface IRelay extends MultiplexWebSocket {
98
108
  /** Open a subscription with retries */
99
109
  subscription(filters: FilterInput, opts?: SubscriptionOptions): Observable<SubscriptionResponse>;
100
110
  /** Negentropy sync events with the relay and an event store */
101
- sync(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, direction?: SyncDirection): Observable<NostrEvent>;
111
+ sync(store: NegentropySyncStore, filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
102
112
  /** Get the NIP-11 information document for the relay */
103
113
  getInformation(): Promise<RelayInformation | null>;
104
114
  /** Get the limitations for the relay */
@@ -113,7 +123,7 @@ export interface IGroup {
113
123
  /** Send an EVENT message */
114
124
  event(event: Parameters<IRelay["event"]>[0]): Observable<PublishResponse>;
115
125
  /** Negentropy sync event ids with the relays and an event store */
116
- negentropy(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
126
+ negentropy(store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: NegentropySyncOptions): Promise<boolean>;
117
127
  /** Add a relay to the group */
118
128
  add(relay: IRelay): void;
119
129
  /** Remove a relay from the group */
@@ -127,9 +137,9 @@ export interface IGroup {
127
137
  /** Open a subscription with retries */
128
138
  subscription(filters: Parameters<IRelay["subscription"]>[0], opts?: GroupSubscriptionOptions): Observable<SubscriptionResponse>;
129
139
  /** Count events on the relays and an event store */
130
- count(filters: FilterWithAnd | FilterWithAnd[], id?: string): Observable<Record<string, CountResponse>>;
140
+ count(filters: Filter | Filter[], id?: string): Observable<Record<string, CountResponse>>;
131
141
  /** Negentropy sync events with the relay and an event store */
132
- sync(store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, direction?: SyncDirection): Observable<NostrEvent>;
142
+ sync(store: NegentropySyncStore, filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
133
143
  }
134
144
  /** Signals emitted by the pool */
135
145
  export interface IPoolSignals {
@@ -149,7 +159,7 @@ export interface IPool extends IPoolSignals {
149
159
  /** Send an EVENT message */
150
160
  event(relays: IPoolRelayInput, event: NostrEvent): Observable<PublishResponse>;
151
161
  /** Negentropy sync event ids with the relays and an event store */
152
- negentropy(relays: IPoolRelayInput, store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, reconcile: ReconcileFunction, opts?: GroupNegentropySyncOptions): Promise<boolean>;
162
+ negentropy(relays: IPoolRelayInput, store: NegentropyReadStore, filter: Filter, reconcile: ReconcileFunction, opts?: GroupNegentropySyncOptions): Promise<boolean>;
153
163
  /** Send an EVENT message to relays with retries */
154
164
  publish(relays: IPoolRelayInput, event: Parameters<IGroup["publish"]>[0], opts?: Parameters<IGroup["publish"]>[1]): Promise<PublishResponse[]>;
155
165
  /** Send a REQ message to relays with retries */
@@ -157,7 +167,7 @@ export interface IPool extends IPoolSignals {
157
167
  /** Open a subscription to relays with retries */
158
168
  subscription(relays: IPoolRelayInput, filters: Parameters<IGroup["subscription"]>[0], opts?: Parameters<IGroup["subscription"]>[1]): Observable<SubscriptionResponse>;
159
169
  /** Count events on the relays and an event store */
160
- count(relays: IPoolRelayInput, filters: FilterWithAnd | FilterWithAnd[], id?: string): Observable<Record<string, CountResponse>>;
170
+ count(relays: IPoolRelayInput, filters: Filter | Filter[], id?: string): Observable<Record<string, CountResponse>>;
161
171
  /** Negentropy sync events with the relay and an event store */
162
- sync(relays: IPoolRelayInput, store: IEventStoreRead | IAsyncEventStoreRead | NostrEvent[], filter: FilterWithAnd, direction?: SyncDirection): Observable<NostrEvent>;
172
+ sync(relays: IPoolRelayInput, store: NegentropySyncStore, filter: Filter, direction?: SyncDirection): Observable<NostrEvent>;
163
173
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-relay",
3
- "version": "0.0.0-next-20251117143754",
3
+ "version": "0.0.0-next-20251203172109",
4
4
  "description": "nostr relay communication framework built on rxjs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -52,17 +52,17 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@noble/hashes": "^1.7.1",
55
- "applesauce-core": "0.0.0-next-20251117143754",
55
+ "applesauce-core": "0.0.0-next-20251203172109",
56
56
  "nanoid": "^5.0.9",
57
- "nostr-tools": "~2.17",
57
+ "nostr-tools": "~2.18",
58
58
  "rxjs": "^7.8.1"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@hirez_io/observer-spy": "^2.2.0",
62
- "applesauce-signers": "^4.2.0",
62
+ "applesauce-signers": "0.0.0-next-20251203172109",
63
63
  "rimraf": "^6.0.1",
64
64
  "typescript": "^5.7.3",
65
- "vitest": "^3.2.4",
65
+ "vitest": "^4.0.15",
66
66
  "vitest-websocket-mock": "^0.5.0"
67
67
  },
68
68
  "funding": {