applesauce-relay 0.0.0-next-20251020143053 → 0.0.0-next-20251027164028
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/negentropy.js +41 -8
- package/dist/pool.d.ts +4 -1
- package/dist/pool.js +10 -1
- package/dist/relay.js +17 -3
- package/package.json +3 -3
package/dist/negentropy.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logger } from "applesauce-core";
|
|
2
|
-
import { map, share, firstValueFrom } from "rxjs";
|
|
2
|
+
import { map, share, firstValueFrom, race, Observable } from "rxjs";
|
|
3
3
|
import { nanoid } from "nanoid";
|
|
4
4
|
import { Negentropy, NegentropyStorageVector } from "./lib/negentropy.js";
|
|
5
5
|
const log = logger.extend("negentropy");
|
|
@@ -52,16 +52,49 @@ export async function negentropySync(storage, socket, filter, reconcile, opts) {
|
|
|
52
52
|
throw new Error(msg[2]);
|
|
53
53
|
return msg[2];
|
|
54
54
|
}), share());
|
|
55
|
+
// Check if already aborted before starting sync
|
|
56
|
+
if (opts?.signal?.aborted)
|
|
57
|
+
return false;
|
|
58
|
+
// Create an observable that emits when abort signal is triggered
|
|
59
|
+
const abortSignal$ = new Observable((observer) => {
|
|
60
|
+
if (opts?.signal?.aborted) {
|
|
61
|
+
observer.next("abort");
|
|
62
|
+
observer.complete();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const onAbort = () => {
|
|
66
|
+
observer.next("abort");
|
|
67
|
+
observer.complete();
|
|
68
|
+
};
|
|
69
|
+
opts?.signal?.addEventListener("abort", onAbort);
|
|
70
|
+
return () => opts?.signal?.removeEventListener("abort", onAbort);
|
|
71
|
+
});
|
|
55
72
|
// keep an additional subscription open while waiting for async operations
|
|
56
|
-
const sub = incoming.subscribe(
|
|
73
|
+
const sub = incoming.subscribe({
|
|
74
|
+
next: (m) => log(m),
|
|
75
|
+
error: () => { }, // Ignore errors here, they'll be caught by firstValueFrom
|
|
76
|
+
});
|
|
57
77
|
try {
|
|
58
78
|
while (msg && opts?.signal?.aborted !== true) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
79
|
+
// Race between incoming message and abort signal
|
|
80
|
+
try {
|
|
81
|
+
const received = await firstValueFrom(race(incoming.pipe(map((m) => ({ type: "message", data: m }))), abortSignal$.pipe(map(() => ({ type: "abort" })))));
|
|
82
|
+
if (received.type === "abort" || opts?.signal?.aborted) {
|
|
83
|
+
sub.unsubscribe();
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
const [newMsg, have, need] = await ne.reconcile(received.data);
|
|
87
|
+
await reconcile(have, need);
|
|
88
|
+
msg = newMsg;
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
// Check if aborted during reconcile or message processing
|
|
92
|
+
if (opts?.signal?.aborted) {
|
|
93
|
+
sub.unsubscribe();
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
65
98
|
}
|
|
66
99
|
}
|
|
67
100
|
catch (err) {
|
package/dist/pool.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IAsyncEventStoreRead, IEventStoreRead } from "applesauce-core";
|
|
2
|
+
import { FilterMap, OutboxMap } from "applesauce-core/helpers";
|
|
2
3
|
import { Filter, type NostrEvent } from "nostr-tools";
|
|
3
4
|
import { BehaviorSubject, Observable, Subject } from "rxjs";
|
|
4
5
|
import { RelayGroup } from "./group.js";
|
|
@@ -33,7 +34,9 @@ export declare class RelayPool implements IPool {
|
|
|
33
34
|
/** Open a subscription to multiple relays */
|
|
34
35
|
subscription(relays: IPoolRelayInput, filters: FilterInput, options?: Parameters<RelayGroup["subscription"]>[1]): Observable<SubscriptionResponse>;
|
|
35
36
|
/** Open a subscription for a map of relays and filters */
|
|
36
|
-
subscriptionMap(relays:
|
|
37
|
+
subscriptionMap(relays: FilterMap | Observable<FilterMap>, options?: Parameters<RelayGroup["subscription"]>[1]): Observable<SubscriptionResponse>;
|
|
38
|
+
/** Open a subscription for an {@link OutboxMap} and filter */
|
|
39
|
+
outboxSubscription(outboxes: OutboxMap | Observable<OutboxMap>, filter: Omit<Filter, "authors">, options?: Parameters<RelayGroup["subscription"]>[1]): Observable<SubscriptionResponse>;
|
|
37
40
|
/** Count events on multiple relays */
|
|
38
41
|
count(relays: IPoolRelayInput, filters: Filter | Filter[], id?: string): Observable<Record<string, CountResponse>>;
|
|
39
42
|
/** Negentropy sync events with the relays and an event store */
|
package/dist/pool.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isFilterEqual, normalizeURL } from "applesauce-core/helpers";
|
|
1
|
+
import { createFilterMap, isFilterEqual, normalizeURL } from "applesauce-core/helpers";
|
|
2
2
|
import { BehaviorSubject, distinctUntilChanged, isObservable, map, of, Subject } from "rxjs";
|
|
3
3
|
import { RelayGroup } from "./group.js";
|
|
4
4
|
import { Relay } from "./relay.js";
|
|
@@ -94,6 +94,15 @@ export class RelayPool {
|
|
|
94
94
|
distinctUntilChanged(isFilterEqual));
|
|
95
95
|
}, options);
|
|
96
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
|
+
}
|
|
97
106
|
/** Count events on multiple relays */
|
|
98
107
|
count(relays, filters, id) {
|
|
99
108
|
return this.group(relays).count(filters, id);
|
package/dist/relay.js
CHANGED
|
@@ -506,6 +506,14 @@ export class Relay {
|
|
|
506
506
|
};
|
|
507
507
|
return new Observable((observer) => {
|
|
508
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
|
+
};
|
|
509
517
|
this.negentropy(store, filter, async (have, need) => {
|
|
510
518
|
// NOTE: it may be more efficient to sync all the events later in a single batch
|
|
511
519
|
// Send missing events to the relay
|
|
@@ -520,11 +528,17 @@ export class Relay {
|
|
|
520
528
|
}
|
|
521
529
|
}, { signal: controller.signal })
|
|
522
530
|
// Complete the observable when the sync is complete
|
|
523
|
-
.then(() =>
|
|
531
|
+
.then(() => {
|
|
532
|
+
if (!cleanupCalled)
|
|
533
|
+
observer.complete();
|
|
534
|
+
})
|
|
524
535
|
// Error the observable when the sync fails
|
|
525
|
-
.catch((err) =>
|
|
536
|
+
.catch((err) => {
|
|
537
|
+
if (!cleanupCalled)
|
|
538
|
+
observer.error(err);
|
|
539
|
+
});
|
|
526
540
|
// Cancel the sync when the observable is unsubscribed
|
|
527
|
-
return
|
|
541
|
+
return cleanup;
|
|
528
542
|
}).pipe(
|
|
529
543
|
// Only create one upstream subscription
|
|
530
544
|
share());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-relay",
|
|
3
|
-
"version": "0.0.0-next-
|
|
3
|
+
"version": "0.0.0-next-20251027164028",
|
|
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": "0.0.0-next-
|
|
55
|
+
"applesauce-core": "0.0.0-next-20251027164028",
|
|
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": "
|
|
62
|
+
"applesauce-signers": "0.0.0-next-20251027164028",
|
|
63
63
|
"rimraf": "^6.0.1",
|
|
64
64
|
"typescript": "^5.7.3",
|
|
65
65
|
"vitest": "^3.2.4",
|