@persistica/flux-mesh 0.0.1 → 0.0.3

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.
@@ -1,23 +0,0 @@
1
- import {
2
- GlobalWebRTCClient,
3
- WebRTCClient,
4
- } from '../_classes/web-rtc-client-interface.class';
5
-
6
- export const facilitateWebRTCConnection = async (
7
- initiator: WebRTCClient | GlobalWebRTCClient,
8
- remoteClient: GlobalWebRTCClient
9
- ) => {
10
- // console.log('Orchestrate ICS COnnection', initiator, remoteClient);
11
-
12
- // initiator.
13
- const offer = await initiator.createOffer();
14
- console.log('Created offer:', !!offer);
15
-
16
- const answer = await remoteClient.acceptOfferAndCreateAnswer(offer);
17
- console.log('Got answer:', !!answer);
18
-
19
- const acceptAnswer: boolean = await initiator.acceptAnswer(answer);
20
- console.log('Answer was accepted by initiator:', acceptAnswer);
21
-
22
- await remoteClient.answerWasAccepted(acceptAnswer);
23
- };
@@ -1,175 +0,0 @@
1
- import {
2
- type RedisConnection,
3
- getMeshRedisConnection,
4
- } from '../routing/redis/redis-connection.class';
5
- import type {
6
- NetworkAgentRedisService,
7
- } from '@flux/mesh/store/redis/network-agent';
8
- import {
9
- NetworkUsageRedisCacheService,
10
- } from '@flux/mesh/store/redis/network-usage';
11
- import type {
12
- TAddress,
13
- TClientId,
14
- TAgentOwnUId,
15
- TNetworkId_S,
16
- } from '@flux/shared/types';
17
- import type {
18
- TFluxClientUID,
19
- } from '@flux/shared/utils';
20
-
21
- export class NetworkAgentManager {
22
- public readonly networkAgentRedisService: NetworkAgentRedisService;
23
- public readonly networkUsageRedisCacheService: NetworkUsageRedisCacheService;
24
-
25
- private readonly redisConnection: RedisConnection = getMeshRedisConnection();
26
- private readonly cache: Map<`${TNetworkId_S}.${TAgentOwnUId}`, TAddress> = new Map(); // ! cleanup
27
- private readonly timers: Map<TClientId, ReturnType<typeof setInterval>> = new Map();
28
-
29
- constructor(
30
-
31
- ) {
32
- this.networkAgentRedisService = this.redisConnection.networkAgentRedisService;
33
- this.networkUsageRedisCacheService = new NetworkUsageRedisCacheService(this.redisConnection['cacheClient'].getClient());
34
- }
35
-
36
- /**
37
- * Register an agent.
38
- *
39
- * @param { TNetworkId_S } networkId
40
- * @param { TClientId } id
41
- * @param { Bun.SocketAddress | null } ip
42
- * @param { TAddress } address
43
- * @param { TAgentOwnUId } [uid]
44
- *
45
- * @returns { void }
46
- */
47
- public registerAgent(
48
- networkId: TNetworkId_S,
49
- id: TClientId,
50
- ip: Bun.SocketAddress | null,
51
- address: TAddress,
52
- throughput: {
53
- bytes: number,
54
- packets: number,
55
- },
56
- uid?: TAgentOwnUId,
57
- machineUID?: TFluxClientUID,
58
- ): Promise<void> {
59
- this.timers.set(
60
- id,
61
- setInterval(async () => {
62
- await this.networkAgentRedisService
63
- .registerAgentThroughput(
64
- networkId,
65
- id,
66
- throughput.bytes,
67
- throughput.packets,
68
- );
69
-
70
- if ((throughput.bytes > 0) || (throughput.packets > 0)) {
71
- await this.networkUsageRedisCacheService
72
- .increaseNetworkUsage(
73
- networkId,
74
- throughput.bytes,
75
- throughput.packets,
76
- );
77
- }
78
-
79
- throughput.bytes = 0;
80
- throughput.packets = 0;
81
- }, 1_000),
82
- );
83
-
84
- return this.networkAgentRedisService
85
- .registerAgent(
86
- networkId,
87
- id,
88
- ip,
89
- address,
90
- uid,
91
- machineUID,
92
- );
93
- }
94
-
95
- /**
96
- * Unregisters a network agent UID and associated data from the Redis hash.
97
- *
98
- * @param { TNetworkId_S } networkId
99
- * @param { TClientId } clientId
100
- * @param { TAgentOwnUId } clientOwnUId
101
- *
102
- * @returns { void }
103
- */
104
- public unregisterNetworkAgent(
105
- networkId: TNetworkId_S,
106
- clientId: TClientId,
107
- clientOwnUId?: TAgentOwnUId,
108
- ): void {
109
- if (clientOwnUId) {
110
- // Unregisters a network client UID and address in the Redis hash.
111
- this.cache.delete(`${networkId}.${clientOwnUId}`);
112
-
113
- this.networkAgentRedisService.unregisterNetworkAgent(
114
- networkId,
115
- clientId,
116
- clientOwnUId,
117
- );
118
- }
119
-
120
- // Cancel the timer
121
- clearInterval(this.timers.get(clientId));
122
- }
123
-
124
- public async resolveNetworkClientAddressByUid(
125
- networkId: TNetworkId_S,
126
- clientOwnUId: TAgentOwnUId,
127
- // retryWithDelay?: number,
128
- ): Promise<TAddress> {
129
- const cached: TAddress | undefined = this.cache.get(
130
- `${networkId}.${clientOwnUId}`
131
- );
132
-
133
- if (cached) {
134
- return cached;
135
- }
136
-
137
- const address: TAddress = await this.redisConnection
138
- .networkAgentRedisService
139
- .readNetworkClientAddressByUIDOrThrow(
140
- networkId,
141
- clientOwnUId
142
- );
143
-
144
- this.cache.set(`${networkId}.${clientOwnUId}`, address);
145
-
146
- return address;
147
- }
148
-
149
- // public register(
150
- // networkId: TNetworkId_S,
151
- // socketId: TClientId,
152
- // ): void {
153
- // this.redisConnection
154
- // .networkAuthoritySet
155
- // .registerNetworkAuthority(
156
- // networkId,
157
- // socketId,
158
- // );
159
- // }
160
-
161
- // public unregister(
162
- // networkId: TNetworkId_S,
163
- // socketId: TClientId,
164
- // ): void {
165
- // this.cache.delete(networkId);
166
-
167
- // this.redisConnection
168
- // .networkAuthoritySet
169
- // .unregister(
170
- // networkId,
171
- // socketId,
172
- // );
173
- // }
174
-
175
- }
@@ -1,123 +0,0 @@
1
- import { splitAddressOrThrow, type TAddress, type TClientId, type TNetworkId_S } from '@flux/shared/types';
2
- import {
3
- type RedisConnection,
4
- getMeshRedisConnection,
5
- } from '../routing/redis/redis-connection.class';
6
- import type {
7
- TFluxClientUID,
8
- } from '@flux/shared/utils';
9
-
10
- export class NetworkAuthorityManager {
11
- private readonly redisConnection: RedisConnection = getMeshRedisConnection();
12
- private readonly cache: Map<TNetworkId_S, Set<TAddress>> = new Map();
13
-
14
- public async register(
15
- networkId: TNetworkId_S,
16
- socketId: TClientId,
17
- machineUID?: TFluxClientUID,
18
- ): Promise<void> {
19
- await this.redisConnection.networkAuthoritySet
20
- .registerNetworkAuthority(
21
- networkId,
22
- socketId,
23
- machineUID,
24
- );
25
- }
26
-
27
- public unregister(
28
- networkId: TNetworkId_S,
29
- networkAuthorityAddress: TAddress,
30
- ): void {
31
- const cached: Set<TAddress> | undefined = this.cache.get(networkId);
32
- if (cached) {
33
- cached.delete(networkAuthorityAddress);
34
- }
35
-
36
- const [_machineAddress, _processId, clientId] = splitAddressOrThrow(networkAuthorityAddress);
37
-
38
- this.redisConnection.networkAuthoritySet.unregister(
39
- networkId,
40
- clientId,
41
- );
42
- }
43
-
44
- /**
45
- * Used for cleanup, in case of discovering an idle authority.
46
- *
47
- * @param { TNetworkId_S } networkId
48
- * @param { TAddress } networkAuthorityAddress
49
- *
50
- * @returns { void }
51
- */
52
- public unregisterGlobal(
53
- networkId: TNetworkId_S,
54
- networkAuthorityAddress: TAddress,
55
- ): void {
56
- const cached: Set<TAddress> | undefined = this.cache.get(networkId);
57
- if (cached) {
58
- cached.delete(networkAuthorityAddress);
59
- }
60
-
61
- this.redisConnection.networkAuthoritySet
62
- .unregisterGlobal(
63
- networkId,
64
- networkAuthorityAddress,
65
- );
66
- }
67
-
68
- /**
69
- * Resolves a random network authority address for a given network ID.
70
- *
71
- * @param { TNetworkId_S } networkId
72
- *
73
- * @returns { Promise<TAddress> }
74
- */
75
- public async resolveNetworkAuthorityAddressOrThrow(
76
- networkId: TNetworkId_S
77
- // retryWithDelay?: number,
78
- ): Promise<TAddress> {
79
- const cached: Set<TAddress> | undefined = this.cache.get(networkId);
80
-
81
- if (cached && (cached.size > 0)) {
82
- const randomItem = [...cached][Math.floor(Math.random() * [...cached].length)];
83
-
84
- if (randomItem) {
85
- return randomItem;
86
- }
87
- }
88
-
89
- const address: TAddress[] = await this.redisConnection
90
- .networkAuthoritySet
91
- .resolveNetworkAuthorityAddressesOrThrow(
92
- networkId,
93
- );
94
-
95
- if (cached) {
96
- for (const a of address) {
97
- cached.add(a);
98
- }
99
- } else {
100
- this.cache.set(networkId, new Set(address));
101
- }
102
-
103
- const cached_: Set<TAddress> = this.cache.get(networkId) as Set<TAddress>;
104
- const randomItem = [...cached_][Math.floor(Math.random() * [...cached_].length)];
105
-
106
- return randomItem;
107
- }
108
-
109
- /**
110
- * Removes a client.
111
- *
112
- * @param { TNetworkId_S } networkId
113
- * @param { TAddress } networkAuthorityAddress
114
- *
115
- * @returns { void }
116
- */
117
- public removeUnresponsiveClient(
118
- networkId: TNetworkId_S,
119
- networkAuthorityAddress: TAddress,
120
- ): void {
121
- this.unregisterGlobal(networkId, networkAuthorityAddress);
122
- }
123
- }
@@ -1,34 +0,0 @@
1
- import * as os from 'node:os';
2
- import type {
3
- TMachineAddress,
4
- TProcessId,
5
- TProcessAddress,
6
- } from '@flux/shared/types';
7
-
8
- export const readProcessId = (): TProcessId => {
9
- return process.pid as TProcessId;
10
- };
11
-
12
- let machineAddress: TMachineAddress | undefined;
13
- export const readMachineAddress = (): TMachineAddress => {
14
- if (machineAddress) {
15
- return machineAddress;
16
- }
17
-
18
- machineAddress = Object.values(os.networkInterfaces())
19
- .flat()
20
- .find((i) => i && !i.internal && i.mac !== '00:00:00:00:00:00')
21
- ?.mac.replaceAll(':', '')
22
- .replace(/^0+/, '') as TMachineAddress;
23
-
24
- return machineAddress;
25
- };
26
-
27
- /**
28
- * Returns the process address.
29
- *
30
- * @returns { TProcessAddress }
31
- */
32
- export const readProcessAddress = (): TProcessAddress => {
33
- return `${readMachineAddress()}/${readProcessId()}`;
34
- };
@@ -1,76 +0,0 @@
1
- /**
2
- * 1. Detects global events and publishes them to all clients subscribed to the websocket channel system.
3
- *
4
- * 2. Sends messages to all nodes in the cluster, and when reached, publishes locally
5
- * on the server channel to everyone subscribed.
6
- */
7
-
8
- import type { TProcessAddress } from '@flux/shared/types';
9
- import type { TConnectedClientSocket } from '../../main';
10
- import type { RedisConnection } from '../redis/redis-connection.class';
11
-
12
- export type TGlobalChannel = string & { __brand: 'global-channel'; };
13
-
14
- export class GlobalChannelPubsub {
15
-
16
- constructor(
17
- private readonly redisConnection: RedisConnection,
18
- private readonly bunServer: Bun.Server,
19
- private readonly processAddress: TProcessAddress,
20
- ) {
21
- this.subscribeWebsocketChannelEvent();
22
- }
23
-
24
- /**
25
- * Subscribes to the websocket channel event and publishes it on the local process.
26
- *
27
- * @returns { void }
28
- */
29
- private subscribeWebsocketChannelEvent(
30
- ): void {
31
- this.redisConnection
32
- .subscribeWebsocketChannelEvent((
33
- channel: TGlobalChannel,
34
- skipProcessAddress: TProcessAddress,
35
- message: string,
36
- ): void => {
37
- if (this.processAddress === skipProcessAddress) {
38
- return;
39
- }
40
-
41
- // Publish it on the local process
42
- this.bunServer.publish(
43
- message,
44
- channel,
45
- );
46
- });
47
- }
48
-
49
- /**
50
- * Publishes a message to the given channel.
51
- *
52
- * @param { string } channel
53
- * @param { string } message
54
- * @param { TConnectedClientSocket } [ws]
55
- *
56
- * @returns { Promise<void> }
57
- */
58
- public async publish(
59
- channel: string,
60
- message: string,
61
- ws?: TConnectedClientSocket,
62
- ): Promise<void> {
63
- // Publish locally on the process to everyone but the client (if a client is provided)
64
- (ws ?? this.bunServer).publish(
65
- channel,
66
- message,
67
- );
68
-
69
- // Publish globally to clients connected to other processes
70
- await this.redisConnection.publishWebsocketChannelEvent(
71
- this.processAddress, // The event is already sent to the local process
72
- channel as TGlobalChannel,
73
- message,
74
- );
75
- }
76
- }
@@ -1,47 +0,0 @@
1
- /**
2
- * Route messages to the correct destination.
3
- */
4
- import type { TClientId } from '@flux/shared/types';
5
- import {
6
- type RedisConnection,
7
- getMeshRedisConnection,
8
- } from './redis/redis-connection.class';
9
-
10
- export class IncommingMessageRouter {
11
- private readonly redisConnection: RedisConnection = getMeshRedisConnection();
12
-
13
- constructor(
14
- private readonly onLocal: (
15
- clientId: TClientId,
16
- onMessage: (message: string) => void
17
- ) => void
18
- ) { }
19
-
20
- /**
21
- *
22
- * @param address
23
- * @param message
24
- *
25
- * @returns { void }
26
- */
27
- // public subscribe(
28
- // clientId: TClientId,
29
- // onMessage: (
30
- // message: string,
31
- // ) => void,
32
- // ): void {
33
- // console.log("subscreibing client", clientId);
34
- // // Listen to local
35
- // this.onLocal(
36
- // clientId,
37
- // onMessage,
38
- // );
39
-
40
- // // Listen to remote
41
- // this.redisConnection
42
- // .subscribe(
43
- // clientId,
44
- // onMessage,
45
- // );
46
- // }
47
- }
@@ -1,72 +0,0 @@
1
- /**
2
- * Route messages to the correct destination.
3
- */
4
- import {
5
- type TAddress,
6
- type TClientId,
7
- type TMachineAddress,
8
- type TProcessId,
9
- splitAddressOrThrow,
10
- } from '@flux/shared/types';
11
- import { readMachineAddress, readProcessId } from './addressing.utils';
12
- import {
13
- type RedisConnection,
14
- getMeshRedisConnection,
15
- } from './redis/redis-connection.class';
16
- import { PicoLogger } from '@utils/pico-logger';
17
-
18
- export class OutgoingMessageRouter {
19
- private readonly processId: TProcessId = readProcessId();
20
- private readonly machineAddress: TMachineAddress = readMachineAddress();
21
-
22
- private readonly redisConnection: RedisConnection = getMeshRedisConnection();
23
-
24
- constructor(
25
- private readonly passToLocalClientOrThrowUnknownClient: (
26
- clientId: TClientId,
27
- message: string,
28
- ) => void,
29
- ) {
30
- console.log(
31
- `Created MessageRouter on machine address: ${this.machineAddress}, process id: ${this.processId}`
32
- );
33
- }
34
-
35
- /**
36
- *
37
- * @param address
38
- * @param message
39
- *
40
- * @returns
41
- */
42
- public message(
43
- address: TAddress,
44
- message: string,
45
- ): void {
46
- const [machineAddress, processId, clientId] = splitAddressOrThrow(address);
47
-
48
- // * Not on the same machine
49
- if (machineAddress !== this.machineAddress) {
50
- PicoLogger.log('🛣️ Routing message to machine', 'routing');
51
-
52
- this.redisConnection.directPublish(address, message);
53
-
54
- return;
55
- }
56
-
57
- // * On the same machine, but not on the same process
58
- if (processId !== this.processId) {
59
- PicoLogger.log(`🛣️ Routing message from process ID '${this.processId}' to process ID: '${processId}'`, 'routing');
60
-
61
- // ! Route through Redis for now, but change to direct process connection
62
- this.redisConnection.directPublish(address, message);
63
-
64
- return;
65
- }
66
-
67
- // * On the same machine and process
68
- PicoLogger.log('🛣️ Routing message to local client', 'routing');
69
-
70
- this.passToLocalClientOrThrowUnknownClient(clientId, message);
71
- }
72
- }
@@ -1,85 +0,0 @@
1
- /**
2
- * Route messages to the correct process if necessary.
3
- */
4
- import {
5
- type TCallbackFunction,
6
- type TMachineAddress,
7
- type TProcessId,
8
- type TProcessAddress,
9
- splitProcessAddress,
10
- } from '@flux/shared/types';
11
- import {
12
- type RedisConnection,
13
- getMeshRedisConnection,
14
- } from './redis/redis-connection.class';
15
- import {
16
- readMachineAddress,
17
- readProcessAddress,
18
- readProcessId,
19
- } from './addressing.utils';
20
- import { PicoLogger } from '@utils/pico-logger';
21
-
22
- export class ProcessMessageRouter {
23
- private readonly machineAddress: TMachineAddress = readMachineAddress();
24
- private readonly processAddress: TProcessAddress = readProcessAddress();
25
- private readonly processId: TProcessId = readProcessId();
26
- private readonly redisConnection: RedisConnection = getMeshRedisConnection();
27
- private readonly localCallbacks: Set<TCallbackFunction> = new Set();
28
-
29
- /**
30
- * Sends a message to a process.
31
- *
32
- * @param { TProcessAddress } address
33
- * @param { string } message
34
- *
35
- * @returns
36
- */
37
- public message(
38
- address: TProcessAddress,
39
- message: string,
40
- ): void {
41
- const [machineAddress, processId] = splitProcessAddress(address);
42
-
43
- // * Not on the same machine
44
- if (machineAddress !== this.machineAddress) {
45
- PicoLogger.log('🛣️ Routing message to machine', 'routing');
46
-
47
- this.redisConnection.directPublish(address, message);
48
-
49
- return;
50
- }
51
-
52
- // * Not on the same process
53
- if (processId !== this.processId) {
54
- PicoLogger.log('🛣️ Routing message to process (todo; direct process)', 'routing');
55
- // ! Route through Redis for now, but change to direct process connection
56
- this.redisConnection.directPublish(address, message);
57
-
58
- return;
59
- }
60
-
61
- // * This must be to local process
62
- PicoLogger.log('🛣️ Routing message to local', 'routing');
63
- for (const localCallback of this.localCallbacks) {
64
- localCallback(message);
65
- }
66
- }
67
-
68
- /**
69
- * Subscribe to mesaages for local process.
70
- *
71
- * @param message
72
- *
73
- * @returns { void }
74
- */
75
- public subscribe(
76
- onMessage: TCallbackFunction,
77
- ): void {
78
- console.log('🗒️ Subscribing process', this.processAddress);
79
-
80
- this.localCallbacks.add(onMessage);
81
-
82
- // Listen to remote
83
- this.redisConnection.subscribe(this.processAddress, onMessage);
84
- }
85
- }
@@ -1,60 +0,0 @@
1
- import type { RedisClient } from 'bun';
2
- import type {
3
- TAddress,
4
- TClientId,
5
- TMachineAddress,
6
- TNetworkId_S,
7
- TProcessId,
8
- } from '@flux/shared/types';
9
- import { readMachineAddress, readProcessId } from '../../addressing.utils';
10
-
11
- export class NetworkAgentRedisSortedSet {
12
- private readonly processId: TProcessId = readProcessId();
13
- private readonly machineAddress: TMachineAddress = readMachineAddress();
14
-
15
- constructor(
16
- private readonly client: RedisClient,
17
- ) { }
18
-
19
- /**
20
- * Registers a network agent in the sorted set.
21
- *
22
- * @param { TNetworkId_S } networkId
23
- * @param { TClientId } socketId
24
- *
25
- * @returns { Promise<void> }
26
- */
27
- public async registerAgent(
28
- networkId: TNetworkId_S,
29
- socketId: TClientId,
30
- ): Promise<void> {
31
- const address: TAddress = `${this.machineAddress}/${this.processId}/${socketId}`;
32
-
33
- await this.client.send('ZADD', [
34
- `networks/${networkId}/agents`, // Key
35
- `${Date.now()}`, // Score
36
- address, // Member
37
- ]);
38
- }
39
-
40
- /**
41
- * Unregisters a network agent from the sorted set.
42
- *
43
- * @param { TNetworkId_S } networkId
44
- * @param { TClientId } socketId
45
- *
46
- * @returns { Promise<number> }
47
- */
48
- public async unregisterAgent(
49
- networkId: TNetworkId_S,
50
- socketId: TClientId
51
- ): Promise<number> {
52
- const address: TAddress = `${this.machineAddress}/${this.processId}/${socketId}`;
53
-
54
- return await this.client.send('ZREM', [
55
- `networks/${networkId}/agents`,
56
- address, // Member
57
- ]);
58
- }
59
-
60
- }