@buoy-gg/sync-broker 7.0.32 → 7.0.34

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.d.mts CHANGED
@@ -203,6 +203,13 @@ interface User {
203
203
  * phone it is attached to is signed in.
204
204
  */
205
205
  licenseKey?: string;
206
+ /**
207
+ * More than one app is registered under this deviceId right now — a pinned
208
+ * `externalSync.deviceId` copied onto two devices. Actions sent to the id
209
+ * land on an arbitrary one. Broker-computed from the per-runtime
210
+ * `instanceId` each socket announces; never set for apps that predate it.
211
+ */
212
+ collision?: boolean;
206
213
  }
207
214
 
208
215
  export { type BrokerHandle, type CreateBrokerOptions, DEFAULT_BROKER_PORT, type DevToolActionMessage, type DevToolActionResultMessage, type DevToolRequestMessage, type DevToolSyncMessage, type DevToolWatchMessage, type DeviceCapabilitiesMessage, SOCKET_CONFIG, SYNC_PROTOCOL_VERSION, type SyncKind, type ToolCapability, type User, createBroker, socketHandle };
package/dist/index.d.ts CHANGED
@@ -203,6 +203,13 @@ interface User {
203
203
  * phone it is attached to is signed in.
204
204
  */
205
205
  licenseKey?: string;
206
+ /**
207
+ * More than one app is registered under this deviceId right now — a pinned
208
+ * `externalSync.deviceId` copied onto two devices. Actions sent to the id
209
+ * land on an arbitrary one. Broker-computed from the per-runtime
210
+ * `instanceId` each socket announces; never set for apps that predate it.
211
+ */
212
+ collision?: boolean;
206
213
  }
207
214
 
208
215
  export { type BrokerHandle, type CreateBrokerOptions, DEFAULT_BROKER_PORT, type DevToolActionMessage, type DevToolActionResultMessage, type DevToolRequestMessage, type DevToolSyncMessage, type DevToolWatchMessage, type DeviceCapabilitiesMessage, SOCKET_CONFIG, SYNC_PROTOCOL_VERSION, type SyncKind, type ToolCapability, type User, createBroker, socketHandle };
package/dist/index.js CHANGED
@@ -135,6 +135,7 @@ var allDevices = [];
135
135
  var dashboardClients = [];
136
136
  var deviceIdMap = /* @__PURE__ */ new Map();
137
137
  var activeConnectionsMap = /* @__PURE__ */ new Map();
138
+ var instanceIdMap = /* @__PURE__ */ new Map();
138
139
  var deviceCapabilities = /* @__PURE__ */ new Map();
139
140
  var watches = new WatchRegistry();
140
141
  var brokerLogBuffer = [];
@@ -151,8 +152,34 @@ function socketHandle({ io }) {
151
152
  }
152
153
  forwardToDashboards("broker-log", entry);
153
154
  }
