@interopio/bridge 0.1.0-beta.0 → 1.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/changelog.md +18 -0
- package/dist/main.js +267 -128
- package/dist/main.js.map +4 -4
- package/package.json +6 -5
package/dist/main.js
CHANGED
|
@@ -35,8 +35,8 @@ var InternalRelays = class {
|
|
|
35
35
|
for (const [node, k] of this.#links) {
|
|
36
36
|
if (k.key === key && (from === void 0 || k.node === from)) {
|
|
37
37
|
const links = k.linksByNode;
|
|
38
|
-
if (this.#logger.enabledFor("
|
|
39
|
-
this.#logger.
|
|
38
|
+
if (this.#logger.enabledFor("info")) {
|
|
39
|
+
this.#logger.info(`${key} unregisters node ${node}, linked to ${Array.from(links.keys()).join(", ")}`);
|
|
40
40
|
}
|
|
41
41
|
this.#links.delete(node);
|
|
42
42
|
this.#eventEmitter.emit("disconnect", key, node, links);
|
|
@@ -44,8 +44,8 @@ var InternalRelays = class {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
#connect(key, node) {
|
|
47
|
-
if (this.#logger.enabledFor("
|
|
48
|
-
this.#logger.
|
|
47
|
+
if (this.#logger.enabledFor("info")) {
|
|
48
|
+
this.#logger.info(`${key} registers node ${node}`);
|
|
49
49
|
}
|
|
50
50
|
this.#links.set(node, { key, node, linksByNode: /* @__PURE__ */ new Map() });
|
|
51
51
|
this.#eventEmitter.emit("connect", key, node);
|
|
@@ -72,6 +72,25 @@ var InternalRelays = class {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
return { decoded };
|
|
75
|
+
} else {
|
|
76
|
+
const node = decoded.to;
|
|
77
|
+
const link = decoded.from;
|
|
78
|
+
const type2 = decoded.type;
|
|
79
|
+
const senderLinkData = this.#links.get(link);
|
|
80
|
+
switch (type2) {
|
|
81
|
+
case "hello":
|
|
82
|
+
if (this.#logger.enabledFor("debug")) {
|
|
83
|
+
this.#logger.debug(`${key} connecting ${link} to ${node}`);
|
|
84
|
+
}
|
|
85
|
+
senderLinkData?.linksByNode.set(decoded.to, key);
|
|
86
|
+
break;
|
|
87
|
+
case "bye":
|
|
88
|
+
if (this.#logger.enabledFor("debug")) {
|
|
89
|
+
this.#logger.debug(`${key} disconnecting ${link} from ${node}`);
|
|
90
|
+
}
|
|
91
|
+
senderLinkData?.linksByNode.delete(decoded.to);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
75
94
|
}
|
|
76
95
|
const encoded = isEncoded(msg) ? msg : codec.encode(msg);
|
|
77
96
|
return { node: from, decoded, encoded };
|
|
@@ -86,23 +105,28 @@ var InternalRelays = class {
|
|
|
86
105
|
return this;
|
|
87
106
|
}
|
|
88
107
|
send(key, node, msg, cb) {
|
|
89
|
-
if (this.#logger.enabledFor("
|
|
108
|
+
if (this.#logger.enabledFor("trace")) {
|
|
90
109
|
this.#logger.debug(`${key} sending msg to ${node}`);
|
|
91
110
|
}
|
|
92
111
|
const encoded = isEncoded(msg) ? msg : codec.encode(msg);
|
|
93
112
|
const decoded = isEncoded(msg) ? codec.decode(msg) : msg;
|
|
94
113
|
{
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
node ??= decoded.to;
|
|
115
|
+
const link = decoded.from;
|
|
116
|
+
const type = decoded.type;
|
|
117
|
+
const senderLinkData = this.#links.get(link);
|
|
118
|
+
switch (type) {
|
|
97
119
|
case "hello":
|
|
98
|
-
if (
|
|
99
|
-
|
|
120
|
+
if (this.#logger.enabledFor("debug")) {
|
|
121
|
+
this.#logger.debug(`${key} connecting ${link} to ${node}`);
|
|
100
122
|
}
|
|
123
|
+
senderLinkData?.linksByNode.set(node, key);
|
|
101
124
|
break;
|
|
102
125
|
case "bye":
|
|
103
|
-
if (
|
|
104
|
-
|
|
126
|
+
if (this.#logger.enabledFor("debug")) {
|
|
127
|
+
this.#logger.debug(`${key} disconnecting ${link} from ${node}`);
|
|
105
128
|
}
|
|
129
|
+
senderLinkData?.linksByNode.delete(node);
|
|
106
130
|
break;
|
|
107
131
|
}
|
|
108
132
|
}
|
|
@@ -116,6 +140,9 @@ var InternalRelays = class {
|
|
|
116
140
|
}
|
|
117
141
|
}
|
|
118
142
|
}
|
|
143
|
+
if (decoded.type === "bye") {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
119
146
|
throw new Error(`${key} no active link for ${node}`);
|
|
120
147
|
}
|
|
121
148
|
};
|
|
@@ -1334,7 +1361,7 @@ function parseVersion(version) {
|
|
|
1334
1361
|
// package.json
|
|
1335
1362
|
var package_default = {
|
|
1336
1363
|
name: "@interopio/bridge",
|
|
1337
|
-
version: "
|
|
1364
|
+
version: "1.0.1",
|
|
1338
1365
|
license: "see license in license.md",
|
|
1339
1366
|
author: "interop.io",
|
|
1340
1367
|
homepage: "https://docs.interop.io/bridge",
|
|
@@ -1347,7 +1374,7 @@ var package_default = {
|
|
|
1347
1374
|
],
|
|
1348
1375
|
repository: {
|
|
1349
1376
|
type: "git",
|
|
1350
|
-
url: "https://github.com/InteropIO/bridge.git",
|
|
1377
|
+
url: "git+https://github.com/InteropIO/bridge.git",
|
|
1351
1378
|
directory: "packages/bridge"
|
|
1352
1379
|
},
|
|
1353
1380
|
type: "module",
|
|
@@ -1364,7 +1391,7 @@ var package_default = {
|
|
|
1364
1391
|
}
|
|
1365
1392
|
},
|
|
1366
1393
|
bin: {
|
|
1367
|
-
bridge: "
|
|
1394
|
+
bridge: "bin/bridge.js"
|
|
1368
1395
|
},
|
|
1369
1396
|
scripts: {
|
|
1370
1397
|
test: "mocha test --recursive",
|
|
@@ -1374,13 +1401,14 @@ var package_default = {
|
|
|
1374
1401
|
build: "npm run build:main && npm run build:index"
|
|
1375
1402
|
},
|
|
1376
1403
|
dependencies: {
|
|
1377
|
-
"@interopio/gateway-server": "^0.
|
|
1404
|
+
"@interopio/gateway-server": "^0.17.0",
|
|
1378
1405
|
dotenv: "^17.2.3",
|
|
1379
1406
|
jsrsasign: "^11.1.0",
|
|
1407
|
+
"hrw-hash": "^2.0.3",
|
|
1380
1408
|
nanoid: "^5.1.6"
|
|
1381
1409
|
},
|
|
1382
1410
|
devDependencies: {
|
|
1383
|
-
"@interopio/gateway": "^0.
|
|
1411
|
+
"@interopio/gateway": "^0.20.0",
|
|
1384
1412
|
"@types/jsrsasign": "^10.5.15",
|
|
1385
1413
|
"@types/ws": "^8.18.1",
|
|
1386
1414
|
"rand-seed": "^3.0.0"
|
|
@@ -1759,9 +1787,13 @@ function routes(config, { handle }) {
|
|
|
1759
1787
|
);
|
|
1760
1788
|
await response.end();
|
|
1761
1789
|
} else {
|
|
1762
|
-
const nodes = json.map((node) =>
|
|
1790
|
+
const nodes = json.map((node) => {
|
|
1791
|
+
return fromRequestNode(node, request);
|
|
1792
|
+
});
|
|
1763
1793
|
const result = connections.announce(nodes).map((connection) => {
|
|
1764
|
-
const connect = connection.connect?.map((node) =>
|
|
1794
|
+
const connect = connection.connect?.map((node) => {
|
|
1795
|
+
return toRequestNode(node, request);
|
|
1796
|
+
});
|
|
1765
1797
|
return { ...connection, connect };
|
|
1766
1798
|
});
|
|
1767
1799
|
const buffer = Buffer.from(JSON.stringify(result), "utf8");
|
|
@@ -1827,9 +1859,10 @@ async function create(log, internal, env) {
|
|
|
1827
1859
|
log.info(`relays-${id} server is listening on ${env.endpoint}`);
|
|
1828
1860
|
let keyId = 0;
|
|
1829
1861
|
return async ({ socket, handshake }) => {
|
|
1830
|
-
const logPrefix = handshake.logPrefix;
|
|
1862
|
+
const logPrefix = handshake.logPrefix ?? "";
|
|
1863
|
+
const query = handshake.url.searchParams;
|
|
1831
1864
|
const key = `r.${id}.${++keyId}`;
|
|
1832
|
-
log.info(`${logPrefix}connected on /relays with assigned key ${key}`);
|
|
1865
|
+
log.info(`${logPrefix}connected on /relays with ${query} and assigned key ${key}`);
|
|
1833
1866
|
internal.add(key, (msg, c, cb) => {
|
|
1834
1867
|
socket.send(msg, { binary: false }, (err) => {
|
|
1835
1868
|
cb(key, err);
|
|
@@ -1869,7 +1902,7 @@ function onMessage(relays, log, key, node, socketsByNodeId, msg) {
|
|
|
1869
1902
|
log.warn(`${k} error writing msg ${msg}: ${err}`);
|
|
1870
1903
|
return;
|
|
1871
1904
|
}
|
|
1872
|
-
if (log.enabledFor("
|
|
1905
|
+
if (log.enabledFor("trace")) {
|
|
1873
1906
|
log.debug(`${k} sent msg ${msg}`);
|
|
1874
1907
|
}
|
|
1875
1908
|
});
|
|
@@ -1897,7 +1930,7 @@ async function create2(log, relays, env) {
|
|
|
1897
1930
|
log.warn(`${key} error writing from ${k} msg ${msg}: ${err}`);
|
|
1898
1931
|
return;
|
|
1899
1932
|
}
|
|
1900
|
-
if (log.enabledFor("
|
|
1933
|
+
if (log.enabledFor("trace")) {
|
|
1901
1934
|
log.debug(`${key} sent from ${k} msg ${msg}`);
|
|
1902
1935
|
}
|
|
1903
1936
|
});
|
|
@@ -1924,7 +1957,7 @@ async function create2(log, relays, env) {
|
|
|
1924
1957
|
const logPrefix = handshake.logPrefix ?? "";
|
|
1925
1958
|
const query = handshake.url.searchParams;
|
|
1926
1959
|
const key = `c.${id}.${++keyId}`;
|
|
1927
|
-
log.info(`${logPrefix}connected on /cluster with ${query}
|
|
1960
|
+
log.info(`${logPrefix}connected on /cluster with ${query} and assigned key ${key}`);
|
|
1928
1961
|
const node = query.get("node");
|
|
1929
1962
|
if (node) {
|
|
1930
1963
|
let sockets = socketsByNodeId.get(node);
|
|
@@ -1969,6 +2002,7 @@ import "@interopio/gateway-server";
|
|
|
1969
2002
|
|
|
1970
2003
|
// ../bridge-mesh/src/mesh/gateway/mesh.ts
|
|
1971
2004
|
import { nanoid as nanoid2 } from "nanoid";
|
|
2005
|
+
import gateway from "@interopio/gateway/package.json" with { type: "json" };
|
|
1972
2006
|
var instanceId = 0;
|
|
1973
2007
|
var BridgeMeshChannel = class {
|
|
1974
2008
|
#logger;
|
|
@@ -1997,6 +2031,7 @@ var BridgeMeshChannel = class {
|
|
|
1997
2031
|
throw new Error(`already subscribed to node ${node}`);
|
|
1998
2032
|
}
|
|
1999
2033
|
this.#state.set(node, { subscriber, users: /* @__PURE__ */ new Set(), members: /* @__PURE__ */ new Set() });
|
|
2034
|
+
this.#announce(node);
|
|
2000
2035
|
this.#relays.receive(key, { type: "hello", from: node, to: "all" });
|
|
2001
2036
|
this.#relays.add(key, this.createRelayClient(key, node));
|
|
2002
2037
|
this.#intervalId ??= setInterval(() => this.#announce(), 3e4);
|
|
@@ -2136,23 +2171,38 @@ var BridgeMeshChannel = class {
|
|
|
2136
2171
|
this.#state.delete(node);
|
|
2137
2172
|
this.#connections.remove(node);
|
|
2138
2173
|
}
|
|
2139
|
-
|
|
2174
|
+
async init(gossipTimestamp = 0) {
|
|
2175
|
+
await this.#connections.init(gossipTimestamp);
|
|
2176
|
+
}
|
|
2177
|
+
async close() {
|
|
2140
2178
|
for (const node of this.#state.keys()) {
|
|
2141
2179
|
this.#delete(node);
|
|
2142
2180
|
this.#relays.remove(`${this.#keyPrefix}-${node}`);
|
|
2143
2181
|
}
|
|
2144
2182
|
clearInterval(this.#intervalId);
|
|
2183
|
+
await this.#connections.close();
|
|
2145
2184
|
}
|
|
2146
2185
|
};
|
|
2147
2186
|
var entryToNode = ([node, state]) => {
|
|
2148
2187
|
const users = Array.from(state?.users);
|
|
2149
|
-
|
|
2150
|
-
return { node, endpoint, users };
|
|
2188
|
+
return { node, users, metadata: { version: gateway.version, type: "io.Bridge" } };
|
|
2151
2189
|
};
|
|
2152
2190
|
|
|
2153
2191
|
// ../bridge-mesh/src/index.ts
|
|
2154
2192
|
function connectNodeRelays(logger, relays, connections) {
|
|
2193
|
+
relays.on("connect", (key, node) => {
|
|
2194
|
+
try {
|
|
2195
|
+
connections.add(node);
|
|
2196
|
+
} catch (e) {
|
|
2197
|
+
logger.warn(`${key} error adding node ${node} to connections: ${e}`);
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
2155
2200
|
relays.on("disconnect", (key, node, links) => {
|
|
2201
|
+
try {
|
|
2202
|
+
connections.remove(node);
|
|
2203
|
+
} catch (e) {
|
|
2204
|
+
logger.warn(`${key} error removing node ${node} from connections: ${e}`);
|
|
2205
|
+
}
|
|
2156
2206
|
for (const [linkNode, linkKey] of links) {
|
|
2157
2207
|
try {
|
|
2158
2208
|
relays.send(linkKey, linkNode, { type: "bye", from: node, to: linkNode }, (k, err) => {
|
|
@@ -2161,11 +2211,11 @@ function connectNodeRelays(logger, relays, connections) {
|
|
|
2161
2211
|
return;
|
|
2162
2212
|
}
|
|
2163
2213
|
if (logger.enabledFor("debug")) {
|
|
2164
|
-
logger.debug(`${k} sent 'bye' msg to ${linkNode}`);
|
|
2214
|
+
logger.debug(`${k} sent 'bye' msg to ${linkNode} from ${node}`);
|
|
2165
2215
|
}
|
|
2166
2216
|
});
|
|
2167
2217
|
} catch (err) {
|
|
2168
|
-
logger.warn(`${linkKey} exception writing 'bye' msg to ${linkNode}: ${err}`);
|
|
2218
|
+
logger.warn(`${linkKey} exception writing 'bye' msg to ${linkNode} from ${node}: ${err}`);
|
|
2169
2219
|
}
|
|
2170
2220
|
}
|
|
2171
2221
|
});
|
|
@@ -2198,123 +2248,205 @@ var mesh = async (options, configurer, config) => {
|
|
|
2198
2248
|
};
|
|
2199
2249
|
|
|
2200
2250
|
// ../bridge-mesh/src/mesh/connections.ts
|
|
2201
|
-
import "
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
} else {
|
|
2222
|
-
this.#elements.splice(~idx, 0, [key, value]);
|
|
2223
|
-
}
|
|
2251
|
+
import { hrwHash } from "hrw-hash";
|
|
2252
|
+
|
|
2253
|
+
// ../bridge-mesh/src/mesh/lamport.ts
|
|
2254
|
+
import { appendFile, readFile, rename, writeFile } from "node:fs/promises";
|
|
2255
|
+
var LamportClock = class _LamportClock {
|
|
2256
|
+
static #FLUSH_INTERVAL = 5e3;
|
|
2257
|
+
// 5 seconds
|
|
2258
|
+
static #CLOCK_FILE = "./lamport.clock";
|
|
2259
|
+
static #WAL_FILE = "./lamport.wal";
|
|
2260
|
+
#timestamp;
|
|
2261
|
+
#instanceId;
|
|
2262
|
+
#flushIntervalId;
|
|
2263
|
+
#pendingWrites = 0;
|
|
2264
|
+
constructor(instanceId2) {
|
|
2265
|
+
this.#instanceId = instanceId2;
|
|
2266
|
+
this.#timestamp = 0;
|
|
2267
|
+
this.#flushIntervalId = setInterval(() => {
|
|
2268
|
+
this.#flush().catch((err) => {
|
|
2269
|
+
});
|
|
2270
|
+
}, _LamportClock.#FLUSH_INTERVAL);
|
|
2224
2271
|
}
|
|
2225
|
-
|
|
2226
|
-
const
|
|
2227
|
-
|
|
2272
|
+
async init(gossipTimestamp = 0) {
|
|
2273
|
+
const baseTimestamp = await this.#loadSnapshot();
|
|
2274
|
+
const journaledIncrements = await this.#replayWal();
|
|
2275
|
+
this.#timestamp = Math.max(baseTimestamp + journaledIncrements, gossipTimestamp);
|
|
2228
2276
|
}
|
|
2229
|
-
|
|
2230
|
-
|
|
2277
|
+
tick() {
|
|
2278
|
+
this.#timestamp += 1;
|
|
2279
|
+
this.#pendingWrites += 1;
|
|
2280
|
+
this.#appendToWal().catch((_err) => {
|
|
2281
|
+
});
|
|
2282
|
+
return { timestamp: this.#timestamp, instanceId: this.#instanceId };
|
|
2231
2283
|
}
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2284
|
+
observe(gossipTimestamp) {
|
|
2285
|
+
this.#timestamp = Math.max(this.#timestamp, gossipTimestamp) + 1;
|
|
2286
|
+
this.#pendingWrites += 1;
|
|
2287
|
+
this.#appendToWal().catch((_err) => {
|
|
2288
|
+
});
|
|
2289
|
+
return { timestamp: this.#timestamp, instanceId: this.#instanceId };
|
|
2290
|
+
}
|
|
2291
|
+
timestamp() {
|
|
2292
|
+
return { timestamp: this.#timestamp, instanceId: this.#instanceId };
|
|
2293
|
+
}
|
|
2294
|
+
async shutdown() {
|
|
2295
|
+
clearInterval(this.#flushIntervalId);
|
|
2296
|
+
await this.#flush();
|
|
2297
|
+
}
|
|
2298
|
+
async #appendToWal() {
|
|
2299
|
+
const data = `${this.#timestamp}
|
|
2300
|
+
`;
|
|
2301
|
+
await appendFile(_LamportClock.#WAL_FILE, data, "utf-8");
|
|
2302
|
+
}
|
|
2303
|
+
async #replayWal() {
|
|
2304
|
+
try {
|
|
2305
|
+
const data = await readFile(_LamportClock.#WAL_FILE, "utf-8");
|
|
2306
|
+
return data.trim().split("\n").filter(Boolean).length;
|
|
2307
|
+
} catch {
|
|
2237
2308
|
}
|
|
2238
|
-
return
|
|
2309
|
+
return 0;
|
|
2239
2310
|
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2311
|
+
async #loadSnapshot() {
|
|
2312
|
+
try {
|
|
2313
|
+
const data = await readFile(_LamportClock.#CLOCK_FILE, "utf-8");
|
|
2314
|
+
return parseInt(data, 10) || 0;
|
|
2315
|
+
} catch {
|
|
2316
|
+
}
|
|
2317
|
+
return 0;
|
|
2242
2318
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2319
|
+
async #flush() {
|
|
2320
|
+
if (this.#pendingWrites === 0) {
|
|
2321
|
+
return;
|
|
2322
|
+
}
|
|
2323
|
+
const tmp = `${_LamportClock.#CLOCK_FILE}.tmp`;
|
|
2324
|
+
const data = `${this.#timestamp}
|
|
2325
|
+
`;
|
|
2326
|
+
await writeFile(tmp, data, { flag: "w" });
|
|
2327
|
+
await rename(tmp, _LamportClock.#CLOCK_FILE);
|
|
2328
|
+
await writeFile(_LamportClock.#WAL_FILE, "", { flag: "w" });
|
|
2329
|
+
this.#pendingWrites = 0;
|
|
2245
2330
|
}
|
|
2246
2331
|
};
|
|
2332
|
+
|
|
2333
|
+
// ../bridge-mesh/src/mesh/connections.ts
|
|
2334
|
+
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
2247
2335
|
var InMemoryNodeConnections = class {
|
|
2248
2336
|
#logger;
|
|
2249
2337
|
#eventEmitter = new EventEmitter2();
|
|
2250
|
-
#nodes = new
|
|
2251
|
-
#
|
|
2338
|
+
#nodes = /* @__PURE__ */ new Map();
|
|
2339
|
+
#clusterNodes;
|
|
2340
|
+
#lamport;
|
|
2252
2341
|
#timeout;
|
|
2253
|
-
constructor(logger, timeout = 6e4) {
|
|
2342
|
+
constructor(logger, instanceId2, timeout = 6e4) {
|
|
2254
2343
|
this.#logger = logger;
|
|
2344
|
+
this.#clusterNodes = [instanceId2];
|
|
2345
|
+
this.#lamport = new LamportClock(instanceId2);
|
|
2255
2346
|
this.#timeout = timeout;
|
|
2256
2347
|
}
|
|
2257
|
-
|
|
2348
|
+
async init(gossipTimestamp = 0) {
|
|
2349
|
+
await this.#lamport.init(gossipTimestamp);
|
|
2350
|
+
}
|
|
2351
|
+
async close() {
|
|
2352
|
+
await this.#lamport.shutdown();
|
|
2353
|
+
}
|
|
2354
|
+
announce(announcements) {
|
|
2258
2355
|
const now = Date.now();
|
|
2259
|
-
for (const
|
|
2260
|
-
const { node: nodeId, users, endpoint } =
|
|
2261
|
-
const
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
this.#
|
|
2271
|
-
this.#
|
|
2356
|
+
for (const announcement of announcements) {
|
|
2357
|
+
const { node: nodeId, users, endpoint: announcedEndpoint, metadata } = announcement;
|
|
2358
|
+
const replicas = hrwHash(nodeId, this.#clusterNodes);
|
|
2359
|
+
const owner = replicas[0];
|
|
2360
|
+
const externalEndpoint = announcedEndpoint !== void 0 && announcedEndpoint !== `/cluster?node=${nodeId}`;
|
|
2361
|
+
const endpoint = externalEndpoint ? announcedEndpoint : `/cluster?node=${nodeId}&owner=${owner}`;
|
|
2362
|
+
const lamport = this.#lamport.tick();
|
|
2363
|
+
const userSet = new Set(users ?? []);
|
|
2364
|
+
let node = this.#nodes.get(nodeId);
|
|
2365
|
+
if (!node) {
|
|
2366
|
+
node = { node: nodeId, users: userSet, firstSeen: lamport, lastSeen: now, owner, replicas, endpoint, metadata, status: "announced" };
|
|
2367
|
+
this.#nodes.set(nodeId, node);
|
|
2368
|
+
this.#logger.info(`${nodeId} announced at endpoint ${endpoint} with meta: ${JSON.stringify(metadata)}`);
|
|
2369
|
+
}
|
|
2370
|
+
node.owner = owner;
|
|
2371
|
+
if (!externalEndpoint) {
|
|
2372
|
+
node.endpoint = endpoint;
|
|
2272
2373
|
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2374
|
+
node.replicas = replicas;
|
|
2375
|
+
node.lastSeen = now;
|
|
2376
|
+
if (!(node.status === "announced" || node.status === "added")) {
|
|
2377
|
+
node.status = "announced";
|
|
2378
|
+
}
|
|
2379
|
+
node.users = userSet;
|
|
2380
|
+
this.#eventEmitter.emit("node-announced", nodeId, endpoint, userSet);
|
|
2381
|
+
}
|
|
2382
|
+
const sortedNodes = this.#sortedNodeValues();
|
|
2383
|
+
return announcements.map((e) => {
|
|
2384
|
+
const node = this.#nodes.get(e.node);
|
|
2385
|
+
const connect = this.#findConnections(sortedNodes, node);
|
|
2386
|
+
return { node: node.node, owner: node.owner, connect };
|
|
2282
2387
|
});
|
|
2283
2388
|
}
|
|
2284
2389
|
find(nodeId) {
|
|
2285
2390
|
const e = this.#nodes.get(nodeId);
|
|
2286
2391
|
if (e !== void 0) {
|
|
2287
|
-
|
|
2392
|
+
const sortedNodes = this.#sortedNodeValues();
|
|
2393
|
+
return this.#findConnections(sortedNodes, e);
|
|
2288
2394
|
}
|
|
2289
2395
|
return void 0;
|
|
2290
2396
|
}
|
|
2397
|
+
add(nodeId) {
|
|
2398
|
+
const added = this.#nodes.get(nodeId);
|
|
2399
|
+
if (added !== void 0 && (added.status === "announced" || added.status === "removed")) {
|
|
2400
|
+
added.status = "added";
|
|
2401
|
+
this.#logger.info(`endpoint ${added.endpoint} connected for ${nodeId}`);
|
|
2402
|
+
this.#eventEmitter.emit("node-connected", nodeId);
|
|
2403
|
+
return true;
|
|
2404
|
+
}
|
|
2405
|
+
return false;
|
|
2406
|
+
}
|
|
2291
2407
|
remove(nodeId) {
|
|
2292
2408
|
const removed = this.#nodes.get(nodeId);
|
|
2293
|
-
if (removed !== void 0) {
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
this.#
|
|
2297
|
-
this.#logger.info(`endpoint ${endpoint} removed for ${nodeId}`);
|
|
2298
|
-
this.#eventEmitter.emit("endpoint-deleted", nodeId, endpoint, "removed");
|
|
2409
|
+
if (removed !== void 0 && (removed.status === "announced" || removed.status === "added")) {
|
|
2410
|
+
removed.status = "removed";
|
|
2411
|
+
this.#logger.info(`endpoint ${removed.endpoint} removed for ${nodeId}`);
|
|
2412
|
+
this.#eventEmitter.emit("node-disconnected", nodeId, "removed");
|
|
2299
2413
|
return true;
|
|
2300
2414
|
}
|
|
2301
2415
|
return false;
|
|
2302
2416
|
}
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
return
|
|
2417
|
+
#sortedNodeValues() {
|
|
2418
|
+
this.#cleanupOldNodes();
|
|
2419
|
+
return Array.from(this.#nodes.values()).filter((n) => n.status === "announced" || n.status === "added").sort((a, b) => {
|
|
2420
|
+
const diff = a.firstSeen.timestamp - b.firstSeen.timestamp;
|
|
2421
|
+
if (diff === 0) {
|
|
2422
|
+
return a.node.localeCompare(b.node);
|
|
2423
|
+
}
|
|
2424
|
+
return diff;
|
|
2425
|
+
});
|
|
2306
2426
|
}
|
|
2307
|
-
cleanupOldNodes() {
|
|
2308
|
-
const
|
|
2309
|
-
|
|
2310
|
-
|
|
2427
|
+
#cleanupOldNodes() {
|
|
2428
|
+
const expiredTimestamp = Date.now() - this.#timeout;
|
|
2429
|
+
const removedTimestamp = Date.now() - this.#timeout * 2;
|
|
2430
|
+
const compactTimestamp = Date.now() - this.#timeout * 10;
|
|
2431
|
+
for (const [nodeId, v] of this.#nodes) {
|
|
2432
|
+
if (v.lastSeen < compactTimestamp && v.status === "removed") {
|
|
2311
2433
|
if (this.#logger.enabledFor("debug")) {
|
|
2312
|
-
this.#logger.debug(`${nodeId}
|
|
2434
|
+
this.#logger.debug(`${nodeId} compact - no announcement since ${new Date(v.lastSeen).toISOString()}, timeout is ${this.#timeout} ms and status is ${v.status}.`);
|
|
2313
2435
|
}
|
|
2314
2436
|
this.#nodes.delete(nodeId);
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
this.#
|
|
2437
|
+
}
|
|
2438
|
+
if (v.lastSeen < removedTimestamp && v.status === "expired") {
|
|
2439
|
+
if (this.#logger.enabledFor("debug")) {
|
|
2440
|
+
this.#logger.debug(`${nodeId} removed - no announcement since ${new Date(v.lastSeen).toISOString()}, timeout is ${this.#timeout} ms.`);
|
|
2441
|
+
}
|
|
2442
|
+
v.status = "removed";
|
|
2443
|
+
}
|
|
2444
|
+
if (v.lastSeen < expiredTimestamp && (v.status === "announced" || v.status === "added")) {
|
|
2445
|
+
if (this.#logger.enabledFor("debug")) {
|
|
2446
|
+
this.#logger.debug(`${nodeId} expired - no announcement since ${new Date(v.lastSeen).toISOString()}, timeout is ${this.#timeout} ms.`);
|
|
2447
|
+
}
|
|
2448
|
+
v.status = "expired";
|
|
2449
|
+
this.#eventEmitter.emit("node-disconnected", nodeId, "expired");
|
|
2318
2450
|
}
|
|
2319
2451
|
}
|
|
2320
2452
|
}
|
|
@@ -2326,26 +2458,29 @@ var InMemoryNodeConnections = class {
|
|
|
2326
2458
|
this.#eventEmitter.off(event, listener);
|
|
2327
2459
|
return this;
|
|
2328
2460
|
}
|
|
2329
|
-
findConnections(node) {
|
|
2330
|
-
const connections =
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
const intersection = new Set(value.users);
|
|
2461
|
+
#findConnections(sortedNodes, node) {
|
|
2462
|
+
const connections = sortedNodes.reduce((l, c) => {
|
|
2463
|
+
if (node !== void 0 && c.firstSeen.timestamp < node.firstSeen.timestamp) {
|
|
2464
|
+
const intersection = new Set(c.users);
|
|
2334
2465
|
node.users.forEach((user) => {
|
|
2335
|
-
if (!
|
|
2466
|
+
if (!c.users.has(user)) {
|
|
2336
2467
|
intersection.delete(user);
|
|
2337
2468
|
}
|
|
2338
2469
|
});
|
|
2339
|
-
|
|
2470
|
+
c.users.forEach((user) => {
|
|
2340
2471
|
if (!node.users.has(user)) {
|
|
2341
2472
|
intersection.delete(user);
|
|
2342
2473
|
}
|
|
2343
2474
|
});
|
|
2344
2475
|
if (intersection.size > 0) {
|
|
2345
|
-
const e = { node:
|
|
2346
|
-
|
|
2476
|
+
const e = { node: c.node, endpoint: c.endpoint, owner: c.owner };
|
|
2477
|
+
return l.concat(e);
|
|
2347
2478
|
}
|
|
2348
2479
|
}
|
|
2480
|
+
return l;
|
|
2481
|
+
}, new Array());
|
|
2482
|
+
if (this.#logger.enabledFor("debug")) {
|
|
2483
|
+
this.#logger.debug(`found ${connections.length} connection(s) for node ${node?.node}: [${connections.map((e) => `${e.node}@${e.endpoint}`).join(", ")}]`);
|
|
2349
2484
|
}
|
|
2350
2485
|
return connections;
|
|
2351
2486
|
}
|
|
@@ -2359,7 +2494,7 @@ function parseStartingContext(env = process.env) {
|
|
|
2359
2494
|
if (!isRunningInConnectDesktop(env)) {
|
|
2360
2495
|
throw new Error("Not running in io.Connect Desktop");
|
|
2361
2496
|
}
|
|
2362
|
-
return JSON.parse(env._GD_STARTING_CONTEXT_)
|
|
2497
|
+
return JSON.parse(env._GD_STARTING_CONTEXT_);
|
|
2363
2498
|
}
|
|
2364
2499
|
|
|
2365
2500
|
// ../bridge-gateway/src/index.ts
|
|
@@ -2369,8 +2504,8 @@ import { join } from "node:path";
|
|
|
2369
2504
|
import "@interopio/gateway";
|
|
2370
2505
|
import "@interopio/gateway-server";
|
|
2371
2506
|
function onGatewayStarted(log) {
|
|
2372
|
-
return (
|
|
2373
|
-
const info = JSON.stringify(
|
|
2507
|
+
return (gateway2) => {
|
|
2508
|
+
const info = JSON.stringify({ ...gateway2.info(), pid: process.pid });
|
|
2374
2509
|
if (isRunningInConnectDesktop()) {
|
|
2375
2510
|
const env = process.env["GLUE-ENV"] || "DEMO";
|
|
2376
2511
|
const region = process.env["GLUE-REGION"] || "INTEROP.IO";
|
|
@@ -2391,14 +2526,15 @@ function onGatewayStarted(log) {
|
|
|
2391
2526
|
}
|
|
2392
2527
|
};
|
|
2393
2528
|
}
|
|
2394
|
-
async function serverGatewayConfig(
|
|
2395
|
-
let enabled =
|
|
2529
|
+
async function serverGatewayConfig(gateway2, bridge, env = process.env) {
|
|
2530
|
+
let enabled = gateway2.enabled;
|
|
2396
2531
|
if (enabled === void 0) {
|
|
2397
2532
|
if (isRunningInConnectDesktop(env)) {
|
|
2398
|
-
|
|
2533
|
+
const applicationConfig = parseStartingContext(env)?.applicationConfig;
|
|
2534
|
+
enabled = applicationConfig?.customProperties?.gatewayApp === true || applicationConfig?.name === "io-connect-gateway";
|
|
2399
2535
|
}
|
|
2400
2536
|
}
|
|
2401
|
-
let contextsLifetime =
|
|
2537
|
+
let contextsLifetime = gateway2.contexts.lifetime;
|
|
2402
2538
|
if (contextsLifetime === void 0) {
|
|
2403
2539
|
if (isRunningInConnectDesktop(env)) {
|
|
2404
2540
|
contextsLifetime = "retained";
|
|
@@ -2412,6 +2548,8 @@ async function serverGatewayConfig(gateway, bridge, env = process.env) {
|
|
|
2412
2548
|
},
|
|
2413
2549
|
ping: bridge.wsPingInterval ?? void 0,
|
|
2414
2550
|
mesh: {
|
|
2551
|
+
auth: { user: null },
|
|
2552
|
+
node: "bridge-gateway",
|
|
2415
2553
|
channel: bridge.meshChannel
|
|
2416
2554
|
},
|
|
2417
2555
|
contexts: {
|
|
@@ -2493,7 +2631,7 @@ var BridgeNode = class {
|
|
|
2493
2631
|
const discoveryConfig = joinConfig.discovery;
|
|
2494
2632
|
this.discoveryService = createDiscoveryService(getLogger("discovery"), discoveryConfig, isAutoDetectionEnabled, localMemberPromise);
|
|
2495
2633
|
const relays = new InternalRelays(this.getLogger("relays"));
|
|
2496
|
-
const connections = new InMemoryNodeConnections(this.getLogger("nodes"), this.config.mesh.timeout ?? 6e4);
|
|
2634
|
+
const connections = new InMemoryNodeConnections(this.getLogger("nodes"), this.uuid, this.config.mesh.timeout ?? 6e4);
|
|
2497
2635
|
this.meshChannel = new BridgeMeshChannel(relays, connections, this.getLogger("channel"));
|
|
2498
2636
|
}
|
|
2499
2637
|
getLogger(name) {
|
|
@@ -2513,6 +2651,7 @@ var BridgeNode = class {
|
|
|
2513
2651
|
return setInterval(task, licenseCheckInterval);
|
|
2514
2652
|
}
|
|
2515
2653
|
async start() {
|
|
2654
|
+
await this.meshChannel.init();
|
|
2516
2655
|
const config = {
|
|
2517
2656
|
port: this.config.server.port,
|
|
2518
2657
|
host: this.config.server.host,
|
|
@@ -2550,7 +2689,7 @@ var BridgeNode = class {
|
|
|
2550
2689
|
await this.discoveryService.close();
|
|
2551
2690
|
} catch (e) {
|
|
2552
2691
|
}
|
|
2553
|
-
this.meshChannel.close();
|
|
2692
|
+
await this.meshChannel.close();
|
|
2554
2693
|
}
|
|
2555
2694
|
};
|
|
2556
2695
|
|