@surelle-ha/dead-fuse 1.0.6 → 1.0.10
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 +58 -1
- package/dist/index.mjs +58 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -186,13 +186,16 @@ function dispatchStateEvent(state, message, config) {
|
|
|
186
186
|
// src/connection.ts
|
|
187
187
|
var MAX_RECONNECT_ATTEMPTS = 10;
|
|
188
188
|
var BASE_RECONNECT_DELAY = 1e3;
|
|
189
|
+
var CLIENT_HEARTBEAT_INTERVAL = 25e3;
|
|
189
190
|
var DeadFuseConnection = class {
|
|
190
191
|
constructor(config) {
|
|
191
192
|
this.supabase = null;
|
|
192
193
|
this.channel = null;
|
|
193
194
|
this.reconnectAttempts = 0;
|
|
194
195
|
this.reconnectTimer = null;
|
|
196
|
+
this.heartbeatTimer = null;
|
|
195
197
|
this.destroyed = false;
|
|
198
|
+
this.clientId = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `df-client-${Math.random().toString(36).slice(2)}`;
|
|
196
199
|
this.config = config;
|
|
197
200
|
}
|
|
198
201
|
connect() {
|
|
@@ -225,7 +228,12 @@ var DeadFuseConnection = class {
|
|
|
225
228
|
supabaseUrl = json.supabaseUrl;
|
|
226
229
|
supabaseAnonKey = json.supabaseAnonKey;
|
|
227
230
|
} catch (err) {
|
|
228
|
-
console.warn("[DeadFuse] Could not fetch config from dashboard:", err);
|
|
231
|
+
console.warn("[DeadFuse] Could not fetch config from dashboard:", err, "master:", masterUrl);
|
|
232
|
+
if (masterUrl.startsWith("wss://")) {
|
|
233
|
+
console.warn(
|
|
234
|
+
"[DeadFuse] The `master` value appears to be a websocket URL. `master` must be the dashboard base URL like https://your-dashboard.example.com"
|
|
235
|
+
);
|
|
236
|
+
}
|
|
229
237
|
this._applyFallback();
|
|
230
238
|
this._scheduleReconnect();
|
|
231
239
|
return;
|
|
@@ -238,6 +246,49 @@ var DeadFuseConnection = class {
|
|
|
238
246
|
this._subscribe();
|
|
239
247
|
this._fetchInitialState();
|
|
240
248
|
}
|
|
249
|
+
async _registerClient() {
|
|
250
|
+
const masterUrl = this.config.master ?? DEFAULT_MASTER;
|
|
251
|
+
if (!masterUrl || !this.config.token) return;
|
|
252
|
+
try {
|
|
253
|
+
const clientHost = typeof window !== "undefined" ? window.location.origin : void 0;
|
|
254
|
+
const res = await fetch(
|
|
255
|
+
`${masterUrl.replace(/\/$/, "")}/api/project/${encodeURIComponent(
|
|
256
|
+
this.config.projectId
|
|
257
|
+
)}/clients/connect`,
|
|
258
|
+
{
|
|
259
|
+
method: "POST",
|
|
260
|
+
headers: { "Content-Type": "application/json" },
|
|
261
|
+
body: JSON.stringify({ token: this.config.token, clientId: this.clientId, host: clientHost })
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
265
|
+
if (!this.heartbeatTimer) {
|
|
266
|
+
this.heartbeatTimer = setInterval(() => {
|
|
267
|
+
if (this.destroyed) return;
|
|
268
|
+
void this._registerClient();
|
|
269
|
+
}, CLIENT_HEARTBEAT_INTERVAL);
|
|
270
|
+
}
|
|
271
|
+
} catch (err) {
|
|
272
|
+
console.warn("[DeadFuse] Could not register client with dashboard:", err);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async _unregisterClient() {
|
|
276
|
+
const masterUrl = this.config.master ?? DEFAULT_MASTER;
|
|
277
|
+
if (!masterUrl || !this.config.token) return;
|
|
278
|
+
try {
|
|
279
|
+
await fetch(
|
|
280
|
+
`${masterUrl.replace(/\/$/, "")}/api/project/${encodeURIComponent(
|
|
281
|
+
this.config.projectId
|
|
282
|
+
)}/clients/disconnect`,
|
|
283
|
+
{
|
|
284
|
+
method: "POST",
|
|
285
|
+
headers: { "Content-Type": "application/json" },
|
|
286
|
+
body: JSON.stringify({ token: this.config.token, clientId: this.clientId })
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
} catch {
|
|
290
|
+
}
|
|
291
|
+
}
|
|
241
292
|
// ── Realtime subscription ──────────────────────────────────────────────────
|
|
242
293
|
_subscribe() {
|
|
243
294
|
if (!this.supabase) return;
|
|
@@ -254,6 +305,7 @@ var DeadFuseConnection = class {
|
|
|
254
305
|
if (this.reconnectAttempts > 0) this.config.onReconnect?.();
|
|
255
306
|
this.reconnectAttempts = 0;
|
|
256
307
|
this.channel?.track({ projectId: this.config.projectId, ts: Date.now() });
|
|
308
|
+
void this._registerClient();
|
|
257
309
|
}
|
|
258
310
|
if (status === "CHANNEL_ERROR" || status === "TIMED_OUT") {
|
|
259
311
|
console.warn(`[DeadFuse] Realtime channel error: ${status}`);
|
|
@@ -346,6 +398,11 @@ var DeadFuseConnection = class {
|
|
|
346
398
|
clearTimeout(this.reconnectTimer);
|
|
347
399
|
this.reconnectTimer = null;
|
|
348
400
|
}
|
|
401
|
+
if (this.heartbeatTimer) {
|
|
402
|
+
clearInterval(this.heartbeatTimer);
|
|
403
|
+
this.heartbeatTimer = null;
|
|
404
|
+
}
|
|
405
|
+
void this._unregisterClient();
|
|
349
406
|
if (this.channel && this.supabase) {
|
|
350
407
|
this.supabase.removeChannel(this.channel);
|
|
351
408
|
this.channel = null;
|
package/dist/index.mjs
CHANGED
|
@@ -159,13 +159,16 @@ function dispatchStateEvent(state, message, config) {
|
|
|
159
159
|
// src/connection.ts
|
|
160
160
|
var MAX_RECONNECT_ATTEMPTS = 10;
|
|
161
161
|
var BASE_RECONNECT_DELAY = 1e3;
|
|
162
|
+
var CLIENT_HEARTBEAT_INTERVAL = 25e3;
|
|
162
163
|
var DeadFuseConnection = class {
|
|
163
164
|
constructor(config) {
|
|
164
165
|
this.supabase = null;
|
|
165
166
|
this.channel = null;
|
|
166
167
|
this.reconnectAttempts = 0;
|
|
167
168
|
this.reconnectTimer = null;
|
|
169
|
+
this.heartbeatTimer = null;
|
|
168
170
|
this.destroyed = false;
|
|
171
|
+
this.clientId = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `df-client-${Math.random().toString(36).slice(2)}`;
|
|
169
172
|
this.config = config;
|
|
170
173
|
}
|
|
171
174
|
connect() {
|
|
@@ -198,7 +201,12 @@ var DeadFuseConnection = class {
|
|
|
198
201
|
supabaseUrl = json.supabaseUrl;
|
|
199
202
|
supabaseAnonKey = json.supabaseAnonKey;
|
|
200
203
|
} catch (err) {
|
|
201
|
-
console.warn("[DeadFuse] Could not fetch config from dashboard:", err);
|
|
204
|
+
console.warn("[DeadFuse] Could not fetch config from dashboard:", err, "master:", masterUrl);
|
|
205
|
+
if (masterUrl.startsWith("wss://")) {
|
|
206
|
+
console.warn(
|
|
207
|
+
"[DeadFuse] The `master` value appears to be a websocket URL. `master` must be the dashboard base URL like https://your-dashboard.example.com"
|
|
208
|
+
);
|
|
209
|
+
}
|
|
202
210
|
this._applyFallback();
|
|
203
211
|
this._scheduleReconnect();
|
|
204
212
|
return;
|
|
@@ -211,6 +219,49 @@ var DeadFuseConnection = class {
|
|
|
211
219
|
this._subscribe();
|
|
212
220
|
this._fetchInitialState();
|
|
213
221
|
}
|
|
222
|
+
async _registerClient() {
|
|
223
|
+
const masterUrl = this.config.master ?? DEFAULT_MASTER;
|
|
224
|
+
if (!masterUrl || !this.config.token) return;
|
|
225
|
+
try {
|
|
226
|
+
const clientHost = typeof window !== "undefined" ? window.location.origin : void 0;
|
|
227
|
+
const res = await fetch(
|
|
228
|
+
`${masterUrl.replace(/\/$/, "")}/api/project/${encodeURIComponent(
|
|
229
|
+
this.config.projectId
|
|
230
|
+
)}/clients/connect`,
|
|
231
|
+
{
|
|
232
|
+
method: "POST",
|
|
233
|
+
headers: { "Content-Type": "application/json" },
|
|
234
|
+
body: JSON.stringify({ token: this.config.token, clientId: this.clientId, host: clientHost })
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
238
|
+
if (!this.heartbeatTimer) {
|
|
239
|
+
this.heartbeatTimer = setInterval(() => {
|
|
240
|
+
if (this.destroyed) return;
|
|
241
|
+
void this._registerClient();
|
|
242
|
+
}, CLIENT_HEARTBEAT_INTERVAL);
|
|
243
|
+
}
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.warn("[DeadFuse] Could not register client with dashboard:", err);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async _unregisterClient() {
|
|
249
|
+
const masterUrl = this.config.master ?? DEFAULT_MASTER;
|
|
250
|
+
if (!masterUrl || !this.config.token) return;
|
|
251
|
+
try {
|
|
252
|
+
await fetch(
|
|
253
|
+
`${masterUrl.replace(/\/$/, "")}/api/project/${encodeURIComponent(
|
|
254
|
+
this.config.projectId
|
|
255
|
+
)}/clients/disconnect`,
|
|
256
|
+
{
|
|
257
|
+
method: "POST",
|
|
258
|
+
headers: { "Content-Type": "application/json" },
|
|
259
|
+
body: JSON.stringify({ token: this.config.token, clientId: this.clientId })
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
} catch {
|
|
263
|
+
}
|
|
264
|
+
}
|
|
214
265
|
// ── Realtime subscription ──────────────────────────────────────────────────
|
|
215
266
|
_subscribe() {
|
|
216
267
|
if (!this.supabase) return;
|
|
@@ -227,6 +278,7 @@ var DeadFuseConnection = class {
|
|
|
227
278
|
if (this.reconnectAttempts > 0) this.config.onReconnect?.();
|
|
228
279
|
this.reconnectAttempts = 0;
|
|
229
280
|
this.channel?.track({ projectId: this.config.projectId, ts: Date.now() });
|
|
281
|
+
void this._registerClient();
|
|
230
282
|
}
|
|
231
283
|
if (status === "CHANNEL_ERROR" || status === "TIMED_OUT") {
|
|
232
284
|
console.warn(`[DeadFuse] Realtime channel error: ${status}`);
|
|
@@ -319,6 +371,11 @@ var DeadFuseConnection = class {
|
|
|
319
371
|
clearTimeout(this.reconnectTimer);
|
|
320
372
|
this.reconnectTimer = null;
|
|
321
373
|
}
|
|
374
|
+
if (this.heartbeatTimer) {
|
|
375
|
+
clearInterval(this.heartbeatTimer);
|
|
376
|
+
this.heartbeatTimer = null;
|
|
377
|
+
}
|
|
378
|
+
void this._unregisterClient();
|
|
322
379
|
if (this.channel && this.supabase) {
|
|
323
380
|
this.supabase.removeChannel(this.channel);
|
|
324
381
|
this.channel = null;
|