155
+ function liveInstances(deviceId) {
156
+ const instances = /* @__PURE__ */ new Set();
157
+ for (const [socketId, mappedDeviceId] of deviceIdMap) {
158
+ if (mappedDeviceId !== deviceId) continue;
159
+ const instance = instanceIdMap.get(socketId);
160
+ if (instance) instances.add(instance);
161
+ }
162
+ return instances.size;
163
+ }
154
164
  function emitAllDevices() {
155
165
  const now = Date.now();
166
+ for (const device of allDevices) {
167
+ if (!device.deviceId) continue;
168
+ const instances = liveInstances(device.deviceId);
169
+ const collision = instances > 1;
170
+ if (collision && !device.collision) {
171
+ brokerLog(
172
+ "warn",
173
+ `${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.`,
174
+ { deviceId: device.deviceId, instances }
175
+ );
176
+ } else if (!collision && device.collision) {
177
+ brokerLog("info", `Device id collision cleared for "${device.deviceId}"`, {
178
+ deviceId: device.deviceId
179
+ });
180
+ }
181
+ device.collision = collision;
182
+ }
156
183
  for (let i = allDevices.length - 1; i >= 0; i--) {
157
184
  const device = allDevices[i];
158
185
  if (!device.isConnected && device.lastSeenAt && now - device.lastSeenAt > MAX_OFFLINE_AGE_MS) {
@@ -171,6 +198,7 @@ function socketHandle({ io }) {
171
198
  const user = users.find((user2) => user2.id === id);
172
199
  const deviceId = deviceIdMap.get(id);
173
200
  deviceIdMap.delete(id);
201
+ instanceIdMap.delete(id);
174
202
  let survivor;
175
203
  if (deviceId) {
176
204
  for (const [socketId, mappedDeviceId] of deviceIdMap) {
@@ -447,8 +475,12 @@ function socketHandle({ io }) {
447
475
  platform,
448
476
  extraDeviceInfo,
449
477
  envVariables,
450
- connectionNonce
478
+ connectionNonce,
479
+ instanceId
451
480
  } = socket.handshake.query;
481
+ if (typeof instanceId === "string" && instanceId) {
482
+ instanceIdMap.set(socket.id, instanceId);
483
+ }
452
484
  console.log(
453
485
  `${LOG_PREFIX} New connection - ID: ${socket.id}, Name: ${deviceName || "Unknown Device"}${deviceId ? `, DeviceId: ${deviceId}` : ""}${platform ? `, Platform: ${platform}` : ""}${extraDeviceInfo ? `, ExtraDeviceInfo: ${extraDeviceInfo}` : ""}${envVariables ? `, EnvVariables: ${envVariables}` : ""}`
454
486
  );
package/dist/index.mjs CHANGED
@@ -95,6 +95,7 @@ var allDevices = [];
95
95
  var dashboardClients = [];
96
96
  var deviceIdMap = /* @__PURE__ */ new Map();
97
97
  var activeConnectionsMap = /* @__PURE__ */ new Map();
98
+ var instanceIdMap = /* @__PURE__ */ new Map();
98
99
  var deviceCapabilities = /* @__PURE__ */ new Map();
99
100
  var watches = new WatchRegistry();
100
101
  var brokerLogBuffer = [];
@@ -111,8 +112,34 @@ function socketHandle({ io }) {
111
112
  }
112
113
  forwardToDashboards("broker-log", entry);
113
114
  }
115
+ function liveInstances(deviceId) {
116
+ const instances = /* @__PURE__ */ new Set();
117
+ for (const [socketId, mappedDeviceId] of deviceIdMap) {
118
+ if (mappedDeviceId !== deviceId) continue;
119
+ const instance = instanceIdMap.get(socketId);
120
+ if (instance) instances.add(instance);
121
+ }
122
+ return instances.size;
123
+ }
114
124
  function emitAllDevices() {
115
125
  const now = Date.now();
126
+ for (const device of allDevices) {
127
+ if (!device.deviceId) continue;
128
+ const instances = liveInstances(device.deviceId);
129
+ const collision = instances > 1;
130
+ if (collision && !device.collision) {
131
+ brokerLog(
132
+ "warn",
133
+ `${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.`,
134
+ { deviceId: device.deviceId, instances }
135
+ );
136
+ } else if (!collision && device.collision) {
137
+ brokerLog("info", `Device id collision cleared for "${device.deviceId}"`, {
138
+ deviceId: device.deviceId
139
+ });
140
+ }
141
+ device.collision = collision;
142
+ }
116
143
  for (let i = allDevices.length - 1; i >= 0; i--) {
117
144
  const device = allDevices[i];
118
145
  if (!device.isConnected && device.lastSeenAt && now - device.lastSeenAt > MAX_OFFLINE_AGE_MS) {
@@ -131,6 +158,7 @@ function socketHandle({ io }) {
131
158
  const user = users.find((user2) => user2.id === id);
132
159
  const deviceId = deviceIdMap.get(id);
133
160
  deviceIdMap.delete(id);
161
+ instanceIdMap.delete(id);
134
162
  let survivor;
135
163
  if (deviceId) {
136
164
  for (const [socketId, mappedDeviceId] of deviceIdMap) {
@@ -407,8 +435,12 @@ function socketHandle({ io }) {
407
435
  platform,
408
436
  extraDeviceInfo,
409
437
  envVariables,
410
- connectionNonce
438
+ connectionNonce,
439
+ instanceId
411
440
  } = socket.handshake.query;
441
+ if (typeof instanceId === "string" && instanceId) {
442
+ instanceIdMap.set(socket.id, instanceId);
443
+ }
412
444
  console.log(
413
445
  `${LOG_PREFIX} New connection - ID: ${socket.id}, Name: ${deviceName || "Unknown Device"}${deviceId ? `, DeviceId: ${deviceId}` : ""}${platform ? `, Platform: ${platform}` : ""}${extraDeviceInfo ? `, ExtraDeviceInfo: ${extraDeviceInfo}` : ""}${envVariables ? `, EnvVariables: ${envVariables}` : ""}`
414
446
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buoy-gg/sync-broker",
3
- "version": "7.0.32",
3
+ "version": "7.0.34",
4
4
  "description": "Headless Socket.IO broker for the Buoy devtool sync protocol (v1) — routes messages between devices and dashboard/MCP clients",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",