patchcord 0.5.3 → 0.5.4
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/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/lib/ws.mjs +11 -3
- package/scripts/subscribe.mjs +33 -10
package/package.json
CHANGED
package/scripts/lib/ws.mjs
CHANGED
|
@@ -68,7 +68,7 @@ export function connect(urlStr, { headers = {} } = {}) {
|
|
|
68
68
|
socket.on("close", () => {
|
|
69
69
|
if (!closed) {
|
|
70
70
|
closed = true;
|
|
71
|
-
emitter.emit("close");
|
|
71
|
+
emitter.emit("close", { code: null, reason: "socket-ended" });
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
|
|
@@ -115,8 +115,16 @@ export function connect(urlStr, { headers = {} } = {}) {
|
|
|
115
115
|
if (opcode === 0x1) {
|
|
116
116
|
emitter.emit("message", payload.toString("utf8"));
|
|
117
117
|
} else if (opcode === 0x8) {
|
|
118
|
-
// close frame
|
|
119
|
-
|
|
118
|
+
// close frame — parse code/reason for diagnostics
|
|
119
|
+
let code = null;
|
|
120
|
+
let reason = "";
|
|
121
|
+
if (payload.length >= 2) {
|
|
122
|
+
code = payload.readUInt16BE(0);
|
|
123
|
+
if (payload.length > 2) {
|
|
124
|
+
reason = payload.slice(2).toString("utf8");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
emitter.emit("close", { code, reason });
|
|
120
128
|
closed = true;
|
|
121
129
|
try {
|
|
122
130
|
socket.write(encodeFrame(0x8, closePayload(1000, ""), true));
|
package/scripts/subscribe.mjs
CHANGED
|
@@ -246,14 +246,29 @@ function runOnce(ticket, baseUrl, token, refreshTicket) {
|
|
|
246
246
|
} catch (_) {}
|
|
247
247
|
}, HEARTBEAT_INTERVAL_MS);
|
|
248
248
|
|
|
249
|
-
const
|
|
250
|
-
(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
249
|
+
const scheduleRefresh = (ttlSec) => {
|
|
250
|
+
const refreshIn = Math.max((ttlSec - JWT_REFRESH_SAFETY_MARGIN_SEC) * 1000, 30_000);
|
|
251
|
+
refreshTimer = setTimeout(doRefresh, refreshIn);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const doRefresh = async () => {
|
|
255
|
+
if (settled) return;
|
|
254
256
|
try {
|
|
255
257
|
const fresh = await refreshTicket();
|
|
256
258
|
currentJwt = fresh.jwt;
|
|
259
|
+
// Socket-level auth update (phoenix topic) — what Supabase
|
|
260
|
+
// actually uses for the connection's own JWT expiry check.
|
|
261
|
+
// Without this, the server closes the socket at the original
|
|
262
|
+
// JWT's exp regardless of per-channel updates.
|
|
263
|
+
ws.send(
|
|
264
|
+
JSON.stringify({
|
|
265
|
+
topic: "phoenix",
|
|
266
|
+
event: "access_token",
|
|
267
|
+
payload: { access_token: currentJwt },
|
|
268
|
+
ref: String(ref++),
|
|
269
|
+
})
|
|
270
|
+
);
|
|
271
|
+
// Channel-level updates — matches supabase-js's setAuth() pattern.
|
|
257
272
|
for (const topic of fresh.topics) {
|
|
258
273
|
ws.send(
|
|
259
274
|
JSON.stringify({
|
|
@@ -265,11 +280,17 @@ function runOnce(ticket, baseUrl, token, refreshTicket) {
|
|
|
265
280
|
);
|
|
266
281
|
}
|
|
267
282
|
process.stderr.write("subscribe: token refreshed\n");
|
|
283
|
+
scheduleRefresh(fresh.jwt_expires_in);
|
|
268
284
|
} catch (e) {
|
|
269
|
-
|
|
270
|
-
|
|
285
|
+
// Transient network/server error — do NOT close the live
|
|
286
|
+
// connection. The current JWT is still valid for ~2 more min
|
|
287
|
+
// (JWT_REFRESH_SAFETY_MARGIN_SEC). Retry sooner.
|
|
288
|
+
process.stderr.write(`subscribe: token refresh failed, retrying in 30s: ${e.message}\n`);
|
|
289
|
+
refreshTimer = setTimeout(doRefresh, 30_000);
|
|
271
290
|
}
|
|
272
|
-
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
scheduleRefresh(ticket.jwt_expires_in);
|
|
273
294
|
});
|
|
274
295
|
|
|
275
296
|
ws.on("message", (raw) => {
|
|
@@ -297,8 +318,10 @@ function runOnce(ticket, baseUrl, token, refreshTicket) {
|
|
|
297
318
|
done(err);
|
|
298
319
|
});
|
|
299
320
|
|
|
300
|
-
ws.on("close", () => {
|
|
301
|
-
|
|
321
|
+
ws.on("close", (info) => {
|
|
322
|
+
const codeStr = info?.code != null ? `code=${info.code}` : "code=none";
|
|
323
|
+
const reasonStr = info?.reason ? ` reason=${JSON.stringify(info.reason)}` : "";
|
|
324
|
+
process.stderr.write(`subscribe: ws closed (${codeStr}${reasonStr})\n`);
|
|
302
325
|
done();
|
|
303
326
|
});
|
|
304
327
|
});
|