@aurabase/realtime 0.6.1 → 0.7.0
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.cjs +30 -8
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +30 -8
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -309,6 +309,11 @@ var AuraChannel = class {
|
|
|
309
309
|
* Retourne une fonction de désabonnement direct `() => void` qui proxy également l'instance du canal pour le chaînage fluide.
|
|
310
310
|
*/
|
|
311
311
|
on(type, filter, callback) {
|
|
312
|
+
if (type !== "postgres_changes" && type !== "broadcast" && type !== "presence") {
|
|
313
|
+
throw new TypeError(
|
|
314
|
+
`[aurabase] channel.on(): unknown listen type "${String(type)}". For table changes use .on('postgres_changes', { event: 'INSERT', schema: 'public', table: '<table>' }, callback); other types: 'broadcast', 'presence'.`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
312
317
|
const id = ++this._listenerIdCounter;
|
|
313
318
|
this._listeners.push({
|
|
314
319
|
id,
|
|
@@ -892,6 +897,9 @@ var RealtimeConnectionError = class _RealtimeConnectionError extends Error {
|
|
|
892
897
|
};
|
|
893
898
|
|
|
894
899
|
// src/HeartbeatManager.ts
|
|
900
|
+
function isUnrefable(value) {
|
|
901
|
+
return typeof value === "object" && value !== null && "unref" in value && typeof value.unref === "function";
|
|
902
|
+
}
|
|
895
903
|
var HeartbeatManager = class _HeartbeatManager {
|
|
896
904
|
_worker = null;
|
|
897
905
|
_workerUrl = null;
|
|
@@ -962,6 +970,9 @@ var HeartbeatManager = class _HeartbeatManager {
|
|
|
962
970
|
this._interval = setInterval(() => {
|
|
963
971
|
this._sendFn?.();
|
|
964
972
|
}, intervalMs);
|
|
973
|
+
if (isUnrefable(this._interval)) {
|
|
974
|
+
this._interval.unref();
|
|
975
|
+
}
|
|
965
976
|
}
|
|
966
977
|
_clearWatchdog() {
|
|
967
978
|
if (this._watchdog) {
|
|
@@ -1044,6 +1055,9 @@ function getTokenProjectId(token) {
|
|
|
1044
1055
|
var HEARTBEAT_INTERVAL_MS = 25e3;
|
|
1045
1056
|
var MAX_TIMEOUT_MS = 2147483647;
|
|
1046
1057
|
var LIVENESS_GUARD_INTERVAL_MS = 3e4;
|
|
1058
|
+
function isUnrefable2(value) {
|
|
1059
|
+
return typeof value === "object" && value !== null && "unref" in value && typeof value.unref === "function";
|
|
1060
|
+
}
|
|
1047
1061
|
var CANAL_QUALIFIE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:/;
|
|
1048
1062
|
var LIVENESS_MAX_SILENCE_MS = 9e4;
|
|
1049
1063
|
var RealtimeService = class _RealtimeService {
|
|
@@ -1243,11 +1257,11 @@ var RealtimeService = class _RealtimeService {
|
|
|
1243
1257
|
this._emitError(
|
|
1244
1258
|
opened ? new RealtimeConnectionError(
|
|
1245
1259
|
"connection",
|
|
1246
|
-
"Realtime
|
|
1260
|
+
"Realtime: WebSocket connection closed unexpectedly.",
|
|
1247
1261
|
{ code: evt?.code, reason: evt?.reason }
|
|
1248
1262
|
) : new RealtimeConnectionError(
|
|
1249
1263
|
"handshake",
|
|
1250
|
-
"Realtime
|
|
1264
|
+
"Realtime: WebSocket handshake failed (closed before opening). Possible causes, in order of likelihood: authentication refused (HTTP 401/403: JWT missing, expired or invalid, apikey rejected), connection quota reached (HTTP 429), or server unreachable / restarting / blocked by a proxy or TLS. The WebSocket specification does not expose the handshake HTTP status to the client: the SDK cannot tell these causes apart.",
|
|
1251
1265
|
{ code: evt?.code ?? 1006, reason: evt?.reason }
|
|
1252
1266
|
)
|
|
1253
1267
|
);
|
|
@@ -1295,9 +1309,12 @@ var RealtimeService = class _RealtimeService {
|
|
|
1295
1309
|
const silence = Date.now() - this._lastServerMessageAt;
|
|
1296
1310
|
if (silence < LIVENESS_MAX_SILENCE_MS) return;
|
|
1297
1311
|
this._handleDeadConnection(
|
|
1298
|
-
`Realtime
|
|
1312
|
+
`Realtime: no message from the server for ${Math.round(silence / 1e3)} s although it pushes one every 30 s: the connection is considered dead. Reconnecting.`
|
|
1299
1313
|
);
|
|
1300
1314
|
}, LIVENESS_GUARD_INTERVAL_MS);
|
|
1315
|
+
if (isUnrefable2(this._livenessGuard)) {
|
|
1316
|
+
this._livenessGuard.unref();
|
|
1317
|
+
}
|
|
1301
1318
|
}
|
|
1302
1319
|
_stopLivenessGuard() {
|
|
1303
1320
|
if (this._livenessGuard) {
|
|
@@ -1347,13 +1364,13 @@ var RealtimeService = class _RealtimeService {
|
|
|
1347
1364
|
if (ws !== this._ws) return;
|
|
1348
1365
|
if (ws.readyState !== WebSocket.OPEN) {
|
|
1349
1366
|
this._handleDeadConnection(
|
|
1350
|
-
"Realtime
|
|
1367
|
+
"Realtime: the WebSocket connection is no longer open but no close event arrived (stuck socket). Reconnecting."
|
|
1351
1368
|
);
|
|
1352
1369
|
return;
|
|
1353
1370
|
}
|
|
1354
1371
|
if (this._awaitingPong) {
|
|
1355
1372
|
this._handleDeadConnection(
|
|
1356
|
-
`Realtime
|
|
1373
|
+
`Realtime: idle connection, no server response within ${HEARTBEAT_INTERVAL_MS} ms after a ping (connection probably dead). Reconnecting.`
|
|
1357
1374
|
);
|
|
1358
1375
|
return;
|
|
1359
1376
|
}
|
|
@@ -1507,7 +1524,7 @@ var RealtimeService = class _RealtimeService {
|
|
|
1507
1524
|
error: new core.AuraError(0, {
|
|
1508
1525
|
error: {
|
|
1509
1526
|
code: "realtime_payload_event_reserved",
|
|
1510
|
-
message: "httpBroadcast:
|
|
1527
|
+
message: "httpBroadcast: the payload contains an `event` key, which is reserved for the event name (it would overwrite it for subscribers). Rename it (`eventName`, `kind`\u2026)."
|
|
1511
1528
|
}
|
|
1512
1529
|
})
|
|
1513
1530
|
});
|
|
@@ -1528,7 +1545,7 @@ var RealtimeService = class _RealtimeService {
|
|
|
1528
1545
|
error: new core.AuraError(0, {
|
|
1529
1546
|
error: {
|
|
1530
1547
|
code: "realtime_channel_non_qualifie",
|
|
1531
|
-
message: `httpBroadcast:
|
|
1548
|
+
message: `httpBroadcast: the channel "${channel}" is not qualified and no session is open, so the SDK has no \`project_id\` to prefix it with, while the server REQUIRES it (403 "The channel does not belong to this project"). Pass the channel as "<project_id>:` + channel + '" or specify the option { projectId: "<uuid>" }, which is the expected form server-side/edge, where there is no user session.'
|
|
1532
1549
|
}
|
|
1533
1550
|
})
|
|
1534
1551
|
});
|
|
@@ -1694,7 +1711,7 @@ var RealtimeService = class _RealtimeService {
|
|
|
1694
1711
|
if (msg.type === "error") {
|
|
1695
1712
|
if (typeof console !== "undefined") {
|
|
1696
1713
|
console.warn(
|
|
1697
|
-
"[aura-realtime]
|
|
1714
|
+
"[aura-realtime] Server error for a channel unknown to this client: ignored (no local channel concerned):",
|
|
1698
1715
|
{ channel: channelFull, message: msg.message }
|
|
1699
1716
|
);
|
|
1700
1717
|
}
|
|
@@ -1731,6 +1748,11 @@ var RealtimeService = class _RealtimeService {
|
|
|
1731
1748
|
for (const ch of this._channels.values()) {
|
|
1732
1749
|
ch._onMessage(msg);
|
|
1733
1750
|
}
|
|
1751
|
+
const text = msg.message ?? "";
|
|
1752
|
+
if (text.startsWith("SESSION_REVOKED") && !this._customAuthToken) {
|
|
1753
|
+
this._reconnectAttempts = this._maxReconnectAttempts;
|
|
1754
|
+
this.client.authStore.clear();
|
|
1755
|
+
}
|
|
1734
1756
|
}
|
|
1735
1757
|
}
|
|
1736
1758
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -354,7 +354,6 @@ declare class RealtimeConnectionError extends Error {
|
|
|
354
354
|
* Le chien de garde porte sur le `ready`, PAS sur le premier battement : mesuré
|
|
355
355
|
* en Chromium réel, le démarrage d'un Worker peut dépasser 1,5 intervalle quand
|
|
356
356
|
* l'intervalle est court, ce qui faisait abandonner à tort un Worker
|
|
357
|
-
* parfaitement sain — et perdre silencieusement l'immunité au throttling, la
|
|
358
357
|
* seule raison d'être de ce Worker.
|
|
359
358
|
*/
|
|
360
359
|
declare class HeartbeatManager {
|
package/dist/index.d.ts
CHANGED
|
@@ -354,7 +354,6 @@ declare class RealtimeConnectionError extends Error {
|
|
|
354
354
|
* Le chien de garde porte sur le `ready`, PAS sur le premier battement : mesuré
|
|
355
355
|
* en Chromium réel, le démarrage d'un Worker peut dépasser 1,5 intervalle quand
|
|
356
356
|
* l'intervalle est court, ce qui faisait abandonner à tort un Worker
|
|
357
|
-
* parfaitement sain — et perdre silencieusement l'immunité au throttling, la
|
|
358
357
|
* seule raison d'être de ce Worker.
|
|
359
358
|
*/
|
|
360
359
|
declare class HeartbeatManager {
|
package/dist/index.js
CHANGED
|
@@ -307,6 +307,11 @@ var AuraChannel = class {
|
|
|
307
307
|
* Retourne une fonction de désabonnement direct `() => void` qui proxy également l'instance du canal pour le chaînage fluide.
|
|
308
308
|
*/
|
|
309
309
|
on(type, filter, callback) {
|
|
310
|
+
if (type !== "postgres_changes" && type !== "broadcast" && type !== "presence") {
|
|
311
|
+
throw new TypeError(
|
|
312
|
+
`[aurabase] channel.on(): unknown listen type "${String(type)}". For table changes use .on('postgres_changes', { event: 'INSERT', schema: 'public', table: '<table>' }, callback); other types: 'broadcast', 'presence'.`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
310
315
|
const id = ++this._listenerIdCounter;
|
|
311
316
|
this._listeners.push({
|
|
312
317
|
id,
|
|
@@ -890,6 +895,9 @@ var RealtimeConnectionError = class _RealtimeConnectionError extends Error {
|
|
|
890
895
|
};
|
|
891
896
|
|
|
892
897
|
// src/HeartbeatManager.ts
|
|
898
|
+
function isUnrefable(value) {
|
|
899
|
+
return typeof value === "object" && value !== null && "unref" in value && typeof value.unref === "function";
|
|
900
|
+
}
|
|
893
901
|
var HeartbeatManager = class _HeartbeatManager {
|
|
894
902
|
_worker = null;
|
|
895
903
|
_workerUrl = null;
|
|
@@ -960,6 +968,9 @@ var HeartbeatManager = class _HeartbeatManager {
|
|
|
960
968
|
this._interval = setInterval(() => {
|
|
961
969
|
this._sendFn?.();
|
|
962
970
|
}, intervalMs);
|
|
971
|
+
if (isUnrefable(this._interval)) {
|
|
972
|
+
this._interval.unref();
|
|
973
|
+
}
|
|
963
974
|
}
|
|
964
975
|
_clearWatchdog() {
|
|
965
976
|
if (this._watchdog) {
|
|
@@ -1042,6 +1053,9 @@ function getTokenProjectId(token) {
|
|
|
1042
1053
|
var HEARTBEAT_INTERVAL_MS = 25e3;
|
|
1043
1054
|
var MAX_TIMEOUT_MS = 2147483647;
|
|
1044
1055
|
var LIVENESS_GUARD_INTERVAL_MS = 3e4;
|
|
1056
|
+
function isUnrefable2(value) {
|
|
1057
|
+
return typeof value === "object" && value !== null && "unref" in value && typeof value.unref === "function";
|
|
1058
|
+
}
|
|
1045
1059
|
var CANAL_QUALIFIE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}:/;
|
|
1046
1060
|
var LIVENESS_MAX_SILENCE_MS = 9e4;
|
|
1047
1061
|
var RealtimeService = class _RealtimeService {
|
|
@@ -1241,11 +1255,11 @@ var RealtimeService = class _RealtimeService {
|
|
|
1241
1255
|
this._emitError(
|
|
1242
1256
|
opened ? new RealtimeConnectionError(
|
|
1243
1257
|
"connection",
|
|
1244
|
-
"Realtime
|
|
1258
|
+
"Realtime: WebSocket connection closed unexpectedly.",
|
|
1245
1259
|
{ code: evt?.code, reason: evt?.reason }
|
|
1246
1260
|
) : new RealtimeConnectionError(
|
|
1247
1261
|
"handshake",
|
|
1248
|
-
"Realtime
|
|
1262
|
+
"Realtime: WebSocket handshake failed (closed before opening). Possible causes, in order of likelihood: authentication refused (HTTP 401/403: JWT missing, expired or invalid, apikey rejected), connection quota reached (HTTP 429), or server unreachable / restarting / blocked by a proxy or TLS. The WebSocket specification does not expose the handshake HTTP status to the client: the SDK cannot tell these causes apart.",
|
|
1249
1263
|
{ code: evt?.code ?? 1006, reason: evt?.reason }
|
|
1250
1264
|
)
|
|
1251
1265
|
);
|
|
@@ -1293,9 +1307,12 @@ var RealtimeService = class _RealtimeService {
|
|
|
1293
1307
|
const silence = Date.now() - this._lastServerMessageAt;
|
|
1294
1308
|
if (silence < LIVENESS_MAX_SILENCE_MS) return;
|
|
1295
1309
|
this._handleDeadConnection(
|
|
1296
|
-
`Realtime
|
|
1310
|
+
`Realtime: no message from the server for ${Math.round(silence / 1e3)} s although it pushes one every 30 s: the connection is considered dead. Reconnecting.`
|
|
1297
1311
|
);
|
|
1298
1312
|
}, LIVENESS_GUARD_INTERVAL_MS);
|
|
1313
|
+
if (isUnrefable2(this._livenessGuard)) {
|
|
1314
|
+
this._livenessGuard.unref();
|
|
1315
|
+
}
|
|
1299
1316
|
}
|
|
1300
1317
|
_stopLivenessGuard() {
|
|
1301
1318
|
if (this._livenessGuard) {
|
|
@@ -1345,13 +1362,13 @@ var RealtimeService = class _RealtimeService {
|
|
|
1345
1362
|
if (ws !== this._ws) return;
|
|
1346
1363
|
if (ws.readyState !== WebSocket.OPEN) {
|
|
1347
1364
|
this._handleDeadConnection(
|
|
1348
|
-
"Realtime
|
|
1365
|
+
"Realtime: the WebSocket connection is no longer open but no close event arrived (stuck socket). Reconnecting."
|
|
1349
1366
|
);
|
|
1350
1367
|
return;
|
|
1351
1368
|
}
|
|
1352
1369
|
if (this._awaitingPong) {
|
|
1353
1370
|
this._handleDeadConnection(
|
|
1354
|
-
`Realtime
|
|
1371
|
+
`Realtime: idle connection, no server response within ${HEARTBEAT_INTERVAL_MS} ms after a ping (connection probably dead). Reconnecting.`
|
|
1355
1372
|
);
|
|
1356
1373
|
return;
|
|
1357
1374
|
}
|
|
@@ -1505,7 +1522,7 @@ var RealtimeService = class _RealtimeService {
|
|
|
1505
1522
|
error: new AuraError(0, {
|
|
1506
1523
|
error: {
|
|
1507
1524
|
code: "realtime_payload_event_reserved",
|
|
1508
|
-
message: "httpBroadcast:
|
|
1525
|
+
message: "httpBroadcast: the payload contains an `event` key, which is reserved for the event name (it would overwrite it for subscribers). Rename it (`eventName`, `kind`\u2026)."
|
|
1509
1526
|
}
|
|
1510
1527
|
})
|
|
1511
1528
|
});
|
|
@@ -1526,7 +1543,7 @@ var RealtimeService = class _RealtimeService {
|
|
|
1526
1543
|
error: new AuraError(0, {
|
|
1527
1544
|
error: {
|
|
1528
1545
|
code: "realtime_channel_non_qualifie",
|
|
1529
|
-
message: `httpBroadcast:
|
|
1546
|
+
message: `httpBroadcast: the channel "${channel}" is not qualified and no session is open, so the SDK has no \`project_id\` to prefix it with, while the server REQUIRES it (403 "The channel does not belong to this project"). Pass the channel as "<project_id>:` + channel + '" or specify the option { projectId: "<uuid>" }, which is the expected form server-side/edge, where there is no user session.'
|
|
1530
1547
|
}
|
|
1531
1548
|
})
|
|
1532
1549
|
});
|
|
@@ -1692,7 +1709,7 @@ var RealtimeService = class _RealtimeService {
|
|
|
1692
1709
|
if (msg.type === "error") {
|
|
1693
1710
|
if (typeof console !== "undefined") {
|
|
1694
1711
|
console.warn(
|
|
1695
|
-
"[aura-realtime]
|
|
1712
|
+
"[aura-realtime] Server error for a channel unknown to this client: ignored (no local channel concerned):",
|
|
1696
1713
|
{ channel: channelFull, message: msg.message }
|
|
1697
1714
|
);
|
|
1698
1715
|
}
|
|
@@ -1729,6 +1746,11 @@ var RealtimeService = class _RealtimeService {
|
|
|
1729
1746
|
for (const ch of this._channels.values()) {
|
|
1730
1747
|
ch._onMessage(msg);
|
|
1731
1748
|
}
|
|
1749
|
+
const text = msg.message ?? "";
|
|
1750
|
+
if (text.startsWith("SESSION_REVOKED") && !this._customAuthToken) {
|
|
1751
|
+
this._reconnectAttempts = this._maxReconnectAttempts;
|
|
1752
|
+
this.client.authStore.clear();
|
|
1753
|
+
}
|
|
1732
1754
|
}
|
|
1733
1755
|
}
|
|
1734
1756
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurabase/realtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Service temps réel du SDK Aurabase (postgres_changes, broadcast, presence)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"author": "Aurabase",
|
|
32
32
|
"repository": {
|
|
33
33
|
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/
|
|
34
|
+
"url": "git+https://github.com/Aurabase-cloud/Aurabase.git",
|
|
35
35
|
"directory": "aurabase-js/packages/realtime"
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://aurabase.cloud",
|
|
38
38
|
"bugs": {
|
|
39
|
-
"url": "https://github.com/
|
|
39
|
+
"url": "https://github.com/Aurabase-cloud/Aurabase/issues"
|
|
40
40
|
},
|
|
41
41
|
"keywords": [
|
|
42
42
|
"aurabase",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"realtime"
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@aurabase/core": "0.
|
|
48
|
+
"@aurabase/core": "0.7.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "20.14.15",
|