@spider-mesh/core 2.0.40 → 2.0.42
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 +175 -272
- package/build/src/Registry.d.ts +19 -0
- package/build/src/Registry.js +77 -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 +242 -226
- package/build/src/SpiderMesh.js.map +1 -1
- package/build/src/decorators/NestJSLinkMicroservice.d.ts +3 -1
- package/build/src/decorators/NestJSLinkMicroservice.js +2 -2
- 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 +14 -2
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- 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,115 @@
|
|
|
1
|
-
import { BehaviorSubject, catchError, EMPTY, filter, finalize, from, lastValueFrom, map,
|
|
1
|
+
import { BehaviorSubject, catchError, EMPTY, filter, finalize, from, lastValueFrom, map, mergeMap, Observable, of, retry, share, Subject, Subscription, 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
|
-
return
|
|
66
|
-
return
|
|
70
|
+
return (this.registry?.listPeers({ service }) || []).filter(node => {
|
|
71
|
+
return typeof node.transporters?.rpc === 'string';
|
|
67
72
|
});
|
|
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
81
|
return null;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return null;
|
|
91
|
-
const transporter = this.#rpcs.get(node.rpc);
|
|
82
|
+
const transporterName = typeof filters.transporter === 'string'
|
|
83
|
+
? filters.transporter
|
|
84
|
+
: filters.transporter?.name;
|
|
85
|
+
if (!this.registry) {
|
|
86
|
+
const transporter = transporterName
|
|
87
|
+
? this.#transporters.rpcs.get(transporterName)
|
|
88
|
+
: this.#transporters.rpcs.values().next().value;
|
|
92
89
|
if (!transporter)
|
|
93
90
|
return null;
|
|
94
|
-
return {
|
|
91
|
+
return {
|
|
92
|
+
node_id: filters.node_id,
|
|
93
|
+
transporter
|
|
94
|
+
};
|
|
95
95
|
}
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
const nodeId = this.registry.pickRpcNode(filters.service, {
|
|
97
|
+
node_id: filters.node_id
|
|
98
|
+
});
|
|
99
|
+
const resolvedTransporterName = filters.node_id || nodeId
|
|
100
|
+
? this.registry.getRpcTransporterName(filters.service, {
|
|
101
|
+
node_id: nodeId || filters.node_id
|
|
102
|
+
})
|
|
103
|
+
: transporterName;
|
|
104
|
+
if (!resolvedTransporterName)
|
|
101
105
|
return null;
|
|
102
|
-
|
|
103
|
-
if (
|
|
106
|
+
const transporter = this.#transporters.rpcs.get(resolvedTransporterName);
|
|
107
|
+
if (!transporter)
|
|
104
108
|
return null;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
return {
|
|
110
|
+
node_id: nodeId || filters.node_id,
|
|
111
|
+
transporter
|
|
112
|
+
};
|
|
108
113
|
}
|
|
109
114
|
#normalizeRpcError(error) {
|
|
110
115
|
if (error && typeof error === 'object') {
|
|
@@ -115,24 +120,6 @@ export class SpiderMesh {
|
|
|
115
120
|
}
|
|
116
121
|
return { message: typeof error === 'string' ? error : 'Unknown RPC error' };
|
|
117
122
|
}
|
|
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
123
|
#completePendingRpc(request_id) {
|
|
137
124
|
this.#rpc.pending.delete(request_id);
|
|
138
125
|
}
|
|
@@ -140,7 +127,10 @@ export class SpiderMesh {
|
|
|
140
127
|
this.#rpc.running.delete(request_id);
|
|
141
128
|
}
|
|
142
129
|
callRemoteService(options) {
|
|
143
|
-
|
|
130
|
+
const source$ = this.registry
|
|
131
|
+
? this.registry.watch(options.service)
|
|
132
|
+
: of(undefined);
|
|
133
|
+
return source$.pipe(map(() => this.#selectRpcTransport(options)), filter((target) => !!target), take(1), mergeMap(target => {
|
|
144
134
|
return new Observable(subscriber => {
|
|
145
135
|
const request_id = `${this.node_id}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
|
|
146
136
|
const pending = {
|
|
@@ -159,28 +149,28 @@ export class SpiderMesh {
|
|
|
159
149
|
subscriber.complete();
|
|
160
150
|
}
|
|
161
151
|
});
|
|
162
|
-
|
|
152
|
+
target.transporter.send({
|
|
163
153
|
kind: 'request',
|
|
164
154
|
request_id,
|
|
165
155
|
source_node_id: this.node_id,
|
|
166
|
-
target_node_id: target.
|
|
156
|
+
target_node_id: target.node_id || '',
|
|
167
157
|
service: options.service,
|
|
168
158
|
method: options.method,
|
|
169
159
|
args: options.args
|
|
170
|
-
}).catch(error => {
|
|
160
|
+
}, target.node_id).catch((error) => {
|
|
171
161
|
pending.finished = true;
|
|
172
162
|
pending.stream.error(this.#normalizeRpcError(error));
|
|
173
163
|
this.#completePendingRpc(request_id);
|
|
174
164
|
});
|
|
175
165
|
return () => {
|
|
176
166
|
subscription.unsubscribe();
|
|
177
|
-
if (!pending.finished && this.#rpc.pending.has(request_id)) {
|
|
178
|
-
void
|
|
167
|
+
if (!pending.finished && this.#rpc.pending.has(request_id) && target.node_id) {
|
|
168
|
+
void target.transporter.send({
|
|
179
169
|
kind: 'cancel',
|
|
180
170
|
request_id,
|
|
181
171
|
source_node_id: this.node_id,
|
|
182
|
-
target_node_id: target.
|
|
183
|
-
}).catch(() => undefined);
|
|
172
|
+
target_node_id: target.node_id,
|
|
173
|
+
}, target.node_id).catch(() => undefined);
|
|
184
174
|
}
|
|
185
175
|
this.#completePendingRpc(request_id);
|
|
186
176
|
};
|
|
@@ -203,83 +193,77 @@ export class SpiderMesh {
|
|
|
203
193
|
}));
|
|
204
194
|
}
|
|
205
195
|
#linkRpcTransporter(name, transporter) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.#rpcs.set(name, transporter);
|
|
196
|
+
this.#transporters.rpcs.set(name, transporter);
|
|
197
|
+
this.#ensureLocalTransporterPresence(name);
|
|
209
198
|
const initialEndpoints = transporter.metadata;
|
|
210
199
|
if (initialEndpoints) {
|
|
211
|
-
this.#
|
|
212
|
-
...this.#metadata$.value,
|
|
200
|
+
this.#refresh({
|
|
213
201
|
transporters: {
|
|
214
|
-
...this.#metadata$.value.transporters,
|
|
215
202
|
[name]: initialEndpoints
|
|
216
|
-
}
|
|
217
|
-
version: this.#metadata$.value.version + 1
|
|
203
|
+
}
|
|
218
204
|
});
|
|
219
205
|
}
|
|
220
206
|
return transporter.pipe(map(({ rpc, offline, endpoints }) => {
|
|
221
207
|
if (rpc) {
|
|
222
208
|
const packet = rpc.packet;
|
|
223
|
-
if (packet?.kind === 'request') {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
209
|
+
if (packet?.kind === 'request' && packet.target_node_id === this.node_id) {
|
|
210
|
+
const reply = async (response) => {
|
|
211
|
+
await transporter.send({
|
|
212
|
+
kind: 'response',
|
|
213
|
+
request_id: packet.request_id,
|
|
214
|
+
source_node_id: this.node_id,
|
|
215
|
+
target_node_id: rpc.node_id,
|
|
216
|
+
...response
|
|
217
|
+
}, rpc.node_id);
|
|
218
|
+
};
|
|
219
|
+
const handleResponse = (response) => {
|
|
220
|
+
if (isSubscribable(response)) {
|
|
221
|
+
let queue = Promise.resolve();
|
|
222
|
+
const running = response.subscribe({
|
|
223
|
+
next: data => {
|
|
224
|
+
queue = queue.then(() => reply({ data }));
|
|
225
|
+
},
|
|
226
|
+
error: error => {
|
|
227
|
+
queue = queue.then(() => reply({
|
|
228
|
+
error: this.#normalizeRpcError(error),
|
|
229
|
+
completed: true
|
|
230
|
+
})).finally(() => this.#completeRunningRpc(packet.request_id));
|
|
231
|
+
},
|
|
232
|
+
complete: () => {
|
|
233
|
+
queue = queue.then(() => reply({ completed: true })).finally(() => this.#completeRunningRpc(packet.request_id));
|
|
234
|
+
}
|
|
232
235
|
});
|
|
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
|
-
|
|
236
|
+
this.#rpc.running.set(packet.request_id, running);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
void reply({ data: response, completed: true });
|
|
240
|
+
};
|
|
241
|
+
const service = this.#localServices.get(packet.service);
|
|
242
|
+
if (!service || typeof service[packet.method] !== 'function') {
|
|
243
|
+
void reply({
|
|
244
|
+
error: {
|
|
245
|
+
code: 'MICROSERVICE_NOT_FOUND',
|
|
246
|
+
message: `Service ${packet.service}.${packet.method} not found`
|
|
247
|
+
},
|
|
248
|
+
completed: true
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
try {
|
|
253
|
+
const response = service[packet.method].apply(service, packet.args);
|
|
254
|
+
Promise.resolve(response)
|
|
255
|
+
.then(handleResponse)
|
|
256
|
+
.catch(error => reply({
|
|
257
|
+
error: this.#normalizeRpcError(error),
|
|
258
|
+
completed: true
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
258
262
|
void reply({
|
|
259
|
-
error:
|
|
260
|
-
code: 'MICROSERVICE_NOT_FOUND',
|
|
261
|
-
message: `Service ${packet.service}.${packet.method} not found`
|
|
262
|
-
},
|
|
263
|
+
error: this.#normalizeRpcError(error),
|
|
263
264
|
completed: true
|
|
264
265
|
});
|
|
265
266
|
}
|
|
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
267
|
}
|
|
284
268
|
}
|
|
285
269
|
if (packet?.kind === 'response') {
|
|
@@ -300,50 +284,41 @@ export class SpiderMesh {
|
|
|
300
284
|
}
|
|
301
285
|
}
|
|
302
286
|
}
|
|
303
|
-
if (packet?.kind === 'cancel') {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
this.#completeRunningRpc(packet.request_id);
|
|
309
|
-
}
|
|
287
|
+
if (packet?.kind === 'cancel' && packet.target_node_id === this.node_id) {
|
|
288
|
+
const stream = this.#rpc.running.get(packet.request_id);
|
|
289
|
+
if (stream) {
|
|
290
|
+
stream.unsubscribe();
|
|
291
|
+
this.#completeRunningRpc(packet.request_id);
|
|
310
292
|
}
|
|
311
293
|
}
|
|
312
294
|
}
|
|
313
295
|
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
|
-
}
|
|
296
|
+
this.registry?.removePeer(offline);
|
|
320
297
|
}
|
|
321
298
|
if (endpoints) {
|
|
322
|
-
this.#
|
|
323
|
-
...this.#metadata$.value,
|
|
299
|
+
this.#refresh({
|
|
324
300
|
transporters: {
|
|
325
|
-
...this.#metadata$.value.transporters,
|
|
326
301
|
[name]: endpoints
|
|
327
|
-
}
|
|
328
|
-
version: this.#metadata$.value.version + 1
|
|
302
|
+
}
|
|
329
303
|
});
|
|
330
304
|
}
|
|
331
|
-
}), finalize(() =>
|
|
332
|
-
this.#rpcs.delete(name);
|
|
333
|
-
}));
|
|
305
|
+
}), finalize(() => undefined));
|
|
334
306
|
}
|
|
335
307
|
#linkPubsubTransporter(name, transporter) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
308
|
+
this.#transporters.pubsubs.set(name, transporter);
|
|
309
|
+
this.#ensureLocalTransporterPresence(name);
|
|
310
|
+
return transporter.pipe(tap(({ endpoints }) => {
|
|
311
|
+
this.#refresh({
|
|
312
|
+
transporters: {
|
|
313
|
+
[name]: endpoints
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}), finalize(() => undefined));
|
|
343
317
|
}
|
|
344
318
|
#linkDiscoveryTransporter(name, transporter) {
|
|
345
|
-
this.#
|
|
346
|
-
|
|
319
|
+
this.#transporters.discoveries.set(name, transporter);
|
|
320
|
+
this.#ensureLocalTransporterPresence(name);
|
|
321
|
+
const announce = this.#me$.subscribe(metadata => {
|
|
347
322
|
void transporter.broadcast({
|
|
348
323
|
hi: true,
|
|
349
324
|
node: metadata,
|
|
@@ -353,52 +328,93 @@ export class SpiderMesh {
|
|
|
353
328
|
return transporter.pipe(tap(({ discovered: node }) => {
|
|
354
329
|
if (!node || typeof node !== 'object' || !('node_id' in node) || !('services' in node))
|
|
355
330
|
return;
|
|
356
|
-
const
|
|
357
|
-
const
|
|
358
|
-
nodes.set(node.node_id, {
|
|
359
|
-
...nodes.get(node.node_id) || {},
|
|
331
|
+
const rpcTransporterName = this.#resolveDiscoveredRpcTransporterName(node);
|
|
332
|
+
const peer = this.registry?.upsertPeer(rpcTransporterName ? {
|
|
360
333
|
...node,
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
334
|
+
transporters: {
|
|
335
|
+
...(node.transporters || {}),
|
|
336
|
+
rpc: rpcTransporterName
|
|
337
|
+
}
|
|
338
|
+
} : node);
|
|
339
|
+
if (!this.registry || !peer)
|
|
340
|
+
return;
|
|
367
341
|
}), finalize(() => {
|
|
368
342
|
announce.unsubscribe();
|
|
369
|
-
this.#discovers.delete(name);
|
|
370
343
|
}));
|
|
371
344
|
}
|
|
372
|
-
#
|
|
373
|
-
return
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
345
|
+
#isRpcTransporter(transporter) {
|
|
346
|
+
return 'send' in transporter;
|
|
347
|
+
}
|
|
348
|
+
#isPubsubTransporter(transporter) {
|
|
349
|
+
return 'publish' in transporter;
|
|
350
|
+
}
|
|
351
|
+
#isDiscoveryTransporter(transporter) {
|
|
352
|
+
return 'broadcast' in transporter;
|
|
353
|
+
}
|
|
354
|
+
#refresh(patch) {
|
|
355
|
+
const current = this.#me$.value;
|
|
356
|
+
this.#me$.next({
|
|
357
|
+
...current,
|
|
358
|
+
...patch,
|
|
359
|
+
version: current.version + 1,
|
|
360
|
+
topics: patch.topics || current.topics,
|
|
361
|
+
services: {
|
|
362
|
+
...current.services,
|
|
363
|
+
...(patch.services || {})
|
|
364
|
+
},
|
|
365
|
+
nodes: {
|
|
366
|
+
...current.nodes,
|
|
367
|
+
...(patch.nodes || {})
|
|
368
|
+
},
|
|
369
|
+
transporters: {
|
|
370
|
+
...current.transporters,
|
|
371
|
+
...(patch.transporters || {})
|
|
384
372
|
}
|
|
385
|
-
|
|
386
|
-
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
#ensureLocalTransporterPresence(name) {
|
|
376
|
+
if (Object.hasOwn(this.#me$.value.transporters, name))
|
|
377
|
+
return;
|
|
378
|
+
this.#refresh({
|
|
379
|
+
transporters: {
|
|
380
|
+
[name]: true
|
|
387
381
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
#resolveDiscoveredRpcTransporterName(node) {
|
|
385
|
+
for (const [name] of this.#transporters.rpcs.entries()) {
|
|
386
|
+
if (node.transporters && Object.hasOwn(node.transporters, name))
|
|
387
|
+
return name;
|
|
388
|
+
}
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
#registerTopic(topic) {
|
|
392
|
+
if (this.#me$.value.topics.includes(topic))
|
|
393
|
+
return;
|
|
394
|
+
this.#refresh({
|
|
395
|
+
topics: [...this.#me$.value.topics, topic]
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
#unregisterTopic(topic) {
|
|
399
|
+
if (!this.#me$.value.topics.includes(topic))
|
|
400
|
+
return;
|
|
401
|
+
this.#refresh({
|
|
402
|
+
topics: this.#me$.value.topics.filter(item => item !== topic)
|
|
403
|
+
});
|
|
394
404
|
}
|
|
395
405
|
linkEvent(factory) {
|
|
396
406
|
const topic = factory.name;
|
|
407
|
+
const listen$ = new Observable(subscriber => {
|
|
408
|
+
this.#registerTopic(topic);
|
|
409
|
+
const subscription = from(this.#transporters.pubsubs.values()).pipe(mergeMap(t => t.listen(topic))).subscribe(subscriber);
|
|
410
|
+
return () => {
|
|
411
|
+
subscription.unsubscribe();
|
|
412
|
+
this.#unregisterTopic(topic);
|
|
413
|
+
};
|
|
414
|
+
}).pipe(share());
|
|
397
415
|
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
|
-
}
|
|
416
|
+
publish: (data) => lastValueFrom(from(this.#transporters.pubsubs.values()).pipe(mergeMap(t => t.publish(topic, data))), { defaultValue: undefined }),
|
|
417
|
+
listen: () => listen$
|
|
402
418
|
};
|
|
403
419
|
}
|
|
404
420
|
}
|