@surelle-ha/dead-fuse 1.0.5 → 1.0.9
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 -2
- package/dist/index.mjs +58 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
29
29
|
var import_supabase_js = require("@supabase/supabase-js");
|
|
30
30
|
|
|
31
31
|
// src/constants.ts
|
|
32
|
-
var DEFAULT_MASTER = "https://dead-fuse
|
|
32
|
+
var DEFAULT_MASTER = "https://dead-fuse.surelle.xyz";
|
|
33
33
|
|
|
34
34
|
// src/stateManager.ts
|
|
35
35
|
var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
@@ -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,48 @@ 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 res = await fetch(
|
|
254
|
+
`${masterUrl.replace(/\/$/, "")}/api/project/${encodeURIComponent(
|
|
255
|
+
this.config.projectId
|
|
256
|
+
)}/clients/connect`,
|
|
257
|
+
{
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers: { "Content-Type": "application/json" },
|
|
260
|
+
body: JSON.stringify({ token: this.config.token, clientId: this.clientId })
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
264
|
+
if (!this.heartbeatTimer) {
|
|
265
|
+
this.heartbeatTimer = setInterval(() => {
|
|
266
|
+
if (this.destroyed) return;
|
|
267
|
+
void this._registerClient();
|
|
268
|
+
}, CLIENT_HEARTBEAT_INTERVAL);
|
|
269
|
+
}
|
|
270
|
+
} catch (err) {
|
|
271
|
+
console.warn("[DeadFuse] Could not register client with dashboard:", err);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async _unregisterClient() {
|
|
275
|
+
const masterUrl = this.config.master ?? DEFAULT_MASTER;
|
|
276
|
+
if (!masterUrl || !this.config.token) return;
|
|
277
|
+
try {
|
|
278
|
+
await fetch(
|
|
279
|
+
`${masterUrl.replace(/\/$/, "")}/api/project/${encodeURIComponent(
|
|
280
|
+
this.config.projectId
|
|
281
|
+
)}/clients/disconnect`,
|
|
282
|
+
{
|
|
283
|
+
method: "POST",
|
|
284
|
+
headers: { "Content-Type": "application/json" },
|
|
285
|
+
body: JSON.stringify({ token: this.config.token, clientId: this.clientId })
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
} catch {
|
|
289
|
+
}
|
|
290
|
+
}
|
|
241
291
|
// ── Realtime subscription ──────────────────────────────────────────────────
|
|
242
292
|
_subscribe() {
|
|
243
293
|
if (!this.supabase) return;
|
|
@@ -254,6 +304,7 @@ var DeadFuseConnection = class {
|
|
|
254
304
|
if (this.reconnectAttempts > 0) this.config.onReconnect?.();
|
|
255
305
|
this.reconnectAttempts = 0;
|
|
256
306
|
this.channel?.track({ projectId: this.config.projectId, ts: Date.now() });
|
|
307
|
+
void this._registerClient();
|
|
257
308
|
}
|
|
258
309
|
if (status === "CHANNEL_ERROR" || status === "TIMED_OUT") {
|
|
259
310
|
console.warn(`[DeadFuse] Realtime channel error: ${status}`);
|
|
@@ -346,6 +397,11 @@ var DeadFuseConnection = class {
|
|
|
346
397
|
clearTimeout(this.reconnectTimer);
|
|
347
398
|
this.reconnectTimer = null;
|
|
348
399
|
}
|
|
400
|
+
if (this.heartbeatTimer) {
|
|
401
|
+
clearInterval(this.heartbeatTimer);
|
|
402
|
+
this.heartbeatTimer = null;
|
|
403
|
+
}
|
|
404
|
+
void this._unregisterClient();
|
|
349
405
|
if (this.channel && this.supabase) {
|
|
350
406
|
this.supabase.removeChannel(this.channel);
|
|
351
407
|
this.channel = null;
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createClient } from "@supabase/supabase-js";
|
|
3
3
|
|
|
4
4
|
// src/constants.ts
|
|
5
|
-
var DEFAULT_MASTER = "https://dead-fuse
|
|
5
|
+
var DEFAULT_MASTER = "https://dead-fuse.surelle.xyz";
|
|
6
6
|
|
|
7
7
|
// src/stateManager.ts
|
|
8
8
|
var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
@@ -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,48 @@ 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 res = await fetch(
|
|
227
|
+
`${masterUrl.replace(/\/$/, "")}/api/project/${encodeURIComponent(
|
|
228
|
+
this.config.projectId
|
|
229
|
+
)}/clients/connect`,
|
|
230
|
+
{
|
|
231
|
+
method: "POST",
|
|
232
|
+
headers: { "Content-Type": "application/json" },
|
|
233
|
+
body: JSON.stringify({ token: this.config.token, clientId: this.clientId })
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
237
|
+
if (!this.heartbeatTimer) {
|
|
238
|
+
this.heartbeatTimer = setInterval(() => {
|
|
239
|
+
if (this.destroyed) return;
|
|
240
|
+
void this._registerClient();
|
|
241
|
+
}, CLIENT_HEARTBEAT_INTERVAL);
|
|
242
|
+
}
|
|
243
|
+
} catch (err) {
|
|
244
|
+
console.warn("[DeadFuse] Could not register client with dashboard:", err);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async _unregisterClient() {
|
|
248
|
+
const masterUrl = this.config.master ?? DEFAULT_MASTER;
|
|
249
|
+
if (!masterUrl || !this.config.token) return;
|
|
250
|
+
try {
|
|
251
|
+
await fetch(
|
|
252
|
+
`${masterUrl.replace(/\/$/, "")}/api/project/${encodeURIComponent(
|
|
253
|
+
this.config.projectId
|
|
254
|
+
)}/clients/disconnect`,
|
|
255
|
+
{
|
|
256
|
+
method: "POST",
|
|
257
|
+
headers: { "Content-Type": "application/json" },
|
|
258
|
+
body: JSON.stringify({ token: this.config.token, clientId: this.clientId })
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
} catch {
|
|
262
|
+
}
|
|
263
|
+
}
|
|
214
264
|
// ── Realtime subscription ──────────────────────────────────────────────────
|
|
215
265
|
_subscribe() {
|
|
216
266
|
if (!this.supabase) return;
|
|
@@ -227,6 +277,7 @@ var DeadFuseConnection = class {
|
|
|
227
277
|
if (this.reconnectAttempts > 0) this.config.onReconnect?.();
|
|
228
278
|
this.reconnectAttempts = 0;
|
|
229
279
|
this.channel?.track({ projectId: this.config.projectId, ts: Date.now() });
|
|
280
|
+
void this._registerClient();
|
|
230
281
|
}
|
|
231
282
|
if (status === "CHANNEL_ERROR" || status === "TIMED_OUT") {
|
|
232
283
|
console.warn(`[DeadFuse] Realtime channel error: ${status}`);
|
|
@@ -319,6 +370,11 @@ var DeadFuseConnection = class {
|
|
|
319
370
|
clearTimeout(this.reconnectTimer);
|
|
320
371
|
this.reconnectTimer = null;
|
|
321
372
|
}
|
|
373
|
+
if (this.heartbeatTimer) {
|
|
374
|
+
clearInterval(this.heartbeatTimer);
|
|
375
|
+
this.heartbeatTimer = null;
|
|
376
|
+
}
|
|
377
|
+
void this._unregisterClient();
|
|
322
378
|
if (this.channel && this.supabase) {
|
|
323
379
|
this.supabase.removeChannel(this.channel);
|
|
324
380
|
this.channel = null;
|