@spider-mesh/core 1.0.152 → 2.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/build/const.d.ts +1 -0
- package/build/const.js +2 -0
- package/build/const.js.map +1 -0
- package/build/src/SpiderMesh.d.ts +25 -70
- package/build/src/SpiderMesh.js +308 -420
- package/build/src/SpiderMesh.js.map +1 -1
- package/build/src/decorators/BeforeMicroserviceOnline.d.ts +1 -2
- package/build/src/decorators/BeforeMicroserviceOnline.js +1 -1
- package/build/src/decorators/BeforeMicroserviceOnline.js.map +1 -1
- package/build/src/decorators/LimitConcurrentRunning.d.ts +2 -3
- package/build/src/decorators/LimitConcurrentRunning.js +2 -27
- package/build/src/decorators/LimitConcurrentRunning.js.map +1 -1
- package/build/src/decorators/Microservice.d.ts +10 -7
- package/build/src/decorators/Microservice.js +13 -12
- package/build/src/decorators/Microservice.js.map +1 -1
- package/build/src/decorators/NestJSExposeMicroservice.d.ts +1 -2
- package/build/src/decorators/NestJSExposeMicroservice.js +9 -7
- package/build/src/decorators/NestJSExposeMicroservice.js.map +1 -1
- package/build/src/decorators/NestJSLinkEvent.d.ts +5 -16
- package/build/src/decorators/NestJSLinkEvent.js +2 -2
- package/build/src/decorators/NestJSLinkEvent.js.map +1 -1
- package/build/src/decorators/NestJSLinkMicroservice.d.ts +2 -2
- package/build/src/decorators/NestJSLinkMicroservice.js +2 -2
- package/build/src/decorators/NestJSLinkMicroservice.js.map +1 -1
- package/build/src/helpers/LimitConcurrency.d.ts +3 -0
- package/build/src/helpers/LimitConcurrency.js +34 -0
- package/build/src/helpers/LimitConcurrency.js.map +1 -0
- package/build/src/helpers/MicroserviceException.d.ts +8 -0
- package/build/src/helpers/MicroserviceException.js +9 -0
- package/build/src/helpers/MicroserviceException.js.map +1 -0
- package/build/src/helpers/MicroserviceNotFound copy.d.ts +5 -0
- package/build/src/helpers/MicroserviceNotFound copy.js +8 -0
- package/build/src/helpers/MicroserviceNotFound copy.js.map +1 -0
- package/build/src/helpers/MicroserviceNotFound.d.ts +5 -0
- package/build/src/helpers/MicroserviceNotFound.js +8 -0
- package/build/src/helpers/MicroserviceNotFound.js.map +1 -0
- package/build/src/helpers/MicroserviceOfflineException.d.ts +5 -0
- package/build/src/helpers/MicroserviceOfflineException.js +8 -0
- package/build/src/helpers/MicroserviceOfflineException.js.map +1 -0
- package/build/src/helpers/MicroserviceRpcTimeout.d.ts +5 -0
- package/build/src/helpers/MicroserviceRpcTimeout.js +8 -0
- package/build/src/helpers/MicroserviceRpcTimeout.js.map +1 -0
- package/build/src/helpers/randomUUID.js.map +1 -1
- package/build/src/helpers/sleep.js +1 -1
- package/build/src/helpers/sleep.js.map +1 -1
- package/build/src/index.d.ts +14 -15
- package/build/src/index.js +14 -12
- package/build/src/index.js.map +1 -1
- package/build/src/interfaces/DiscoveryTransporter.d.ts +5 -0
- package/build/src/interfaces/DiscoveryTransporter.js +3 -0
- package/build/src/interfaces/DiscoveryTransporter.js.map +1 -0
- package/build/src/interfaces/PubsubTransporter.d.ts +10 -0
- package/build/src/interfaces/PubsubTransporter.js +3 -0
- package/build/src/interfaces/PubsubTransporter.js.map +1 -0
- package/build/src/interfaces/RemoteService.d.ts +12 -19
- package/build/src/interfaces/RpcTransporter.d.ts +30 -0
- package/build/src/interfaces/RpcTransporter.js +3 -0
- package/build/src/interfaces/RpcTransporter.js.map +1 -0
- package/build/src/interfaces/SpiderMeshNode.d.ts +14 -19
- package/build/tsconfig.tsbuildinfo +1 -0
- package/package.json +7 -8
- package/tsconfig.json +2 -2
- package/build/src/Encoder.d.ts +0 -8
- package/build/src/Encoder.js +0 -76
- package/build/src/Encoder.js.map +0 -1
- package/build/src/RPCOptions.d.ts +0 -11
- package/build/src/RPCOptions.js +0 -11
- package/build/src/RPCOptions.js.map +0 -1
- package/build/src/const.d.ts +0 -6
- package/build/src/const.js +0 -8
- package/build/src/const.js.map +0 -1
- package/build/src/decorators/CustomSpiderMesh.d.ts +0 -6
- package/build/src/decorators/CustomSpiderMesh.js +0 -9
- package/build/src/decorators/CustomSpiderMesh.js.map +0 -1
- package/build/src/decorators/DeepProxy.d.ts +0 -7
- package/build/src/decorators/DeepProxy.js +0 -24
- package/build/src/decorators/DeepProxy.js.map +0 -1
- package/build/src/decorators/ListenEvent.d.ts +0 -19
- package/build/src/decorators/ListenEvent.js +0 -23
- package/build/src/decorators/ListenEvent.js.map +0 -1
- package/build/src/interfaces/SpiderMeshTransporter.d.ts +0 -33
- package/build/src/interfaces/SpiderMeshTransporter.js +0 -2
- package/build/src/interfaces/SpiderMeshTransporter.js.map +0 -1
package/build/src/SpiderMesh.js
CHANGED
|
@@ -1,345 +1,237 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { $
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
resolve(data.ip);
|
|
14
|
-
}
|
|
15
|
-
catch (error) {
|
|
16
|
-
resolve(undefined);
|
|
17
|
-
}
|
|
18
|
-
});
|
|
1
|
+
import { BehaviorSubject, catchError, EMPTY, filter, finalize, firstValueFrom, from, lastValueFrom, map, mergeAll, mergeMap, of, retry, tap, throwError, timeout, timer } from "rxjs";
|
|
2
|
+
import { listBeforeMicroserviceOnlineMethods } from "./decorators/BeforeMicroserviceOnline.js";
|
|
3
|
+
import { PubsubTransporter } from "./interfaces/PubsubTransporter.js";
|
|
4
|
+
import { RpcTransporter } from "./interfaces/RpcTransporter.js";
|
|
5
|
+
import { MicroserviceException } from "./helpers/MicroserviceException.js";
|
|
6
|
+
import { MicroserviceOfflineException } from "./helpers/MicroserviceOfflineException.js";
|
|
7
|
+
import { services$ } from "./decorators/Microservice.js";
|
|
8
|
+
import { networkInterfaces } from "os";
|
|
9
|
+
import { MicroserviceNotFound } from "./helpers/MicroserviceNotFound.js";
|
|
10
|
+
import { DiscoveryTransporter } from "./interfaces/DiscoveryTransporter.js";
|
|
11
|
+
import { SPIDERMESH_NAMESPACE } from "const.js";
|
|
12
|
+
import { MicroserviceRpcTimeout } from "./helpers/MicroserviceRpcTimeout.js";
|
|
19
13
|
export class SpiderMesh {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
14
|
+
node_id = `${Date.now()}`;
|
|
15
|
+
namespace = SPIDERMESH_NAMESPACE;
|
|
16
|
+
#rpcs = new Map();
|
|
17
|
+
#pubsubs = new BehaviorSubject(new Map());
|
|
18
|
+
#discovers = new Map();
|
|
19
|
+
#metadata$ = new BehaviorSubject({
|
|
20
|
+
ips: Object.values(networkInterfaces()).flat(2).filter(a => !a?.internal && !!a?.address).map(a => a?.address),
|
|
21
|
+
host: '',
|
|
22
|
+
namespace: SPIDERMESH_NAMESPACE,
|
|
23
|
+
node_id: this.node_id,
|
|
24
|
+
services: {},
|
|
25
|
+
topics: [],
|
|
26
|
+
transporters: {},
|
|
27
|
+
nodes: {},
|
|
28
|
+
version: 0
|
|
29
|
+
});
|
|
30
|
+
#nodes = new BehaviorSubject({
|
|
31
|
+
nodes: new Map(),
|
|
32
|
+
last_updated_node_id: ''
|
|
33
|
+
});
|
|
34
|
+
#services$ = new BehaviorSubject(new Map());
|
|
35
|
+
constructor() {
|
|
36
|
+
services$.pipe(filter(list => Object.keys(list).length > 0), mergeMap(async (services) => {
|
|
37
|
+
for (const v of Object.values(services)) {
|
|
38
|
+
const metadata = v['$'] || (typeof v.metadata == 'function' ? await v.metadata() : v.metadata);
|
|
39
|
+
Object.defineProperty(v, '$', { value: metadata });
|
|
40
|
+
}
|
|
41
|
+
return services;
|
|
42
|
+
}), tap(services => {
|
|
43
|
+
const metadata = {
|
|
44
|
+
...this.#metadata$.value,
|
|
45
|
+
services: Object.entries(services).reduce((p, [name, v]) => {
|
|
46
|
+
return {
|
|
47
|
+
...p,
|
|
48
|
+
[name]: v['$']
|
|
49
|
+
};
|
|
50
|
+
}, {}),
|
|
51
|
+
version: this.#metadata$.value.version + 1
|
|
52
|
+
};
|
|
53
|
+
this.#metadata$.next(metadata);
|
|
54
|
+
}), catchError(e => EMPTY)).subscribe();
|
|
42
55
|
}
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
return
|
|
50
|
-
|
|
56
|
+
#selectRpcTarget(filters = {}) {
|
|
57
|
+
if (!filters.service)
|
|
58
|
+
return null;
|
|
59
|
+
if (filters.node_id) {
|
|
60
|
+
const node = this.#nodes.value.nodes.get(filters.node_id);
|
|
61
|
+
if (!node)
|
|
62
|
+
return null;
|
|
63
|
+
if (!node.services[filters.service])
|
|
64
|
+
return null;
|
|
65
|
+
if (!node.rpc)
|
|
66
|
+
return null;
|
|
67
|
+
const transporter = this.#rpcs.get(node.rpc);
|
|
68
|
+
if (!transporter)
|
|
69
|
+
return null;
|
|
70
|
+
return { node, transporter };
|
|
51
71
|
}
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const rpc_node_id = this.#caculate_rpc_node_id(service, options);
|
|
63
|
-
if (!rpc_node_id) {
|
|
64
|
-
const e = new Error(`SERVICE_NOT_RUNNING:${service}`, {
|
|
65
|
-
cause: {
|
|
66
|
-
service,
|
|
67
|
-
method,
|
|
68
|
-
args,
|
|
69
|
-
options
|
|
72
|
+
const state = this.#services$.value.get(filters.service);
|
|
73
|
+
if (!state || state.nodes.length == 0)
|
|
74
|
+
return null;
|
|
75
|
+
const nodes = state.nodes.map(id => {
|
|
76
|
+
const node = this.#nodes.value.nodes.get(id);
|
|
77
|
+
const transporter = node && node.rpc && this.#rpcs.get(node.rpc);
|
|
78
|
+
if (node && transporter) {
|
|
79
|
+
if (filters.ip) {
|
|
80
|
+
if (!node.ips.includes(filters.ip))
|
|
81
|
+
return null;
|
|
70
82
|
}
|
|
71
|
-
|
|
72
|
-
return Object.assign(new Observable(o => o.error(e)), {
|
|
73
|
-
then: async (success, error) => {
|
|
74
|
-
error(e);
|
|
75
|
-
},
|
|
76
|
-
async catch() {
|
|
77
|
-
return {};
|
|
78
|
-
},
|
|
79
|
-
async finally() {
|
|
80
|
-
return {};
|
|
81
|
-
},
|
|
82
|
-
[Symbol.toStringTag]: ''
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
if (options.$forgot) {
|
|
86
|
-
this.publish({
|
|
87
|
-
topic: rpc_node_id,
|
|
88
|
-
payload: [{
|
|
89
|
-
request: {
|
|
90
|
-
args,
|
|
91
|
-
method,
|
|
92
|
-
request_id: '#',
|
|
93
|
-
service
|
|
94
|
-
}
|
|
95
|
-
}],
|
|
96
|
-
node_id: rpc_node_id
|
|
97
|
-
});
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const $ = new Observable($response => {
|
|
101
|
-
const request_id = randomUUID();
|
|
102
|
-
this.#requests.set(request_id, { $response, rpc_node_id });
|
|
103
|
-
this.publish({
|
|
104
|
-
topic: rpc_node_id,
|
|
105
|
-
payload: [{
|
|
106
|
-
request: {
|
|
107
|
-
args,
|
|
108
|
-
method,
|
|
109
|
-
request_id,
|
|
110
|
-
service
|
|
111
|
-
}
|
|
112
|
-
}],
|
|
113
|
-
node_id: rpc_node_id
|
|
114
|
-
});
|
|
115
|
-
return () => {
|
|
116
|
-
this.#requests.delete(request_id);
|
|
117
|
-
};
|
|
118
|
-
}).pipe(retry({ count: options.$retry || 0, delay: options.$retry_delay || 1000 }), options.$timeout ? timeout(options.$timeout) : tap(), catchError(err => {
|
|
119
|
-
if (options.$fallback) {
|
|
120
|
-
return of(options.$fallback);
|
|
83
|
+
return { node, transporter };
|
|
121
84
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
$.subscribe({ error: () => { }, next: () => { } });
|
|
125
|
-
return Object.assign($, {
|
|
126
|
-
then: async (success, error) => {
|
|
127
|
-
try {
|
|
128
|
-
const value = await firstValueFrom($);
|
|
129
|
-
success(options.$safe_mode ? [null, value] : value);
|
|
130
|
-
}
|
|
131
|
-
catch (e) {
|
|
132
|
-
options.$safe_mode ? success([e, null]) : error(e);
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
async catch() {
|
|
136
|
-
return {};
|
|
137
|
-
},
|
|
138
|
-
async finally() {
|
|
139
|
-
return {};
|
|
140
|
-
},
|
|
141
|
-
[Symbol.toStringTag]: ''
|
|
142
|
-
});
|
|
85
|
+
}).filter(Boolean).map(e => e);
|
|
86
|
+
return nodes[state.index++ % nodes.length];
|
|
143
87
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}), {});
|
|
149
|
-
const isolated_nodes = [
|
|
150
|
-
...new Set([...this.#linked_nodes.values()]
|
|
151
|
-
.filter(node => node.isolated)
|
|
152
|
-
.map(node => node.local_transporter_id))
|
|
153
|
-
];
|
|
154
|
-
const metadata = {
|
|
155
|
-
metadata: this.metadata,
|
|
156
|
-
node_id: this.#node_id,
|
|
157
|
-
node_version: process.version,
|
|
158
|
-
last_online: Date.now(),
|
|
159
|
-
online: true,
|
|
160
|
-
services,
|
|
161
|
-
namespace: NAMEPSACE,
|
|
162
|
-
linked: [...this.#linked_nodes.keys()],
|
|
163
|
-
isolated_nodes,
|
|
164
|
-
isolated: this.#$isolated.value,
|
|
165
|
-
public_ip: await $public_ip
|
|
166
|
-
};
|
|
167
|
-
return metadata;
|
|
168
|
-
}
|
|
169
|
-
async #self_introduce(local_transporter_id, node_id) {
|
|
170
|
-
const list = local_transporter_id ? [this.#transporters.get(local_transporter_id)] : [...this.#transporters.values()];
|
|
171
|
-
for (const target of list) {
|
|
88
|
+
callRemoteService(options) {
|
|
89
|
+
return of(0).pipe(mergeMap(async () => {
|
|
90
|
+
await this.waitServiceOnline(options.service);
|
|
91
|
+
const target = this.#selectRpcTarget(options);
|
|
172
92
|
if (!target)
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
throw
|
|
196
|
-
|
|
197
|
-
throw 'RPC_METHOD_NOT_FOUND';
|
|
198
|
-
return service;
|
|
93
|
+
throw new MicroserviceOfflineException();
|
|
94
|
+
const force = !!options.node_id || !!options.ip;
|
|
95
|
+
return target.transporter.rpc(options, target.node, force);
|
|
96
|
+
}), mergeMap($ => $), retry({
|
|
97
|
+
delay: (e, count) => {
|
|
98
|
+
if (e instanceof MicroserviceOfflineException || e.message == MicroserviceOfflineException.code) {
|
|
99
|
+
if (options.retry && count < options.retry)
|
|
100
|
+
return timer(1000);
|
|
101
|
+
}
|
|
102
|
+
throw e;
|
|
103
|
+
}
|
|
104
|
+
}), options.timeout ? timeout({
|
|
105
|
+
each: options.timeout,
|
|
106
|
+
with: () => throwError(() => new MicroserviceRpcTimeout())
|
|
107
|
+
}) : tap(), catchError(e => {
|
|
108
|
+
if (options.fallback != undefined)
|
|
109
|
+
return of(options.fallback);
|
|
110
|
+
if (e.code) {
|
|
111
|
+
const error = new MicroserviceException(e);
|
|
112
|
+
error.stack = e.stack;
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
throw e;
|
|
116
|
+
}));
|
|
199
117
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
payload,
|
|
218
|
-
topic: smnid,
|
|
219
|
-
node_id: smnid
|
|
220
|
-
}, transporter);
|
|
221
|
-
};
|
|
222
|
-
try {
|
|
223
|
-
const instance = this.#get_rpc_target(event.request);
|
|
224
|
-
try {
|
|
225
|
-
const value = await instance?.[event.request.method]?.(...event.request.args || []);
|
|
226
|
-
if (event.request.request_id == '#')
|
|
227
|
-
return;
|
|
228
|
-
if (value instanceof Observable) {
|
|
229
|
-
value.subscribe({
|
|
230
|
-
complete: () => response({ completed: true }),
|
|
231
|
-
error: error => response({ error }),
|
|
232
|
-
next: value => response({ data: { value } })
|
|
233
|
-
});
|
|
234
|
-
return;
|
|
118
|
+
linkTransporter(transporter) {
|
|
119
|
+
const transporter_name = Object.getPrototypeOf(transporter).constructor.name;
|
|
120
|
+
if (transporter instanceof RpcTransporter) {
|
|
121
|
+
const rpc = transporter;
|
|
122
|
+
if (this.#rpcs.has(transporter_name))
|
|
123
|
+
return;
|
|
124
|
+
this.#rpcs.set(transporter_name, rpc);
|
|
125
|
+
return transporter.link(this.#nodes).pipe(map(e => {
|
|
126
|
+
const rpc = e.rpc;
|
|
127
|
+
if (rpc) {
|
|
128
|
+
try {
|
|
129
|
+
const service = services$.value[rpc.service];
|
|
130
|
+
const instance = service?.instance;
|
|
131
|
+
if (!instance)
|
|
132
|
+
return rpc.callback(throwError(() => new MicroserviceNotFound()));
|
|
133
|
+
const response = instance[rpc.method](...rpc.args);
|
|
134
|
+
rpc.callback(response);
|
|
235
135
|
}
|
|
236
|
-
|
|
237
|
-
|
|
136
|
+
catch (err) {
|
|
137
|
+
rpc.callback(throwError(() => err));
|
|
238
138
|
}
|
|
239
139
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
140
|
+
const online = e.online;
|
|
141
|
+
if (online) {
|
|
142
|
+
const node = this.#nodes.value.nodes.get(online);
|
|
143
|
+
if (node) {
|
|
144
|
+
node.rpc = transporter_name;
|
|
145
|
+
for (const service of Object.keys(node.services || {})) {
|
|
146
|
+
const services = this.#services$.value;
|
|
147
|
+
const target = services.get(service) || { index: 0, nodes: [] };
|
|
148
|
+
services.set(service, {
|
|
149
|
+
index: target.index,
|
|
150
|
+
nodes: [
|
|
151
|
+
...target.nodes.filter(id => id != node.node_id),
|
|
152
|
+
node.node_id
|
|
153
|
+
]
|
|
154
|
+
});
|
|
155
|
+
this.#services$.next(services);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
244
158
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
159
|
+
const offline = e.offline;
|
|
160
|
+
if (offline) {
|
|
161
|
+
const nodes = this.#nodes.value.nodes;
|
|
162
|
+
const node = nodes.get(offline);
|
|
163
|
+
if (node) {
|
|
164
|
+
nodes.delete(offline);
|
|
165
|
+
this.#nodes.next({ nodes, last_updated_node_id: node.node_id });
|
|
166
|
+
for (const service of Object.values(node.services)) {
|
|
167
|
+
const services = this.#services$.value;
|
|
168
|
+
const target = services.get(service) || { index: 0, nodes: [] };
|
|
169
|
+
const nodes = target.nodes.filter(id => id != node.node_id);
|
|
170
|
+
nodes.length == 0 ? services.delete(service) : services.set(service, { ...target, nodes });
|
|
171
|
+
this.#services$.next(services);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const metadata = e.metadata;
|
|
176
|
+
if (metadata) {
|
|
177
|
+
this.#metadata$.next({
|
|
178
|
+
...this.#metadata$.value,
|
|
179
|
+
transporters: {
|
|
180
|
+
...this.#metadata$.value.transporters,
|
|
181
|
+
[transporter_name]: metadata
|
|
182
|
+
},
|
|
183
|
+
version: this.#metadata$.value.version + 1
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}), finalize(() => {
|
|
187
|
+
this.#rpcs.delete(transporter_name);
|
|
188
|
+
})).subscribe();
|
|
251
189
|
}
|
|
252
|
-
if (
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
190
|
+
if (transporter instanceof PubsubTransporter) {
|
|
191
|
+
const transporters = this.#pubsubs.getValue();
|
|
192
|
+
transporters.set(transporter_name, transporter);
|
|
193
|
+
this.#pubsubs.next(transporters);
|
|
194
|
+
return transporter.link(this.#nodes).pipe(map(a => a.metadata), filter(Boolean), tap(metadata => {
|
|
195
|
+
this.#metadata$.next({
|
|
196
|
+
...this.#metadata$.value,
|
|
197
|
+
transporters: {
|
|
198
|
+
...this.#metadata$.value.transporters,
|
|
199
|
+
[transporter_name]: metadata
|
|
200
|
+
},
|
|
201
|
+
version: this.#metadata$.value.version + 1
|
|
202
|
+
});
|
|
203
|
+
}), finalize(() => {
|
|
204
|
+
transporters.delete(transporter_name);
|
|
205
|
+
this.#pubsubs.next(transporters);
|
|
206
|
+
})).subscribe();
|
|
265
207
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
...payload,
|
|
275
|
-
local_transporter_id: transporter.transporter_id,
|
|
276
|
-
remote_transporter_id: sti,
|
|
277
|
-
})), filter(node => node.node_id != this.#node_id), mergeMap(node => this.#on_node_discovered(node), 1)))).subscribe();
|
|
278
|
-
transporter.$nodes_status.pipe(tap(node => !node.online && this.#on_node_offline(transporter.transporter_id, node.remote_transporter_id)), groupBy(node => node.remote_transporter_id), mergeMap(grouped => grouped.pipe(scan((prev, current) => {
|
|
279
|
-
if (!prev || (prev.online != current.online))
|
|
280
|
-
return current;
|
|
281
|
-
return undefined;
|
|
282
|
-
}, undefined), filter(Boolean), map(node => node), filter(node => node.online), tap(node => this.#self_introduce(transporter.transporter_id))))).subscribe();
|
|
283
|
-
$services.subscribe(({ instance }) => {
|
|
284
|
-
const prototype = Object.getPrototypeOf(instance);
|
|
285
|
-
const name = prototype.constructor.name;
|
|
286
|
-
this.listen(name, transporter).subscribe(evt => this.#node_event_handler(transporter, evt));
|
|
287
|
-
const event_subscribers = listEventSubscribers(prototype);
|
|
288
|
-
for (const { event, method, buffer_ms } of event_subscribers) {
|
|
289
|
-
this.listen(event, transporter).pipe(filter(() => !this.#$isolated.value), buffer_ms ? pipe(bufferTime(buffer_ms), filter(l => l.length > 0)) : pipe(map(item => [item]))).subscribe(e => instance[method]?.(e, this));
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
async #on_node_discovered(node) {
|
|
294
|
-
if (node.node_id == this.#node_id)
|
|
295
|
-
return;
|
|
296
|
-
const saved_node = this.#linked_nodes.get(node.node_id);
|
|
297
|
-
if (saved_node && saved_node.last_online > node.last_online)
|
|
298
|
-
return;
|
|
299
|
-
const peer_updated = node.linked.includes(this.#node_id);
|
|
300
|
-
const new_node = {
|
|
301
|
-
...node,
|
|
302
|
-
online: true
|
|
303
|
-
};
|
|
304
|
-
this.#linked_nodes.set(node.node_id, new_node);
|
|
305
|
-
this.#transporters.get(node.local_transporter_id)?.nodes.set(node.remote_transporter_id, node.node_id);
|
|
306
|
-
if (!peer_updated && !this.#$isolated.value)
|
|
307
|
-
await this.#self_introduce(node.local_transporter_id, node.node_id);
|
|
308
|
-
for (const service_id of Object.keys(node.services)) {
|
|
309
|
-
if (!this.#remote_services.has(service_id)) {
|
|
310
|
-
this.#remote_services.set(service_id, {
|
|
311
|
-
last_call_index: -1,
|
|
312
|
-
nodes: []
|
|
208
|
+
if (transporter instanceof DiscoveryTransporter) {
|
|
209
|
+
const discover = transporter;
|
|
210
|
+
this.#discovers.set(transporter_name, discover);
|
|
211
|
+
return discover.link(this.#metadata$).pipe(tap(node => {
|
|
212
|
+
const nodes = this.#nodes.value.nodes;
|
|
213
|
+
nodes.set(node.node_id, {
|
|
214
|
+
...nodes.get(node.node_id) || {},
|
|
215
|
+
...node
|
|
313
216
|
});
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
217
|
+
const last_updated_node_id = node.node_id;
|
|
218
|
+
this.#nodes.next({
|
|
219
|
+
nodes,
|
|
220
|
+
last_updated_node_id
|
|
221
|
+
});
|
|
222
|
+
}), finalize(() => this.#discovers.delete(transporter_name))).subscribe();
|
|
318
223
|
}
|
|
319
|
-
this.$nodes_monitor.next(node);
|
|
320
224
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
return;
|
|
328
|
-
for (const service of this.#remote_services.values()) {
|
|
329
|
-
service.nodes = service.nodes.filter(node => node.remote_transporter_id != remote_transporter_id);
|
|
330
|
-
}
|
|
331
|
-
for (const [rid, { $response, rpc_node_id }] of this.#requests) {
|
|
332
|
-
if (rpc_node_id == node_id) {
|
|
333
|
-
$response.error('OFFLINE');
|
|
334
|
-
this.#requests.delete(rid);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
node && this.$nodes_monitor.next({ ...node, online: false });
|
|
338
|
-
this.#linked_nodes.delete(node_id);
|
|
225
|
+
waitServiceOnline(service, check = (nodes => nodes.length > 0)) {
|
|
226
|
+
return firstValueFrom(this.#services$.pipe(map(() => {
|
|
227
|
+
const targets = this.#services$.value.get(service)?.nodes || [];
|
|
228
|
+
const nodes = targets.map(id => this.#nodes.value.nodes.get(id)).filter(Boolean);
|
|
229
|
+
return nodes;
|
|
230
|
+
}), mergeMap(async (targets) => check(targets)), filter(Boolean)));
|
|
339
231
|
}
|
|
340
|
-
|
|
341
|
-
const
|
|
342
|
-
const
|
|
232
|
+
linkRemoteService(factory) {
|
|
233
|
+
const service = factory.name;
|
|
234
|
+
const omitProperties = new Set([
|
|
343
235
|
'caller',
|
|
344
236
|
'callee',
|
|
345
237
|
'arguments',
|
|
@@ -361,114 +253,110 @@ export class SpiderMesh {
|
|
|
361
253
|
'beforeApplicationShutdown',
|
|
362
254
|
'onApplicationShutdown'
|
|
363
255
|
]);
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
256
|
+
const listRpcNodes = () => {
|
|
257
|
+
const targets = this.#services$.value.get(service);
|
|
258
|
+
if (!targets || targets.nodes.length == 0)
|
|
259
|
+
return [];
|
|
260
|
+
return targets.nodes.map(id => {
|
|
261
|
+
const node = this.#nodes.value.nodes.get(id);
|
|
262
|
+
if (node && node.rpc)
|
|
263
|
+
return node;
|
|
264
|
+
}).filter(Boolean).map(node => node);
|
|
265
|
+
};
|
|
266
|
+
const target = new Proxy({}, {
|
|
267
|
+
get: (_, $) => {
|
|
268
|
+
if (omitProperties.has($))
|
|
269
|
+
return null;
|
|
270
|
+
if ($ == 'wait$') {
|
|
271
|
+
return (fn) => this.waitServiceOnline(service, fn);
|
|
375
272
|
}
|
|
376
|
-
if (
|
|
377
|
-
return ()
|
|
378
|
-
if (
|
|
379
|
-
return (
|
|
273
|
+
if ($ == 'nodes')
|
|
274
|
+
return listRpcNodes();
|
|
275
|
+
if ($ == 'watch$')
|
|
276
|
+
return (fn = (nodes => nodes.length > 0)) => {
|
|
277
|
+
return this.#services$.pipe(map(services => services.get(service)?.nodes || []), map(targets => targets.map(id => this.#nodes.value.nodes.get(id)).filter(Boolean)));
|
|
278
|
+
};
|
|
279
|
+
if ($.startsWith('__batch__')) {
|
|
280
|
+
const method = $.split('__batch__')?.[1];
|
|
281
|
+
return (...args) => from(listRpcNodes()).pipe(mergeMap(node => (this.callRemoteService({
|
|
282
|
+
service,
|
|
283
|
+
args,
|
|
284
|
+
method,
|
|
285
|
+
node_id: node.node_id,
|
|
286
|
+
}).pipe(map(data => ({ node, data })), catchError(e => of({ node, e }))))));
|
|
380
287
|
}
|
|
381
|
-
if (
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
288
|
+
if ($ == 'set') {
|
|
289
|
+
return (options) => new Proxy({}, {
|
|
290
|
+
get: (_, method) => {
|
|
291
|
+
return (...args) => {
|
|
292
|
+
const response = this.callRemoteService({
|
|
293
|
+
...options,
|
|
294
|
+
args,
|
|
295
|
+
method,
|
|
296
|
+
service
|
|
297
|
+
});
|
|
298
|
+
return Object.assign(response, {
|
|
299
|
+
then: async (s, r) => {
|
|
300
|
+
try {
|
|
301
|
+
s(await firstValueFrom(response));
|
|
302
|
+
}
|
|
303
|
+
catch (e) {
|
|
304
|
+
r(e);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return (...args) => {
|
|
313
|
+
const response = this.callRemoteService({
|
|
314
|
+
args,
|
|
315
|
+
method: $,
|
|
316
|
+
service
|
|
317
|
+
});
|
|
318
|
+
return Object.assign(response, {
|
|
319
|
+
then: async (s, r) => {
|
|
389
320
|
try {
|
|
390
|
-
|
|
391
|
-
return { node, data };
|
|
321
|
+
s(await firstValueFrom(response));
|
|
392
322
|
}
|
|
393
|
-
catch (
|
|
394
|
-
|
|
323
|
+
catch (e) {
|
|
324
|
+
r(e);
|
|
395
325
|
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
-
if (actions.has(method) || method.startsWith('$')) {
|
|
401
|
-
return new DeepProxy(method => RPCOptionsList.has(method), (method, options) => ((...args) => this.rpc(service_name, method, args, options))).nest()[method];
|
|
402
|
-
}
|
|
403
|
-
return null;
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
};
|
|
404
329
|
}
|
|
405
330
|
});
|
|
331
|
+
return new Proxy(target, {
|
|
332
|
+
get(target, p) {
|
|
333
|
+
if (p == 'then')
|
|
334
|
+
return target;
|
|
335
|
+
return target[p];
|
|
336
|
+
},
|
|
337
|
+
});
|
|
406
338
|
}
|
|
407
|
-
|
|
408
|
-
const
|
|
409
|
-
const $$ = publish_buffer_ms ? $.pipe(bufferTime(publish_buffer_ms), filter(l => l.length > 0)) : $;
|
|
410
|
-
$$.subscribe(data => this.publish({
|
|
411
|
-
payload: [data],
|
|
412
|
-
topic: event_factory.name
|
|
413
|
-
}));
|
|
339
|
+
linkEvent(factory) {
|
|
340
|
+
const topic = factory.name;
|
|
414
341
|
return {
|
|
415
|
-
publish:
|
|
416
|
-
listen: () =>
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
async #wait_service_online(service_name = 'all', fn = nodes => nodes.length > 0) {
|
|
420
|
-
while (true) {
|
|
421
|
-
await sleep(1000);
|
|
422
|
-
if (service_name == 'all') {
|
|
423
|
-
const ok = await firstValueFrom(from(this.#remote_services.values()).pipe(mergeMap(async (e) => await fn(e.nodes)), toArray(), map(list => list.every(ok => ok))));
|
|
424
|
-
if (ok)
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
const nodes = this.#remote_services.get(service_name)?.nodes || [];
|
|
429
|
-
if (await fn(nodes))
|
|
430
|
-
return;
|
|
342
|
+
publish: (data) => lastValueFrom(from(this.#pubsubs.getValue().values()).pipe(mergeMap(t => t.publish(topic, data))), { defaultValue: undefined }),
|
|
343
|
+
listen: () => {
|
|
344
|
+
return this.#pubsubs.pipe(map(list => [...list.values()]), mergeAll(), mergeMap(t => t.listen(topic)));
|
|
431
345
|
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
publish({ payload, topic, node_id, local_transporter_id }, transporter) {
|
|
435
|
-
const event = typeof topic == 'string' ? topic : topic.name;
|
|
436
|
-
const e = {
|
|
437
|
-
event,
|
|
438
|
-
metadata: { smnid: this.#node_id },
|
|
439
|
-
payload
|
|
440
346
|
};
|
|
441
|
-
if (transporter) {
|
|
442
|
-
return transporter.publish(e);
|
|
443
|
-
}
|
|
444
|
-
if (node_id) {
|
|
445
|
-
const node = this.#linked_nodes.get(node_id);
|
|
446
|
-
if (!node)
|
|
447
|
-
return;
|
|
448
|
-
const target = this.#transporters.get(node.local_transporter_id);
|
|
449
|
-
if (!target)
|
|
450
|
-
return;
|
|
451
|
-
return target.transporter.publish({
|
|
452
|
-
...e,
|
|
453
|
-
rti: node.remote_transporter_id
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
if (local_transporter_id) {
|
|
457
|
-
const target = this.#transporters.get(local_transporter_id);
|
|
458
|
-
return target && target.transporter.publish(e);
|
|
459
|
-
}
|
|
460
|
-
for (const { transporter } of this.#transporters.values()) {
|
|
461
|
-
transporter.publish(e);
|
|
462
|
-
}
|
|
463
347
|
}
|
|
464
|
-
|
|
465
|
-
const
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
348
|
+
async exposeLocalService(name, instance) {
|
|
349
|
+
const hooks = listBeforeMicroserviceOnlineMethods(instance);
|
|
350
|
+
for (const method of hooks)
|
|
351
|
+
await instance[method];
|
|
352
|
+
services$.next({
|
|
353
|
+
...services$.value,
|
|
354
|
+
[name]: {
|
|
355
|
+
instance,
|
|
356
|
+
metadata: {},
|
|
357
|
+
name
|
|
358
|
+
}
|
|
359
|
+
});
|
|
472
360
|
}
|
|
473
361
|
}
|
|
474
362
|
//# sourceMappingURL=SpiderMesh.js.map
|