@spider-mesh/core 2.0.41 → 2.0.43
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 +193 -372
- package/build/src/Registry.d.ts +17 -0
- package/build/src/Registry.js +76 -0
- package/build/src/Registry.js.map +1 -0
- package/build/src/RemoteService.d.ts +3 -9
- package/build/src/RemoteService.js +4 -5
- package/build/src/RemoteService.js.map +1 -1
- package/build/src/SpiderMesh.d.ts +8 -19
- package/build/src/SpiderMesh.js +225 -237
- package/build/src/SpiderMesh.js.map +1 -1
- package/build/src/decorators/NestJSLinkMicroservice.d.ts +3 -2
- package/build/src/decorators/NestJSLinkMicroservice.js +2 -5
- package/build/src/decorators/NestJSLinkMicroservice.js.map +1 -1
- package/build/src/index.d.ts +2 -2
- package/build/src/index.js +1 -1
- package/build/src/index.js.map +1 -1
- package/build/src/types.d.ts +17 -14
- package/build/tests/fixtures/process-mesh.d.ts +1 -0
- package/build/tests/fixtures/process-mesh.js +91 -0
- package/build/tests/fixtures/process-mesh.js.map +1 -0
- package/build/tests/mock-e2e.test.d.ts +1 -0
- package/build/tests/mock-e2e.test.js +173 -0
- package/build/tests/mock-e2e.test.js.map +1 -0
- package/build/tests/process-e2e.test.d.ts +1 -0
- package/build/tests/process-e2e.test.js +93 -0
- package/build/tests/process-e2e.test.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/tsconfig.json +1 -3
- package/build/src/helpers/randomUUID.d.ts +0 -1
- package/build/src/helpers/randomUUID.js +0 -13
- package/build/src/helpers/randomUUID.js.map +0 -1
package/build/src/SpiderMesh.js
CHANGED
|
@@ -1,110 +1,98 @@
|
|
|
1
|
-
import { BehaviorSubject, catchError, EMPTY,
|
|
1
|
+
import { BehaviorSubject, catchError, EMPTY, finalize, firstValueFrom, from, lastValueFrom, map, mergeMap, Observable, of, retry, share, Subject, Subscription, switchMap, take, tap, throwError, timeout, timer } from "rxjs";
|
|
2
2
|
import { listBeforeMicroserviceOnlineMethods } from "./decorators/BeforeMicroserviceOnline.js";
|
|
3
3
|
import { LOCAL_SERVICES$ } from "./decorators/Microservice.js";
|
|
4
4
|
import { SPIDERMESH_NAMESPACE, SPIDERMESH_NODE_HOSTNAME } from "../const.js";
|
|
5
5
|
const isSubscribable = (value) => {
|
|
6
6
|
return !!value && typeof value === 'object' && typeof value.subscribe === 'function';
|
|
7
7
|
};
|
|
8
|
+
const isNamedTransporter = (value) => {
|
|
9
|
+
return !!value && typeof value === 'object';
|
|
10
|
+
};
|
|
8
11
|
export class SpiderMesh {
|
|
9
|
-
|
|
10
|
-
node_id = `${Date.now().toString(36).toUpperCase()}`;
|
|
12
|
+
registry;
|
|
13
|
+
node_id = `${Date.now().toString(36).toUpperCase()}|${Math.random().toString(36).slice(10).toUpperCase()}`;
|
|
11
14
|
namespace = SPIDERMESH_NAMESPACE;
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
#transporters = {
|
|
16
|
+
rpcs: new Map(),
|
|
17
|
+
pubsubs: new Map(),
|
|
18
|
+
discoveries: new Map()
|
|
19
|
+
};
|
|
20
|
+
#localServices = new Map();
|
|
21
|
+
#me$ = new BehaviorSubject({
|
|
16
22
|
host: SPIDERMESH_NODE_HOSTNAME,
|
|
17
23
|
namespace: SPIDERMESH_NAMESPACE,
|
|
18
24
|
node_id: this.node_id,
|
|
25
|
+
topics: [],
|
|
19
26
|
services: {},
|
|
20
27
|
transporters: {},
|
|
21
28
|
nodes: {},
|
|
22
29
|
version: 0,
|
|
23
30
|
});
|
|
24
|
-
#nodes$ = new BehaviorSubject({
|
|
25
|
-
nodes: new Map(),
|
|
26
|
-
last_updated_node_id: ''
|
|
27
|
-
});
|
|
28
|
-
#local_services = new Map();
|
|
29
31
|
#rpc = {
|
|
30
|
-
indexes: new Map(),
|
|
31
32
|
pending: new Map(),
|
|
32
33
|
running: new Map()
|
|
33
34
|
};
|
|
34
|
-
constructor(
|
|
35
|
-
this.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
constructor(registry) {
|
|
36
|
+
this.registry = registry;
|
|
37
|
+
LOCAL_SERVICES$.pipe(mergeMap(async (service) => {
|
|
38
|
+
const list = listBeforeMicroserviceOnlineMethods(service.instance);
|
|
39
|
+
for (const method of list) {
|
|
40
|
+
await service.instance[method]();
|
|
41
|
+
}
|
|
42
|
+
this.#localServices.set(service.name, service.instance);
|
|
43
|
+
this.#refresh({
|
|
44
|
+
services: {
|
|
45
|
+
[service.name]: service.metadata
|
|
43
46
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
...this.#metadata$.value,
|
|
47
|
-
services: {
|
|
48
|
-
...this.#metadata$.value.services,
|
|
49
|
-
[service.name]: service.metadata
|
|
50
|
-
},
|
|
51
|
-
version: this.#metadata$.value.version + 1
|
|
52
|
-
};
|
|
53
|
-
this.#metadata$.next(metadata);
|
|
54
|
-
}, 1), catchError(e => EMPTY)).subscribe();
|
|
55
|
-
this.#linkTransporters();
|
|
56
|
-
}, 0);
|
|
47
|
+
});
|
|
48
|
+
}, 1), catchError(() => EMPTY)).subscribe();
|
|
57
49
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
registerTransporter(meshTransporter, name) {
|
|
51
|
+
const resolvedName = name
|
|
52
|
+
|| (isNamedTransporter(meshTransporter) ? meshTransporter.constructor?.name : undefined)
|
|
53
|
+
|| 'AnonymousTransporter';
|
|
54
|
+
const subscription = new Subscription();
|
|
55
|
+
if (this.registry && typeof meshTransporter.linkRegistry === 'function') {
|
|
56
|
+
meshTransporter.linkRegistry(this.registry);
|
|
57
|
+
}
|
|
58
|
+
if (this.#isRpcTransporter(meshTransporter)) {
|
|
59
|
+
subscription.add(this.#linkRpcTransporter(resolvedName, meshTransporter).subscribe());
|
|
60
|
+
}
|
|
61
|
+
if (this.#isPubsubTransporter(meshTransporter)) {
|
|
62
|
+
subscription.add(this.#linkPubsubTransporter(resolvedName, meshTransporter).subscribe());
|
|
63
|
+
}
|
|
64
|
+
if (this.#isDiscoveryTransporter(meshTransporter)) {
|
|
65
|
+
subscription.add(this.#linkDiscoveryTransporter(resolvedName, meshTransporter).subscribe());
|
|
66
|
+
}
|
|
67
|
+
return subscription;
|
|
63
68
|
}
|
|
64
69
|
listRpcNodes(service) {
|
|
65
|
-
|
|
66
|
-
return
|
|
67
|
-
|
|
70
|
+
if (!this.registry)
|
|
71
|
+
return [];
|
|
72
|
+
return this.registry.listPeers(service).filter(node => !!node.transporters.rpc);
|
|
68
73
|
}
|
|
69
74
|
watchService(service) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (e.last_updated_node_id.startsWith('offline:')) {
|
|
74
|
-
return e.last_updated_node_id.split(':')[1].includes(service);
|
|
75
|
-
}
|
|
76
|
-
const node = e.nodes.get(e.last_updated_node_id);
|
|
77
|
-
return node ? node.services[service] != undefined : false;
|
|
78
|
-
}), map(e => [...e.nodes.values()].filter(node => node.services[service] != undefined)));
|
|
75
|
+
if (!this.registry)
|
|
76
|
+
return EMPTY;
|
|
77
|
+
return this.registry.watch(service).pipe(map(() => this.listRpcNodes(service)));
|
|
79
78
|
}
|
|
80
|
-
#
|
|
79
|
+
#selectRpcTransport(filters = {}) {
|
|
81
80
|
if (!filters.service)
|
|
82
|
-
return
|
|
83
|
-
if (filters.
|
|
84
|
-
const
|
|
85
|
-
if (!
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
return null;
|
|
89
|
-
if (!node.rpc)
|
|
90
|
-
return null;
|
|
91
|
-
const transporter = this.#rpcs.get(node.rpc);
|
|
92
|
-
if (!transporter)
|
|
93
|
-
return null;
|
|
94
|
-
return { node, transporter };
|
|
81
|
+
return undefined;
|
|
82
|
+
if (filters.transporter) {
|
|
83
|
+
const name = typeof filters.transporter === 'string' ? filters.transporter : filters.transporter.name;
|
|
84
|
+
if (!name)
|
|
85
|
+
return;
|
|
86
|
+
return this.#transporters.rpcs.get(name);
|
|
95
87
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return null;
|
|
105
|
-
const index = this.#rpc.indexes.get(filters.service) || 0;
|
|
106
|
-
this.#rpc.indexes.set(filters.service, (index + 1) % nodes.length);
|
|
107
|
-
return nodes[index % nodes.length];
|
|
88
|
+
if (!this.registry) {
|
|
89
|
+
return this.#transporters.rpcs.values().next().value;
|
|
90
|
+
}
|
|
91
|
+
const name = this.registry.getRpcTransporterName(filters.service);
|
|
92
|
+
if (!name)
|
|
93
|
+
return;
|
|
94
|
+
const transporter = this.#transporters.rpcs.get(name);
|
|
95
|
+
return transporter;
|
|
108
96
|
}
|
|
109
97
|
#normalizeRpcError(error) {
|
|
110
98
|
if (error && typeof error === 'object') {
|
|
@@ -115,24 +103,6 @@ export class SpiderMesh {
|
|
|
115
103
|
}
|
|
116
104
|
return { message: typeof error === 'string' ? error : 'Unknown RPC error' };
|
|
117
105
|
}
|
|
118
|
-
#sendRpcPacket(transporter, node, packet) {
|
|
119
|
-
return transporter.send(packet, node);
|
|
120
|
-
}
|
|
121
|
-
#resolveRpcNode(node_id) {
|
|
122
|
-
const known = this.#nodes$.value.nodes.get(node_id);
|
|
123
|
-
if (known) {
|
|
124
|
-
return known;
|
|
125
|
-
}
|
|
126
|
-
return {
|
|
127
|
-
node_id,
|
|
128
|
-
namespace: this.namespace,
|
|
129
|
-
host: '',
|
|
130
|
-
version: 0,
|
|
131
|
-
services: {},
|
|
132
|
-
nodes: {},
|
|
133
|
-
transporters: {}
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
106
|
#completePendingRpc(request_id) {
|
|
137
107
|
this.#rpc.pending.delete(request_id);
|
|
138
108
|
}
|
|
@@ -140,7 +110,10 @@ export class SpiderMesh {
|
|
|
140
110
|
this.#rpc.running.delete(request_id);
|
|
141
111
|
}
|
|
142
112
|
callRemoteService(options) {
|
|
143
|
-
return
|
|
113
|
+
return of(1).pipe(switchMap(() => this.registry ? firstValueFrom(this.registry.watch(options.service)) : of(1)), take(1), mergeMap(() => {
|
|
114
|
+
const transporter = this.#selectRpcTransport(options);
|
|
115
|
+
if (!transporter)
|
|
116
|
+
throw { code: 'MICROSERVICE_OFFLINE', message: `No transporter available for service ${options.service}` };
|
|
144
117
|
return new Observable(subscriber => {
|
|
145
118
|
const request_id = `${this.node_id}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
|
|
146
119
|
const pending = {
|
|
@@ -159,29 +132,20 @@ export class SpiderMesh {
|
|
|
159
132
|
subscriber.complete();
|
|
160
133
|
}
|
|
161
134
|
});
|
|
162
|
-
|
|
135
|
+
transporter.send({
|
|
163
136
|
kind: 'request',
|
|
164
137
|
request_id,
|
|
165
|
-
|
|
166
|
-
target_node_id: target.node.node_id,
|
|
138
|
+
sender_node_id: this.node_id,
|
|
167
139
|
service: options.service,
|
|
168
140
|
method: options.method,
|
|
169
141
|
args: options.args
|
|
170
|
-
}).catch(error => {
|
|
142
|
+
}, options.node_id).catch((error) => {
|
|
171
143
|
pending.finished = true;
|
|
172
144
|
pending.stream.error(this.#normalizeRpcError(error));
|
|
173
145
|
this.#completePendingRpc(request_id);
|
|
174
146
|
});
|
|
175
147
|
return () => {
|
|
176
148
|
subscription.unsubscribe();
|
|
177
|
-
if (!pending.finished && this.#rpc.pending.has(request_id)) {
|
|
178
|
-
void this.#sendRpcPacket(target.transporter, target.node, {
|
|
179
|
-
kind: 'cancel',
|
|
180
|
-
request_id,
|
|
181
|
-
source_node_id: this.node_id,
|
|
182
|
-
target_node_id: target.node.node_id,
|
|
183
|
-
}).catch(() => undefined);
|
|
184
|
-
}
|
|
185
149
|
this.#completePendingRpc(request_id);
|
|
186
150
|
};
|
|
187
151
|
});
|
|
@@ -203,83 +167,75 @@ export class SpiderMesh {
|
|
|
203
167
|
}));
|
|
204
168
|
}
|
|
205
169
|
#linkRpcTransporter(name, transporter) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.#rpcs.set(name, transporter);
|
|
170
|
+
this.#transporters.rpcs.set(name, transporter);
|
|
171
|
+
this.#ensureLocalTransporterPresence(name);
|
|
209
172
|
const initialEndpoints = transporter.metadata;
|
|
210
173
|
if (initialEndpoints) {
|
|
211
|
-
this.#
|
|
212
|
-
...this.#metadata$.value,
|
|
174
|
+
this.#refresh({
|
|
213
175
|
transporters: {
|
|
214
|
-
...this.#metadata$.value.transporters,
|
|
215
176
|
[name]: initialEndpoints
|
|
216
|
-
}
|
|
217
|
-
version: this.#metadata$.value.version + 1
|
|
177
|
+
}
|
|
218
178
|
});
|
|
219
179
|
}
|
|
220
180
|
return transporter.pipe(map(({ rpc, offline, endpoints }) => {
|
|
221
181
|
if (rpc) {
|
|
222
|
-
const packet = rpc
|
|
223
|
-
if (packet
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
182
|
+
const packet = rpc;
|
|
183
|
+
if (packet.kind == 'request') {
|
|
184
|
+
const reply = async (response) => {
|
|
185
|
+
await transporter.send({
|
|
186
|
+
kind: 'response',
|
|
187
|
+
request_id: packet.request_id,
|
|
188
|
+
...response
|
|
189
|
+
}, packet.sender_node_id);
|
|
190
|
+
};
|
|
191
|
+
const handleResponse = (response) => {
|
|
192
|
+
if (isSubscribable(response)) {
|
|
193
|
+
let queue = Promise.resolve();
|
|
194
|
+
const running = response.subscribe({
|
|
195
|
+
next: data => {
|
|
196
|
+
queue = queue.then(() => reply({ data }));
|
|
197
|
+
},
|
|
198
|
+
error: error => {
|
|
199
|
+
queue = queue.then(() => reply({
|
|
200
|
+
error: this.#normalizeRpcError(error),
|
|
201
|
+
completed: true
|
|
202
|
+
})).finally(() => this.#completeRunningRpc(packet.request_id));
|
|
203
|
+
},
|
|
204
|
+
complete: () => {
|
|
205
|
+
queue = queue.then(() => reply({ completed: true })).finally(() => this.#completeRunningRpc(packet.request_id));
|
|
206
|
+
}
|
|
232
207
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
208
|
+
this.#rpc.running.set(packet.request_id, running);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
void reply({ data: response, completed: true });
|
|
212
|
+
};
|
|
213
|
+
const service = this.#localServices.get(packet.service);
|
|
214
|
+
if (!service || typeof service[packet.method] !== 'function') {
|
|
215
|
+
void reply({
|
|
216
|
+
error: {
|
|
217
|
+
code: 'MICROSERVICE_NOT_FOUND',
|
|
218
|
+
message: `Service ${packet.service}.${packet.method} not found`
|
|
219
|
+
},
|
|
220
|
+
completed: true
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
try {
|
|
225
|
+
const response = service[packet.method].apply(service, packet.args);
|
|
226
|
+
Promise.resolve(response)
|
|
227
|
+
.then(handleResponse)
|
|
228
|
+
.catch(error => reply({
|
|
229
|
+
error: this.#normalizeRpcError(error),
|
|
230
|
+
completed: true
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
258
234
|
void reply({
|
|
259
|
-
error:
|
|
260
|
-
code: 'MICROSERVICE_NOT_FOUND',
|
|
261
|
-
message: `Service ${packet.service}.${packet.method} not found`
|
|
262
|
-
},
|
|
235
|
+
error: this.#normalizeRpcError(error),
|
|
263
236
|
completed: true
|
|
264
237
|
});
|
|
265
238
|
}
|
|
266
|
-
else {
|
|
267
|
-
try {
|
|
268
|
-
const response = service[packet.method].apply(service, packet.args);
|
|
269
|
-
Promise.resolve(response)
|
|
270
|
-
.then(handleResponse)
|
|
271
|
-
.catch(error => reply({
|
|
272
|
-
error: this.#normalizeRpcError(error),
|
|
273
|
-
completed: true
|
|
274
|
-
}));
|
|
275
|
-
}
|
|
276
|
-
catch (error) {
|
|
277
|
-
void reply({
|
|
278
|
-
error: this.#normalizeRpcError(error),
|
|
279
|
-
completed: true
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
239
|
}
|
|
284
240
|
}
|
|
285
241
|
if (packet?.kind === 'response') {
|
|
@@ -301,49 +257,40 @@ export class SpiderMesh {
|
|
|
301
257
|
}
|
|
302
258
|
}
|
|
303
259
|
if (packet?.kind === 'cancel') {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
this.#completeRunningRpc(packet.request_id);
|
|
309
|
-
}
|
|
260
|
+
const stream = this.#rpc.running.get(packet.request_id);
|
|
261
|
+
if (stream) {
|
|
262
|
+
stream.unsubscribe();
|
|
263
|
+
this.#completeRunningRpc(packet.request_id);
|
|
310
264
|
}
|
|
311
265
|
}
|
|
312
266
|
}
|
|
313
267
|
if (offline) {
|
|
314
|
-
|
|
315
|
-
const node = nodes.get(offline);
|
|
316
|
-
if (node) {
|
|
317
|
-
nodes.delete(offline);
|
|
318
|
-
this.#nodes$.next({ nodes, last_updated_node_id: `offline:${Object.keys(node.services).join(',')}` });
|
|
319
|
-
}
|
|
268
|
+
this.registry?.removePeer(offline);
|
|
320
269
|
}
|
|
321
270
|
if (endpoints) {
|
|
322
|
-
this.#
|
|
323
|
-
...this.#metadata$.value,
|
|
271
|
+
this.#refresh({
|
|
324
272
|
transporters: {
|
|
325
|
-
...this.#metadata$.value.transporters,
|
|
326
273
|
[name]: endpoints
|
|
327
|
-
}
|
|
328
|
-
version: this.#metadata$.value.version + 1
|
|
274
|
+
}
|
|
329
275
|
});
|
|
330
276
|
}
|
|
331
|
-
}), finalize(() =>
|
|
332
|
-
this.#rpcs.delete(name);
|
|
333
|
-
}));
|
|
277
|
+
}), finalize(() => undefined));
|
|
334
278
|
}
|
|
335
279
|
#linkPubsubTransporter(name, transporter) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
280
|
+
this.#transporters.pubsubs.set(name, transporter);
|
|
281
|
+
this.#ensureLocalTransporterPresence(name);
|
|
282
|
+
return transporter.pipe(tap(({ endpoints }) => {
|
|
283
|
+
this.#refresh({
|
|
284
|
+
transporters: {
|
|
285
|
+
[name]: endpoints
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}), finalize(() => undefined));
|
|
343
289
|
}
|
|
344
290
|
#linkDiscoveryTransporter(name, transporter) {
|
|
345
|
-
this.#
|
|
346
|
-
|
|
291
|
+
this.#transporters.discoveries.set(name, transporter);
|
|
292
|
+
this.#ensureLocalTransporterPresence(name);
|
|
293
|
+
const announce = this.#me$.subscribe(metadata => {
|
|
347
294
|
void transporter.broadcast({
|
|
348
295
|
hi: true,
|
|
349
296
|
node: metadata,
|
|
@@ -353,52 +300,93 @@ export class SpiderMesh {
|
|
|
353
300
|
return transporter.pipe(tap(({ discovered: node }) => {
|
|
354
301
|
if (!node || typeof node !== 'object' || !('node_id' in node) || !('services' in node))
|
|
355
302
|
return;
|
|
356
|
-
const
|
|
357
|
-
const
|
|
358
|
-
nodes.set(node.node_id, {
|
|
359
|
-
...nodes.get(node.node_id) || {},
|
|
303
|
+
const rpcTransporterName = this.#resolveDiscoveredRpcTransporterName(node);
|
|
304
|
+
const peer = this.registry?.upsertPeer(rpcTransporterName ? {
|
|
360
305
|
...node,
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
306
|
+
transporters: {
|
|
307
|
+
...(node.transporters || {}),
|
|
308
|
+
rpc: rpcTransporterName
|
|
309
|
+
}
|
|
310
|
+
} : node);
|
|
311
|
+
if (!this.registry || !peer)
|
|
312
|
+
return;
|
|
367
313
|
}), finalize(() => {
|
|
368
314
|
announce.unsubscribe();
|
|
369
|
-
this.#discovers.delete(name);
|
|
370
315
|
}));
|
|
371
316
|
}
|
|
372
|
-
#
|
|
373
|
-
return
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
317
|
+
#isRpcTransporter(transporter) {
|
|
318
|
+
return 'send' in transporter;
|
|
319
|
+
}
|
|
320
|
+
#isPubsubTransporter(transporter) {
|
|
321
|
+
return 'publish' in transporter;
|
|
322
|
+
}
|
|
323
|
+
#isDiscoveryTransporter(transporter) {
|
|
324
|
+
return 'broadcast' in transporter;
|
|
325
|
+
}
|
|
326
|
+
#refresh(patch) {
|
|
327
|
+
const current = this.#me$.value;
|
|
328
|
+
this.#me$.next({
|
|
329
|
+
...current,
|
|
330
|
+
...patch,
|
|
331
|
+
version: current.version + 1,
|
|
332
|
+
topics: patch.topics || current.topics,
|
|
333
|
+
services: {
|
|
334
|
+
...current.services,
|
|
335
|
+
...(patch.services || {})
|
|
336
|
+
},
|
|
337
|
+
nodes: {
|
|
338
|
+
...current.nodes,
|
|
339
|
+
...(patch.nodes || {})
|
|
340
|
+
},
|
|
341
|
+
transporters: {
|
|
342
|
+
...current.transporters,
|
|
343
|
+
...(patch.transporters || {})
|
|
384
344
|
}
|
|
385
|
-
|
|
386
|
-
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
#ensureLocalTransporterPresence(name) {
|
|
348
|
+
if (Object.hasOwn(this.#me$.value.transporters, name))
|
|
349
|
+
return;
|
|
350
|
+
this.#refresh({
|
|
351
|
+
transporters: {
|
|
352
|
+
[name]: true
|
|
387
353
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
#resolveDiscoveredRpcTransporterName(node) {
|
|
357
|
+
for (const [name] of this.#transporters.rpcs.entries()) {
|
|
358
|
+
if (node.transporters && Object.hasOwn(node.transporters, name))
|
|
359
|
+
return name;
|
|
360
|
+
}
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
#registerTopic(topic) {
|
|
364
|
+
if (this.#me$.value.topics.includes(topic))
|
|
365
|
+
return;
|
|
366
|
+
this.#refresh({
|
|
367
|
+
topics: [...this.#me$.value.topics, topic]
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
#unregisterTopic(topic) {
|
|
371
|
+
if (!this.#me$.value.topics.includes(topic))
|
|
372
|
+
return;
|
|
373
|
+
this.#refresh({
|
|
374
|
+
topics: this.#me$.value.topics.filter(item => item !== topic)
|
|
375
|
+
});
|
|
394
376
|
}
|
|
395
377
|
linkEvent(factory) {
|
|
396
378
|
const topic = factory.name;
|
|
379
|
+
const listen$ = new Observable(subscriber => {
|
|
380
|
+
this.#registerTopic(topic);
|
|
381
|
+
const subscription = from(this.#transporters.pubsubs.values()).pipe(mergeMap(t => t.listen(topic))).subscribe(subscriber);
|
|
382
|
+
return () => {
|
|
383
|
+
subscription.unsubscribe();
|
|
384
|
+
this.#unregisterTopic(topic);
|
|
385
|
+
};
|
|
386
|
+
}).pipe(share());
|
|
397
387
|
return {
|
|
398
|
-
publish: (data) => lastValueFrom(from(this.#pubsubs.
|
|
399
|
-
listen: () =>
|
|
400
|
-
return this.#pubsubs.pipe(map(list => [...list.values()]), mergeAll(), mergeMap(t => t.listen(topic)));
|
|
401
|
-
}
|
|
388
|
+
publish: (data) => lastValueFrom(from(this.#transporters.pubsubs.values()).pipe(mergeMap(t => t.publish(topic, data))), { defaultValue: undefined }),
|
|
389
|
+
listen: () => listen$
|
|
402
390
|
};
|
|
403
391
|
}
|
|
404
392
|
}
|