@buoy-gg/sync-broker 7.0.41 → 7.0.43

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/dist/index.js CHANGED
@@ -1,764 +1 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
33
- DEFAULT_BROKER_PORT: () => DEFAULT_BROKER_PORT,
34
- SOCKET_CONFIG: () => SOCKET_CONFIG,
35
- SYNC_PROTOCOL_VERSION: () => SYNC_PROTOCOL_VERSION,
36
- createBroker: () => createBroker,
37
- socketHandle: () => socketHandle
38
- });
39
- module.exports = __toCommonJS(index_exports);
40
-
41
- // src/protocol.ts
42
- var SYNC_PROTOCOL_VERSION = 1;
43
- function tierFromLicensePayload(payload) {
44
- if (payload?.tier === "pro" || payload?.tier === "free") return payload.tier;
45
- if (payload?.tier === "locked" || payload?.tier === "anonymous") return "locked";
46
- return payload?.isPro === "1" || payload?.isPro === true ? "pro" : "locked";
47
- }
48
-
49
- // src/watchRegistry.ts
50
- var toWatchKey = (targetDeviceId, toolId) => JSON.stringify([targetDeviceId, toolId]);
51
- var fromWatchKey = (key) => JSON.parse(key);
52
- var WatchRegistry = class {
53
- constructor() {
54
- /** Per dashboard: the key it watches → the mode it asked for. */
55
- this.byDashboard = /* @__PURE__ */ new Map();
56
- this.streamCounts = /* @__PURE__ */ new Map();
57
- this.captureCounts = /* @__PURE__ */ new Map();
58
- }
59
- counts(mode) {
60
- return mode === "stream" ? this.streamCounts : this.captureCounts;
61
- }
62
- bump(mode, key, delta) {
63
- const counts = this.counts(mode);
64
- const next = (counts.get(key) ?? 0) + delta;
65
- if (next > 0) counts.set(key, next);
66
- else counts.delete(key);
67
- }
68
- /** The strongest mode any dashboard currently wants; null for nobody. */
69
- effective(key) {
70
- if ((this.streamCounts.get(key) ?? 0) > 0) return "stream";
71
- if ((this.captureCounts.get(key) ?? 0) > 0) return "capture";
72
- return null;
73
- }
74
- /** Every key with at least one watcher (for replay to a reconnecting device). */
75
- activeKeys() {
76
- return [
77
- .../* @__PURE__ */ new Set([...this.streamCounts.keys(), ...this.captureCounts.keys()])
78
- ];
79
- }
80
- /**
81
- * Record a watch. Returns the mode to send the device — always non-null,
82
- * because the device answers a watch with a snapshot and that is how a
83
- * dashboard joining a quiet tool gets its first paint. Re-watching at the
84
- * same mode is therefore NOT a no-op at the socket layer.
85
- */
86
- watch(dashboardId, key, mode) {
87
- let held = this.byDashboard.get(dashboardId);
88
- if (!held) {
89
- held = /* @__PURE__ */ new Map();
90
- this.byDashboard.set(dashboardId, held);
91
- }
92
- const previous = held.get(key);
93
- if (previous !== mode) {
94
- if (previous) this.bump(previous, key, -1);
95
- this.bump(mode, key, 1);
96
- held.set(key, mode);
97
- }
98
- return this.effective(key);
99
- }
100
- /**
101
- * Drop one dashboard's claim. Returns the new effective mode ONLY when it
102
- * changed — the device is left alone while someone else still wants the
103
- * same thing.
104
- */
105
- unwatch(dashboardId, key) {
106
- const held = this.byDashboard.get(dashboardId);
107
- const mode = held?.get(key);
108
- if (!held || !mode) return { changed: false, mode: this.effective(key) };
109
- const before = this.effective(key);
110
- held.delete(key);
111
- this.bump(mode, key, -1);
112
- const after = this.effective(key);
113
- return { changed: after !== before, mode: after };
114
- }
115
- /** Release everything a disconnecting dashboard held. */
116
- releaseDashboard(dashboardId) {
117
- const held = this.byDashboard.get(dashboardId);
118
- if (!held) return [];
119
- this.byDashboard.delete(dashboardId);
120
- const changes = [];
121
- for (const [key, mode] of held) {
122
- const before = this.effective(key);
123
- this.bump(mode, key, -1);
124
- const after = this.effective(key);
125
- if (after !== before) changes.push({ key, mode: after });
126
- }
127
- return changes;
128
- }
129
- };
130
-
131
- // src/socketHandle.ts
132
- var BROKER_LOG_BUFFER_MAX = 200;
133
- var MAX_OFFLINE_AGE_MS = 24 * 60 * 60 * 1e3;
134
- function redactAccountKeys(raw) {
135
- if (!raw) return void 0;
136
- try {
137
- const value = JSON.parse(raw);
138
- if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
139
- for (const key of ["BUOY_LICENSE_KEY", "BUOY_KEY", "EXPO_PUBLIC_BUOY_KEY", "VITE_BUOY_KEY", "REACT_APP_BUOY_KEY", "licenseKey"]) delete value[key];
140
- return JSON.stringify(value);
141
- } catch {
142
- return void 0;
143
- }
144
- }
145
- function socketHandle({ io, canUseDesktop = () => false }) {
146
- const LOG_PREFIX = "[SERVER]";
147
- let users = [];
148
- const allDevices = [];
149
- let dashboardClients = [];
150
- const deviceIdMap = /* @__PURE__ */ new Map();
151
- const socketTiers = /* @__PURE__ */ new Map();
152
- const activeConnectionsMap = /* @__PURE__ */ new Map();
153
- const instanceIdMap = /* @__PURE__ */ new Map();
154
- const deviceCapabilities = /* @__PURE__ */ new Map();
155
- let watches = new WatchRegistry();
156
- const brokerLogBuffer = [];
157
- function brokerLog(level, message, data) {
158
- console.log(`${LOG_PREFIX} ${message}`);
159
- const entry = { ts: Date.now(), level, message, data };
160
- brokerLogBuffer.push(entry);
161
- if (brokerLogBuffer.length > BROKER_LOG_BUFFER_MAX) {
162
- brokerLogBuffer.shift();
163
- }
164
- forwardToDashboards("broker-log", entry);
165
- }
166
- function liveInstances(deviceId) {
167
- const instances = /* @__PURE__ */ new Set();
168
- for (const [socketId, mappedDeviceId] of deviceIdMap) {
169
- if (mappedDeviceId !== deviceId) continue;
170
- const instance = instanceIdMap.get(socketId);
171
- if (instance) instances.add(instance);
172
- }
173
- return instances.size;
174
- }
175
- function emitAllDevices() {
176
- if (!canUseDesktop()) {
177
- io.emit("all-devices-update", []);
178
- return;
179
- }
180
- for (const device of [...users, ...allDevices]) {
181
- if (device.deviceName === "Dashboard" || !device.isConnected) continue;
182
- const tier = socketTiers.get(device.id) ?? "locked";
183
- device.tier = tier === "pro" ? "pro" : tier === "free" ? "free" : "locked";
184
- device.isPro = tier === "pro";
185
- if (tier !== "free" && tier !== "pro") {
186
- delete device.envVariables;
187
- delete device.extraDeviceInfo;
188
- }
189
- }
190
- const now = Date.now();
191
- for (const device of allDevices) {
192
- if (!device.deviceId) continue;
193
- const instances = liveInstances(device.deviceId);
194
- const collision = instances > 1;
195
- if (collision && !device.collision) {
196
- brokerLog(
197
- "warn",
198
- `${instances} apps are registered as "${device.deviceId}" \u2014 actions sent to it land on an arbitrary one. Give each device its own externalSync.deviceId, or leave it unset for a per-install default.`,
199
- { deviceId: device.deviceId, instances }
200
- );
201
- } else if (!collision && device.collision) {
202
- brokerLog("info", `Device id collision cleared for "${device.deviceId}"`, {
203
- deviceId: device.deviceId
204
- });
205
- }
206
- device.collision = collision;
207
- }
208
- for (let i = allDevices.length - 1; i >= 0; i--) {
209
- const device = allDevices[i];
210
- if (!device.isConnected && device.lastSeenAt && now - device.lastSeenAt > MAX_OFFLINE_AGE_MS) {
211
- allDevices.splice(i, 1);
212
- if (device.deviceId) {
213
- deviceCapabilities.delete(device.deviceId);
214
- }
215
- }
216
- }
217
- io.emit(
218
- "all-devices-update",
219
- allDevices.filter((device) => device.deviceName !== "Dashboard").map(({ licenseKey: _key, ...device }) => device)
220
- );
221
- }
222
- function handleClose(id, reason) {
223
- const user = users.find((user2) => user2.id === id);
224
- const deviceId = deviceIdMap.get(id);
225
- deviceIdMap.delete(id);
226
- instanceIdMap.delete(id);
227
- socketTiers.delete(id);
228
- let survivor;
229
- if (deviceId) {
230
- for (const [socketId, mappedDeviceId] of deviceIdMap) {
231
- if (mappedDeviceId === deviceId) survivor = socketId;
232
- }
233
- }
234
- if (deviceId && survivor) {
235
- const userIndex = users.findIndex((u) => u.deviceId === deviceId);
236
- if (userIndex !== -1 && users[userIndex].id === id) {
237
- users[userIndex].id = survivor;
238
- }
239
- const deviceIndex = allDevices.findIndex((d) => d.deviceId === deviceId);
240
- if (deviceIndex !== -1 && allDevices[deviceIndex].id === id) {
241
- allDevices[deviceIndex].id = survivor;
242
- }
243
- if (activeConnectionsMap.get(deviceId) === id) {
244
- activeConnectionsMap.set(deviceId, survivor);
245
- }
246
- console.log(
247
- `${LOG_PREFIX} Client disconnected - ID: ${id}, Name: ${user?.deviceName || "Unknown"} - Device ${deviceId} still connected via ${survivor}`
248
- );
249
- } else {
250
- users = users.filter((user2) => user2.id !== id);
251
- console.log(
252
- `${LOG_PREFIX} Client disconnected - ID: ${id}, Name: ${user?.deviceName || "Unknown"} - Removing user record`
253
- );
254
- if (deviceId) {
255
- const deviceIndex = allDevices.findIndex(
256
- (u) => u.deviceId === deviceId
257
- );
258
- if (deviceIndex !== -1) {
259
- allDevices[deviceIndex].isConnected = false;
260
- allDevices[deviceIndex].lastDisconnectReason = reason;
261
- allDevices[deviceIndex].lastSeenAt = Date.now();
262
- activeConnectionsMap.delete(deviceId);
263
- }
264
- }
265
- }
266
- const wasDashboard = dashboardClients.includes(id);
267
- dashboardClients = dashboardClients.filter((clientId) => clientId !== id);
268
- releaseDashboardWatches(id);
269
- const stillConnected = Boolean(deviceId && survivor);
270
- brokerLog(
271
- wasDashboard || stillConnected ? "debug" : "warn",
272
- stillConnected ? `Socket closed for ${user?.deviceName || id} (${reason ?? "unknown"}) \u2014 device still connected via another socket` : `Disconnected: ${user?.deviceName || id} (${reason ?? "unknown"})`,
273
- { socketId: id, reason, wasDashboard, stillConnected }
274
- );
275
- emitAllDevices();
276
- console.log(
277
- `${LOG_PREFIX} Updated users list: ${users.map((u) => u.deviceName).join(", ")}`
278
- );
279
- }
280
- function addNewUser({
281
- id,
282
- deviceName,
283
- deviceId,
284
- platform,
285
- extraDeviceInfo,
286
- envVariables,
287
- connectionNonce
288
- }) {
289
- const isDashboard = deviceName === "Dashboard";
290
- if (deviceId) {
291
- deviceIdMap.set(id, deviceId);
292
- activeConnectionsMap.set(deviceId, id);
293
- const existingUserIndex = users.findIndex(
294
- (user) => user.deviceId === deviceId
295
- );
296
- const existingDeviceIndex = allDevices.findIndex(
297
- (device) => device.deviceId === deviceId
298
- );
299
- if (existingUserIndex !== -1) {
300
- brokerLog(
301
- "success",
302
- `Reconnected: ${deviceName} (${platform || "unknown platform"})`,
303
- { socketId: id, deviceId }
304
- );
305
- users[existingUserIndex].id = id;
306
- if (platform) {
307
- users[existingUserIndex].platform = platform;
308
- }
309
- if (extraDeviceInfo) {
310
- users[existingUserIndex].extraDeviceInfo = extraDeviceInfo;
311
- }
312
- if (envVariables) {
313
- users[existingUserIndex].envVariables = envVariables;
314
- }
315
- users[existingUserIndex].isConnected = true;
316
- users[existingUserIndex].connectionNonce = connectionNonce;
317
- if (existingDeviceIndex !== -1) {
318
- allDevices[existingDeviceIndex].id = id;
319
- allDevices[existingDeviceIndex].isConnected = true;
320
- allDevices[existingDeviceIndex].connectionNonce = connectionNonce;
321
- allDevices[existingDeviceIndex].lastSeenAt = Date.now();
322
- if (platform) {
323
- allDevices[existingDeviceIndex].platform = platform;
324
- }
325
- if (extraDeviceInfo) {
326
- allDevices[existingDeviceIndex].extraDeviceInfo = extraDeviceInfo;
327
- }
328
- if (envVariables) {
329
- allDevices[existingDeviceIndex].envVariables = envVariables;
330
- }
331
- } else if (deviceName !== "Dashboard") {
332
- allDevices.push({
333
- id,
334
- deviceName,
335
- deviceId,
336
- platform,
337
- isConnected: true,
338
- extraDeviceInfo,
339
- envVariables,
340
- lastSeenAt: Date.now()
341
- });
342
- }
343
- emitAllDevices();
344
- console.log(
345
- `${LOG_PREFIX} Updated users list: ${users.map((u) => u.deviceName).join(", ")}`
346
- );
347
- return;
348
- }
349
- }
350
- const extractNumber = (name) => {
351
- const match = name.match(/(\d+)$/);
352
- return match ? parseInt(match[1], 10) : 1;
353
- };
354
- let highestNumber = 0;
355
- users.forEach((user) => {
356
- if (user.deviceName.startsWith(deviceName)) {
357
- const number = extractNumber(user.deviceName);
358
- if (number >= highestNumber) {
359
- highestNumber = number;
360
- }
361
- }
362
- });
363
- if (highestNumber > 0 && !isDashboard) {
364
- const renamed = `${deviceName} #${highestNumber + 1}`;
365
- brokerLog(
366
- "warn",
367
- `Duplicate device name "${deviceName}" \u2014 renamed to "${renamed}"`,
368
- { socketId: id, deviceId }
369
- );
370
- deviceName = renamed;
371
- }
372
- if (id) {
373
- const newUser = {
374
- id,
375
- deviceName,
376
- deviceId,
377
- platform,
378
- isConnected: true,
379
- extraDeviceInfo,
380
- envVariables,
381
- connectionNonce,
382
- lastSeenAt: Date.now()
383
- };
384
- users.push(newUser);
385
- if (deviceName !== "Dashboard" && deviceId) {
386
- const existingDeviceIndex = allDevices.findIndex(
387
- (device) => device.deviceId === deviceId
388
- );
389
- if (existingDeviceIndex !== -1) {
390
- allDevices[existingDeviceIndex] = { ...newUser };
391
- } else {
392
- allDevices.push({ ...newUser });
393
- }
394
- }
395
- brokerLog(
396
- isDashboard ? "debug" : "info",
397
- `Connected: ${deviceName}${platform ? ` (${platform})` : ""}`,
398
- { socketId: id, deviceId }
399
- );
400
- if (isDashboard && id) {
401
- dashboardClients.push(id);
402
- console.log(`${LOG_PREFIX} Registered dashboard client with ID: ${id}`);
403
- }
404
- emitAllDevices();
405
- console.log(
406
- `${LOG_PREFIX} Updated device list: ${users.map((u) => u.deviceName).join(", ")}`
407
- );
408
- }
409
- }
410
- function getDeviceFromDeviceId(deviceId) {
411
- return allDevices.find((device) => device.deviceId === deviceId);
412
- }
413
- function handleUserMessage(message, deviceName) {
414
- console.log(`${LOG_PREFIX} Message from ${deviceName}: ${message}`);
415
- if (!canUseDesktop()) return;
416
- io.emit("message", `Device ${deviceName} sent message: ${message}`);
417
- }
418
- function forwardToDashboards(event, message) {
419
- if (!canUseDesktop()) return;
420
- dashboardClients = dashboardClients.filter(
421
- (id) => io.sockets.sockets.has(id)
422
- );
423
- if (dashboardClients.length === 0) {
424
- return;
425
- }
426
- for (const dashboardId of dashboardClients) {
427
- io.sockets.sockets.get(dashboardId)?.emit(event, message);
428
- }
429
- }
430
- function withTargetUsers(targetDeviceId, action, actionName) {
431
- if (!canUseDesktop()) return;
432
- const admitted = (user) => {
433
- const tier = socketTiers.get(user.id);
434
- return tier === "free" || tier === "pro";
435
- };
436
- const deviceName = getDeviceFromDeviceId(targetDeviceId)?.deviceName;
437
- if (targetDeviceId === "No devices available") {
438
- console.log(
439
- `${LOG_PREFIX} Skipping ${actionName} - No devices available`
440
- );
441
- return;
442
- }
443
- if (targetDeviceId === "All") {
444
- console.log(`${LOG_PREFIX} Broadcasting ${actionName} to all devices`);
445
- const deviceUsers = users.filter(
446
- (user) => user.deviceName !== "Dashboard"
447
- );
448
- console.log(
449
- `${LOG_PREFIX} Sending to ${deviceUsers.length} devices: ${deviceUsers.map((u) => u.deviceName).join(", ")}`
450
- );
451
- deviceUsers.filter(admitted).forEach(action);
452
- } else {
453
- const targetUser = users.find((user) => user.deviceId === targetDeviceId);
454
- if (targetUser && admitted(targetUser)) {
455
- console.log(
456
- `${LOG_PREFIX} Sending to target device ${targetUser.deviceName} (${targetUser.deviceId}) targetDeviceId: ${targetDeviceId}`
457
- );
458
- action(targetUser);
459
- } else {
460
- console.log(
461
- `${LOG_PREFIX} Target device not found - DeviceId: ${targetDeviceId}, DeviceName: ${deviceName}`
462
- );
463
- }
464
- }
465
- }
466
- function emitWatchState(key, mode) {
467
- const [targetDeviceId, toolId] = fromWatchKey(key);
468
- const message = mode === null ? { toolId, targetDeviceId, watching: false, consolidated: true } : {
469
- toolId,
470
- targetDeviceId,
471
- watching: true,
472
- stream: mode === "stream",
473
- consolidated: true
474
- };
475
- withTargetUsers(
476
- targetDeviceId,
477
- (user) => io.to(user.id).emit("devtool-watch", message),
478
- `devtool ${mode === null ? "unwatch" : `watch(${mode})`} ${toolId}`
479
- );
480
- }
481
- function handleDevtoolWatch(dashboardId, message) {
482
- const key = toWatchKey(message.targetDeviceId, message.toolId);
483
- if (message.watching) {
484
- const mode2 = message.stream === false ? "capture" : "stream";
485
- const effective = watches.watch(dashboardId, key, mode2);
486
- console.log(
487
- `${LOG_PREFIX} Dashboard ${dashboardId} watching "${message.toolId}" on ${message.targetDeviceId} (${mode2}, effective ${effective})`
488
- );
489
- emitWatchState(key, effective);
490
- return;
491
- }
492
- const { changed, mode } = watches.unwatch(dashboardId, key);
493
- if (!changed) return;
494
- console.log(
495
- mode === null ? `${LOG_PREFIX} Last dashboard stopped watching "${message.toolId}" on ${message.targetDeviceId}` : `${LOG_PREFIX} "${message.toolId}" on ${message.targetDeviceId} downgraded to ${mode}`
496
- );
497
- emitWatchState(key, mode);
498
- }
499
- function releaseDashboardWatches(dashboardId) {
500
- for (const { key, mode } of watches.releaseDashboard(dashboardId)) {
501
- emitWatchState(key, mode);
502
- }
503
- }
504
- io.on("connection", (socket) => {
505
- const sendAccess = () => socket.emit("buoy:access", { protocol: 1, ready: canUseDesktop() });
506
- sendAccess();
507
- socket.on("buoy:access:request", sendAccess);
508
- socket.use((_packet, next) => {
509
- if (_packet[0] === "buoy:access:request") {
510
- next();
511
- return;
512
- }
513
- if (!canUseDesktop()) {
514
- sendAccess();
515
- return;
516
- }
517
- const control = _packet[0] === "device-license";
518
- if (control) {
519
- const payload = _packet[1] ?? {};
520
- socketTiers.set(socket.id, tierFromLicensePayload(payload));
521
- }
522
- const deviceAdmitted = socketTiers.get(socket.id) === "free" || socketTiers.get(socket.id) === "pro";
523
- if (control || dashboardClients.includes(socket.id) || deviceAdmitted) next();
524
- else sendAccess();
525
- });
526
- const {
527
- deviceName,
528
- deviceId,
529
- platform,
530
- extraDeviceInfo,
531
- envVariables,
532
- connectionNonce,
533
- instanceId
534
- } = socket.handshake.query;
535
- if (typeof instanceId === "string" && instanceId) {
536
- instanceIdMap.set(socket.id, instanceId);
537
- }
538
- console.log(
539
- `${LOG_PREFIX} New connection - ID: ${socket.id}, Name: ${deviceName || "Unknown Device"}${deviceId ? `, DeviceId: ${deviceId}` : ""}${platform ? `, Platform: ${platform}` : ""}`
540
- );
541
- addNewUser({
542
- id: socket.id,
543
- deviceName: deviceName || "Unknown Device Name",
544
- deviceId,
545
- platform,
546
- extraDeviceInfo: void 0,
547
- envVariables: void 0,
548
- connectionNonce
549
- });
550
- if (canUseDesktop() && deviceName === "Dashboard") {
551
- for (const capabilities of deviceCapabilities.values()) {
552
- socket.emit("devtool-capabilities", capabilities);
553
- }
554
- for (const entry of brokerLogBuffer) {
555
- socket.emit("broker-log", entry);
556
- }
557
- }
558
- socket.on(
559
- "device-license",
560
- (payload) => {
561
- const nextTier = tierFromLicensePayload(payload ?? {});
562
- const nextIsPro = nextTier === "pro";
563
- socketTiers.set(socket.id, nextTier);
564
- const user = users.find((u) => u.id === socket.id);
565
- if (user) {
566
- user.isPro = nextIsPro;
567
- user.tier = nextTier;
568
- delete user.licenseKey;
569
- }
570
- const did = user?.deviceId || deviceIdMap.get(socket.id);
571
- if (nextTier !== "locked") {
572
- for (const record of [...users, ...allDevices].filter((record2) => record2.id === socket.id)) {
573
- record.extraDeviceInfo = redactAccountKeys(extraDeviceInfo);
574
- record.envVariables = redactAccountKeys(envVariables);
575
- }
576
- }
577
- if (did) {
578
- if (nextTier === "locked") deviceCapabilities.delete(did);
579
- const device = allDevices.find((d) => d.deviceId === did);
580
- if (device) {
581
- device.isPro = nextIsPro;
582
- device.tier = nextTier;
583
- delete device.licenseKey;
584
- }
585
- }
586
- brokerLog(
587
- "debug",
588
- `device-license from ${user?.deviceName || socket.id}: tier=${nextTier}`,
589
- { socketId: socket.id }
590
- );
591
- emitAllDevices();
592
- }
593
- );
594
- socket.on(
595
- "disconnect",
596
- (reason) => handleClose(socket.id, reason)
597
- );
598
- socket.on("message", (msg) => {
599
- handleUserMessage(msg, deviceName || "Unknown");
600
- });
601
- socket.on("devtool-capabilities", (message) => {
602
- if (!message || !Array.isArray(message.tools) || message.persistentDeviceId !== deviceIdMap.get(socket.id)) return;
603
- console.log(
604
- `${LOG_PREFIX} Capabilities from ${message.persistentDeviceId}: ${message.tools.map((tool) => `${tool.toolId}@v${tool.version}`).join(", ")}`
605
- );
606
- if (message.protocolVersion !== SYNC_PROTOCOL_VERSION) {
607
- brokerLog(
608
- "warn",
609
- `Protocol version mismatch from ${message.persistentDeviceId}: device speaks v${message.protocolVersion}, broker speaks v${SYNC_PROTOCOL_VERSION} \u2014 update @buoy-gg packages or Buoy Desktop`,
610
- { deviceId: message.persistentDeviceId }
611
- );
612
- }
613
- deviceCapabilities.set(message.persistentDeviceId, message);
614
- forwardToDashboards("devtool-capabilities", message);
615
- for (const key of watches.activeKeys()) {
616
- const [targetDeviceId, toolId] = fromWatchKey(key);
617
- if (targetDeviceId !== "All" && targetDeviceId !== message.persistentDeviceId) {
618
- continue;
619
- }
620
- const mode = watches.effective(key);
621
- if (!mode) continue;
622
- socket.emit("devtool-watch", {
623
- toolId,
624
- targetDeviceId,
625
- watching: true,
626
- stream: mode === "stream",
627
- consolidated: true
628
- });
629
- }
630
- });
631
- socket.on("devtool-sync", (message) => {
632
- if (!message || message.persistentDeviceId !== deviceIdMap.get(socket.id)) return;
633
- forwardToDashboards("devtool-sync", message);
634
- });
635
- socket.on(
636
- "devtool-action-result",
637
- (message) => {
638
- if (!message || message.persistentDeviceId !== deviceIdMap.get(socket.id)) return;
639
- console.log(
640
- `${LOG_PREFIX} Action result from ${message.persistentDeviceId}: ${message.toolId}.${message.action} -> ${message.ok ? "ok" : `error: ${message.error}`}`
641
- );
642
- forwardToDashboards("devtool-action-result", message);
643
- }
644
- );
645
- socket.on("devtool-watch", (message) => {
646
- handleDevtoolWatch(socket.id, message);
647
- });
648
- socket.on("request-devtool-state", (message) => {
649
- withTargetUsers(
650
- message.targetDeviceId,
651
- (user) => io.to(user.id).emit("request-devtool-state", message),
652
- `devtool state request ${message.toolId}`
653
- );
654
- });
655
- socket.on("forget-device", (payload) => {
656
- const targetDeviceId = payload?.deviceId;
657
- if (!dashboardClients.includes(socket.id)) return;
658
- if (!targetDeviceId || activeConnectionsMap.has(targetDeviceId)) return;
659
- const target = allDevices.find((d) => d.deviceId === targetDeviceId);
660
- users = users.filter((u) => u.deviceId !== targetDeviceId);
661
- const historyIndex = allDevices.findIndex(
662
- (d) => d.deviceId === targetDeviceId
663
- );
664
- if (historyIndex !== -1) {
665
- allDevices.splice(historyIndex, 1);
666
- }
667
- deviceCapabilities.delete(targetDeviceId);
668
- for (const [socketId, mappedDeviceId] of deviceIdMap) {
669
- if (mappedDeviceId === targetDeviceId) {
670
- deviceIdMap.delete(socketId);
671
- }
672
- }
673
- brokerLog(
674
- "info",
675
- `Forgot device: ${target?.deviceName ?? targetDeviceId}`,
676
- { deviceId: targetDeviceId }
677
- );
678
- emitAllDevices();
679
- });
680
- socket.on("devtool-action", (message) => {
681
- console.log(
682
- `${LOG_PREFIX} Devtool action from dashboard: ${message.toolId}.${message.action} -> ${message.targetDeviceId}`
683
- );
684
- withTargetUsers(
685
- message.targetDeviceId,
686
- (user) => io.to(user.id).emit("devtool-action", message),
687
- `devtool action ${message.toolId}.${message.action}`
688
- );
689
- });
690
- });
691
- return {
692
- refreshAccess() {
693
- const ready = canUseDesktop();
694
- if (!ready) {
695
- socketTiers.clear();
696
- deviceCapabilities.clear();
697
- brokerLogBuffer.length = 0;
698
- watches = new WatchRegistry();
699
- for (const device of [...users, ...allDevices]) {
700
- delete device.licenseKey;
701
- delete device.envVariables;
702
- delete device.extraDeviceInfo;
703
- }
704
- }
705
- io.emit("buoy:access", { protocol: 1, ready });
706
- emitAllDevices();
707
- }
708
- };
709
- }
710
-
711
- // src/createBroker.ts
712
- var import_http = __toESM(require("http"));
713
- var import_socket = require("socket.io");
714
- var DEFAULT_BROKER_PORT = 42831;
715
- var SOCKET_CONFIG = {
716
- cors: {
717
- origin: "*",
718
- methods: ["GET", "POST"]
719
- },
720
- // Dev-tool snapshots are whole app-state dumps (a dehydrated React Query
721
- // cache, a Redux/Zustand store, an aggregated event log, etc.) and a device
722
- // pushes one per watched tool on connect. socket.io v3+ caps inbound messages
723
- // at 1 MB by default and TERMINATES the connection ("transport error") when a
724
- // single message exceeds it — which, with a dashboard watching all ~11 tools
725
- // at once, makes a device's connection flap. These are local, trusted dev
726
- // connections, so lift the cap well clear of any realistic state dump.
727
- maxHttpBufferSize: 1e8,
728
- // 100 MB
729
- transports: ["websocket", "polling"],
730
- pingTimeout: 3e4,
731
- pingInterval: 25e3
732
- };
733
- function createBroker(options = {}) {
734
- const port = options.port ?? DEFAULT_BROKER_PORT;
735
- const httpServer = import_http.default.createServer((_req, res) => {
736
- res.writeHead(200, { "content-type": "text/plain" });
737
- res.end(`Buoy sync broker running on port ${port}`);
738
- });
739
- const io = new import_socket.Server(httpServer, {
740
- ...SOCKET_CONFIG,
741
- ...options.socketOptions
742
- });
743
- const routing = socketHandle({ io, canUseDesktop: options.canUseDesktop });
744
- httpServer.on("error", (error) => {
745
- options.onError?.(error);
746
- });
747
- httpServer.listen(port, () => {
748
- options.onListening?.(port);
749
- });
750
- const close = () => new Promise((resolve) => {
751
- io.close(() => {
752
- httpServer.close(() => resolve());
753
- });
754
- });
755
- return { io, httpServer, port, close, refreshAccess: routing.refreshAccess };
756
- }
757
- // Annotate the CommonJS export names for ESM import in node:
758
- 0 && (module.exports = {
759
- DEFAULT_BROKER_PORT,
760
- SOCKET_CONFIG,
761
- SYNC_PROTOCOL_VERSION,
762
- createBroker,
763
- socketHandle
764
- });
1
+ "use strict";var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(i,r)=>{for(var d in r)__defProp(i,d,{get:r[d],enumerable:true})};var __copyProps=(i,r,d,c)=>{if(r&&typeof r==="object"||typeof r==="function"){for(let s of __getOwnPropNames(r))if(!__hasOwnProp.call(i,s)&&s!==d)__defProp(i,s,{get:()=>r[s],enumerable:!(c=__getOwnPropDesc(r,s))||c.enumerable})}return i};var __toESM=(i,r,d)=>(d=i!=null?__create(__getProtoOf(i)):{},__copyProps(r||!i||!i.__esModule?__defProp(d,"default",{value:i,enumerable:true}):d,i));var __toCommonJS=i=>__copyProps(__defProp({},"__esModule",{value:true}),i);var index_exports={};__export(index_exports,{DEFAULT_BROKER_PORT:()=>DEFAULT_BROKER_PORT,SOCKET_CONFIG:()=>SOCKET_CONFIG,SYNC_PROTOCOL_VERSION:()=>SYNC_PROTOCOL_VERSION,createBroker:()=>createBroker,socketHandle:()=>socketHandle});module.exports=__toCommonJS(index_exports);var SYNC_PROTOCOL_VERSION=1;function tierFromLicensePayload(i){if(i?.tier==="pro"||i?.tier==="free")return i.tier;if(i?.tier==="locked"||i?.tier==="anonymous")return"locked";return i?.isPro==="1"||i?.isPro===true?"pro":"locked"}var toWatchKey=(i,r)=>JSON.stringify([i,r]);var fromWatchKey=i=>JSON.parse(i);var WatchRegistry=class{constructor(){this.byDashboard=new Map;this.streamCounts=new Map;this.captureCounts=new Map}counts(i){return i==="stream"?this.streamCounts:this.captureCounts}bump(i,r,d){const c=this.counts(i);const s=(c.get(r)??0)+d;if(s>0)c.set(r,s);else c.delete(r)}effective(i){if((this.streamCounts.get(i)??0)>0)return"stream";if((this.captureCounts.get(i)??0)>0)return"capture";return null}activeKeys(){return[...new Set([...this.streamCounts.keys(),...this.captureCounts.keys()])]}watch(i,r,d){let c=this.byDashboard.get(i);if(!c){c=new Map;this.byDashboard.set(i,c)}const s=c.get(r);if(s!==d){if(s)this.bump(s,r,-1);this.bump(d,r,1);c.set(r,d)}return this.effective(r)}unwatch(i,r){const d=this.byDashboard.get(i);const c=d?.get(r);if(!d||!c)return{changed:false,mode:this.effective(r)};const s=this.effective(r);d.delete(r);this.bump(c,r,-1);const b=this.effective(r);return{changed:b!==s,mode:b}}releaseDashboard(i){const r=this.byDashboard.get(i);if(!r)return[];this.byDashboard.delete(i);const d=[];for(const[c,s]of r){const b=this.effective(c);this.bump(s,c,-1);const I=this.effective(c);if(I!==b)d.push({key:c,mode:I})}return d}};var BROKER_LOG_BUFFER_MAX=200;var MAX_OFFLINE_AGE_MS=24*60*60*1e3;function redactAccountKeys(i){if(!i)return void 0;try{const r=JSON.parse(i);if(!r||typeof r!=="object"||Array.isArray(r))return void 0;for(const d of["BUOY_LICENSE_KEY","BUOY_KEY","EXPO_PUBLIC_BUOY_KEY","VITE_BUOY_KEY","REACT_APP_BUOY_KEY","licenseKey"])delete r[d];return JSON.stringify(r)}catch{return void 0}}function socketHandle({io:i,canUseDesktop:r=()=>false}){const d="[SERVER]";let c=[];const s=[];let b=[];const I=new Map;const w=new Map;const N=new Map;const P=new Map;const y=new Map;let m=new WatchRegistry;const S=[];function O(e,t,n){console.log(`${d} ${t}`);const l={ts:Date.now(),level:e,message:t,data:n};S.push(l);if(S.length>BROKER_LOG_BUFFER_MAX){S.shift()}E("broker-log",l)}function K(e){const t=new Set;for(const[n,l]of I){if(l!==e)continue;const u=P.get(n);if(u)t.add(u)}return t.size}function C(){if(!r()){i.emit("all-devices-update",[]);return}for(const t of[...c,...s]){if(t.deviceName==="Dashboard"||!t.isConnected)continue;const n=w.get(t.id)??"locked";t.tier=n==="pro"?"pro":n==="free"?"free":"locked";t.isPro=n==="pro";if(n!=="free"&&n!=="pro"){delete t.envVariables;delete t.extraDeviceInfo}}const e=Date.now();for(const t of s){if(!t.deviceId)continue;const n=K(t.deviceId);const l=n>1;if(l&&!t.collision){O("warn",`${n} apps are registered as "${t.deviceId}" \u2014 actions sent to it land on an arbitrary one. Give each device its own externalSync.deviceId, or leave it unset for a per-install default.`,{deviceId:t.deviceId,instances:n})}else if(!l&&t.collision){O("info",`Device id collision cleared for "${t.deviceId}"`,{deviceId:t.deviceId})}t.collision=l}for(let t=s.length-1;t>=0;t--){const n=s[t];if(!n.isConnected&&n.lastSeenAt&&e-n.lastSeenAt>MAX_OFFLINE_AGE_MS){s.splice(t,1);if(n.deviceId){y.delete(n.deviceId)}}}i.emit("all-devices-update",s.filter(t=>t.deviceName!=="Dashboard").map(({licenseKey:t,...n})=>n))}function T(e,t){const n=c.find(h=>h.id===e);const l=I.get(e);I.delete(e);P.delete(e);w.delete(e);let u;if(l){for(const[h,D]of I){if(D===l)u=h}}if(l&&u){const h=c.findIndex(o=>o.deviceId===l);if(h!==-1&&c[h].id===e){c[h].id=u}const D=s.findIndex(o=>o.deviceId===l);if(D!==-1&&s[D].id===e){s[D].id=u}if(N.get(l)===e){N.set(l,u)}console.log(`${d} Client disconnected - ID: ${e}, Name: ${n?.deviceName||"Unknown"} - Device ${l} still connected via ${u}`)}else{c=c.filter(h=>h.id!==e);console.log(`${d} Client disconnected - ID: ${e}, Name: ${n?.deviceName||"Unknown"} - Removing user record`);if(l){const h=s.findIndex(D=>D.deviceId===l);if(h!==-1){s[h].isConnected=false;s[h].lastDisconnectReason=t;s[h].lastSeenAt=Date.now();N.delete(l)}}}const v=b.includes(e);b=b.filter(h=>h!==e);F(e);const p=Boolean(l&&u);O(v||p?"debug":"warn",p?`Socket closed for ${n?.deviceName||e} (${t??"unknown"}) \u2014 device still connected via another socket`:`Disconnected: ${n?.deviceName||e} (${t??"unknown"})`,{socketId:e,reason:t,wasDashboard:v,stillConnected:p});C();console.log(`${d} Updated users list: ${c.map(h=>h.deviceName).join(", ")}`)}function B({id:e,deviceName:t,deviceId:n,platform:l,extraDeviceInfo:u,envVariables:v,connectionNonce:p}){const h=t==="Dashboard";if(n){I.set(e,n);N.set(n,e);const a=c.findIndex($=>$.deviceId===n);const f=s.findIndex($=>$.deviceId===n);if(a!==-1){O("success",`Reconnected: ${t} (${l||"unknown platform"})`,{socketId:e,deviceId:n});c[a].id=e;if(l){c[a].platform=l}if(u){c[a].extraDeviceInfo=u}if(v){c[a].envVariables=v}c[a].isConnected=true;c[a].connectionNonce=p;if(f!==-1){s[f].id=e;s[f].isConnected=true;s[f].connectionNonce=p;s[f].lastSeenAt=Date.now();if(l){s[f].platform=l}if(u){s[f].extraDeviceInfo=u}if(v){s[f].envVariables=v}}else if(t!=="Dashboard"){s.push({id:e,deviceName:t,deviceId:n,platform:l,isConnected:true,extraDeviceInfo:u,envVariables:v,lastSeenAt:Date.now()})}C();console.log(`${d} Updated users list: ${c.map($=>$.deviceName).join(", ")}`);return}}const D=a=>{const f=a.match(/(\d+)$/);return f?parseInt(f[1],10):1};let o=0;c.forEach(a=>{if(a.deviceName.startsWith(t)){const f=D(a.deviceName);if(f>=o){o=f}}});if(o>0&&!h){const a=`${t} #${o+1}`;O("warn",`Duplicate device name "${t}" \u2014 renamed to "${a}"`,{socketId:e,deviceId:n});t=a}if(e){const a={id:e,deviceName:t,deviceId:n,platform:l,isConnected:true,extraDeviceInfo:u,envVariables:v,connectionNonce:p,lastSeenAt:Date.now()};c.push(a);if(t!=="Dashboard"&&n){const f=s.findIndex($=>$.deviceId===n);if(f!==-1){s[f]={...a}}else{s.push({...a})}}O(h?"debug":"info",`Connected: ${t}${l?` (${l})`:""}`,{socketId:e,deviceId:n});if(h&&e){b.push(e);console.log(`${d} Registered dashboard client with ID: ${e}`)}C();console.log(`${d} Updated device list: ${c.map(f=>f.deviceName).join(", ")}`)}}function M(e){return s.find(t=>t.deviceId===e)}function U(e,t){console.log(`${d} Message from ${t}: ${e}`);if(!r())return;i.emit("message",`Device ${t} sent message: ${e}`)}function E(e,t){if(!r())return;b=b.filter(n=>i.sockets.sockets.has(n));if(b.length===0){return}for(const n of b){i.sockets.sockets.get(n)?.emit(e,t)}}function x(e,t,n){if(!r())return;const l=v=>{const p=w.get(v.id);return p==="free"||p==="pro"};const u=M(e)?.deviceName;if(e==="No devices available"){console.log(`${d} Skipping ${n} - No devices available`);return}if(e==="All"){console.log(`${d} Broadcasting ${n} to all devices`);const v=c.filter(p=>p.deviceName!=="Dashboard");console.log(`${d} Sending to ${v.length} devices: ${v.map(p=>p.deviceName).join(", ")}`);v.filter(l).forEach(t)}else{const v=c.find(p=>p.deviceId===e);if(v&&l(v)){console.log(`${d} Sending to target device ${v.deviceName} (${v.deviceId}) targetDeviceId: ${e}`);t(v)}else{console.log(`${d} Target device not found - DeviceId: ${e}, DeviceName: ${u}`)}}}function A(e,t){const[n,l]=fromWatchKey(e);const u=t===null?{toolId:l,targetDeviceId:n,watching:false,consolidated:true}:{toolId:l,targetDeviceId:n,watching:true,stream:t==="stream",consolidated:true};x(n,v=>i.to(v.id).emit("devtool-watch",u),`devtool ${t===null?"unwatch":`watch(${t})`} ${l}`)}function L(e,t){const n=toWatchKey(t.targetDeviceId,t.toolId);if(t.watching){const v=t.stream===false?"capture":"stream";const p=m.watch(e,n,v);console.log(`${d} Dashboard ${e} watching "${t.toolId}" on ${t.targetDeviceId} (${v}, effective ${p})`);A(n,p);return}const{changed:l,mode:u}=m.unwatch(e,n);if(!l)return;console.log(u===null?`${d} Last dashboard stopped watching "${t.toolId}" on ${t.targetDeviceId}`:`${d} "${t.toolId}" on ${t.targetDeviceId} downgraded to ${u}`);A(n,u)}function F(e){for(const{key:t,mode:n}of m.releaseDashboard(e)){A(t,n)}}i.on("connection",e=>{const t=()=>e.emit("buoy:access",{protocol:1,ready:r()});t();e.on("buoy:access:request",t);e.use((o,a)=>{if(o[0]==="buoy:access:request"){a();return}if(!r()){t();return}const f=o[0]==="device-license";if(f){const g=o[1]??{};w.set(e.id,tierFromLicensePayload(g))}const $=w.get(e.id)==="free"||w.get(e.id)==="pro";if(f||b.includes(e.id)||$)a();else t()});const{deviceName:n,deviceId:l,platform:u,extraDeviceInfo:v,envVariables:p,connectionNonce:h,instanceId:D}=e.handshake.query;if(typeof D==="string"&&D){P.set(e.id,D)}console.log(`${d} New connection - ID: ${e.id}, Name: ${n||"Unknown Device"}${l?`, DeviceId: ${l}`:""}${u?`, Platform: ${u}`:""}`);B({id:e.id,deviceName:n||"Unknown Device Name",deviceId:l,platform:u,extraDeviceInfo:void 0,envVariables:void 0,connectionNonce:h});if(r()&&n==="Dashboard"){for(const o of y.values()){e.emit("devtool-capabilities",o)}for(const o of S){e.emit("broker-log",o)}}e.on("device-license",o=>{const a=tierFromLicensePayload(o??{});const f=a==="pro";w.set(e.id,a);const $=c.find(_=>_.id===e.id);if($){$.isPro=f;$.tier=a;delete $.licenseKey}const g=$?.deviceId||I.get(e.id);if(a!=="locked"){for(const _ of[...c,...s].filter(R=>R.id===e.id)){_.extraDeviceInfo=redactAccountKeys(v);_.envVariables=redactAccountKeys(p)}}if(g){if(a==="locked")y.delete(g);const _=s.find(R=>R.deviceId===g);if(_){_.isPro=f;_.tier=a;delete _.licenseKey}}O("debug",`device-license from ${$?.deviceName||e.id}: tier=${a}`,{socketId:e.id});C()});e.on("disconnect",o=>T(e.id,o));e.on("message",o=>{U(o,n||"Unknown")});e.on("devtool-capabilities",o=>{if(!o||!Array.isArray(o.tools)||o.persistentDeviceId!==I.get(e.id))return;console.log(`${d} Capabilities from ${o.persistentDeviceId}: ${o.tools.map(a=>`${a.toolId}@v${a.version}`).join(", ")}`);if(o.protocolVersion!==SYNC_PROTOCOL_VERSION){O("warn",`Protocol version mismatch from ${o.persistentDeviceId}: device speaks v${o.protocolVersion}, broker speaks v${SYNC_PROTOCOL_VERSION} \u2014 update @buoy-gg packages or Buoy Desktop`,{deviceId:o.persistentDeviceId})}y.set(o.persistentDeviceId,o);E("devtool-capabilities",o);for(const a of m.activeKeys()){const[f,$]=fromWatchKey(a);if(f!=="All"&&f!==o.persistentDeviceId){continue}const g=m.effective(a);if(!g)continue;e.emit("devtool-watch",{toolId:$,targetDeviceId:f,watching:true,stream:g==="stream",consolidated:true})}});e.on("devtool-sync",o=>{if(!o||o.persistentDeviceId!==I.get(e.id))return;E("devtool-sync",o)});e.on("devtool-action-result",o=>{if(!o||o.persistentDeviceId!==I.get(e.id))return;console.log(`${d} Action result from ${o.persistentDeviceId}: ${o.toolId}.${o.action} -> ${o.ok?"ok":`error: ${o.error}`}`);E("devtool-action-result",o)});e.on("devtool-watch",o=>{L(e.id,o)});e.on("request-devtool-state",o=>{x(o.targetDeviceId,a=>i.to(a.id).emit("request-devtool-state",o),`devtool state request ${o.toolId}`)});e.on("forget-device",o=>{const a=o?.deviceId;if(!b.includes(e.id))return;if(!a||N.has(a))return;const f=s.find(g=>g.deviceId===a);c=c.filter(g=>g.deviceId!==a);const $=s.findIndex(g=>g.deviceId===a);if($!==-1){s.splice($,1)}y.delete(a);for(const[g,_]of I){if(_===a){I.delete(g)}}O("info",`Forgot device: ${f?.deviceName??a}`,{deviceId:a});C()});e.on("devtool-action",o=>{console.log(`${d} Devtool action from dashboard: ${o.toolId}.${o.action} -> ${o.targetDeviceId}`);x(o.targetDeviceId,a=>i.to(a.id).emit("devtool-action",o),`devtool action ${o.toolId}.${o.action}`)})});return{refreshAccess(){const e=r();if(!e){w.clear();y.clear();S.length=0;m=new WatchRegistry;for(const t of[...c,...s]){delete t.licenseKey;delete t.envVariables;delete t.extraDeviceInfo}}i.emit("buoy:access",{protocol:1,ready:e});C()}}}var import_http=__toESM(require("http"));var import_socket=require("socket.io");var DEFAULT_BROKER_PORT=42831;var SOCKET_CONFIG={cors:{origin:"*",methods:["GET","POST"]},maxHttpBufferSize:1e8,transports:["websocket","polling"],pingTimeout:3e4,pingInterval:25e3};function createBroker(i={}){const r=i.port??DEFAULT_BROKER_PORT;const d=import_http.default.createServer((I,w)=>{w.writeHead(200,{"content-type":"text/plain"});w.end(`Buoy sync broker running on port ${r}`)});const c=new import_socket.Server(d,{...SOCKET_CONFIG,...i.socketOptions});const s=socketHandle({io:c,canUseDesktop:i.canUseDesktop});d.on("error",I=>{i.onError?.(I)});d.listen(r,()=>{i.onListening?.(r)});const b=()=>new Promise(I=>{c.close(()=>{d.close(()=>I())})});return{io:c,httpServer:d,port:r,close:b,refreshAccess:s.refreshAccess}}