nuxt4, and some changes on packages
This commit is contained in:
+83
-31
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
@@ -39,21 +49,21 @@ var REALTIME_POSTGRES_CHANGES_LISTEN_EVENT;
|
||||
REALTIME_POSTGRES_CHANGES_LISTEN_EVENT["INSERT"] = "INSERT";
|
||||
REALTIME_POSTGRES_CHANGES_LISTEN_EVENT["UPDATE"] = "UPDATE";
|
||||
REALTIME_POSTGRES_CHANGES_LISTEN_EVENT["DELETE"] = "DELETE";
|
||||
})(REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = exports.REALTIME_POSTGRES_CHANGES_LISTEN_EVENT || (exports.REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = {}));
|
||||
})(REALTIME_POSTGRES_CHANGES_LISTEN_EVENT || (exports.REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = {}));
|
||||
var REALTIME_LISTEN_TYPES;
|
||||
(function (REALTIME_LISTEN_TYPES) {
|
||||
REALTIME_LISTEN_TYPES["BROADCAST"] = "broadcast";
|
||||
REALTIME_LISTEN_TYPES["PRESENCE"] = "presence";
|
||||
REALTIME_LISTEN_TYPES["POSTGRES_CHANGES"] = "postgres_changes";
|
||||
REALTIME_LISTEN_TYPES["SYSTEM"] = "system";
|
||||
})(REALTIME_LISTEN_TYPES = exports.REALTIME_LISTEN_TYPES || (exports.REALTIME_LISTEN_TYPES = {}));
|
||||
})(REALTIME_LISTEN_TYPES || (exports.REALTIME_LISTEN_TYPES = REALTIME_LISTEN_TYPES = {}));
|
||||
var REALTIME_SUBSCRIBE_STATES;
|
||||
(function (REALTIME_SUBSCRIBE_STATES) {
|
||||
REALTIME_SUBSCRIBE_STATES["SUBSCRIBED"] = "SUBSCRIBED";
|
||||
REALTIME_SUBSCRIBE_STATES["TIMED_OUT"] = "TIMED_OUT";
|
||||
REALTIME_SUBSCRIBE_STATES["CLOSED"] = "CLOSED";
|
||||
REALTIME_SUBSCRIBE_STATES["CHANNEL_ERROR"] = "CHANNEL_ERROR";
|
||||
})(REALTIME_SUBSCRIBE_STATES = exports.REALTIME_SUBSCRIBE_STATES || (exports.REALTIME_SUBSCRIBE_STATES = {}));
|
||||
})(REALTIME_SUBSCRIBE_STATES || (exports.REALTIME_SUBSCRIBE_STATES = REALTIME_SUBSCRIBE_STATES = {}));
|
||||
exports.REALTIME_CHANNEL_STATES = constants_1.CHANNEL_STATES;
|
||||
/** A channel is the basic building block of Realtime
|
||||
* and narrows the scope of data flow to subscribed clients.
|
||||
@@ -74,7 +84,7 @@ class RealtimeChannel {
|
||||
this.subTopic = topic.replace(/^realtime:/i, '');
|
||||
this.params.config = Object.assign({
|
||||
broadcast: { ack: false, self: false },
|
||||
presence: { key: '' },
|
||||
presence: { key: '', enabled: false },
|
||||
private: false,
|
||||
}, params.config);
|
||||
this.timeout = this.socket.timeout;
|
||||
@@ -108,12 +118,19 @@ class RealtimeChannel {
|
||||
this.state = constants_1.CHANNEL_STATES.errored;
|
||||
this.rejoinTimer.scheduleTimeout();
|
||||
});
|
||||
this.joinPush.receive('error', (reason) => {
|
||||
if (this._isLeaving() || this._isClosed()) {
|
||||
return;
|
||||
}
|
||||
this.socket.log('channel', `error ${this.topic}`, reason);
|
||||
this.state = constants_1.CHANNEL_STATES.errored;
|
||||
this.rejoinTimer.scheduleTimeout();
|
||||
});
|
||||
this._on(constants_1.CHANNEL_EVENTS.reply, {}, (payload, ref) => {
|
||||
this._trigger(this._replyEventName(ref), payload);
|
||||
});
|
||||
this.presence = new RealtimePresence_1.default(this);
|
||||
this.broadcastEndpointURL =
|
||||
(0, transformers_1.httpEndpointURL)(this.socket.endPoint) + '/api/broadcast';
|
||||
this.broadcastEndpointURL = (0, transformers_1.httpEndpointURL)(this.socket.endPoint);
|
||||
this.private = this.params.config.private || false;
|
||||
}
|
||||
/** Subscribe registers your client with the server */
|
||||
@@ -122,23 +139,23 @@ class RealtimeChannel {
|
||||
if (!this.socket.isConnected()) {
|
||||
this.socket.connect();
|
||||
}
|
||||
if (this.joinedOnce) {
|
||||
throw `tried to subscribe multiple times. 'subscribe' can only be called a single time per channel instance`;
|
||||
}
|
||||
else {
|
||||
if (this.state == constants_1.CHANNEL_STATES.closed) {
|
||||
const { config: { broadcast, presence, private: isPrivate }, } = this.params;
|
||||
this._onError((e) => callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, e));
|
||||
this._onClose(() => callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CLOSED));
|
||||
const postgres_changes = (_b = (_a = this.bindings.postgres_changes) === null || _a === void 0 ? void 0 : _a.map((r) => r.filter)) !== null && _b !== void 0 ? _b : [];
|
||||
const presence_enabled = !!this.bindings[REALTIME_LISTEN_TYPES.PRESENCE] &&
|
||||
this.bindings[REALTIME_LISTEN_TYPES.PRESENCE].length > 0;
|
||||
const accessTokenPayload = {};
|
||||
const config = {
|
||||
broadcast,
|
||||
presence,
|
||||
postgres_changes: (_b = (_a = this.bindings.postgres_changes) === null || _a === void 0 ? void 0 : _a.map((r) => r.filter)) !== null && _b !== void 0 ? _b : [],
|
||||
presence: Object.assign(Object.assign({}, presence), { enabled: presence_enabled }),
|
||||
postgres_changes,
|
||||
private: isPrivate,
|
||||
};
|
||||
if (this.socket.accessTokenValue) {
|
||||
accessTokenPayload.access_token = this.socket.accessTokenValue;
|
||||
}
|
||||
this._onError((e) => callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, e));
|
||||
this._onClose(() => callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CLOSED));
|
||||
this.updateJoinPayload(Object.assign({ config }, accessTokenPayload));
|
||||
this.joinedOnce = true;
|
||||
this._rejoin(timeout);
|
||||
@@ -167,6 +184,7 @@ class RealtimeChannel {
|
||||
}
|
||||
else {
|
||||
this.unsubscribe();
|
||||
this.state = constants_1.CHANNEL_STATES.errored;
|
||||
callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, new Error('mismatch between server and client bindings for postgres changes'));
|
||||
return;
|
||||
}
|
||||
@@ -177,6 +195,7 @@ class RealtimeChannel {
|
||||
}
|
||||
})
|
||||
.receive('error', (error) => {
|
||||
this.state = constants_1.CHANNEL_STATES.errored;
|
||||
callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, new Error(JSON.stringify(Object.values(error).join(', ') || 'error')));
|
||||
return;
|
||||
})
|
||||
@@ -204,6 +223,11 @@ class RealtimeChannel {
|
||||
}, opts);
|
||||
}
|
||||
on(type, filter, callback) {
|
||||
if (this.state === constants_1.CHANNEL_STATES.joined &&
|
||||
type === REALTIME_LISTEN_TYPES.PRESENCE) {
|
||||
this.socket.log('channel', `resubscribe to ${this.topic} due to change in presence callbacks on joined channel`);
|
||||
this.unsubscribe().then(() => this.subscribe());
|
||||
}
|
||||
return this._on(type, filter, callback);
|
||||
}
|
||||
/**
|
||||
@@ -285,11 +309,10 @@ class RealtimeChannel {
|
||||
this.socket.log('channel', `leave ${this.topic}`);
|
||||
this._trigger(constants_1.CHANNEL_EVENTS.close, 'leave', this._joinRef());
|
||||
};
|
||||
this.rejoinTimer.reset();
|
||||
// Destroy joinPush to avoid connection timeouts during unscription phase
|
||||
this.joinPush.destroy();
|
||||
let leavePush = null;
|
||||
return new Promise((resolve) => {
|
||||
const leavePush = new push_1.default(this, constants_1.CHANNEL_EVENTS.leave, {}, timeout);
|
||||
leavePush = new push_1.default(this, constants_1.CHANNEL_EVENTS.leave, {}, timeout);
|
||||
leavePush
|
||||
.receive('ok', () => {
|
||||
onClose();
|
||||
@@ -306,8 +329,23 @@ class RealtimeChannel {
|
||||
if (!this._canPush()) {
|
||||
leavePush.trigger('ok', {});
|
||||
}
|
||||
}).finally(() => {
|
||||
leavePush === null || leavePush === void 0 ? void 0 : leavePush.destroy();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Teardown the channel.
|
||||
*
|
||||
* Destroys and stops related timers.
|
||||
*/
|
||||
teardown() {
|
||||
this.pushBuffer.forEach((push) => push.destroy());
|
||||
this.pushBuffer = [];
|
||||
this.rejoinTimer.reset();
|
||||
this.joinPush.destroy();
|
||||
this.state = constants_1.CHANNEL_STATES.closed;
|
||||
this.bindings = {};
|
||||
}
|
||||
/** @internal */
|
||||
async _fetchWithTimeout(url, options, timeout) {
|
||||
const controller = new AbortController();
|
||||
@@ -326,11 +364,23 @@ class RealtimeChannel {
|
||||
pushEvent.send();
|
||||
}
|
||||
else {
|
||||
pushEvent.startTimeout();
|
||||
this.pushBuffer.push(pushEvent);
|
||||
this._addToPushBuffer(pushEvent);
|
||||
}
|
||||
return pushEvent;
|
||||
}
|
||||
/** @internal */
|
||||
_addToPushBuffer(pushEvent) {
|
||||
pushEvent.startTimeout();
|
||||
this.pushBuffer.push(pushEvent);
|
||||
// Enforce buffer size limit
|
||||
if (this.pushBuffer.length > constants_1.MAX_PUSH_BUFFER_SIZE) {
|
||||
const removedPush = this.pushBuffer.shift();
|
||||
if (removedPush) {
|
||||
removedPush.destroy();
|
||||
this.socket.log('channel', `discarded push due to buffer overflow: ${removedPush.event}`, removedPush.payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Overridable message hook
|
||||
*
|
||||
@@ -450,11 +500,13 @@ class RealtimeChannel {
|
||||
/** @internal */
|
||||
_off(type, filter) {
|
||||
const typeLower = type.toLocaleLowerCase();
|
||||
this.bindings[typeLower] = this.bindings[typeLower].filter((bind) => {
|
||||
var _a;
|
||||
return !(((_a = bind.type) === null || _a === void 0 ? void 0 : _a.toLocaleLowerCase()) === typeLower &&
|
||||
RealtimeChannel.isEqual(bind.filter, filter));
|
||||
});
|
||||
if (this.bindings[typeLower]) {
|
||||
this.bindings[typeLower] = this.bindings[typeLower].filter((bind) => {
|
||||
var _a;
|
||||
return !(((_a = bind.type) === null || _a === void 0 ? void 0 : _a.toLocaleLowerCase()) === typeLower &&
|
||||
RealtimeChannel.isEqual(bind.filter, filter));
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** @internal */
|
||||
|
||||
+411
-188
@@ -15,24 +15,42 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const websocket_factory_1 = __importDefault(require("./lib/websocket-factory"));
|
||||
const constants_1 = require("./lib/constants");
|
||||
const serializer_1 = __importDefault(require("./lib/serializer"));
|
||||
const timer_1 = __importDefault(require("./lib/timer"));
|
||||
const transformers_1 = require("./lib/transformers");
|
||||
const RealtimeChannel_1 = __importDefault(require("./RealtimeChannel"));
|
||||
const noop = () => { };
|
||||
const NATIVE_WEBSOCKET_AVAILABLE = typeof WebSocket !== 'undefined';
|
||||
// Connection-related constants
|
||||
const CONNECTION_TIMEOUTS = {
|
||||
HEARTBEAT_INTERVAL: 25000,
|
||||
RECONNECT_DELAY: 10,
|
||||
HEARTBEAT_TIMEOUT_FALLBACK: 100,
|
||||
};
|
||||
const RECONNECT_INTERVALS = [1000, 2000, 5000, 10000];
|
||||
const DEFAULT_RECONNECT_FALLBACK = 10000;
|
||||
const WORKER_SCRIPT = `
|
||||
addEventListener("message", (e) => {
|
||||
if (e.data.event === "start") {
|
||||
@@ -45,12 +63,13 @@ class RealtimeClient {
|
||||
*
|
||||
* @param endPoint The string WebSocket endpoint, ie, "ws://example.com/socket", "wss://example.com", "/socket" (inherited host & protocol)
|
||||
* @param httpEndpoint The string HTTP endpoint, ie, "https://example.com", "/" (inherited host & protocol)
|
||||
* @param options.transport The Websocket Transport, for example WebSocket.
|
||||
* @param options.transport The Websocket Transport, for example WebSocket. This can be a custom implementation
|
||||
* @param options.timeout The default timeout in milliseconds to trigger push timeouts.
|
||||
* @param options.params The optional params to pass when connecting.
|
||||
* @param options.headers The optional headers to pass when connecting.
|
||||
* @param options.headers Deprecated: headers cannot be set on websocket connections and this option will be removed in the future.
|
||||
* @param options.heartbeatIntervalMs The millisec interval to send a heartbeat message.
|
||||
* @param options.logger The optional function for specialized logging, ie: logger: (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }
|
||||
* @param options.logLevel Sets the log level for Realtime
|
||||
* @param options.encode The function to encode outgoing messages. Defaults to JSON: (payload, callback) => callback(JSON.stringify(payload))
|
||||
* @param options.decode The function to decode incoming messages. Defaults to Serializer's decode.
|
||||
* @param options.reconnectAfterMs he optional function that returns the millsec reconnect interval. Defaults to stepped backoff off.
|
||||
@@ -61,16 +80,20 @@ class RealtimeClient {
|
||||
var _a;
|
||||
this.accessTokenValue = null;
|
||||
this.apiKey = null;
|
||||
this.channels = [];
|
||||
this.channels = new Array();
|
||||
this.endPoint = '';
|
||||
this.httpEndpoint = '';
|
||||
this.headers = constants_1.DEFAULT_HEADERS;
|
||||
/** @deprecated headers cannot be set on websocket connections */
|
||||
this.headers = {};
|
||||
this.params = {};
|
||||
this.timeout = constants_1.DEFAULT_TIMEOUT;
|
||||
this.heartbeatIntervalMs = 30000;
|
||||
this.transport = null;
|
||||
this.heartbeatIntervalMs = CONNECTION_TIMEOUTS.HEARTBEAT_INTERVAL;
|
||||
this.heartbeatTimer = undefined;
|
||||
this.pendingHeartbeatRef = null;
|
||||
this.heartbeatCallback = noop;
|
||||
this.ref = 0;
|
||||
this.reconnectTimer = null;
|
||||
this.logger = noop;
|
||||
this.conn = null;
|
||||
this.sendBuffer = [];
|
||||
@@ -82,6 +105,9 @@ class RealtimeClient {
|
||||
message: [],
|
||||
};
|
||||
this.accessToken = null;
|
||||
this._connectionState = 'disconnected';
|
||||
this._wasManualDisconnect = false;
|
||||
this._authPromise = null;
|
||||
/**
|
||||
* Use either custom fetch, if provided, or default fetch to make HTTP requests
|
||||
*
|
||||
@@ -93,92 +119,72 @@ class RealtimeClient {
|
||||
_fetch = customFetch;
|
||||
}
|
||||
else if (typeof fetch === 'undefined') {
|
||||
_fetch = (...args) => Promise.resolve().then(() => __importStar(require('@supabase/node-fetch'))).then(({ default: fetch }) => fetch(...args));
|
||||
// Node.js environment without native fetch
|
||||
_fetch = (...args) => Promise.resolve(`${'@supabase/node-fetch'}`).then(s => __importStar(require(s))).then(({ default: fetch }) => fetch(...args))
|
||||
.catch((error) => {
|
||||
throw new Error(`Failed to load @supabase/node-fetch: ${error.message}. ` +
|
||||
`This is required for HTTP requests in Node.js environments without native fetch.`);
|
||||
});
|
||||
}
|
||||
else {
|
||||
_fetch = fetch;
|
||||
}
|
||||
return (...args) => _fetch(...args);
|
||||
};
|
||||
// Validate required parameters
|
||||
if (!((_a = options === null || options === void 0 ? void 0 : options.params) === null || _a === void 0 ? void 0 : _a.apikey)) {
|
||||
throw new Error('API key is required to connect to Realtime');
|
||||
}
|
||||
this.apiKey = options.params.apikey;
|
||||
// Initialize endpoint URLs
|
||||
this.endPoint = `${endPoint}/${constants_1.TRANSPORTS.websocket}`;
|
||||
this.httpEndpoint = (0, transformers_1.httpEndpointURL)(endPoint);
|
||||
if (options === null || options === void 0 ? void 0 : options.transport) {
|
||||
this.transport = options.transport;
|
||||
}
|
||||
else {
|
||||
this.transport = null;
|
||||
}
|
||||
if (options === null || options === void 0 ? void 0 : options.params)
|
||||
this.params = options.params;
|
||||
if (options === null || options === void 0 ? void 0 : options.headers)
|
||||
this.headers = Object.assign(Object.assign({}, this.headers), options.headers);
|
||||
if (options === null || options === void 0 ? void 0 : options.timeout)
|
||||
this.timeout = options.timeout;
|
||||
if (options === null || options === void 0 ? void 0 : options.logger)
|
||||
this.logger = options.logger;
|
||||
if (options === null || options === void 0 ? void 0 : options.heartbeatIntervalMs)
|
||||
this.heartbeatIntervalMs = options.heartbeatIntervalMs;
|
||||
const accessTokenValue = (_a = options === null || options === void 0 ? void 0 : options.params) === null || _a === void 0 ? void 0 : _a.apikey;
|
||||
if (accessTokenValue) {
|
||||
this.accessTokenValue = accessTokenValue;
|
||||
this.apiKey = accessTokenValue;
|
||||
}
|
||||
this.reconnectAfterMs = (options === null || options === void 0 ? void 0 : options.reconnectAfterMs)
|
||||
? options.reconnectAfterMs
|
||||
: (tries) => {
|
||||
return [1000, 2000, 5000, 10000][tries - 1] || 10000;
|
||||
};
|
||||
this.encode = (options === null || options === void 0 ? void 0 : options.encode)
|
||||
? options.encode
|
||||
: (payload, callback) => {
|
||||
return callback(JSON.stringify(payload));
|
||||
};
|
||||
this.decode = (options === null || options === void 0 ? void 0 : options.decode)
|
||||
? options.decode
|
||||
: this.serializer.decode.bind(this.serializer);
|
||||
this.reconnectTimer = new timer_1.default(async () => {
|
||||
this.disconnect();
|
||||
this.connect();
|
||||
}, this.reconnectAfterMs);
|
||||
this._initializeOptions(options);
|
||||
this._setupReconnectionTimer();
|
||||
this.fetch = this._resolveFetch(options === null || options === void 0 ? void 0 : options.fetch);
|
||||
if (options === null || options === void 0 ? void 0 : options.worker) {
|
||||
if (typeof window !== 'undefined' && !window.Worker) {
|
||||
throw new Error('Web Worker is not supported');
|
||||
}
|
||||
this.worker = (options === null || options === void 0 ? void 0 : options.worker) || false;
|
||||
this.workerUrl = options === null || options === void 0 ? void 0 : options.workerUrl;
|
||||
}
|
||||
this.accessToken = (options === null || options === void 0 ? void 0 : options.accessToken) || null;
|
||||
}
|
||||
/**
|
||||
* Connects the socket, unless already connected.
|
||||
*/
|
||||
connect() {
|
||||
if (this.conn) {
|
||||
// Skip if already connecting, disconnecting, or connected
|
||||
if (this.isConnecting() ||
|
||||
this.isDisconnecting() ||
|
||||
(this.conn !== null && this.isConnected())) {
|
||||
return;
|
||||
}
|
||||
this._setConnectionState('connecting');
|
||||
this._setAuthSafely('connect');
|
||||
// Establish WebSocket connection
|
||||
if (this.transport) {
|
||||
this.conn = new this.transport(this.endpointURL(), undefined, {
|
||||
headers: this.headers,
|
||||
});
|
||||
return;
|
||||
// Use custom transport if provided
|
||||
this.conn = new this.transport(this.endpointURL());
|
||||
}
|
||||
if (NATIVE_WEBSOCKET_AVAILABLE) {
|
||||
this.conn = new WebSocket(this.endpointURL());
|
||||
this.setupConnection();
|
||||
return;
|
||||
else {
|
||||
// Try to use native WebSocket
|
||||
try {
|
||||
this.conn = websocket_factory_1.default.createWebSocket(this.endpointURL());
|
||||
}
|
||||
catch (error) {
|
||||
this._setConnectionState('disconnected');
|
||||
const errorMessage = error.message;
|
||||
// Provide helpful error message based on environment
|
||||
if (errorMessage.includes('Node.js')) {
|
||||
throw new Error(`${errorMessage}\n\n` +
|
||||
'To use Realtime in Node.js, you need to provide a WebSocket implementation:\n\n' +
|
||||
'Option 1: Use Node.js 22+ which has native WebSocket support\n' +
|
||||
'Option 2: Install and provide the "ws" package:\n\n' +
|
||||
' npm install ws\n\n' +
|
||||
' import ws from "ws"\n' +
|
||||
' const client = new RealtimeClient(url, {\n' +
|
||||
' ...options,\n' +
|
||||
' transport: ws\n' +
|
||||
' })');
|
||||
}
|
||||
throw new Error(`WebSocket not available: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
this.conn = new WSWebSocketDummy(this.endpointURL(), undefined, {
|
||||
close: () => {
|
||||
this.conn = null;
|
||||
},
|
||||
});
|
||||
Promise.resolve().then(() => __importStar(require('ws'))).then(({ default: WS }) => {
|
||||
this.conn = new WS(this.endpointURL(), undefined, {
|
||||
headers: this.headers,
|
||||
});
|
||||
this.setupConnection();
|
||||
});
|
||||
this._setupConnectionHandlers();
|
||||
}
|
||||
/**
|
||||
* Returns the URL of the websocket.
|
||||
@@ -194,18 +200,30 @@ class RealtimeClient {
|
||||
* @param reason A custom reason for the disconnect.
|
||||
*/
|
||||
disconnect(code, reason) {
|
||||
if (this.isDisconnecting()) {
|
||||
return;
|
||||
}
|
||||
this._setConnectionState('disconnecting', true);
|
||||
if (this.conn) {
|
||||
this.conn.onclose = function () { }; // noop
|
||||
// Setup fallback timer to prevent hanging in disconnecting state
|
||||
const fallbackTimer = setTimeout(() => {
|
||||
this._setConnectionState('disconnected');
|
||||
}, 100);
|
||||
this.conn.onclose = () => {
|
||||
clearTimeout(fallbackTimer);
|
||||
this._setConnectionState('disconnected');
|
||||
};
|
||||
// Close the WebSocket connection
|
||||
if (code) {
|
||||
this.conn.close(code, reason !== null && reason !== void 0 ? reason : '');
|
||||
}
|
||||
else {
|
||||
this.conn.close();
|
||||
}
|
||||
this.conn = null;
|
||||
// remove open handles
|
||||
this.heartbeatTimer && clearInterval(this.heartbeatTimer);
|
||||
this.reconnectTimer.reset();
|
||||
this._teardownConnection();
|
||||
}
|
||||
else {
|
||||
this._setConnectionState('disconnected');
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -230,6 +248,7 @@ class RealtimeClient {
|
||||
*/
|
||||
async removeAllChannels() {
|
||||
const values_1 = await Promise.all(this.channels.map((channel) => channel.unsubscribe()));
|
||||
this.channels = [];
|
||||
this.disconnect();
|
||||
return values_1;
|
||||
}
|
||||
@@ -262,10 +281,29 @@ class RealtimeClient {
|
||||
isConnected() {
|
||||
return this.connectionState() === constants_1.CONNECTION_STATE.Open;
|
||||
}
|
||||
/**
|
||||
* Returns `true` if the connection is currently connecting.
|
||||
*/
|
||||
isConnecting() {
|
||||
return this._connectionState === 'connecting';
|
||||
}
|
||||
/**
|
||||
* Returns `true` if the connection is currently disconnecting.
|
||||
*/
|
||||
isDisconnecting() {
|
||||
return this._connectionState === 'disconnecting';
|
||||
}
|
||||
channel(topic, params = { config: {} }) {
|
||||
const chan = new RealtimeChannel_1.default(`realtime:${topic}`, params, this);
|
||||
this.channels.push(chan);
|
||||
return chan;
|
||||
const realtimeTopic = `realtime:${topic}`;
|
||||
const exists = this.getChannels().find((c) => c.topic === realtimeTopic);
|
||||
if (!exists) {
|
||||
const chan = new RealtimeChannel_1.default(`realtime:${topic}`, params, this);
|
||||
this.channels.push(chan);
|
||||
return chan;
|
||||
}
|
||||
else {
|
||||
return exists;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Push out a message if the socket is connected.
|
||||
@@ -298,32 +336,12 @@ class RealtimeClient {
|
||||
* @param token A JWT string to override the token set on the client.
|
||||
*/
|
||||
async setAuth(token = null) {
|
||||
let tokenToSend = token ||
|
||||
(this.accessToken && (await this.accessToken())) ||
|
||||
this.accessTokenValue;
|
||||
if (tokenToSend) {
|
||||
let parsed = null;
|
||||
try {
|
||||
parsed = JSON.parse(atob(tokenToSend.split('.')[1]));
|
||||
}
|
||||
catch (_error) { }
|
||||
if (parsed && parsed.exp) {
|
||||
let now = Math.floor(Date.now() / 1000);
|
||||
let valid = now - parsed.exp < 0;
|
||||
if (!valid) {
|
||||
this.log('auth', `InvalidJWTToken: Invalid value for JWT claim "exp" with value ${parsed.exp}`);
|
||||
return Promise.reject(`InvalidJWTToken: Invalid value for JWT claim "exp" with value ${parsed.exp}`);
|
||||
}
|
||||
}
|
||||
this.accessTokenValue = tokenToSend;
|
||||
this.channels.forEach((channel) => {
|
||||
tokenToSend && channel.updateJoinPayload({ access_token: tokenToSend });
|
||||
if (channel.joinedOnce && channel._isJoined()) {
|
||||
channel._push(constants_1.CHANNEL_EVENTS.access_token, {
|
||||
access_token: tokenToSend,
|
||||
});
|
||||
}
|
||||
});
|
||||
this._authPromise = this._performAuth(token);
|
||||
try {
|
||||
await this._authPromise;
|
||||
}
|
||||
finally {
|
||||
this._authPromise = null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -332,14 +350,26 @@ class RealtimeClient {
|
||||
async sendHeartbeat() {
|
||||
var _a;
|
||||
if (!this.isConnected()) {
|
||||
this.heartbeatCallback('disconnected');
|
||||
return;
|
||||
}
|
||||
// Handle heartbeat timeout and force reconnection if needed
|
||||
if (this.pendingHeartbeatRef) {
|
||||
this.pendingHeartbeatRef = null;
|
||||
this.log('transport', 'heartbeat timeout. Attempting to re-establish connection');
|
||||
(_a = this.conn) === null || _a === void 0 ? void 0 : _a.close(constants_1.WS_CLOSE_NORMAL, 'hearbeat timeout');
|
||||
this.heartbeatCallback('timeout');
|
||||
// Force reconnection after heartbeat timeout
|
||||
this._wasManualDisconnect = false;
|
||||
(_a = this.conn) === null || _a === void 0 ? void 0 : _a.close(constants_1.WS_CLOSE_NORMAL, 'heartbeat timeout');
|
||||
setTimeout(() => {
|
||||
var _a;
|
||||
if (!this.isConnected()) {
|
||||
(_a = this.reconnectTimer) === null || _a === void 0 ? void 0 : _a.scheduleTimeout();
|
||||
}
|
||||
}, CONNECTION_TIMEOUTS.HEARTBEAT_TIMEOUT_FALLBACK);
|
||||
return;
|
||||
}
|
||||
// Send heartbeat message to server
|
||||
this.pendingHeartbeatRef = this._makeRef();
|
||||
this.push({
|
||||
topic: 'phoenix',
|
||||
@@ -347,7 +377,11 @@ class RealtimeClient {
|
||||
payload: {},
|
||||
ref: this.pendingHeartbeatRef,
|
||||
});
|
||||
this.setAuth();
|
||||
this.heartbeatCallback('sent');
|
||||
this._setAuthSafely('heartbeat');
|
||||
}
|
||||
onHeartbeat(callback) {
|
||||
this.heartbeatCallback = callback;
|
||||
}
|
||||
/**
|
||||
* Flushes send buffer
|
||||
@@ -393,83 +427,149 @@ class RealtimeClient {
|
||||
* @internal
|
||||
*/
|
||||
_remove(channel) {
|
||||
this.channels = this.channels.filter((c) => c._joinRef() !== channel._joinRef());
|
||||
}
|
||||
/**
|
||||
* Sets up connection handlers.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
setupConnection() {
|
||||
if (this.conn) {
|
||||
this.conn.binaryType = 'arraybuffer';
|
||||
this.conn.onopen = () => this._onConnOpen();
|
||||
this.conn.onerror = (error) => this._onConnError(error);
|
||||
this.conn.onmessage = (event) => this._onConnMessage(event);
|
||||
this.conn.onclose = (event) => this._onConnClose(event);
|
||||
}
|
||||
this.channels = this.channels.filter((c) => c.topic !== channel.topic);
|
||||
}
|
||||
/** @internal */
|
||||
_onConnMessage(rawMessage) {
|
||||
this.decode(rawMessage.data, (msg) => {
|
||||
let { topic, event, payload, ref } = msg;
|
||||
if (ref && ref === this.pendingHeartbeatRef) {
|
||||
// Handle heartbeat responses
|
||||
if (msg.topic === 'phoenix' && msg.event === 'phx_reply') {
|
||||
this.heartbeatCallback(msg.payload.status === 'ok' ? 'ok' : 'error');
|
||||
}
|
||||
// Handle pending heartbeat reference cleanup
|
||||
if (msg.ref && msg.ref === this.pendingHeartbeatRef) {
|
||||
this.pendingHeartbeatRef = null;
|
||||
}
|
||||
this.log('receive', `${payload.status || ''} ${topic} ${event} ${(ref && '(' + ref + ')') || ''}`, payload);
|
||||
// Log incoming message
|
||||
const { topic, event, payload, ref } = msg;
|
||||
const refString = ref ? `(${ref})` : '';
|
||||
const status = payload.status || '';
|
||||
this.log('receive', `${status} ${topic} ${event} ${refString}`.trim(), payload);
|
||||
// Route message to appropriate channels
|
||||
this.channels
|
||||
.filter((channel) => channel._isMember(topic))
|
||||
.forEach((channel) => channel._trigger(event, payload, ref));
|
||||
this.stateChangeCallbacks.message.forEach((callback) => callback(msg));
|
||||
this._triggerStateCallbacks('message', msg);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Clear specific timer
|
||||
* @internal
|
||||
*/
|
||||
_clearTimer(timer) {
|
||||
var _a;
|
||||
if (timer === 'heartbeat' && this.heartbeatTimer) {
|
||||
clearInterval(this.heartbeatTimer);
|
||||
this.heartbeatTimer = undefined;
|
||||
}
|
||||
else if (timer === 'reconnect') {
|
||||
(_a = this.reconnectTimer) === null || _a === void 0 ? void 0 : _a.reset();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Clear all timers
|
||||
* @internal
|
||||
*/
|
||||
_clearAllTimers() {
|
||||
this._clearTimer('heartbeat');
|
||||
this._clearTimer('reconnect');
|
||||
}
|
||||
/**
|
||||
* Setup connection handlers for WebSocket events
|
||||
* @internal
|
||||
*/
|
||||
_setupConnectionHandlers() {
|
||||
if (!this.conn)
|
||||
return;
|
||||
// Set binary type if supported (browsers and most WebSocket implementations)
|
||||
if ('binaryType' in this.conn) {
|
||||
;
|
||||
this.conn.binaryType = 'arraybuffer';
|
||||
}
|
||||
this.conn.onopen = () => this._onConnOpen();
|
||||
this.conn.onerror = (error) => this._onConnError(error);
|
||||
this.conn.onmessage = (event) => this._onConnMessage(event);
|
||||
this.conn.onclose = (event) => this._onConnClose(event);
|
||||
}
|
||||
/**
|
||||
* Teardown connection and cleanup resources
|
||||
* @internal
|
||||
*/
|
||||
_teardownConnection() {
|
||||
if (this.conn) {
|
||||
this.conn.onopen = null;
|
||||
this.conn.onerror = null;
|
||||
this.conn.onmessage = null;
|
||||
this.conn.onclose = null;
|
||||
this.conn = null;
|
||||
}
|
||||
this._clearAllTimers();
|
||||
this.channels.forEach((channel) => channel.teardown());
|
||||
}
|
||||
/** @internal */
|
||||
_onConnOpen() {
|
||||
this._setConnectionState('connected');
|
||||
this.log('transport', `connected to ${this.endpointURL()}`);
|
||||
this.flushSendBuffer();
|
||||
this._clearTimer('reconnect');
|
||||
if (!this.worker) {
|
||||
this._startHeartbeat();
|
||||
}
|
||||
else {
|
||||
if (!this.workerRef) {
|
||||
this._startWorkerHeartbeat();
|
||||
}
|
||||
}
|
||||
this._triggerStateCallbacks('open');
|
||||
}
|
||||
/** @internal */
|
||||
_startHeartbeat() {
|
||||
this.heartbeatTimer && clearInterval(this.heartbeatTimer);
|
||||
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), this.heartbeatIntervalMs);
|
||||
}
|
||||
/** @internal */
|
||||
_startWorkerHeartbeat() {
|
||||
if (this.workerUrl) {
|
||||
this.log('worker', `starting worker for from ${this.workerUrl}`);
|
||||
}
|
||||
else {
|
||||
this.log('worker', `starting default worker`);
|
||||
}
|
||||
const objectUrl = this._workerObjectUrl(this.workerUrl);
|
||||
this.workerRef = new Worker(objectUrl);
|
||||
this.workerRef.onerror = (error) => {
|
||||
this.log('worker', 'worker error', error.message);
|
||||
this.workerRef.terminate();
|
||||
};
|
||||
this.workerRef.onmessage = (event) => {
|
||||
if (event.data.event === 'keepAlive') {
|
||||
this.sendHeartbeat();
|
||||
}
|
||||
};
|
||||
this.workerRef.postMessage({
|
||||
event: 'start',
|
||||
interval: this.heartbeatIntervalMs,
|
||||
});
|
||||
}
|
||||
/** @internal */
|
||||
async _onConnOpen() {
|
||||
this.log('transport', `connected to ${this.endpointURL()}`);
|
||||
this.flushSendBuffer();
|
||||
this.reconnectTimer.reset();
|
||||
if (!this.worker) {
|
||||
this.heartbeatTimer && clearInterval(this.heartbeatTimer);
|
||||
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), this.heartbeatIntervalMs);
|
||||
}
|
||||
else {
|
||||
if (this.workerUrl) {
|
||||
this.log('worker', `starting worker for from ${this.workerUrl}`);
|
||||
}
|
||||
else {
|
||||
this.log('worker', `starting default worker`);
|
||||
}
|
||||
const objectUrl = this._workerObjectUrl(this.workerUrl);
|
||||
this.workerRef = new Worker(objectUrl);
|
||||
this.workerRef.onerror = (error) => {
|
||||
this.log('worker', 'worker error', error.message);
|
||||
this.workerRef.terminate();
|
||||
};
|
||||
this.workerRef.onmessage = (event) => {
|
||||
if (event.data.event === 'keepAlive') {
|
||||
this.sendHeartbeat();
|
||||
}
|
||||
};
|
||||
this.workerRef.postMessage({
|
||||
event: 'start',
|
||||
interval: this.heartbeatIntervalMs,
|
||||
});
|
||||
}
|
||||
this.stateChangeCallbacks.open.forEach((callback) => callback());
|
||||
}
|
||||
/** @internal */
|
||||
_onConnClose(event) {
|
||||
var _a;
|
||||
this._setConnectionState('disconnected');
|
||||
this.log('transport', 'close', event);
|
||||
this._triggerChanError();
|
||||
this.heartbeatTimer && clearInterval(this.heartbeatTimer);
|
||||
this.reconnectTimer.scheduleTimeout();
|
||||
this.stateChangeCallbacks.close.forEach((callback) => callback(event));
|
||||
this._clearTimer('heartbeat');
|
||||
// Only schedule reconnection if it wasn't a manual disconnect
|
||||
if (!this._wasManualDisconnect) {
|
||||
(_a = this.reconnectTimer) === null || _a === void 0 ? void 0 : _a.scheduleTimeout();
|
||||
}
|
||||
this._triggerStateCallbacks('close', event);
|
||||
}
|
||||
/** @internal */
|
||||
_onConnError(error) {
|
||||
this.log('transport', error.message);
|
||||
this._setConnectionState('disconnected');
|
||||
this.log('transport', `${error}`);
|
||||
this._triggerChanError();
|
||||
this.stateChangeCallbacks.error.forEach((callback) => callback(error));
|
||||
this._triggerStateCallbacks('error', error);
|
||||
}
|
||||
/** @internal */
|
||||
_triggerChanError() {
|
||||
@@ -495,20 +595,143 @@ class RealtimeClient {
|
||||
}
|
||||
return result_url;
|
||||
}
|
||||
}
|
||||
exports.default = RealtimeClient;
|
||||
class WSWebSocketDummy {
|
||||
constructor(address, _protocols, options) {
|
||||
this.binaryType = 'arraybuffer';
|
||||
this.onclose = () => { };
|
||||
this.onerror = () => { };
|
||||
this.onmessage = () => { };
|
||||
this.onopen = () => { };
|
||||
this.readyState = constants_1.SOCKET_STATES.connecting;
|
||||
this.send = () => { };
|
||||
this.url = null;
|
||||
this.url = address;
|
||||
this.close = options.close;
|
||||
/**
|
||||
* Set connection state with proper state management
|
||||
* @internal
|
||||
*/
|
||||
_setConnectionState(state, manual = false) {
|
||||
this._connectionState = state;
|
||||
if (state === 'connecting') {
|
||||
this._wasManualDisconnect = false;
|
||||
}
|
||||
else if (state === 'disconnecting') {
|
||||
this._wasManualDisconnect = manual;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Perform the actual auth operation
|
||||
* @internal
|
||||
*/
|
||||
async _performAuth(token = null) {
|
||||
let tokenToSend;
|
||||
if (token) {
|
||||
tokenToSend = token;
|
||||
}
|
||||
else if (this.accessToken) {
|
||||
// Always call the accessToken callback to get fresh token
|
||||
tokenToSend = await this.accessToken();
|
||||
}
|
||||
else {
|
||||
tokenToSend = this.accessTokenValue;
|
||||
}
|
||||
if (this.accessTokenValue != tokenToSend) {
|
||||
this.accessTokenValue = tokenToSend;
|
||||
this.channels.forEach((channel) => {
|
||||
const payload = {
|
||||
access_token: tokenToSend,
|
||||
version: constants_1.DEFAULT_VERSION,
|
||||
};
|
||||
tokenToSend && channel.updateJoinPayload(payload);
|
||||
if (channel.joinedOnce && channel._isJoined()) {
|
||||
channel._push(constants_1.CHANNEL_EVENTS.access_token, {
|
||||
access_token: tokenToSend,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Wait for any in-flight auth operations to complete
|
||||
* @internal
|
||||
*/
|
||||
async _waitForAuthIfNeeded() {
|
||||
if (this._authPromise) {
|
||||
await this._authPromise;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Safely call setAuth with standardized error handling
|
||||
* @internal
|
||||
*/
|
||||
_setAuthSafely(context = 'general') {
|
||||
this.setAuth().catch((e) => {
|
||||
this.log('error', `error setting auth in ${context}`, e);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Trigger state change callbacks with proper error handling
|
||||
* @internal
|
||||
*/
|
||||
_triggerStateCallbacks(event, data) {
|
||||
try {
|
||||
this.stateChangeCallbacks[event].forEach((callback) => {
|
||||
try {
|
||||
callback(data);
|
||||
}
|
||||
catch (e) {
|
||||
this.log('error', `error in ${event} callback`, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
this.log('error', `error triggering ${event} callbacks`, e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Setup reconnection timer with proper configuration
|
||||
* @internal
|
||||
*/
|
||||
_setupReconnectionTimer() {
|
||||
this.reconnectTimer = new timer_1.default(async () => {
|
||||
setTimeout(async () => {
|
||||
await this._waitForAuthIfNeeded();
|
||||
if (!this.isConnected()) {
|
||||
this.connect();
|
||||
}
|
||||
}, CONNECTION_TIMEOUTS.RECONNECT_DELAY);
|
||||
}, this.reconnectAfterMs);
|
||||
}
|
||||
/**
|
||||
* Initialize client options with defaults
|
||||
* @internal
|
||||
*/
|
||||
_initializeOptions(options) {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h;
|
||||
// Set defaults
|
||||
this.transport = (_a = options === null || options === void 0 ? void 0 : options.transport) !== null && _a !== void 0 ? _a : null;
|
||||
this.timeout = (_b = options === null || options === void 0 ? void 0 : options.timeout) !== null && _b !== void 0 ? _b : constants_1.DEFAULT_TIMEOUT;
|
||||
this.heartbeatIntervalMs =
|
||||
(_c = options === null || options === void 0 ? void 0 : options.heartbeatIntervalMs) !== null && _c !== void 0 ? _c : CONNECTION_TIMEOUTS.HEARTBEAT_INTERVAL;
|
||||
this.worker = (_d = options === null || options === void 0 ? void 0 : options.worker) !== null && _d !== void 0 ? _d : false;
|
||||
this.accessToken = (_e = options === null || options === void 0 ? void 0 : options.accessToken) !== null && _e !== void 0 ? _e : null;
|
||||
// Handle special cases
|
||||
if (options === null || options === void 0 ? void 0 : options.params)
|
||||
this.params = options.params;
|
||||
if (options === null || options === void 0 ? void 0 : options.logger)
|
||||
this.logger = options.logger;
|
||||
if ((options === null || options === void 0 ? void 0 : options.logLevel) || (options === null || options === void 0 ? void 0 : options.log_level)) {
|
||||
this.logLevel = options.logLevel || options.log_level;
|
||||
this.params = Object.assign(Object.assign({}, this.params), { log_level: this.logLevel });
|
||||
}
|
||||
// Set up functions with defaults
|
||||
this.reconnectAfterMs =
|
||||
(_f = options === null || options === void 0 ? void 0 : options.reconnectAfterMs) !== null && _f !== void 0 ? _f : ((tries) => {
|
||||
return RECONNECT_INTERVALS[tries - 1] || DEFAULT_RECONNECT_FALLBACK;
|
||||
});
|
||||
this.encode =
|
||||
(_g = options === null || options === void 0 ? void 0 : options.encode) !== null && _g !== void 0 ? _g : ((payload, callback) => {
|
||||
return callback(JSON.stringify(payload));
|
||||
});
|
||||
this.decode =
|
||||
(_h = options === null || options === void 0 ? void 0 : options.decode) !== null && _h !== void 0 ? _h : this.serializer.decode.bind(this.serializer);
|
||||
// Handle worker setup
|
||||
if (this.worker) {
|
||||
if (typeof window !== 'undefined' && !window.Worker) {
|
||||
throw new Error('Web Worker is not supported');
|
||||
}
|
||||
this.workerUrl = options === null || options === void 0 ? void 0 : options.workerUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.default = RealtimeClient;
|
||||
//# sourceMappingURL=RealtimeClient.js.map
|
||||
+2
-1
@@ -10,7 +10,7 @@ var REALTIME_PRESENCE_LISTEN_EVENTS;
|
||||
REALTIME_PRESENCE_LISTEN_EVENTS["SYNC"] = "sync";
|
||||
REALTIME_PRESENCE_LISTEN_EVENTS["JOIN"] = "join";
|
||||
REALTIME_PRESENCE_LISTEN_EVENTS["LEAVE"] = "leave";
|
||||
})(REALTIME_PRESENCE_LISTEN_EVENTS = exports.REALTIME_PRESENCE_LISTEN_EVENTS || (exports.REALTIME_PRESENCE_LISTEN_EVENTS = {}));
|
||||
})(REALTIME_PRESENCE_LISTEN_EVENTS || (exports.REALTIME_PRESENCE_LISTEN_EVENTS = REALTIME_PRESENCE_LISTEN_EVENTS = {}));
|
||||
class RealtimePresence {
|
||||
/**
|
||||
* Initializes the Presence.
|
||||
@@ -24,6 +24,7 @@ class RealtimePresence {
|
||||
this.state = {};
|
||||
this.pendingDiffs = [];
|
||||
this.joinRef = null;
|
||||
this.enabled = false;
|
||||
this.caller = {
|
||||
onJoin: () => { },
|
||||
onLeave: () => { },
|
||||
|
||||
+20
-8
@@ -15,18 +15,28 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.REALTIME_CHANNEL_STATES = exports.REALTIME_SUBSCRIBE_STATES = exports.REALTIME_PRESENCE_LISTEN_EVENTS = exports.REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = exports.REALTIME_LISTEN_TYPES = exports.RealtimeClient = exports.RealtimeChannel = exports.RealtimePresence = void 0;
|
||||
exports.WebSocketFactory = exports.REALTIME_CHANNEL_STATES = exports.REALTIME_SUBSCRIBE_STATES = exports.REALTIME_PRESENCE_LISTEN_EVENTS = exports.REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = exports.REALTIME_LISTEN_TYPES = exports.RealtimeClient = exports.RealtimeChannel = exports.RealtimePresence = void 0;
|
||||
const RealtimeClient_1 = __importDefault(require("./RealtimeClient"));
|
||||
exports.RealtimeClient = RealtimeClient_1.default;
|
||||
const RealtimeChannel_1 = __importStar(require("./RealtimeChannel"));
|
||||
@@ -38,4 +48,6 @@ Object.defineProperty(exports, "REALTIME_CHANNEL_STATES", { enumerable: true, ge
|
||||
const RealtimePresence_1 = __importStar(require("./RealtimePresence"));
|
||||
exports.RealtimePresence = RealtimePresence_1.default;
|
||||
Object.defineProperty(exports, "REALTIME_PRESENCE_LISTEN_EVENTS", { enumerable: true, get: function () { return RealtimePresence_1.REALTIME_PRESENCE_LISTEN_EVENTS; } });
|
||||
const websocket_factory_1 = __importDefault(require("./lib/websocket-factory"));
|
||||
exports.WebSocketFactory = websocket_factory_1.default;
|
||||
//# sourceMappingURL=index.js.map
|
||||
+9
-7
@@ -1,18 +1,20 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CONNECTION_STATE = exports.TRANSPORTS = exports.CHANNEL_EVENTS = exports.CHANNEL_STATES = exports.SOCKET_STATES = exports.WS_CLOSE_NORMAL = exports.DEFAULT_TIMEOUT = exports.VSN = exports.DEFAULT_HEADERS = void 0;
|
||||
exports.CONNECTION_STATE = exports.TRANSPORTS = exports.CHANNEL_EVENTS = exports.CHANNEL_STATES = exports.SOCKET_STATES = exports.MAX_PUSH_BUFFER_SIZE = exports.WS_CLOSE_NORMAL = exports.DEFAULT_TIMEOUT = exports.VERSION = exports.VSN = exports.DEFAULT_VERSION = void 0;
|
||||
const version_1 = require("./version");
|
||||
exports.DEFAULT_HEADERS = { 'X-Client-Info': `realtime-js/${version_1.version}` };
|
||||
exports.DEFAULT_VERSION = `realtime-js/${version_1.version}`;
|
||||
exports.VSN = '1.0.0';
|
||||
exports.VERSION = version_1.version;
|
||||
exports.DEFAULT_TIMEOUT = 10000;
|
||||
exports.WS_CLOSE_NORMAL = 1000;
|
||||
exports.MAX_PUSH_BUFFER_SIZE = 100;
|
||||
var SOCKET_STATES;
|
||||
(function (SOCKET_STATES) {
|
||||
SOCKET_STATES[SOCKET_STATES["connecting"] = 0] = "connecting";
|
||||
SOCKET_STATES[SOCKET_STATES["open"] = 1] = "open";
|
||||
SOCKET_STATES[SOCKET_STATES["closing"] = 2] = "closing";
|
||||
SOCKET_STATES[SOCKET_STATES["closed"] = 3] = "closed";
|
||||
})(SOCKET_STATES = exports.SOCKET_STATES || (exports.SOCKET_STATES = {}));
|
||||
})(SOCKET_STATES || (exports.SOCKET_STATES = SOCKET_STATES = {}));
|
||||
var CHANNEL_STATES;
|
||||
(function (CHANNEL_STATES) {
|
||||
CHANNEL_STATES["closed"] = "closed";
|
||||
@@ -20,7 +22,7 @@ var CHANNEL_STATES;
|
||||
CHANNEL_STATES["joined"] = "joined";
|
||||
CHANNEL_STATES["joining"] = "joining";
|
||||
CHANNEL_STATES["leaving"] = "leaving";
|
||||
})(CHANNEL_STATES = exports.CHANNEL_STATES || (exports.CHANNEL_STATES = {}));
|
||||
})(CHANNEL_STATES || (exports.CHANNEL_STATES = CHANNEL_STATES = {}));
|
||||
var CHANNEL_EVENTS;
|
||||
(function (CHANNEL_EVENTS) {
|
||||
CHANNEL_EVENTS["close"] = "phx_close";
|
||||
@@ -29,16 +31,16 @@ var CHANNEL_EVENTS;
|
||||
CHANNEL_EVENTS["reply"] = "phx_reply";
|
||||
CHANNEL_EVENTS["leave"] = "phx_leave";
|
||||
CHANNEL_EVENTS["access_token"] = "access_token";
|
||||
})(CHANNEL_EVENTS = exports.CHANNEL_EVENTS || (exports.CHANNEL_EVENTS = {}));
|
||||
})(CHANNEL_EVENTS || (exports.CHANNEL_EVENTS = CHANNEL_EVENTS = {}));
|
||||
var TRANSPORTS;
|
||||
(function (TRANSPORTS) {
|
||||
TRANSPORTS["websocket"] = "websocket";
|
||||
})(TRANSPORTS = exports.TRANSPORTS || (exports.TRANSPORTS = {}));
|
||||
})(TRANSPORTS || (exports.TRANSPORTS = TRANSPORTS = {}));
|
||||
var CONNECTION_STATE;
|
||||
(function (CONNECTION_STATE) {
|
||||
CONNECTION_STATE["Connecting"] = "connecting";
|
||||
CONNECTION_STATE["Open"] = "open";
|
||||
CONNECTION_STATE["Closing"] = "closing";
|
||||
CONNECTION_STATE["Closed"] = "closed";
|
||||
})(CONNECTION_STATE = exports.CONNECTION_STATE || (exports.CONNECTION_STATE = {}));
|
||||
})(CONNECTION_STATE || (exports.CONNECTION_STATE = CONNECTION_STATE = {}));
|
||||
//# sourceMappingURL=constants.js.map
|
||||
+1
@@ -24,6 +24,7 @@ class Timer {
|
||||
reset() {
|
||||
this.tries = 0;
|
||||
clearTimeout(this.timer);
|
||||
this.timer = undefined;
|
||||
}
|
||||
// Cancels any previous scheduleTimeout and schedules callback
|
||||
scheduleTimeout() {
|
||||
|
||||
+2
-2
@@ -32,7 +32,7 @@ var PostgresTypes;
|
||||
PostgresTypes["timetz"] = "timetz";
|
||||
PostgresTypes["tsrange"] = "tsrange";
|
||||
PostgresTypes["tstzrange"] = "tstzrange";
|
||||
})(PostgresTypes = exports.PostgresTypes || (exports.PostgresTypes = {}));
|
||||
})(PostgresTypes || (exports.PostgresTypes = PostgresTypes = {}));
|
||||
/**
|
||||
* Takes an array of columns and an object of string values then converts each string value
|
||||
* to its mapped type.
|
||||
@@ -223,7 +223,7 @@ const httpEndpointURL = (socketUrl) => {
|
||||
let url = socketUrl;
|
||||
url = url.replace(/^ws/i, 'http');
|
||||
url = url.replace(/(\/socket\/websocket|\/socket|\/websocket)\/?$/i, '');
|
||||
return url.replace(/\/+$/, '');
|
||||
return url.replace(/\/+$/, '') + '/api/broadcast';
|
||||
};
|
||||
exports.httpEndpointURL = httpEndpointURL;
|
||||
//# sourceMappingURL=transformers.js.map
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.version = void 0;
|
||||
exports.version = '2.11.2';
|
||||
exports.version = '2.15.1';
|
||||
//# sourceMappingURL=version.js.map
|
||||
Generated
Vendored
+95
@@ -0,0 +1,95 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.WebSocketFactory = void 0;
|
||||
class WebSocketFactory {
|
||||
static detectEnvironment() {
|
||||
var _a;
|
||||
if (typeof WebSocket !== 'undefined') {
|
||||
return { type: 'native', constructor: WebSocket };
|
||||
}
|
||||
if (typeof globalThis !== 'undefined' &&
|
||||
typeof globalThis.WebSocket !== 'undefined') {
|
||||
return { type: 'native', constructor: globalThis.WebSocket };
|
||||
}
|
||||
if (typeof global !== 'undefined' &&
|
||||
typeof global.WebSocket !== 'undefined') {
|
||||
return { type: 'native', constructor: global.WebSocket };
|
||||
}
|
||||
if (typeof globalThis !== 'undefined' &&
|
||||
typeof globalThis.WebSocketPair !== 'undefined' &&
|
||||
typeof globalThis.WebSocket === 'undefined') {
|
||||
return {
|
||||
type: 'cloudflare',
|
||||
error: 'Cloudflare Workers detected. WebSocket clients are not supported in Cloudflare Workers.',
|
||||
workaround: 'Use Cloudflare Workers WebSocket API for server-side WebSocket handling, or deploy to a different runtime.',
|
||||
};
|
||||
}
|
||||
if ((typeof globalThis !== 'undefined' && globalThis.EdgeRuntime) ||
|
||||
(typeof navigator !== 'undefined' &&
|
||||
((_a = navigator.userAgent) === null || _a === void 0 ? void 0 : _a.includes('Vercel-Edge')))) {
|
||||
return {
|
||||
type: 'unsupported',
|
||||
error: 'Edge runtime detected (Vercel Edge/Netlify Edge). WebSockets are not supported in edge functions.',
|
||||
workaround: 'Use serverless functions or a different deployment target for WebSocket functionality.',
|
||||
};
|
||||
}
|
||||
if (typeof process !== 'undefined' &&
|
||||
process.versions &&
|
||||
process.versions.node) {
|
||||
const nodeVersion = parseInt(process.versions.node.split('.')[0]);
|
||||
// Node.js 22+ should have native WebSocket
|
||||
if (nodeVersion >= 22) {
|
||||
// Check if native WebSocket is available (should be in Node.js 22+)
|
||||
if (typeof globalThis.WebSocket !== 'undefined') {
|
||||
return { type: 'native', constructor: globalThis.WebSocket };
|
||||
}
|
||||
// If not available, user needs to provide it
|
||||
return {
|
||||
type: 'unsupported',
|
||||
error: `Node.js ${nodeVersion} detected but native WebSocket not found.`,
|
||||
workaround: 'Provide a WebSocket implementation via the transport option.',
|
||||
};
|
||||
}
|
||||
// Node.js < 22 doesn't have native WebSocket
|
||||
return {
|
||||
type: 'unsupported',
|
||||
error: `Node.js ${nodeVersion} detected without native WebSocket support.`,
|
||||
workaround: 'For Node.js < 22, install "ws" package and provide it via the transport option:\n' +
|
||||
'import ws from "ws"\n' +
|
||||
'new RealtimeClient(url, { transport: ws })',
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: 'unsupported',
|
||||
error: 'Unknown JavaScript runtime without WebSocket support.',
|
||||
workaround: "Ensure you're running in a supported environment (browser, Node.js, Deno) or provide a custom WebSocket implementation.",
|
||||
};
|
||||
}
|
||||
static getWebSocketConstructor() {
|
||||
const env = this.detectEnvironment();
|
||||
if (env.constructor) {
|
||||
return env.constructor;
|
||||
}
|
||||
let errorMessage = env.error || 'WebSocket not supported in this environment.';
|
||||
if (env.workaround) {
|
||||
errorMessage += `\n\nSuggested solution: ${env.workaround}`;
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
static createWebSocket(url, protocols) {
|
||||
const WS = this.getWebSocketConstructor();
|
||||
return new WS(url, protocols);
|
||||
}
|
||||
static isWebSocketSupported() {
|
||||
try {
|
||||
const env = this.detectEnvironment();
|
||||
return env.type === 'native' || env.type === 'ws';
|
||||
}
|
||||
catch (_a) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.WebSocketFactory = WebSocketFactory;
|
||||
exports.default = WebSocketFactory;
|
||||
//# sourceMappingURL=websocket-factory.js.map
|
||||
+13
-14
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@supabase/realtime-js",
|
||||
"version": "2.11.2",
|
||||
"version": "2.15.1",
|
||||
"description": "Listen to realtime updates to your PostgreSQL database",
|
||||
"keywords": [
|
||||
"realtime",
|
||||
@@ -32,22 +32,22 @@
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"coverage": "vitest run --coverage",
|
||||
"coverage:text": "vitest run --coverage.enabled true --coverage.reporter=text",
|
||||
"docs": "typedoc src/index.ts --out docs/v2",
|
||||
"docs:json": "typedoc --json docs/v2/spec.json --excludeExternals src/index.ts",
|
||||
"check-exports": "attw --pack .",
|
||||
"ci": "run-s test coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/node-fetch": "^2.6.14",
|
||||
"@types/phoenix": "^1.5.4",
|
||||
"@types/ws": "^8.5.10",
|
||||
"ws": "^8.18.0"
|
||||
"@supabase/node-fetch": "^2.6.13",
|
||||
"@types/phoenix": "^1.6.6",
|
||||
"ws": "^8.18.2",
|
||||
"@types/ws": "^8.18.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@arethetypeswrong/cli": "^0.16.2",
|
||||
"@types/sinon": "^17.0.3",
|
||||
"@vitest/coverage-v8": "^2.0.5",
|
||||
"eslint": "^7.0.0",
|
||||
"@arethetypeswrong/cli": "^0.16.4",
|
||||
"@vitest/coverage-v8": "^3.1.4",
|
||||
"eslint": "^9.27.0",
|
||||
"esm": "^3.2.25",
|
||||
"jsdom": "^16.7.0",
|
||||
"jsdom-global": "3.0.0",
|
||||
@@ -55,12 +55,11 @@
|
||||
"mock-socket": "^9.3.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^2.1.2",
|
||||
"prettier": "^2.8.8",
|
||||
"semantic-release-plugin-update-version-in-files": "^1.1.0",
|
||||
"sinon": "^18.0.0",
|
||||
"typedoc": "^0.22.16",
|
||||
"typescript": "^4.0.3",
|
||||
"vitest": "^2.0.5",
|
||||
"typedoc": "^0.27.9",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^3.1.4",
|
||||
"web-worker": "1.2.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user