freertc 0.1.1 → 0.1.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.
- package/bin/freertc.mjs +2 -2
- package/package.json +1 -1
- package/scripts/project-bootstrap.mjs +19 -5
- package/scripts/wrangler-install-wizard.mjs +3 -3
- package/src/index.js +80 -45
package/bin/freertc.mjs
CHANGED
|
@@ -34,7 +34,7 @@ Examples:
|
|
|
34
34
|
|
|
35
35
|
function runInProject(command, args, { bootstrap = false } = {}) {
|
|
36
36
|
if (bootstrap) {
|
|
37
|
-
ensureProjectFiles(PROJECT_ROOT);
|
|
37
|
+
ensureProjectFiles(PROJECT_ROOT, { updateExisting: true });
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const result = spawnSync(command, args, {
|
|
@@ -87,7 +87,7 @@ if (subcommand === 'init' || subcommand === 'install') {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
if (subcommand === 'deploy') {
|
|
90
|
-
ensureProjectFiles(PROJECT_ROOT);
|
|
90
|
+
ensureProjectFiles(PROJECT_ROOT, { updateExisting: true });
|
|
91
91
|
requireWranglerConfig();
|
|
92
92
|
const wrangler = resolveWranglerCommand(PROJECT_ROOT);
|
|
93
93
|
runInProject(wrangler.command, [...wrangler.baseArgs, 'deploy', '--env', 'production', ...rest]);
|
package/package.json
CHANGED
|
@@ -59,28 +59,42 @@ export function resolveProjectRoot(startDir = process.cwd()) {
|
|
|
59
59
|
);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
function filesMatch(sourcePath, targetPath) {
|
|
63
|
+
if (!fs.existsSync(sourcePath) || !fs.existsSync(targetPath)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const source = fs.readFileSync(sourcePath);
|
|
68
|
+
const target = fs.readFileSync(targetPath);
|
|
69
|
+
return source.equals(target);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function ensureProjectFiles(projectRoot, { updateExisting = false } = {}) {
|
|
63
73
|
const targetRoot = path.resolve(projectRoot);
|
|
64
74
|
if (targetRoot === PACKAGE_ROOT) {
|
|
65
75
|
return [];
|
|
66
76
|
}
|
|
67
77
|
|
|
68
|
-
const
|
|
78
|
+
const synced = [];
|
|
69
79
|
|
|
70
80
|
for (const [sourceRelativePath, targetRelativePath] of PROJECT_FILE_MAPPINGS) {
|
|
71
81
|
const sourcePath = path.join(PACKAGE_ROOT, sourceRelativePath);
|
|
72
82
|
const targetPath = path.join(targetRoot, targetRelativePath);
|
|
73
83
|
|
|
74
|
-
if (
|
|
84
|
+
if (!fs.existsSync(sourcePath)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (fs.existsSync(targetPath) && (!updateExisting || filesMatch(sourcePath, targetPath))) {
|
|
75
89
|
continue;
|
|
76
90
|
}
|
|
77
91
|
|
|
78
92
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
79
93
|
fs.copyFileSync(sourcePath, targetPath);
|
|
80
|
-
|
|
94
|
+
synced.push(targetRelativePath);
|
|
81
95
|
}
|
|
82
96
|
|
|
83
|
-
return
|
|
97
|
+
return synced;
|
|
84
98
|
}
|
|
85
99
|
|
|
86
100
|
export function resolveWranglerCommand(cwd = process.cwd()) {
|
|
@@ -402,7 +402,7 @@ async function main() {
|
|
|
402
402
|
const forcedMode = modeFromArgs(process.argv);
|
|
403
403
|
|
|
404
404
|
try {
|
|
405
|
-
const copiedFiles = ensureProjectFiles(ROOT);
|
|
405
|
+
const copiedFiles = ensureProjectFiles(ROOT, { updateExisting: true });
|
|
406
406
|
|
|
407
407
|
console.log(`\n${PROJECT_NAME} Wrangler Install Wizard\n`);
|
|
408
408
|
console.log(`Using project root: ${ROOT}`);
|
|
@@ -416,7 +416,7 @@ async function main() {
|
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
if (copiedFiles.length > 0) {
|
|
419
|
-
console.log('
|
|
419
|
+
console.log('Synced package-managed files into this project:');
|
|
420
420
|
for (const file of copiedFiles) {
|
|
421
421
|
console.log(` - ${file}`);
|
|
422
422
|
}
|
|
@@ -447,7 +447,7 @@ async function main() {
|
|
|
447
447
|
if (copiedFiles.length === 0) {
|
|
448
448
|
console.log('Required worker files already exist in this project.');
|
|
449
449
|
} else {
|
|
450
|
-
console.log('Project
|
|
450
|
+
console.log('Project files refreshed from the published freertc package.');
|
|
451
451
|
}
|
|
452
452
|
|
|
453
453
|
resolveWrangler();
|
package/src/index.js
CHANGED
|
@@ -227,15 +227,29 @@ async function forwardToRelay(relayUrl, message, selfRelayId) {
|
|
|
227
227
|
const ws = resp.webSocket;
|
|
228
228
|
ws.accept();
|
|
229
229
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
230
|
+
await new Promise((resolve) => {
|
|
231
|
+
const relayPeerId = selfRelayId || "relay-bridge";
|
|
232
|
+
const closeTimer = setTimeout(() => {
|
|
233
|
+
try { ws.close(); } catch {}
|
|
234
|
+
resolve();
|
|
235
|
+
}, 250);
|
|
236
|
+
|
|
237
|
+
const finish = () => {
|
|
238
|
+
clearTimeout(closeTimer);
|
|
239
|
+
resolve();
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
ws.addEventListener("error", finish, { once: true });
|
|
243
|
+
ws.addEventListener("close", finish, { once: true });
|
|
244
|
+
|
|
245
|
+
// Outbound Worker WebSocket: send immediately after accept(), no open event needed.
|
|
246
|
+
ws.send(JSON.stringify({
|
|
247
|
+
psp_version: PSP_VERSION, type: "announce", network: message.network,
|
|
248
|
+
from: relayPeerId, message_id: crypto.randomUUID(),
|
|
249
|
+
timestamp: Date.now(), ttl_ms: 10_000, body: { capabilities: { relay: true } }
|
|
250
|
+
}));
|
|
251
|
+
ws.send(JSON.stringify(message));
|
|
252
|
+
});
|
|
239
253
|
} catch {}
|
|
240
254
|
}
|
|
241
255
|
|
|
@@ -260,6 +274,41 @@ async function listRelays(db) {
|
|
|
260
274
|
return (result.results || []).map(r => ({ url: r.url, name: r.name }));
|
|
261
275
|
}
|
|
262
276
|
|
|
277
|
+
function mergeDiscoveredPeers(...peerGroups) {
|
|
278
|
+
const merged = new Map();
|
|
279
|
+
|
|
280
|
+
for (const peers of peerGroups) {
|
|
281
|
+
if (!Array.isArray(peers)) continue;
|
|
282
|
+
for (const peer of peers) {
|
|
283
|
+
const peerId = peer?.peer_id;
|
|
284
|
+
if (typeof peerId !== "string" || !peerId) continue;
|
|
285
|
+
|
|
286
|
+
const existing = merged.get(peerId);
|
|
287
|
+
const nextTimestamp = Number(peer?.timestamp || 0);
|
|
288
|
+
const existingTimestamp = Number(existing?.timestamp || 0);
|
|
289
|
+
if (!existing || nextTimestamp >= existingTimestamp) {
|
|
290
|
+
merged.set(peerId, peer);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return Array.from(merged.values()).sort((left, right) => left.peer_id.localeCompare(right.peer_id));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function sendPeerList(socket, network, peers, to = null, from = "bootstrap-relay") {
|
|
299
|
+
socket.send(JSON.stringify({
|
|
300
|
+
psp_version: PSP_VERSION,
|
|
301
|
+
type: "peer_list",
|
|
302
|
+
network,
|
|
303
|
+
from,
|
|
304
|
+
to,
|
|
305
|
+
message_id: crypto.randomUUID(),
|
|
306
|
+
timestamp: Date.now(),
|
|
307
|
+
ttl_ms: DEFAULT_TTL_MS,
|
|
308
|
+
body: { peers }
|
|
309
|
+
}));
|
|
310
|
+
}
|
|
311
|
+
|
|
263
312
|
// Broadcast peer list to all connected peers in a network
|
|
264
313
|
async function broadcastPeerList(db, network) {
|
|
265
314
|
const sockets = networkSubscribers.get(network);
|
|
@@ -279,23 +328,9 @@ async function broadcastPeerList(db, network) {
|
|
|
279
328
|
session_id: row.session_id,
|
|
280
329
|
timestamp: row.updated_at_ms
|
|
281
330
|
}));
|
|
282
|
-
|
|
283
|
-
const message = {
|
|
284
|
-
psp_version: PSP_VERSION,
|
|
285
|
-
type: "peer_list",
|
|
286
|
-
network,
|
|
287
|
-
from: "bootstrap-relay",
|
|
288
|
-
to: null,
|
|
289
|
-
message_id: crypto.randomUUID(),
|
|
290
|
-
timestamp: Date.now(),
|
|
291
|
-
ttl_ms: DEFAULT_TTL_MS,
|
|
292
|
-
body: { peers }
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
const payload = JSON.stringify(message);
|
|
296
331
|
for (const socket of sockets) {
|
|
297
332
|
try {
|
|
298
|
-
socket
|
|
333
|
+
sendPeerList(socket, network, peers);
|
|
299
334
|
} catch (e) {
|
|
300
335
|
sockets.delete(socket);
|
|
301
336
|
}
|
|
@@ -566,36 +601,36 @@ async function handleClientMessage(socket, rawData, env, ctx, prevPeerKey = null
|
|
|
566
601
|
}
|
|
567
602
|
|
|
568
603
|
} else if (type === "discover") {
|
|
569
|
-
|
|
604
|
+
let localPeers = [];
|
|
570
605
|
if (db) {
|
|
571
|
-
|
|
606
|
+
localPeers = await findPeers(db, network, peerId);
|
|
572
607
|
}
|
|
573
|
-
|
|
608
|
+
|
|
609
|
+
let remotePeers = [];
|
|
574
610
|
if (env.RELAY_URL && env.DB) {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
const remoteUrls = allRelays.map(r => r.url).filter(u => u !== selfUrl);
|
|
580
|
-
if (!remoteUrls.length) return;
|
|
611
|
+
const selfRelayId = env.RELAY_PEER_ID || "relay-bridge";
|
|
612
|
+
const selfUrl = normalizeRelayUrl(env.RELAY_URL);
|
|
613
|
+
const allRelays = await listRelays(env.DB);
|
|
614
|
+
const remoteUrls = allRelays.map(r => r.url).filter(u => u !== selfUrl);
|
|
581
615
|
|
|
616
|
+
if (remoteUrls.length) {
|
|
582
617
|
const results = await Promise.all(
|
|
583
618
|
remoteUrls.map(u => queryRelayForPeers(u, network, selfRelayId, env.DB, allRelays))
|
|
584
619
|
);
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
const message = {
|
|
589
|
-
psp_version: PSP_VERSION, type: "peer_list", network,
|
|
590
|
-
from: selfRelayId, to: peerId,
|
|
591
|
-
message_id: crypto.randomUUID(), timestamp: Date.now(),
|
|
592
|
-
ttl_ms: DEFAULT_TTL_MS,
|
|
593
|
-
body: { peers: remotePeers }
|
|
594
|
-
};
|
|
595
|
-
try { socket.send(JSON.stringify(message)); } catch {}
|
|
596
|
-
})());
|
|
620
|
+
remotePeers = results.flat();
|
|
621
|
+
}
|
|
597
622
|
}
|
|
598
623
|
|
|
624
|
+
try {
|
|
625
|
+
sendPeerList(
|
|
626
|
+
socket,
|
|
627
|
+
network,
|
|
628
|
+
mergeDiscoveredPeers(localPeers, remotePeers).filter(peer => peer.peer_id !== peerId),
|
|
629
|
+
peerId,
|
|
630
|
+
env.RELAY_PEER_ID || "bootstrap-relay"
|
|
631
|
+
);
|
|
632
|
+
} catch {}
|
|
633
|
+
|
|
599
634
|
} else if (type === "ext" && message.body?.action === "relay_list") {
|
|
600
635
|
// Remote relay is sharing its known relay list — cache any new entries
|
|
601
636
|
if (db) {
|