applesauce-relay 0.12.0 → 1.0.1
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 +115 -0
- package/dist/__tests__/auth.test.d.ts +1 -0
- package/dist/__tests__/auth.test.js +111 -0
- package/dist/__tests__/group.test.d.ts +1 -0
- package/dist/__tests__/group.test.js +106 -0
- package/dist/__tests__/pool.test.d.ts +1 -0
- package/dist/__tests__/pool.test.js +81 -0
- package/dist/__tests__/relay.test.d.ts +1 -0
- package/dist/__tests__/relay.test.js +561 -0
- package/dist/group.d.ts +19 -0
- package/dist/group.js +54 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/lib/negentropy.d.ts +61 -0
- package/dist/lib/negentropy.js +533 -0
- package/dist/negentropy.d.ts +15 -0
- package/dist/negentropy.js +68 -0
- package/dist/operators/complete-on-eose.d.ts +6 -0
- package/dist/operators/complete-on-eose.js +7 -0
- package/dist/operators/index.d.ts +4 -1
- package/dist/operators/index.js +4 -1
- package/dist/operators/mark-from-relay.d.ts +1 -1
- package/dist/operators/only-events.d.ts +1 -1
- package/dist/operators/store-events.d.ts +5 -0
- package/dist/operators/store-events.js +7 -0
- package/dist/operators/to-event-store.d.ts +6 -0
- package/dist/operators/to-event-store.js +19 -0
- package/dist/pool.d.ts +18 -5
- package/dist/pool.js +33 -23
- package/dist/relay.d.ts +73 -22
- package/dist/relay.js +278 -59
- package/dist/types.d.ts +104 -0
- package/dist/types.js +1 -0
- package/package.json +28 -6
package/dist/operators/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { MonoTypeOperatorFunction } from "rxjs";
|
|
2
|
-
import { SubscriptionResponse } from "../
|
|
2
|
+
import { SubscriptionResponse } from "../types.js";
|
|
3
3
|
/** Marks all events as from the relay */
|
|
4
4
|
export declare function markFromRelay(relay: string): MonoTypeOperatorFunction<SubscriptionResponse>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OperatorFunction } from "rxjs";
|
|
2
2
|
import { NostrEvent } from "nostr-tools";
|
|
3
|
-
import { SubscriptionResponse } from "../
|
|
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>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { IEventStore } from "applesauce-core";
|
|
2
|
+
import { MonoTypeOperatorFunction } from "rxjs";
|
|
3
|
+
import { SubscriptionResponse } from "../types.js";
|
|
4
|
+
/** Sends all events to the event store */
|
|
5
|
+
export declare function storeEvents(eventStore: IEventStore): MonoTypeOperatorFunction<SubscriptionResponse>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { OperatorFunction } from "rxjs";
|
|
2
|
+
import { IEventStore } from "applesauce-core";
|
|
3
|
+
import { NostrEvent } from "nostr-tools";
|
|
4
|
+
import { SubscriptionResponse } from "../types.js";
|
|
5
|
+
/** Adds all events to event store and returns a deduplicated timeline when EOSE is received */
|
|
6
|
+
export declare function toEventStore(eventStore: IEventStore): OperatorFunction<SubscriptionResponse, NostrEvent[]>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { scan } from "rxjs";
|
|
2
|
+
import { insertEventIntoDescendingList } from "nostr-tools/utils";
|
|
3
|
+
import { completeOnEose } from "./complete-on-eose.js";
|
|
4
|
+
/** Adds all events to event store and returns a deduplicated timeline when EOSE is received */
|
|
5
|
+
export function toEventStore(eventStore) {
|
|
6
|
+
return (source) => source.pipe(
|
|
7
|
+
// Complete when there are not events
|
|
8
|
+
completeOnEose(),
|
|
9
|
+
// Add the events to an array
|
|
10
|
+
scan((events, event) => {
|
|
11
|
+
// Get the current instance of this event
|
|
12
|
+
let e = eventStore.add(event);
|
|
13
|
+
// If its not in the timeline, add it
|
|
14
|
+
if (events.includes(e))
|
|
15
|
+
return events;
|
|
16
|
+
else
|
|
17
|
+
return insertEventIntoDescendingList(events, e);
|
|
18
|
+
}, []));
|
|
19
|
+
}
|
package/dist/pool.d.ts
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
import { NostrEvent, type Filter } from "nostr-tools";
|
|
2
2
|
import { Observable } from "rxjs";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { RelayGroup } from "./group.js";
|
|
4
|
+
import { Relay, RelayOptions } from "./relay.js";
|
|
5
|
+
import { IPool, PublishResponse, PublishOptions, RequestOptions, SubscriptionOptions, SubscriptionResponse } from "./types.js";
|
|
6
|
+
export declare class RelayPool implements IPool {
|
|
7
|
+
options?: RelayOptions | undefined;
|
|
5
8
|
relays: Map<string, Relay>;
|
|
9
|
+
groups: Map<string, RelayGroup>;
|
|
10
|
+
constructor(options?: RelayOptions | undefined);
|
|
6
11
|
/** Get or create a new relay connection */
|
|
7
12
|
relay(url: string): Relay;
|
|
8
|
-
/**
|
|
9
|
-
|
|
13
|
+
/** Create a group of relays */
|
|
14
|
+
group(relays: string[]): RelayGroup;
|
|
15
|
+
/** Make a REQ to multiple relays that does not deduplicate events */
|
|
16
|
+
req(relays: string[], filters: Filter | Filter[], id?: string): Observable<SubscriptionResponse>;
|
|
10
17
|
/** Send an EVENT message to multiple relays */
|
|
11
|
-
event(relays: string[], event: NostrEvent): Observable<PublishResponse
|
|
18
|
+
event(relays: string[], event: NostrEvent): Observable<PublishResponse>;
|
|
19
|
+
/** Publish an event to multiple relays */
|
|
20
|
+
publish(relays: string[], event: NostrEvent, opts?: PublishOptions): Observable<PublishResponse[]>;
|
|
21
|
+
/** Request events from multiple relays */
|
|
22
|
+
request(relays: string[], filters: Filter | Filter[], opts?: RequestOptions): Observable<NostrEvent>;
|
|
23
|
+
/** Open a subscription to multiple relays */
|
|
24
|
+
subscription(relays: string[], filters: Filter | Filter[], opts?: SubscriptionOptions): Observable<SubscriptionResponse>;
|
|
12
25
|
}
|
package/dist/pool.js
CHANGED
|
@@ -1,41 +1,51 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { nanoid } from "nanoid";
|
|
1
|
+
import { RelayGroup } from "./group.js";
|
|
3
2
|
import { Relay } from "./relay.js";
|
|
4
|
-
import { onlyEvents } from "./operators/only-events.js";
|
|
5
3
|
export class RelayPool {
|
|
4
|
+
options;
|
|
6
5
|
relays = new Map();
|
|
6
|
+
groups = new Map();
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
}
|
|
7
10
|
/** Get or create a new relay connection */
|
|
8
11
|
relay(url) {
|
|
9
12
|
let relay = this.relays.get(url);
|
|
10
13
|
if (relay)
|
|
11
14
|
return relay;
|
|
12
15
|
else {
|
|
13
|
-
relay = new Relay(url);
|
|
16
|
+
relay = new Relay(url, this.options);
|
|
14
17
|
this.relays.set(url, relay);
|
|
15
18
|
return relay;
|
|
16
19
|
}
|
|
17
20
|
}
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
endWith("EOSE"));
|
|
32
|
-
// create a stream that only emits events
|
|
33
|
-
const events = merge(...requests).pipe(onlyEvents());
|
|
34
|
-
// merge events and single EOSE streams
|
|
35
|
-
return merge(events, eose);
|
|
21
|
+
/** Create a group of relays */
|
|
22
|
+
group(relays) {
|
|
23
|
+
const key = relays.sort().join(",");
|
|
24
|
+
let group = this.groups.get(key);
|
|
25
|
+
if (group)
|
|
26
|
+
return group;
|
|
27
|
+
group = new RelayGroup(relays.map((url) => this.relay(url)));
|
|
28
|
+
this.groups.set(key, group);
|
|
29
|
+
return group;
|
|
30
|
+
}
|
|
31
|
+
/** Make a REQ to multiple relays that does not deduplicate events */
|
|
32
|
+
req(relays, filters, id) {
|
|
33
|
+
return this.group(relays).req(filters, id);
|
|
36
34
|
}
|
|
37
35
|
/** Send an EVENT message to multiple relays */
|
|
38
36
|
event(relays, event) {
|
|
39
|
-
return
|
|
37
|
+
return this.group(relays).event(event);
|
|
38
|
+
}
|
|
39
|
+
/** Publish an event to multiple relays */
|
|
40
|
+
publish(relays, event, opts) {
|
|
41
|
+
return this.group(relays).publish(event, opts);
|
|
42
|
+
}
|
|
43
|
+
/** Request events from multiple relays */
|
|
44
|
+
request(relays, filters, opts) {
|
|
45
|
+
return this.group(relays).request(filters, opts);
|
|
46
|
+
}
|
|
47
|
+
/** Open a subscription to multiple relays */
|
|
48
|
+
subscription(relays, filters, opts) {
|
|
49
|
+
return this.group(relays).subscription(filters, opts);
|
|
40
50
|
}
|
|
41
51
|
}
|
package/dist/relay.d.ts
CHANGED
|
@@ -1,35 +1,86 @@
|
|
|
1
|
+
import { logger } from "applesauce-core";
|
|
2
|
+
import { type Filter, type NostrEvent } from "nostr-tools";
|
|
1
3
|
import { BehaviorSubject, Observable } from "rxjs";
|
|
2
4
|
import { WebSocketSubject, WebSocketSubjectConfig } from "rxjs/webSocket";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
message?: string;
|
|
9
|
-
from: string;
|
|
10
|
-
};
|
|
5
|
+
import { RelayInformation } from "nostr-tools/nip11";
|
|
6
|
+
import { AuthSigner, IRelay, PublishOptions, PublishResponse, RequestOptions, SubscriptionOptions, SubscriptionResponse } from "./types.js";
|
|
7
|
+
/** An error that is thrown when a REQ is closed from the relay side */
|
|
8
|
+
export declare class ReqCloseError extends Error {
|
|
9
|
+
}
|
|
11
10
|
export type RelayOptions = {
|
|
12
11
|
WebSocket?: WebSocketSubjectConfig<any>["WebSocketCtor"];
|
|
13
12
|
};
|
|
14
|
-
export declare class Relay {
|
|
13
|
+
export declare class Relay implements IRelay {
|
|
15
14
|
url: string;
|
|
16
|
-
log: typeof logger;
|
|
17
|
-
socket
|
|
15
|
+
protected log: typeof logger;
|
|
16
|
+
protected socket: WebSocketSubject<any>;
|
|
17
|
+
/** Whether the relay is ready for subscriptions or event publishing. setting this to false will cause all .req and .event observables to hang until the relay is ready */
|
|
18
|
+
protected ready$: BehaviorSubject<boolean>;
|
|
19
|
+
/** A method that returns an Observable that emits when the relay should reconnect */
|
|
20
|
+
reconnectTimer: (error: CloseEvent | Error, attempts: number) => Observable<number>;
|
|
21
|
+
/** How many times the relay has tried to reconnect */
|
|
22
|
+
attempts$: BehaviorSubject<number>;
|
|
23
|
+
/** Whether the relay is connected */
|
|
18
24
|
connected$: BehaviorSubject<boolean>;
|
|
19
|
-
challenge
|
|
25
|
+
/** The authentication challenge string from the relay */
|
|
26
|
+
challenge$: BehaviorSubject<string | null>;
|
|
27
|
+
/** Whether the client is authenticated with the relay */
|
|
20
28
|
authenticated$: BehaviorSubject<boolean>;
|
|
21
|
-
notices
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
/** The notices from the relay */
|
|
30
|
+
notices$: BehaviorSubject<string[]>;
|
|
31
|
+
/** An observable that emits the NIP-11 information document for the relay */
|
|
32
|
+
information$: Observable<RelayInformation | null>;
|
|
33
|
+
protected _nip11: RelayInformation | null;
|
|
34
|
+
/** An observable that emits the limitations for the relay */
|
|
35
|
+
limitations$: Observable<RelayInformation["limitation"] | null>;
|
|
36
|
+
/** An observable of all messages from the relay */
|
|
37
|
+
message$: Observable<any>;
|
|
38
|
+
/** An observable of NOTICE messages from the relay */
|
|
39
|
+
notice$: Observable<string>;
|
|
40
|
+
get connected(): boolean;
|
|
41
|
+
get challenge(): string | null;
|
|
42
|
+
get notices(): string[];
|
|
43
|
+
get authenticated(): boolean;
|
|
44
|
+
get information(): RelayInformation | null;
|
|
45
|
+
/** If an EOSE message is not seen in this time, emit one locally */
|
|
46
|
+
eoseTimeout: number;
|
|
47
|
+
/** How long to wait for an OK message from the relay */
|
|
48
|
+
eventTimeout: number;
|
|
49
|
+
/** How long to keep the connection alive after nothing is subscribed */
|
|
50
|
+
keepAlive: number;
|
|
51
|
+
protected receivedAuthRequiredForReq: BehaviorSubject<boolean>;
|
|
52
|
+
protected receivedAuthRequiredForEvent: BehaviorSubject<boolean>;
|
|
53
|
+
protected authRequiredForReq: Observable<boolean>;
|
|
54
|
+
protected authRequiredForEvent: Observable<boolean>;
|
|
55
|
+
protected resetState(): void;
|
|
56
|
+
/** An internal observable that is responsible for watching all messages and updating state */
|
|
57
|
+
protected watchTower: Observable<never>;
|
|
25
58
|
constructor(url: string, opts?: RelayOptions);
|
|
59
|
+
/** Set ready = false and start the reconnect timer */
|
|
60
|
+
protected startReconnectTimer(error: Error | CloseEvent): void;
|
|
61
|
+
/** Wait for ready and authenticated */
|
|
26
62
|
protected waitForAuth<T extends unknown = unknown>(requireAuth: Observable<boolean>, observable: Observable<T>): Observable<T>;
|
|
27
|
-
|
|
28
|
-
|
|
63
|
+
/** Wait for the relay to be ready to accept connections */
|
|
64
|
+
protected waitForReady<T extends unknown = unknown>(observable: Observable<T>): Observable<T>;
|
|
65
|
+
multiplex<T>(open: () => any, close: () => any, filter: (message: any) => boolean): Observable<T>;
|
|
66
|
+
/** Send a message to the relay */
|
|
67
|
+
next(message: any): void;
|
|
68
|
+
/** Create a REQ observable that emits events or "EOSE" or errors */
|
|
69
|
+
req(filters: Filter | Filter[], id?: string): Observable<SubscriptionResponse>;
|
|
70
|
+
/** Send an EVENT or AUTH message and return an observable of PublishResponse that completes or errors */
|
|
29
71
|
event(event: NostrEvent, verb?: "EVENT" | "AUTH"): Observable<PublishResponse>;
|
|
30
72
|
/** send and AUTH message */
|
|
31
|
-
auth(event: NostrEvent): Observable<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
73
|
+
auth(event: NostrEvent): Observable<PublishResponse>;
|
|
74
|
+
/** Authenticate with the relay using a signer */
|
|
75
|
+
authenticate(signer: AuthSigner): Observable<PublishResponse>;
|
|
76
|
+
/** Creates a REQ that retries when relay errors ( default 3 retries ) */
|
|
77
|
+
subscription(filters: Filter | Filter[], opts?: SubscriptionOptions): Observable<SubscriptionResponse>;
|
|
78
|
+
/** Makes a single request that retires on errors and completes on EOSE */
|
|
79
|
+
request(filters: Filter | Filter[], opts?: RequestOptions): Observable<NostrEvent>;
|
|
80
|
+
/** Publishes an event to the relay and retries when relay errors or responds with auth-required ( default 3 retries ) */
|
|
81
|
+
publish(event: NostrEvent, opts?: PublishOptions): Observable<PublishResponse>;
|
|
82
|
+
/** Static method to fetch the NIP-11 information document for a relay */
|
|
83
|
+
static fetchInformationDocument(url: string): Observable<RelayInformation | null>;
|
|
84
|
+
/** Static method to create a reconnection method for each relay */
|
|
85
|
+
static createReconnectTimer(_relay: string): (_error?: Error | CloseEvent, tries?: number) => Observable<0>;
|
|
35
86
|
}
|