@persistica/flux-mesh 0.0.1 → 0.0.4
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 +9 -0
- package/main.js +6 -0
- package/package.json +24 -21
- package/build.js +0 -21
- package/message-routing.excalidraw +0 -2433
- package/project.json +0 -80
- package/src/_classes/web-rtc-client-interface.class.ts +0 -79
- package/src/_routes/auth/network-authority.post.route.ts +0 -74
- package/src/_routes/auth/network-client.post.route.ts +0 -169
- package/src/_routes/options.route.ts +0 -9
- package/src/auth/auth.ts +0 -29
- package/src/business-logic/channels/channel-manager.class.ts +0 -209
- package/src/index.ts +0 -3
- package/src/main.ts +0 -680
- package/src/orchestrators/facilitate-webrtc-connection.class.ts +0 -23
- package/src/register/network-client-manager.class.ts +0 -175
- package/src/register/register-network-authority.class.ts +0 -123
- package/src/routing/addressing.utils.ts +0 -34
- package/src/routing/global-channel/global-channel-pubsub.class.ts +0 -76
- package/src/routing/incomming-message-router.class.ts +0 -47
- package/src/routing/outgoing-message-router.class.ts +0 -72
- package/src/routing/process-message-router.class.ts +0 -85
- package/src/routing/redis/hash/network-agent.redis.sorted-set.ts +0 -60
- package/src/routing/redis/network-agent.redis.ts +0 -135
- package/src/routing/redis/redis-connection.class.ts +0 -261
- package/src/routing/rpc/core/global-rpc-client.class.ts +0 -215
- package/src/test.spec.ts +0 -7
- package/tsconfig.json +0 -23
- package/tsconfig.lib.json +0 -20
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Register information on a specific network agent.
|
|
3
|
-
*/
|
|
4
|
-
import type { RedisClient } from 'bun';
|
|
5
|
-
import type {
|
|
6
|
-
TAddress,
|
|
7
|
-
TClientId,
|
|
8
|
-
TAgentOwnUId,
|
|
9
|
-
TNetworkId_S,
|
|
10
|
-
} from '@flux/shared/types';
|
|
11
|
-
import { NetworkAgentRedisSortedSet } from './hash/network-agent.redis.sorted-set';
|
|
12
|
-
|
|
13
|
-
export class NetworkAgentRedis {
|
|
14
|
-
private readonly networkAgentRedisSortedSet: NetworkAgentRedisSortedSet;
|
|
15
|
-
private readonly cashedDataUsage: Map<string, number> = new Map();
|
|
16
|
-
|
|
17
|
-
constructor(
|
|
18
|
-
private readonly client: RedisClient,
|
|
19
|
-
) {
|
|
20
|
-
this.networkAgentRedisSortedSet = new NetworkAgentRedisSortedSet(client);
|
|
21
|
-
|
|
22
|
-
// Update the network agent data usage regularly
|
|
23
|
-
setInterval(this.pushDataUsage.bind(this), 3_000);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Registers a network agent in the sorted set.
|
|
28
|
-
*
|
|
29
|
-
* @param { TNetworkId_S } networkId
|
|
30
|
-
* @param { TClientId } socketId
|
|
31
|
-
*
|
|
32
|
-
* @returns { Promise<void> }
|
|
33
|
-
*/
|
|
34
|
-
public async registerNetworkAgent(
|
|
35
|
-
networkId: TNetworkId_S,
|
|
36
|
-
clientId: TAddress,
|
|
37
|
-
socketId: TClientId,
|
|
38
|
-
uid: TAgentOwnUId,
|
|
39
|
-
): Promise<void> {
|
|
40
|
-
const key: string = `networks/${networkId}/agents/${socketId}`;
|
|
41
|
-
|
|
42
|
-
await this.client.hmset(key, [
|
|
43
|
-
'data-usage',
|
|
44
|
-
'0',
|
|
45
|
-
|
|
46
|
-
'clientId',
|
|
47
|
-
clientId,
|
|
48
|
-
|
|
49
|
-
'name',
|
|
50
|
-
uid,
|
|
51
|
-
|
|
52
|
-
'registerAt',
|
|
53
|
-
new Date().toISOString(),
|
|
54
|
-
]);
|
|
55
|
-
|
|
56
|
-
await this.client.expire(key, 500);
|
|
57
|
-
|
|
58
|
-
await this.networkAgentRedisSortedSet
|
|
59
|
-
.registerAgent(
|
|
60
|
-
networkId,
|
|
61
|
-
socketId,
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Unregisters a network agent from the sorted set.
|
|
67
|
-
*
|
|
68
|
-
* @param { TNetworkId_S } networkId
|
|
69
|
-
* @param { TClientId } socketId
|
|
70
|
-
*
|
|
71
|
-
* @returns { Promise<number> }
|
|
72
|
-
*/
|
|
73
|
-
public async unregisterNetworkAgent(
|
|
74
|
-
networkId: TNetworkId_S,
|
|
75
|
-
socketId: TClientId,
|
|
76
|
-
): Promise<number> {
|
|
77
|
-
const key: string = `networks/${networkId}/agents/${socketId}`;
|
|
78
|
-
|
|
79
|
-
await this.client.hmset(key, [
|
|
80
|
-
'unregisteredAt',
|
|
81
|
-
new Date().toISOString(),
|
|
82
|
-
]);
|
|
83
|
-
|
|
84
|
-
return await this.networkAgentRedisSortedSet
|
|
85
|
-
.unregisterAgent(
|
|
86
|
-
networkId,
|
|
87
|
-
socketId,
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
*
|
|
93
|
-
* @param socketId
|
|
94
|
-
* @param usage
|
|
95
|
-
*
|
|
96
|
-
* @returns { void }
|
|
97
|
-
*/
|
|
98
|
-
public registerDataUsage(
|
|
99
|
-
networkId: TNetworkId_S,
|
|
100
|
-
socketId: TClientId,
|
|
101
|
-
usage: number,
|
|
102
|
-
): void {
|
|
103
|
-
const key: string = `networks/${networkId}/agents/${socketId}`;
|
|
104
|
-
|
|
105
|
-
const cashedDataUsage: number | undefined = this.cashedDataUsage.get(socketId);
|
|
106
|
-
|
|
107
|
-
if (cashedDataUsage === undefined) {
|
|
108
|
-
this.cashedDataUsage.set(key, usage);
|
|
109
|
-
} else {
|
|
110
|
-
this.cashedDataUsage.set(key, usage + cashedDataUsage);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Pushes the data usage to the Redis server.
|
|
116
|
-
*
|
|
117
|
-
* @returns { Promise<void> }
|
|
118
|
-
*/
|
|
119
|
-
private async pushDataUsage(
|
|
120
|
-
|
|
121
|
-
): Promise<void> {
|
|
122
|
-
|
|
123
|
-
for (const [redisKey, usage] of this.cashedDataUsage) {
|
|
124
|
-
await this.client
|
|
125
|
-
.hincrby(
|
|
126
|
-
redisKey,
|
|
127
|
-
'data-usage',
|
|
128
|
-
usage,
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
this.cashedDataUsage.delete(redisKey);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
}
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type TRedisEventChannel,
|
|
3
|
-
BunRedisClient,
|
|
4
|
-
BunRedisPubSub,
|
|
5
|
-
} from '@core/redis/bun';
|
|
6
|
-
import type { TAddress, TClientId, TProcessAddress } from '@flux/shared/types';
|
|
7
|
-
import { NetworkAuthorityRedisSortedSet } from '@flux/mesh/store/redis/network-authority';
|
|
8
|
-
import { NetworkAgentRedisService } from '@flux/mesh/store/redis/network-agent';
|
|
9
|
-
import type { TGlobalChannel } from '../global-channel/global-channel-pubsub.class';
|
|
10
|
-
|
|
11
|
-
let redisConnection: RedisConnection | undefined;
|
|
12
|
-
|
|
13
|
-
if (!process.env['FLUX_MESH_REDIS_URL']) {
|
|
14
|
-
throw new Error('Missing FLUX_MESH_REDIS_URL in .env');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Singleton function to get the Redis connection
|
|
19
|
-
*
|
|
20
|
-
* @returns
|
|
21
|
-
*/
|
|
22
|
-
export const getMeshRedisConnection = (
|
|
23
|
-
connectionString?: string,
|
|
24
|
-
) => {
|
|
25
|
-
// We have to read the env variable here, because otherwise it can't be modified in tests
|
|
26
|
-
redisConnection ??= new RedisConnection(process.env['FLUX_MESH_REDIS_URL'] as string);
|
|
27
|
-
|
|
28
|
-
return redisConnection;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export type MessageCallback = (message: string, channel?: string) => unknown;
|
|
32
|
-
|
|
33
|
-
export class RedisConnection {
|
|
34
|
-
// Dedicated client for handling hash
|
|
35
|
-
private readonly cacheClient: BunRedisClient;
|
|
36
|
-
|
|
37
|
-
// Dedicated clients for handling pub/sub
|
|
38
|
-
private readonly pubSub: BunRedisPubSub;
|
|
39
|
-
|
|
40
|
-
public readonly networkAuthoritySet: NetworkAuthorityRedisSortedSet;
|
|
41
|
-
public readonly networkAgentRedisService: NetworkAgentRedisService;
|
|
42
|
-
|
|
43
|
-
// Wrapper to not expose BunRedisClient functions
|
|
44
|
-
public readonly hash;
|
|
45
|
-
|
|
46
|
-
constructor(
|
|
47
|
-
private readonly url: string,
|
|
48
|
-
) {
|
|
49
|
-
this.cacheClient = new BunRedisClient({
|
|
50
|
-
url: this.url,
|
|
51
|
-
socket: {
|
|
52
|
-
reconnectStrategy: (
|
|
53
|
-
retries: number,
|
|
54
|
-
) => {
|
|
55
|
-
// if (retries > 10) {
|
|
56
|
-
// console.error('❌ Redis reconnect failed after 10 attempts.');
|
|
57
|
-
// return new Error('Redis reconnect limit reached');
|
|
58
|
-
// }
|
|
59
|
-
|
|
60
|
-
console.warn(`🔄 Redis reconnection attempt #${retries}`);
|
|
61
|
-
|
|
62
|
-
// Backoff in ms
|
|
63
|
-
return Math.min(retries * 100, 3_000);
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
this.cacheClient.connect();
|
|
70
|
-
} catch {
|
|
71
|
-
console.log('caught');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
this.cacheClient
|
|
75
|
-
.on('error', (error) => {
|
|
76
|
-
console.error('❌ Redis client error:', error.message);
|
|
77
|
-
|
|
78
|
-
// Socket closed unexpectedly
|
|
79
|
-
// Failed to connect
|
|
80
|
-
})
|
|
81
|
-
.on('reconnecting', () => {
|
|
82
|
-
console.log('🔄 Redis reconnecting...');
|
|
83
|
-
})
|
|
84
|
-
.on('ready', () => {
|
|
85
|
-
console.log('✅ Redis client ready');
|
|
86
|
-
})
|
|
87
|
-
.on('end', () => {
|
|
88
|
-
console.warn('🚫 Redis connection closed');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
this.networkAuthoritySet = new NetworkAuthorityRedisSortedSet(this.cacheClient.getClient());
|
|
92
|
-
this.networkAgentRedisService = new NetworkAgentRedisService(this.cacheClient.getClient());
|
|
93
|
-
|
|
94
|
-
// *** Create Redis subscriber
|
|
95
|
-
this.pubSub = new BunRedisPubSub(
|
|
96
|
-
{
|
|
97
|
-
url: this.url,
|
|
98
|
-
socket: {
|
|
99
|
-
reconnectStrategy: (
|
|
100
|
-
retries: number,
|
|
101
|
-
) => {
|
|
102
|
-
console.warn(`🔄 Redis reconnection attempt #${retries}`);
|
|
103
|
-
|
|
104
|
-
// Backoff in ms
|
|
105
|
-
return Math.min(retries * 100, 3_000);
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
this.pubSub
|
|
111
|
-
.connect()
|
|
112
|
-
.catch(() => console.log('pubsub connection failed'));
|
|
113
|
-
|
|
114
|
-
// Dedicated client
|
|
115
|
-
const hashClient = this.cacheClient.getClient();
|
|
116
|
-
|
|
117
|
-
this.hash = {
|
|
118
|
-
sadd: hashClient.sadd.bind(hashClient),
|
|
119
|
-
hmset: hashClient.hmset.bind(hashClient),
|
|
120
|
-
smembers: hashClient.smembers.bind(hashClient),
|
|
121
|
-
srem: hashClient.srem.bind(hashClient),
|
|
122
|
-
scard: hashClient.scard.bind(hashClient),
|
|
123
|
-
del: hashClient.del.bind(hashClient),
|
|
124
|
-
hincrby: hashClient.hincrby.bind(hashClient),
|
|
125
|
-
hmget: hashClient.hmget.bind(hashClient),
|
|
126
|
-
expire: hashClient.expire.bind(hashClient),
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// ****************************************************************************
|
|
131
|
-
// *** Publish To All Channels
|
|
132
|
-
// ****************************************************************************
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
*
|
|
136
|
-
* @param { TProcessAddress } skipProcessAddress
|
|
137
|
-
* @param { TGlobalChannel } channel
|
|
138
|
-
* @param { string } message
|
|
139
|
-
*
|
|
140
|
-
* @returns { Promise<void> }
|
|
141
|
-
*/
|
|
142
|
-
public async publishWebsocketChannelEvent(
|
|
143
|
-
skipProcessAddress: TProcessAddress,
|
|
144
|
-
channel: TGlobalChannel,
|
|
145
|
-
message: string,
|
|
146
|
-
): Promise<void> {
|
|
147
|
-
try {
|
|
148
|
-
await this.pubSub.publish(`~${channel}`, `${skipProcessAddress}:${message}`);
|
|
149
|
-
} catch {
|
|
150
|
-
console.error(`Publish failed on global channel event: '${channel}'`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
*
|
|
156
|
-
* @returns { void }
|
|
157
|
-
*/
|
|
158
|
-
public async subscribeWebsocketChannelEvent(
|
|
159
|
-
callback: (
|
|
160
|
-
channel: TGlobalChannel,
|
|
161
|
-
skipProcessAddress: TProcessAddress,
|
|
162
|
-
message: string,
|
|
163
|
-
) => unknown,
|
|
164
|
-
): Promise<void> {
|
|
165
|
-
try {
|
|
166
|
-
await this.pubSub.subscribe('~*', (
|
|
167
|
-
message: string,
|
|
168
|
-
preFixedChannel: TRedisEventChannel,
|
|
169
|
-
) => {
|
|
170
|
-
const index = message.indexOf(':');
|
|
171
|
-
|
|
172
|
-
const [skipProcessAddress, message_] = index !== -1
|
|
173
|
-
? [message.slice(0, index), message.slice(index + 1)]
|
|
174
|
-
: [message, ''];
|
|
175
|
-
|
|
176
|
-
callback(
|
|
177
|
-
preFixedChannel.slice(1) as TGlobalChannel,
|
|
178
|
-
skipProcessAddress as TProcessAddress,
|
|
179
|
-
message_,
|
|
180
|
-
);
|
|
181
|
-
});
|
|
182
|
-
} catch {
|
|
183
|
-
console.log('Error caught while subscribing to websocket channel event globally');
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// ****************************************************************************
|
|
188
|
-
// *** Publish Directly to Address
|
|
189
|
-
// ****************************************************************************
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
*
|
|
193
|
-
* @param { TAddress | TProcessAddress } address
|
|
194
|
-
* @param { string } message
|
|
195
|
-
*
|
|
196
|
-
* @returns { Promise<void> }
|
|
197
|
-
*/
|
|
198
|
-
public async directPublish(
|
|
199
|
-
address: TAddress | TProcessAddress,
|
|
200
|
-
message: string,
|
|
201
|
-
): Promise<void> {
|
|
202
|
-
try {
|
|
203
|
-
await this.pubSub.publish(address, message);
|
|
204
|
-
} catch {
|
|
205
|
-
console.error(`Publish failed on address: ${address}`);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
public subscribe(
|
|
210
|
-
channelId: TProcessAddress | TClientId,
|
|
211
|
-
callback: MessageCallback
|
|
212
|
-
): void {
|
|
213
|
-
try {
|
|
214
|
-
const redisCallback = (message: string) => callback(message);
|
|
215
|
-
|
|
216
|
-
this.pubSub.subscribe(channelId, redisCallback);
|
|
217
|
-
} catch {
|
|
218
|
-
console.log('error caught #2');
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
public unsubscribe(
|
|
223
|
-
channelId: string,
|
|
224
|
-
callback: MessageCallback,
|
|
225
|
-
): void {
|
|
226
|
-
try {
|
|
227
|
-
this.pubSub.unsubscribe(channelId, callback);
|
|
228
|
-
} catch {
|
|
229
|
-
console.log('error caught #1');
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
public async setConnected(
|
|
234
|
-
address: string,
|
|
235
|
-
): Promise<void> {
|
|
236
|
-
await this.hash.hmset(`machines/processes/${address}`, [
|
|
237
|
-
'status', 'connected',
|
|
238
|
-
'updatedAt', new Date().toISOString(),
|
|
239
|
-
]);
|
|
240
|
-
|
|
241
|
-
await this.hash.expire(`machines/processes/${address}`, 5);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
public async setDisconnected(
|
|
245
|
-
_address: string,
|
|
246
|
-
): Promise<void> {
|
|
247
|
-
// !TODO
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Disconnects the Redis cache and pub/sub.
|
|
252
|
-
*
|
|
253
|
-
* @returns { void }
|
|
254
|
-
*/
|
|
255
|
-
public disconnect(
|
|
256
|
-
|
|
257
|
-
): void {
|
|
258
|
-
this.cacheClient.disconnect();
|
|
259
|
-
this.pubSub.disconnect();
|
|
260
|
-
}
|
|
261
|
-
}
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The class to handling the *calling* side of the RPC.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
type TAddress,
|
|
7
|
-
type TProcessAddress,
|
|
8
|
-
GlobalRPCTimeoutError,
|
|
9
|
-
RPC_REQUEST,
|
|
10
|
-
} from '@flux/shared/types';
|
|
11
|
-
import type {
|
|
12
|
-
TCallback,
|
|
13
|
-
RPCRequest,
|
|
14
|
-
RPCResponse,
|
|
15
|
-
} from '@flux/shared/ws';
|
|
16
|
-
import type { OutgoingMessageRouter } from '../../outgoing-message-router.class';
|
|
17
|
-
import type { ProcessMessageRouter } from '../../process-message-router.class';
|
|
18
|
-
import { readProcessAddress } from '../../addressing.utils';
|
|
19
|
-
|
|
20
|
-
export class GlobalRPCClient<TMethods> {
|
|
21
|
-
// TODO OD IS USED TO ROUTE THE MESSAGE; SO MAKE GLOBALLY UNIQUE IN THE FUTURE!!!
|
|
22
|
-
private requestId: number = 0; // Math.floor(Math.random() * 100_000);
|
|
23
|
-
|
|
24
|
-
// Success and error callbacks for each request.
|
|
25
|
-
private readonly pendingRequests: Map<number, [TCallback, TCallback]> =
|
|
26
|
-
new Map();
|
|
27
|
-
|
|
28
|
-
private readonly processAddress: TProcessAddress = readProcessAddress();
|
|
29
|
-
|
|
30
|
-
constructor(
|
|
31
|
-
private readonly _outgoingMessageRouter: OutgoingMessageRouter,
|
|
32
|
-
private readonly _processMessageRouter: ProcessMessageRouter,
|
|
33
|
-
) {
|
|
34
|
-
this._processMessageRouter.subscribe((message: string): void => {
|
|
35
|
-
const message_: RPCResponse = JSON.parse(
|
|
36
|
-
message.substring(message.indexOf(':') + 1)
|
|
37
|
-
) as RPCResponse;
|
|
38
|
-
|
|
39
|
-
this.handleResponseMessage(message_);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Calls a function on the other side.
|
|
45
|
-
*
|
|
46
|
-
* @param { TAddress } rpcServerClientAddress
|
|
47
|
-
* @param { TMethods } method
|
|
48
|
-
*
|
|
49
|
-
* @returns { Promise<any> }
|
|
50
|
-
*/
|
|
51
|
-
public call(
|
|
52
|
-
rpcServerClientAddress: TAddress,
|
|
53
|
-
method: TMethods,
|
|
54
|
-
...params: any
|
|
55
|
-
): Promise<any> {
|
|
56
|
-
const id: number = this.requestId++;
|
|
57
|
-
|
|
58
|
-
const rpcRequest: RPCRequest<TMethods> = {
|
|
59
|
-
id,
|
|
60
|
-
originProcessAddress: this.processAddress,
|
|
61
|
-
method,
|
|
62
|
-
params,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
return new Promise((resolve, reject) => {
|
|
66
|
-
this.pendingRequests.set(id, [resolve, reject]);
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
this._outgoingMessageRouter.message(
|
|
70
|
-
rpcServerClientAddress,
|
|
71
|
-
`${RPC_REQUEST}:${JSON.stringify(rpcRequest)}`
|
|
72
|
-
);
|
|
73
|
-
} catch (error) {
|
|
74
|
-
if (this.pendingRequests.get(id)) {
|
|
75
|
-
this.pendingRequests.delete(id);
|
|
76
|
-
reject(error instanceof Error ? error : new Error(error as string));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
setTimeout(() => {
|
|
81
|
-
if (this.pendingRequests.get(id)) {
|
|
82
|
-
this.pendingRequests.delete(id);
|
|
83
|
-
reject(new GlobalRPCTimeoutError(method as string));
|
|
84
|
-
}
|
|
85
|
-
}, 5_000);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Handles the response from the server.
|
|
91
|
-
*
|
|
92
|
-
* @param { RPCResponse } response
|
|
93
|
-
*
|
|
94
|
-
* @returns { boolean }
|
|
95
|
-
*/
|
|
96
|
-
private handleResponseMessage(
|
|
97
|
-
response: RPCResponse,
|
|
98
|
-
): void {
|
|
99
|
-
const cb: [TCallback, TCallback] | undefined = this.pendingRequests.get(
|
|
100
|
-
response.id
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
if (cb) {
|
|
104
|
-
this.pendingRequests.delete(response.id);
|
|
105
|
-
|
|
106
|
-
if (response.error) {
|
|
107
|
-
cb[1](response.error);
|
|
108
|
-
|
|
109
|
-
return;
|
|
110
|
-
// throw new Error(response.error);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
cb[0](response.result);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export class GlobalRPCClient2<TMethods> {
|
|
119
|
-
// TODOOD IS USED TO ROUTE THE MESSAGE; SO MAKE GLOBALLY UNIQWUE IN THE FUTURE!!!
|
|
120
|
-
private requestId: number = 0; // Math.floor(Math.random() * 100_000);
|
|
121
|
-
|
|
122
|
-
// Success and error callbacks for each request.
|
|
123
|
-
private readonly pendingRequests: Map<number, [TCallback, TCallback]> =
|
|
124
|
-
new Map();
|
|
125
|
-
|
|
126
|
-
private readonly processAddress: TProcessAddress = readProcessAddress();
|
|
127
|
-
|
|
128
|
-
constructor(
|
|
129
|
-
private readonly _rpcServerClientAddress: TAddress,
|
|
130
|
-
private readonly _outgoingMessageRouter: OutgoingMessageRouter,
|
|
131
|
-
private readonly _processMessageRouter: ProcessMessageRouter
|
|
132
|
-
) {
|
|
133
|
-
this._processMessageRouter.subscribe((message: string): void => {
|
|
134
|
-
const message_: RPCResponse = JSON.parse(
|
|
135
|
-
message.substring(message.indexOf(':') + 1)
|
|
136
|
-
) as RPCResponse;
|
|
137
|
-
|
|
138
|
-
this.handleResponseMessage(message_);
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Calls a function on the other side.
|
|
144
|
-
*
|
|
145
|
-
* @param { TAddress } rpcServerClientAddress
|
|
146
|
-
* @param { TMethods } method
|
|
147
|
-
*
|
|
148
|
-
* @returns { Promise<any> }
|
|
149
|
-
*/
|
|
150
|
-
public call(
|
|
151
|
-
method: TMethods,
|
|
152
|
-
...params: any
|
|
153
|
-
): Promise<any> {
|
|
154
|
-
const id: number = this.requestId++;
|
|
155
|
-
|
|
156
|
-
const rpcRequest: RPCRequest<TMethods> = {
|
|
157
|
-
id,
|
|
158
|
-
originProcessAddress: this.processAddress,
|
|
159
|
-
method,
|
|
160
|
-
params,
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
return new Promise((resolve, reject) => {
|
|
164
|
-
|
|
165
|
-
this.pendingRequests.set(id, [resolve, reject]);
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
this._outgoingMessageRouter.message(
|
|
169
|
-
this._rpcServerClientAddress,
|
|
170
|
-
`${RPC_REQUEST}:${JSON.stringify(rpcRequest)}`
|
|
171
|
-
);
|
|
172
|
-
} catch (error) {
|
|
173
|
-
if (this.pendingRequests.get(id)) {
|
|
174
|
-
this.pendingRequests.delete(id);
|
|
175
|
-
reject(error instanceof Error ? error : new Error(error as string));
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
setTimeout(() => {
|
|
180
|
-
if (this.pendingRequests.get(id)) {
|
|
181
|
-
this.pendingRequests.delete(id);
|
|
182
|
-
reject(new GlobalRPCTimeoutError(method as string));
|
|
183
|
-
}
|
|
184
|
-
}, 5_000);
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Handles the response from the server.
|
|
190
|
-
*
|
|
191
|
-
* @param { RPCResponse } response
|
|
192
|
-
*
|
|
193
|
-
* @returns { boolean }
|
|
194
|
-
*/
|
|
195
|
-
private handleResponseMessage(
|
|
196
|
-
response: RPCResponse,
|
|
197
|
-
): void {
|
|
198
|
-
const cb: [TCallback, TCallback] | undefined = this.pendingRequests.get(
|
|
199
|
-
response.id
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
if (cb) {
|
|
203
|
-
this.pendingRequests.delete(response.id);
|
|
204
|
-
|
|
205
|
-
if (response.error) {
|
|
206
|
-
cb[1](response.error);
|
|
207
|
-
|
|
208
|
-
return;
|
|
209
|
-
// throw new Error(response.error);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
cb[0](response.result);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
package/src/test.spec.ts
DELETED
package/tsconfig.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"forceConsistentCasingInFileNames": true,
|
|
6
|
-
"strict": true,
|
|
7
|
-
"importHelpers": true,
|
|
8
|
-
"noImplicitOverride": true,
|
|
9
|
-
"noImplicitReturns": true,
|
|
10
|
-
"noFallthroughCasesInSwitch": true,
|
|
11
|
-
"noPropertyAccessFromIndexSignature": true,
|
|
12
|
-
"lib": [
|
|
13
|
-
"es2021"
|
|
14
|
-
]
|
|
15
|
-
},
|
|
16
|
-
"files": [],
|
|
17
|
-
"include": [],
|
|
18
|
-
"references": [
|
|
19
|
-
{
|
|
20
|
-
"path": "./tsconfig.lib.json"
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
}
|
package/tsconfig.lib.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "../../../dist/out-tsc",
|
|
5
|
-
"declaration": true,
|
|
6
|
-
"types": [
|
|
7
|
-
"node"
|
|
8
|
-
],
|
|
9
|
-
"target": "ESNext",
|
|
10
|
-
"moduleResolution": "bundler",
|
|
11
|
-
"module": "EsNext"
|
|
12
|
-
},
|
|
13
|
-
"include": [
|
|
14
|
-
"src/**/*.ts"
|
|
15
|
-
],
|
|
16
|
-
"exclude": [
|
|
17
|
-
"src/**/*.spec.ts",
|
|
18
|
-
"src/**/*.test.ts"
|
|
19
|
-
]
|
|
20
|
-
}
|