@peerbit/pubsub 5.3.9 → 5.3.11
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/src/fanout-tree.d.ts +1 -0
- package/dist/src/fanout-tree.d.ts.map +1 -1
- package/dist/src/fanout-tree.js +43 -12
- package/dist/src/fanout-tree.js.map +1 -1
- package/dist/src/index.d.ts +15 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +256 -82
- package/dist/src/index.js.map +1 -1
- package/dist/src/topic-root-control-plane.d.ts +11 -8
- package/dist/src/topic-root-control-plane.d.ts.map +1 -1
- package/dist/src/topic-root-control-plane.js +51 -15
- package/dist/src/topic-root-control-plane.js.map +1 -1
- package/package.json +4 -4
- package/src/fanout-tree.ts +51 -12
- package/src/index.ts +356 -109
- package/src/topic-root-control-plane.ts +89 -15
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RustTopicRootDirectoryState } from "@peerbit/stream";
|
|
2
|
+
import { AbortError } from "@peerbit/time";
|
|
2
3
|
|
|
3
4
|
const topicHash32 = (topic: string) => {
|
|
4
5
|
let hash = 0x811c9dc5; // FNV-1a
|
|
@@ -9,12 +10,20 @@ const topicHash32 = (topic: string) => {
|
|
|
9
10
|
return hash >>> 0;
|
|
10
11
|
};
|
|
11
12
|
|
|
13
|
+
export type TopicRootResolutionOptions = {
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
};
|
|
16
|
+
|
|
12
17
|
export type TopicRootResolver = (
|
|
13
18
|
topic: string,
|
|
19
|
+
options?: TopicRootResolutionOptions,
|
|
14
20
|
) => string | undefined | Promise<string | undefined>;
|
|
15
21
|
|
|
16
22
|
export type TopicRootTracker = {
|
|
17
|
-
resolveRoot(
|
|
23
|
+
resolveRoot(
|
|
24
|
+
topic: string,
|
|
25
|
+
options?: TopicRootResolutionOptions,
|
|
26
|
+
): string | undefined | Promise<string | undefined>;
|
|
18
27
|
};
|
|
19
28
|
|
|
20
29
|
export type TopicRootDirectoryOptions = {
|
|
@@ -22,6 +31,43 @@ export type TopicRootDirectoryOptions = {
|
|
|
22
31
|
resolver?: TopicRootResolver;
|
|
23
32
|
};
|
|
24
33
|
|
|
34
|
+
const abortReason = (signal: AbortSignal) =>
|
|
35
|
+
signal.reason ?? new AbortError("Topic-root resolution aborted");
|
|
36
|
+
|
|
37
|
+
const throwIfAborted = (signal?: AbortSignal): void => {
|
|
38
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const awaitWithSignal = async <T>(
|
|
42
|
+
value: T | PromiseLike<T>,
|
|
43
|
+
signal?: AbortSignal,
|
|
44
|
+
): Promise<T> => {
|
|
45
|
+
if (signal?.aborted) {
|
|
46
|
+
void Promise.resolve(value).catch(() => {});
|
|
47
|
+
throw abortReason(signal);
|
|
48
|
+
}
|
|
49
|
+
if (!signal) return await value;
|
|
50
|
+
|
|
51
|
+
return new Promise<T>((resolve, reject) => {
|
|
52
|
+
const onAbort = () => {
|
|
53
|
+
cleanup();
|
|
54
|
+
reject(abortReason(signal));
|
|
55
|
+
};
|
|
56
|
+
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
57
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
58
|
+
Promise.resolve(value).then(
|
|
59
|
+
(result) => {
|
|
60
|
+
cleanup();
|
|
61
|
+
resolve(result);
|
|
62
|
+
},
|
|
63
|
+
(error) => {
|
|
64
|
+
cleanup();
|
|
65
|
+
reject(error);
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
25
71
|
export class TopicRootDirectory {
|
|
26
72
|
private readonly explicitRootsByTopic = new Map<string, string>();
|
|
27
73
|
private defaultCandidates: string[] = [];
|
|
@@ -103,18 +149,27 @@ export class TopicRootDirectory {
|
|
|
103
149
|
this.resolver = resolver;
|
|
104
150
|
}
|
|
105
151
|
|
|
106
|
-
public async resolveRoot(
|
|
107
|
-
|
|
152
|
+
public async resolveRoot(
|
|
153
|
+
topic: string,
|
|
154
|
+
options?: TopicRootResolutionOptions,
|
|
155
|
+
): Promise<string | undefined> {
|
|
156
|
+
const local = await this.resolveLocal(topic, options);
|
|
108
157
|
if (local) return local;
|
|
109
158
|
|
|
159
|
+
throwIfAborted(options?.signal);
|
|
110
160
|
return this.resolveDeterministicCandidate(topic);
|
|
111
161
|
}
|
|
112
162
|
|
|
113
|
-
public async resolveLocal(
|
|
163
|
+
public async resolveLocal(
|
|
164
|
+
topic: string,
|
|
165
|
+
options?: TopicRootResolutionOptions,
|
|
166
|
+
): Promise<string | undefined> {
|
|
167
|
+
throwIfAborted(options?.signal);
|
|
114
168
|
const explicit = this.getRoot(topic);
|
|
115
169
|
if (explicit) return explicit;
|
|
116
170
|
|
|
117
|
-
|
|
171
|
+
if (!this.resolver) return undefined;
|
|
172
|
+
return awaitWithSignal(this.resolver(topic, options), options?.signal);
|
|
118
173
|
}
|
|
119
174
|
|
|
120
175
|
public resolveDeterministicCandidate(topic: string): string | undefined {
|
|
@@ -183,45 +238,64 @@ export class TopicRootControlPlane {
|
|
|
183
238
|
return [...this.trackers];
|
|
184
239
|
}
|
|
185
240
|
|
|
186
|
-
public resolveLocalTopicRoot(
|
|
187
|
-
|
|
241
|
+
public resolveLocalTopicRoot(
|
|
242
|
+
topic: string,
|
|
243
|
+
options?: TopicRootResolutionOptions,
|
|
244
|
+
) {
|
|
245
|
+
return this.directory.resolveLocal(topic, options);
|
|
188
246
|
}
|
|
189
247
|
|
|
190
248
|
public resolveDeterministicTopicRoot(topic: string) {
|
|
191
249
|
return this.directory.resolveDeterministicCandidate(topic);
|
|
192
250
|
}
|
|
193
251
|
|
|
194
|
-
public resolveCanonicalTopicRoot(
|
|
195
|
-
|
|
252
|
+
public resolveCanonicalTopicRoot(
|
|
253
|
+
topic: string,
|
|
254
|
+
options?: TopicRootResolutionOptions,
|
|
255
|
+
) {
|
|
256
|
+
return this.directory.resolveRoot(topic, options);
|
|
196
257
|
}
|
|
197
258
|
|
|
198
|
-
public resolveTrackedTopicRoot(
|
|
199
|
-
|
|
259
|
+
public resolveTrackedTopicRoot(
|
|
260
|
+
topic: string,
|
|
261
|
+
options?: TopicRootResolutionOptions,
|
|
262
|
+
) {
|
|
263
|
+
return this.resolveWithTrackers(topic, false, options);
|
|
200
264
|
}
|
|
201
265
|
|
|
202
|
-
public resolveTopicRoot(
|
|
203
|
-
|
|
266
|
+
public resolveTopicRoot(
|
|
267
|
+
topic: string,
|
|
268
|
+
options?: TopicRootResolutionOptions,
|
|
269
|
+
) {
|
|
270
|
+
return this.resolveWithTrackers(topic, true, options);
|
|
204
271
|
}
|
|
205
272
|
|
|
206
273
|
private async resolveWithTrackers(
|
|
207
274
|
topic: string,
|
|
208
275
|
fallbackToDeterministic = true,
|
|
276
|
+
options?: TopicRootResolutionOptions,
|
|
209
277
|
): Promise<string | undefined> {
|
|
210
|
-
const local = await this.directory.resolveLocal(topic);
|
|
278
|
+
const local = await this.directory.resolveLocal(topic, options);
|
|
211
279
|
if (local) {
|
|
212
280
|
return local;
|
|
213
281
|
}
|
|
214
282
|
|
|
215
283
|
for (const tracker of this.trackers) {
|
|
216
284
|
try {
|
|
217
|
-
|
|
285
|
+
throwIfAborted(options?.signal);
|
|
286
|
+
const resolved = await awaitWithSignal(
|
|
287
|
+
tracker.resolveRoot(topic, options),
|
|
288
|
+
options?.signal,
|
|
289
|
+
);
|
|
218
290
|
if (resolved) {
|
|
219
291
|
return resolved;
|
|
220
292
|
}
|
|
221
293
|
} catch {
|
|
294
|
+
throwIfAborted(options?.signal);
|
|
222
295
|
// ignore tracker failures and continue with remaining trackers
|
|
223
296
|
}
|
|
224
297
|
}
|
|
298
|
+
throwIfAborted(options?.signal);
|
|
225
299
|
return fallbackToDeterministic
|
|
226
300
|
? this.directory.resolveDeterministicCandidate(topic)
|
|
227
301
|
: undefined;
|