@supabase/realtime-js
Advanced tools
Comparing version 2.10.9 to 2.11.0
@@ -1,2 +0,2 @@ | ||
export declare const version = "2.10.9"; | ||
export declare const version = "2.11.0"; | ||
//# sourceMappingURL=version.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.version = void 0; | ||
exports.version = '2.10.9'; | ||
exports.version = '2.11.0'; | ||
//# sourceMappingURL=version.js.map |
@@ -131,3 +131,3 @@ import { CHANNEL_STATES } from './lib/constants'; | ||
/** Subscribe registers your client with the server */ | ||
subscribe(callback?: (status: `${REALTIME_SUBSCRIBE_STATES}`, err?: Error) => void, timeout?: number): RealtimeChannel; | ||
subscribe(callback?: (status: REALTIME_SUBSCRIBE_STATES, err?: Error) => void, timeout?: number): RealtimeChannel; | ||
presenceState<T extends { | ||
@@ -134,0 +134,0 @@ [key: string]: any; |
@@ -130,4 +130,4 @@ "use strict"; | ||
const { config: { broadcast, presence, private: isPrivate }, } = this.params; | ||
this._onError((e) => callback && callback('CHANNEL_ERROR', e)); | ||
this._onClose(() => callback && callback('CLOSED')); | ||
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 accessTokenPayload = {}; | ||
@@ -140,4 +140,4 @@ const config = { | ||
}; | ||
if (this.socket.accessToken) { | ||
accessTokenPayload.access_token = this.socket.accessToken; | ||
if (this.socket.accessTokenValue) { | ||
accessTokenPayload.access_token = this.socket.accessTokenValue; | ||
} | ||
@@ -148,8 +148,7 @@ this.updateJoinPayload(Object.assign({ config }, accessTokenPayload)); | ||
this.joinPush | ||
.receive('ok', ({ postgres_changes: serverPostgresFilters, }) => { | ||
.receive('ok', async ({ postgres_changes }) => { | ||
var _a; | ||
this.socket.accessToken && | ||
this.socket.setAuth(this.socket.accessToken); | ||
if (serverPostgresFilters === undefined) { | ||
callback && callback('SUBSCRIBED'); | ||
this.socket.setAuth(); | ||
if (postgres_changes === undefined) { | ||
callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED); | ||
return; | ||
@@ -164,3 +163,3 @@ } | ||
const { filter: { event, schema, table, filter }, } = clientPostgresBinding; | ||
const serverPostgresFilter = serverPostgresFilters && serverPostgresFilters[i]; | ||
const serverPostgresFilter = postgres_changes && postgres_changes[i]; | ||
if (serverPostgresFilter && | ||
@@ -175,4 +174,3 @@ serverPostgresFilter.event === event && | ||
this.unsubscribe(); | ||
callback && | ||
callback('CHANNEL_ERROR', new Error('mismatch between server and client bindings for postgres changes')); | ||
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; | ||
@@ -182,3 +180,3 @@ } | ||
this.bindings.postgres_changes = newPostgresBindings; | ||
callback && callback('SUBSCRIBED'); | ||
callback && callback(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED); | ||
return; | ||
@@ -188,8 +186,7 @@ } | ||
.receive('error', (error) => { | ||
callback && | ||
callback('CHANNEL_ERROR', new Error(JSON.stringify(Object.values(error).join(', ') || 'error'))); | ||
callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, new Error(JSON.stringify(Object.values(error).join(', ') || 'error'))); | ||
return; | ||
}) | ||
.receive('timeout', () => { | ||
callback && callback('TIMED_OUT'); | ||
callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.TIMED_OUT); | ||
return; | ||
@@ -232,8 +229,9 @@ }); | ||
const { event, payload: endpoint_payload } = args; | ||
const authorization = this.socket.accessTokenValue | ||
? `Bearer ${this.socket.accessTokenValue}` | ||
: ''; | ||
const options = { | ||
method: 'POST', | ||
headers: { | ||
Authorization: this.socket.accessToken | ||
? `Bearer ${this.socket.accessToken}` | ||
: '', | ||
Authorization: authorization, | ||
apikey: this.socket.apiKey ? this.socket.apiKey : '', | ||
@@ -240,0 +238,0 @@ 'Content-Type': 'application/json', |
@@ -32,2 +32,3 @@ import type { WebSocket as WSWebSocket } from 'ws'; | ||
workerUrl?: string; | ||
accessToken?: () => Promise<string>; | ||
}; | ||
@@ -42,3 +43,3 @@ export declare type RealtimeMessage = { | ||
export declare type RealtimeRemoveChannelResponse = 'ok' | 'timed out' | 'error'; | ||
interface WebSocketLikeConstructor { | ||
export interface WebSocketLikeConstructor { | ||
new (address: string | URL, _ignored?: any, options?: { | ||
@@ -48,5 +49,10 @@ headers: Object | undefined; | ||
} | ||
declare type WebSocketLike = WebSocket | WSWebSocket | WSWebSocketDummy; | ||
export declare type WebSocketLike = WebSocket | WSWebSocket | WSWebSocketDummy; | ||
export interface WebSocketLikeError { | ||
error: any; | ||
message: string; | ||
type: string; | ||
} | ||
export default class RealtimeClient { | ||
accessToken: string | null; | ||
accessTokenValue: string | null; | ||
apiKey: string | null; | ||
@@ -83,2 +89,3 @@ channels: RealtimeChannel[]; | ||
fetch: Fetch; | ||
accessToken: (() => Promise<string>) | null; | ||
worker?: boolean; | ||
@@ -110,2 +117,7 @@ workerUrl?: string; | ||
/** | ||
* Returns the URL of the websocket. | ||
* @returns string The URL of the websocket. | ||
*/ | ||
endpointURL(): string; | ||
/** | ||
* Disconnects the socket. | ||
@@ -154,5 +166,17 @@ * | ||
* | ||
* @param token A JWT string. | ||
* If param is null it will use the `accessToken` callback function or the token set on the client. | ||
* | ||
* On callback used, it will set the value of the token internal to the client. | ||
* | ||
* @param token A JWT string to override the token set on the client. | ||
*/ | ||
setAuth(token: string | null): void; | ||
setAuth(token?: string | null): Promise<void>; | ||
/** | ||
* Sends a heartbeat message if the socket is connected. | ||
*/ | ||
sendHeartbeat(): Promise<void>; | ||
/** | ||
* Flushes send buffer | ||
*/ | ||
flushSendBuffer(): void; | ||
private _workerObjectUrl; | ||
@@ -159,0 +183,0 @@ } |
@@ -62,3 +62,3 @@ "use strict"; | ||
var _a; | ||
this.accessToken = null; | ||
this.accessTokenValue = null; | ||
this.apiKey = null; | ||
@@ -85,2 +85,3 @@ this.channels = []; | ||
}; | ||
this.accessToken = null; | ||
/** | ||
@@ -122,6 +123,6 @@ * Use either custom fetch, if provided, or default fetch to make HTTP requests | ||
this.heartbeatIntervalMs = options.heartbeatIntervalMs; | ||
const accessToken = (_a = options === null || options === void 0 ? void 0 : options.params) === null || _a === void 0 ? void 0 : _a.apikey; | ||
if (accessToken) { | ||
this.accessToken = accessToken; | ||
this.apiKey = accessToken; | ||
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; | ||
} | ||
@@ -153,2 +154,3 @@ this.reconnectAfterMs = (options === null || options === void 0 ? void 0 : options.reconnectAfterMs) | ||
} | ||
this.accessToken = (options === null || options === void 0 ? void 0 : options.accessToken) || null; | ||
} | ||
@@ -163,3 +165,3 @@ /** | ||
if (this.transport) { | ||
this.conn = new this.transport(this._endPointURL(), undefined, { | ||
this.conn = new this.transport(this.endpointURL(), undefined, { | ||
headers: this.headers, | ||
@@ -170,7 +172,7 @@ }); | ||
if (NATIVE_WEBSOCKET_AVAILABLE) { | ||
this.conn = new WebSocket(this._endPointURL()); | ||
this.conn = new WebSocket(this.endpointURL()); | ||
this.setupConnection(); | ||
return; | ||
} | ||
this.conn = new WSWebSocketDummy(this._endPointURL(), undefined, { | ||
this.conn = new WSWebSocketDummy(this.endpointURL(), undefined, { | ||
close: () => { | ||
@@ -181,3 +183,3 @@ this.conn = null; | ||
Promise.resolve().then(() => __importStar(require('ws'))).then(({ default: WS }) => { | ||
this.conn = new WS(this._endPointURL(), undefined, { | ||
this.conn = new WS(this.endpointURL(), undefined, { | ||
headers: this.headers, | ||
@@ -189,2 +191,9 @@ }); | ||
/** | ||
* Returns the URL of the websocket. | ||
* @returns string The URL of the websocket. | ||
*/ | ||
endpointURL() { | ||
return this._appendParams(this.endPoint, Object.assign({}, this.params, { vsn: constants_1.VSN })); | ||
} | ||
/** | ||
* Disconnects the socket. | ||
@@ -293,9 +302,16 @@ * | ||
* | ||
* @param token A JWT string. | ||
* If param is null it will use the `accessToken` callback function or the token set on the client. | ||
* | ||
* On callback used, it will set the value of the token internal to the client. | ||
* | ||
* @param token A JWT string to override the token set on the client. | ||
*/ | ||
setAuth(token) { | ||
if (token) { | ||
async setAuth(token = null) { | ||
let tokenToSend = token || | ||
(this.accessToken && (await this.accessToken())) || | ||
this.accessTokenValue; | ||
if (tokenToSend) { | ||
let parsed = null; | ||
try { | ||
parsed = JSON.parse(atob(token.split('.')[1])); | ||
parsed = JSON.parse(atob(tokenToSend.split('.')[1])); | ||
} | ||
@@ -308,15 +324,49 @@ catch (_error) { } | ||
this.log('auth', `InvalidJWTToken: Invalid value for JWT claim "exp" with value ${parsed.exp}`); | ||
return; | ||
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.accessToken = token; | ||
this.channels.forEach((channel) => { | ||
token && channel.updateJoinPayload({ access_token: token }); | ||
if (channel.joinedOnce && channel._isJoined()) { | ||
channel._push(constants_1.CHANNEL_EVENTS.access_token, { access_token: token }); | ||
} | ||
} | ||
/** | ||
* Sends a heartbeat message if the socket is connected. | ||
*/ | ||
async sendHeartbeat() { | ||
var _a; | ||
if (!this.isConnected()) { | ||
return; | ||
} | ||
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'); | ||
return; | ||
} | ||
this.pendingHeartbeatRef = this._makeRef(); | ||
this.push({ | ||
topic: 'phoenix', | ||
event: 'heartbeat', | ||
payload: {}, | ||
ref: this.pendingHeartbeatRef, | ||
}); | ||
this.setAuth(); | ||
} | ||
/** | ||
* Flushes send buffer | ||
*/ | ||
flushSendBuffer() { | ||
if (this.isConnected() && this.sendBuffer.length > 0) { | ||
this.sendBuffer.forEach((callback) => callback()); | ||
this.sendBuffer = []; | ||
} | ||
} | ||
/** | ||
* Return the next message ref, accounting for overflows | ||
@@ -372,10 +422,2 @@ * | ||
} | ||
/** | ||
* Returns the URL of the websocket. | ||
* | ||
* @internal | ||
*/ | ||
_endPointURL() { | ||
return this._appendParams(this.endPoint, Object.assign({}, this.params, { vsn: constants_1.VSN })); | ||
} | ||
/** @internal */ | ||
@@ -385,4 +427,3 @@ _onConnMessage(rawMessage) { | ||
let { topic, event, payload, ref } = msg; | ||
if ((ref && ref === this.pendingHeartbeatRef) || | ||
event === (payload === null || payload === void 0 ? void 0 : payload.type)) { | ||
if (ref && ref === this.pendingHeartbeatRef) { | ||
this.pendingHeartbeatRef = null; | ||
@@ -399,8 +440,8 @@ } | ||
async _onConnOpen() { | ||
this.log('transport', `connected to ${this._endPointURL()}`); | ||
this._flushSendBuffer(); | ||
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); | ||
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), this.heartbeatIntervalMs); | ||
} | ||
@@ -422,3 +463,3 @@ else { | ||
if (event.data.event === 'keepAlive') { | ||
this._sendHeartbeat(); | ||
this.sendHeartbeat(); | ||
} | ||
@@ -460,30 +501,2 @@ }; | ||
} | ||
/** @internal */ | ||
_flushSendBuffer() { | ||
if (this.isConnected() && this.sendBuffer.length > 0) { | ||
this.sendBuffer.forEach((callback) => callback()); | ||
this.sendBuffer = []; | ||
} | ||
} | ||
/** @internal */ | ||
_sendHeartbeat() { | ||
var _a; | ||
if (!this.isConnected()) { | ||
return; | ||
} | ||
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'); | ||
return; | ||
} | ||
this.pendingHeartbeatRef = this._makeRef(); | ||
this.push({ | ||
topic: 'phoenix', | ||
event: 'heartbeat', | ||
payload: {}, | ||
ref: this.pendingHeartbeatRef, | ||
}); | ||
this.setAuth(this.accessToken); | ||
} | ||
_workerObjectUrl(url) { | ||
@@ -490,0 +503,0 @@ let result_url; |
@@ -1,2 +0,2 @@ | ||
export declare const version = "2.10.9"; | ||
export declare const version = "2.11.0"; | ||
//# sourceMappingURL=version.d.ts.map |
@@ -1,2 +0,2 @@ | ||
export const version = '2.10.9'; | ||
export const version = '2.11.0'; | ||
//# sourceMappingURL=version.js.map |
@@ -131,3 +131,3 @@ import { CHANNEL_STATES } from './lib/constants'; | ||
/** Subscribe registers your client with the server */ | ||
subscribe(callback?: (status: `${REALTIME_SUBSCRIBE_STATES}`, err?: Error) => void, timeout?: number): RealtimeChannel; | ||
subscribe(callback?: (status: REALTIME_SUBSCRIBE_STATES, err?: Error) => void, timeout?: number): RealtimeChannel; | ||
presenceState<T extends { | ||
@@ -134,0 +134,0 @@ [key: string]: any; |
@@ -101,4 +101,4 @@ import { CHANNEL_EVENTS, CHANNEL_STATES } from './lib/constants'; | ||
const { config: { broadcast, presence, private: isPrivate }, } = this.params; | ||
this._onError((e) => callback && callback('CHANNEL_ERROR', e)); | ||
this._onClose(() => callback && callback('CLOSED')); | ||
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 accessTokenPayload = {}; | ||
@@ -111,4 +111,4 @@ const config = { | ||
}; | ||
if (this.socket.accessToken) { | ||
accessTokenPayload.access_token = this.socket.accessToken; | ||
if (this.socket.accessTokenValue) { | ||
accessTokenPayload.access_token = this.socket.accessTokenValue; | ||
} | ||
@@ -119,8 +119,7 @@ this.updateJoinPayload(Object.assign({ config }, accessTokenPayload)); | ||
this.joinPush | ||
.receive('ok', ({ postgres_changes: serverPostgresFilters, }) => { | ||
.receive('ok', async ({ postgres_changes }) => { | ||
var _a; | ||
this.socket.accessToken && | ||
this.socket.setAuth(this.socket.accessToken); | ||
if (serverPostgresFilters === undefined) { | ||
callback && callback('SUBSCRIBED'); | ||
this.socket.setAuth(); | ||
if (postgres_changes === undefined) { | ||
callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED); | ||
return; | ||
@@ -135,3 +134,3 @@ } | ||
const { filter: { event, schema, table, filter }, } = clientPostgresBinding; | ||
const serverPostgresFilter = serverPostgresFilters && serverPostgresFilters[i]; | ||
const serverPostgresFilter = postgres_changes && postgres_changes[i]; | ||
if (serverPostgresFilter && | ||
@@ -146,4 +145,3 @@ serverPostgresFilter.event === event && | ||
this.unsubscribe(); | ||
callback && | ||
callback('CHANNEL_ERROR', new Error('mismatch between server and client bindings for postgres changes')); | ||
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; | ||
@@ -153,3 +151,3 @@ } | ||
this.bindings.postgres_changes = newPostgresBindings; | ||
callback && callback('SUBSCRIBED'); | ||
callback && callback(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED); | ||
return; | ||
@@ -159,8 +157,7 @@ } | ||
.receive('error', (error) => { | ||
callback && | ||
callback('CHANNEL_ERROR', new Error(JSON.stringify(Object.values(error).join(', ') || 'error'))); | ||
callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, new Error(JSON.stringify(Object.values(error).join(', ') || 'error'))); | ||
return; | ||
}) | ||
.receive('timeout', () => { | ||
callback && callback('TIMED_OUT'); | ||
callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.TIMED_OUT); | ||
return; | ||
@@ -203,8 +200,9 @@ }); | ||
const { event, payload: endpoint_payload } = args; | ||
const authorization = this.socket.accessTokenValue | ||
? `Bearer ${this.socket.accessTokenValue}` | ||
: ''; | ||
const options = { | ||
method: 'POST', | ||
headers: { | ||
Authorization: this.socket.accessToken | ||
? `Bearer ${this.socket.accessToken}` | ||
: '', | ||
Authorization: authorization, | ||
apikey: this.socket.apiKey ? this.socket.apiKey : '', | ||
@@ -211,0 +209,0 @@ 'Content-Type': 'application/json', |
@@ -32,2 +32,3 @@ import type { WebSocket as WSWebSocket } from 'ws'; | ||
workerUrl?: string; | ||
accessToken?: () => Promise<string>; | ||
}; | ||
@@ -42,3 +43,3 @@ export declare type RealtimeMessage = { | ||
export declare type RealtimeRemoveChannelResponse = 'ok' | 'timed out' | 'error'; | ||
interface WebSocketLikeConstructor { | ||
export interface WebSocketLikeConstructor { | ||
new (address: string | URL, _ignored?: any, options?: { | ||
@@ -48,5 +49,10 @@ headers: Object | undefined; | ||
} | ||
declare type WebSocketLike = WebSocket | WSWebSocket | WSWebSocketDummy; | ||
export declare type WebSocketLike = WebSocket | WSWebSocket | WSWebSocketDummy; | ||
export interface WebSocketLikeError { | ||
error: any; | ||
message: string; | ||
type: string; | ||
} | ||
export default class RealtimeClient { | ||
accessToken: string | null; | ||
accessTokenValue: string | null; | ||
apiKey: string | null; | ||
@@ -83,2 +89,3 @@ channels: RealtimeChannel[]; | ||
fetch: Fetch; | ||
accessToken: (() => Promise<string>) | null; | ||
worker?: boolean; | ||
@@ -110,2 +117,7 @@ workerUrl?: string; | ||
/** | ||
* Returns the URL of the websocket. | ||
* @returns string The URL of the websocket. | ||
*/ | ||
endpointURL(): string; | ||
/** | ||
* Disconnects the socket. | ||
@@ -154,5 +166,17 @@ * | ||
* | ||
* @param token A JWT string. | ||
* If param is null it will use the `accessToken` callback function or the token set on the client. | ||
* | ||
* On callback used, it will set the value of the token internal to the client. | ||
* | ||
* @param token A JWT string to override the token set on the client. | ||
*/ | ||
setAuth(token: string | null): void; | ||
setAuth(token?: string | null): Promise<void>; | ||
/** | ||
* Sends a heartbeat message if the socket is connected. | ||
*/ | ||
sendHeartbeat(): Promise<void>; | ||
/** | ||
* Flushes send buffer | ||
*/ | ||
flushSendBuffer(): void; | ||
private _workerObjectUrl; | ||
@@ -159,0 +183,0 @@ } |
@@ -34,3 +34,3 @@ import { CHANNEL_EVENTS, CONNECTION_STATE, DEFAULT_HEADERS, DEFAULT_TIMEOUT, SOCKET_STATES, TRANSPORTS, VSN, WS_CLOSE_NORMAL, } from './lib/constants'; | ||
var _a; | ||
this.accessToken = null; | ||
this.accessTokenValue = null; | ||
this.apiKey = null; | ||
@@ -57,2 +57,3 @@ this.channels = []; | ||
}; | ||
this.accessToken = null; | ||
/** | ||
@@ -94,6 +95,6 @@ * Use either custom fetch, if provided, or default fetch to make HTTP requests | ||
this.heartbeatIntervalMs = options.heartbeatIntervalMs; | ||
const accessToken = (_a = options === null || options === void 0 ? void 0 : options.params) === null || _a === void 0 ? void 0 : _a.apikey; | ||
if (accessToken) { | ||
this.accessToken = accessToken; | ||
this.apiKey = accessToken; | ||
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; | ||
} | ||
@@ -125,2 +126,3 @@ this.reconnectAfterMs = (options === null || options === void 0 ? void 0 : options.reconnectAfterMs) | ||
} | ||
this.accessToken = (options === null || options === void 0 ? void 0 : options.accessToken) || null; | ||
} | ||
@@ -135,3 +137,3 @@ /** | ||
if (this.transport) { | ||
this.conn = new this.transport(this._endPointURL(), undefined, { | ||
this.conn = new this.transport(this.endpointURL(), undefined, { | ||
headers: this.headers, | ||
@@ -142,7 +144,7 @@ }); | ||
if (NATIVE_WEBSOCKET_AVAILABLE) { | ||
this.conn = new WebSocket(this._endPointURL()); | ||
this.conn = new WebSocket(this.endpointURL()); | ||
this.setupConnection(); | ||
return; | ||
} | ||
this.conn = new WSWebSocketDummy(this._endPointURL(), undefined, { | ||
this.conn = new WSWebSocketDummy(this.endpointURL(), undefined, { | ||
close: () => { | ||
@@ -153,3 +155,3 @@ this.conn = null; | ||
import('ws').then(({ default: WS }) => { | ||
this.conn = new WS(this._endPointURL(), undefined, { | ||
this.conn = new WS(this.endpointURL(), undefined, { | ||
headers: this.headers, | ||
@@ -161,2 +163,9 @@ }); | ||
/** | ||
* Returns the URL of the websocket. | ||
* @returns string The URL of the websocket. | ||
*/ | ||
endpointURL() { | ||
return this._appendParams(this.endPoint, Object.assign({}, this.params, { vsn: VSN })); | ||
} | ||
/** | ||
* Disconnects the socket. | ||
@@ -265,9 +274,16 @@ * | ||
* | ||
* @param token A JWT string. | ||
* If param is null it will use the `accessToken` callback function or the token set on the client. | ||
* | ||
* On callback used, it will set the value of the token internal to the client. | ||
* | ||
* @param token A JWT string to override the token set on the client. | ||
*/ | ||
setAuth(token) { | ||
if (token) { | ||
async setAuth(token = null) { | ||
let tokenToSend = token || | ||
(this.accessToken && (await this.accessToken())) || | ||
this.accessTokenValue; | ||
if (tokenToSend) { | ||
let parsed = null; | ||
try { | ||
parsed = JSON.parse(atob(token.split('.')[1])); | ||
parsed = JSON.parse(atob(tokenToSend.split('.')[1])); | ||
} | ||
@@ -280,15 +296,49 @@ catch (_error) { } | ||
this.log('auth', `InvalidJWTToken: Invalid value for JWT claim "exp" with value ${parsed.exp}`); | ||
return; | ||
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(CHANNEL_EVENTS.access_token, { | ||
access_token: tokenToSend, | ||
}); | ||
} | ||
}); | ||
} | ||
this.accessToken = token; | ||
this.channels.forEach((channel) => { | ||
token && channel.updateJoinPayload({ access_token: token }); | ||
if (channel.joinedOnce && channel._isJoined()) { | ||
channel._push(CHANNEL_EVENTS.access_token, { access_token: token }); | ||
} | ||
} | ||
/** | ||
* Sends a heartbeat message if the socket is connected. | ||
*/ | ||
async sendHeartbeat() { | ||
var _a; | ||
if (!this.isConnected()) { | ||
return; | ||
} | ||
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(WS_CLOSE_NORMAL, 'hearbeat timeout'); | ||
return; | ||
} | ||
this.pendingHeartbeatRef = this._makeRef(); | ||
this.push({ | ||
topic: 'phoenix', | ||
event: 'heartbeat', | ||
payload: {}, | ||
ref: this.pendingHeartbeatRef, | ||
}); | ||
this.setAuth(); | ||
} | ||
/** | ||
* Flushes send buffer | ||
*/ | ||
flushSendBuffer() { | ||
if (this.isConnected() && this.sendBuffer.length > 0) { | ||
this.sendBuffer.forEach((callback) => callback()); | ||
this.sendBuffer = []; | ||
} | ||
} | ||
/** | ||
* Return the next message ref, accounting for overflows | ||
@@ -344,10 +394,2 @@ * | ||
} | ||
/** | ||
* Returns the URL of the websocket. | ||
* | ||
* @internal | ||
*/ | ||
_endPointURL() { | ||
return this._appendParams(this.endPoint, Object.assign({}, this.params, { vsn: VSN })); | ||
} | ||
/** @internal */ | ||
@@ -357,4 +399,3 @@ _onConnMessage(rawMessage) { | ||
let { topic, event, payload, ref } = msg; | ||
if ((ref && ref === this.pendingHeartbeatRef) || | ||
event === (payload === null || payload === void 0 ? void 0 : payload.type)) { | ||
if (ref && ref === this.pendingHeartbeatRef) { | ||
this.pendingHeartbeatRef = null; | ||
@@ -371,8 +412,8 @@ } | ||
async _onConnOpen() { | ||
this.log('transport', `connected to ${this._endPointURL()}`); | ||
this._flushSendBuffer(); | ||
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); | ||
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), this.heartbeatIntervalMs); | ||
} | ||
@@ -394,3 +435,3 @@ else { | ||
if (event.data.event === 'keepAlive') { | ||
this._sendHeartbeat(); | ||
this.sendHeartbeat(); | ||
} | ||
@@ -432,30 +473,2 @@ }; | ||
} | ||
/** @internal */ | ||
_flushSendBuffer() { | ||
if (this.isConnected() && this.sendBuffer.length > 0) { | ||
this.sendBuffer.forEach((callback) => callback()); | ||
this.sendBuffer = []; | ||
} | ||
} | ||
/** @internal */ | ||
_sendHeartbeat() { | ||
var _a; | ||
if (!this.isConnected()) { | ||
return; | ||
} | ||
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(WS_CLOSE_NORMAL, 'hearbeat timeout'); | ||
return; | ||
} | ||
this.pendingHeartbeatRef = this._makeRef(); | ||
this.push({ | ||
topic: 'phoenix', | ||
event: 'heartbeat', | ||
payload: {}, | ||
ref: this.pendingHeartbeatRef, | ||
}); | ||
this.setAuth(this.accessToken); | ||
} | ||
_workerObjectUrl(url) { | ||
@@ -462,0 +475,0 @@ let result_url; |
{ | ||
"name": "@supabase/realtime-js", | ||
"version": "2.10.9", | ||
"version": "2.11.0", | ||
"description": "Listen to realtime updates to your PostgreSQL database", | ||
@@ -55,3 +55,3 @@ "keywords": [ | ||
"jsonwebtoken": "^9.0.2", | ||
"mock-socket": "^9.0.3", | ||
"mock-socket": "^9.3.1", | ||
"npm-run-all": "^4.1.5", | ||
@@ -58,0 +58,0 @@ "nyc": "^15.1.0", |
@@ -1,1 +0,1 @@ | ||
export const version = '2.10.9' | ||
export const version = '2.11.0' |
@@ -113,2 +113,11 @@ import { CHANNEL_EVENTS, CHANNEL_STATES } from './lib/constants' | ||
interface PostgresChangesFilters { | ||
postgres_changes: { | ||
id: string | ||
event: string | ||
schema?: string | ||
table?: string | ||
filter?: string | ||
}[] | ||
} | ||
/** A channel is the basic building block of Realtime | ||
@@ -206,3 +215,3 @@ * and narrows the scope of data flow to subscribed clients. | ||
subscribe( | ||
callback?: (status: `${REALTIME_SUBSCRIBE_STATES}`, err?: Error) => void, | ||
callback?: (status: REALTIME_SUBSCRIBE_STATES, err?: Error) => void, | ||
timeout = this.timeout | ||
@@ -213,3 +222,2 @@ ): RealtimeChannel { | ||
} | ||
if (this.joinedOnce) { | ||
@@ -221,5 +229,8 @@ throw `tried to subscribe multiple times. 'subscribe' can only be called a single time per channel instance` | ||
} = this.params | ||
this._onError((e: Error) => callback && callback('CHANNEL_ERROR', e)) | ||
this._onClose(() => callback && callback('CLOSED')) | ||
this._onError((e: Error) => | ||
callback?.(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, e) | ||
) | ||
this._onClose(() => callback?.(REALTIME_SUBSCRIBE_STATES.CLOSED)) | ||
const accessTokenPayload: { access_token?: string } = {} | ||
@@ -234,4 +245,4 @@ const config = { | ||
if (this.socket.accessToken) { | ||
accessTokenPayload.access_token = this.socket.accessToken | ||
if (this.socket.accessTokenValue) { | ||
accessTokenPayload.access_token = this.socket.accessTokenValue | ||
} | ||
@@ -245,81 +256,63 @@ | ||
this.joinPush | ||
.receive( | ||
'ok', | ||
({ | ||
postgres_changes: serverPostgresFilters, | ||
}: { | ||
postgres_changes: { | ||
id: string | ||
event: string | ||
schema?: string | ||
table?: string | ||
filter?: string | ||
}[] | ||
}) => { | ||
this.socket.accessToken && | ||
this.socket.setAuth(this.socket.accessToken) | ||
.receive('ok', async ({ postgres_changes }: PostgresChangesFilters) => { | ||
this.socket.setAuth() | ||
if (postgres_changes === undefined) { | ||
callback?.(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED) | ||
return | ||
} else { | ||
const clientPostgresBindings = this.bindings.postgres_changes | ||
const bindingsLen = clientPostgresBindings?.length ?? 0 | ||
const newPostgresBindings = [] | ||
if (serverPostgresFilters === undefined) { | ||
callback && callback('SUBSCRIBED') | ||
return | ||
} else { | ||
const clientPostgresBindings = this.bindings.postgres_changes | ||
const bindingsLen = clientPostgresBindings?.length ?? 0 | ||
const newPostgresBindings = [] | ||
for (let i = 0; i < bindingsLen; i++) { | ||
const clientPostgresBinding = clientPostgresBindings[i] | ||
const { | ||
filter: { event, schema, table, filter }, | ||
} = clientPostgresBinding | ||
const serverPostgresFilter = | ||
postgres_changes && postgres_changes[i] | ||
for (let i = 0; i < bindingsLen; i++) { | ||
const clientPostgresBinding = clientPostgresBindings[i] | ||
const { | ||
filter: { event, schema, table, filter }, | ||
} = clientPostgresBinding | ||
const serverPostgresFilter = | ||
serverPostgresFilters && serverPostgresFilters[i] | ||
if ( | ||
serverPostgresFilter && | ||
serverPostgresFilter.event === event && | ||
serverPostgresFilter.schema === schema && | ||
serverPostgresFilter.table === table && | ||
serverPostgresFilter.filter === filter | ||
) { | ||
newPostgresBindings.push({ | ||
...clientPostgresBinding, | ||
id: serverPostgresFilter.id, | ||
}) | ||
} else { | ||
this.unsubscribe() | ||
callback && | ||
callback( | ||
'CHANNEL_ERROR', | ||
new Error( | ||
'mismatch between server and client bindings for postgres changes' | ||
) | ||
) | ||
return | ||
} | ||
if ( | ||
serverPostgresFilter && | ||
serverPostgresFilter.event === event && | ||
serverPostgresFilter.schema === schema && | ||
serverPostgresFilter.table === table && | ||
serverPostgresFilter.filter === filter | ||
) { | ||
newPostgresBindings.push({ | ||
...clientPostgresBinding, | ||
id: serverPostgresFilter.id, | ||
}) | ||
} else { | ||
this.unsubscribe() | ||
callback?.( | ||
REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, | ||
new Error( | ||
'mismatch between server and client bindings for postgres changes' | ||
) | ||
) | ||
return | ||
} | ||
} | ||
this.bindings.postgres_changes = newPostgresBindings | ||
this.bindings.postgres_changes = newPostgresBindings | ||
callback && callback('SUBSCRIBED') | ||
return | ||
} | ||
callback && callback(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED) | ||
return | ||
} | ||
) | ||
}) | ||
.receive('error', (error: { [key: string]: any }) => { | ||
callback && | ||
callback( | ||
'CHANNEL_ERROR', | ||
new Error( | ||
JSON.stringify(Object.values(error).join(', ') || 'error') | ||
) | ||
callback?.( | ||
REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, | ||
new Error( | ||
JSON.stringify(Object.values(error).join(', ') || 'error') | ||
) | ||
) | ||
return | ||
}) | ||
.receive('timeout', () => { | ||
callback && callback('TIMED_OUT') | ||
callback?.(REALTIME_SUBSCRIBE_STATES.TIMED_OUT) | ||
return | ||
}) | ||
} | ||
return this | ||
@@ -454,8 +447,9 @@ } | ||
const { event, payload: endpoint_payload } = args | ||
const authorization = this.socket.accessTokenValue | ||
? `Bearer ${this.socket.accessTokenValue}` | ||
: '' | ||
const options = { | ||
method: 'POST', | ||
headers: { | ||
Authorization: this.socket.accessToken | ||
? `Bearer ${this.socket.accessToken}` | ||
: '', | ||
Authorization: authorization, | ||
apikey: this.socket.apiKey ? this.socket.apiKey : '', | ||
@@ -533,3 +527,2 @@ 'Content-Type': 'application/json', | ||
const leavePush = new Push(this, CHANNEL_EVENTS.leave, {}, timeout) | ||
leavePush | ||
@@ -549,3 +542,2 @@ .receive('ok', () => { | ||
leavePush.send() | ||
if (!this._canPush()) { | ||
@@ -552,0 +544,0 @@ leavePush.trigger('ok', {}) |
@@ -43,2 +43,3 @@ import type { WebSocket as WSWebSocket } from 'ws' | ||
workerUrl?: string | ||
accessToken?: () => Promise<string> | ||
} | ||
@@ -58,3 +59,3 @@ | ||
interface WebSocketLikeConstructor { | ||
export interface WebSocketLikeConstructor { | ||
new ( | ||
@@ -67,5 +68,5 @@ address: string | URL, | ||
type WebSocketLike = WebSocket | WSWebSocket | WSWebSocketDummy | ||
export type WebSocketLike = WebSocket | WSWebSocket | WSWebSocketDummy | ||
interface WebSocketLikeError { | ||
export interface WebSocketLikeError { | ||
error: any | ||
@@ -84,3 +85,3 @@ message: string | ||
export default class RealtimeClient { | ||
accessToken: string | null = null | ||
accessTokenValue: string | null = null | ||
apiKey: string | null = null | ||
@@ -118,2 +119,3 @@ channels: RealtimeChannel[] = [] | ||
fetch: Fetch | ||
accessToken: (() => Promise<string>) | null = null | ||
worker?: boolean | ||
@@ -155,6 +157,6 @@ workerUrl?: string | ||
const accessToken = options?.params?.apikey | ||
if (accessToken) { | ||
this.accessToken = accessToken | ||
this.apiKey = accessToken | ||
const accessTokenValue = options?.params?.apikey | ||
if (accessTokenValue) { | ||
this.accessTokenValue = accessTokenValue | ||
this.apiKey = accessTokenValue | ||
} | ||
@@ -188,2 +190,3 @@ | ||
} | ||
this.accessToken = options?.accessToken || null | ||
} | ||
@@ -200,3 +203,3 @@ | ||
if (this.transport) { | ||
this.conn = new this.transport(this._endPointURL(), undefined, { | ||
this.conn = new this.transport(this.endpointURL(), undefined, { | ||
headers: this.headers, | ||
@@ -206,4 +209,5 @@ }) | ||
} | ||
if (NATIVE_WEBSOCKET_AVAILABLE) { | ||
this.conn = new WebSocket(this._endPointURL()) | ||
this.conn = new WebSocket(this.endpointURL()) | ||
this.setupConnection() | ||
@@ -213,3 +217,3 @@ return | ||
this.conn = new WSWebSocketDummy(this._endPointURL(), undefined, { | ||
this.conn = new WSWebSocketDummy(this.endpointURL(), undefined, { | ||
close: () => { | ||
@@ -221,3 +225,3 @@ this.conn = null | ||
import('ws').then(({ default: WS }) => { | ||
this.conn = new WS(this._endPointURL(), undefined, { | ||
this.conn = new WS(this.endpointURL(), undefined, { | ||
headers: this.headers, | ||
@@ -230,2 +234,13 @@ }) | ||
/** | ||
* Returns the URL of the websocket. | ||
* @returns string The URL of the websocket. | ||
*/ | ||
endpointURL(): string { | ||
return this._appendParams( | ||
this.endPoint, | ||
Object.assign({}, this.params, { vsn: VSN }) | ||
) | ||
} | ||
/** | ||
* Disconnects the socket. | ||
@@ -347,9 +362,18 @@ * | ||
* | ||
* @param token A JWT string. | ||
* If param is null it will use the `accessToken` callback function or the token set on the client. | ||
* | ||
* On callback used, it will set the value of the token internal to the client. | ||
* | ||
* @param token A JWT string to override the token set on the client. | ||
*/ | ||
setAuth(token: string | null): void { | ||
if (token) { | ||
async setAuth(token: string | null = null): Promise<void> { | ||
let tokenToSend = | ||
token || | ||
(this.accessToken && (await this.accessToken())) || | ||
this.accessTokenValue | ||
if (tokenToSend) { | ||
let parsed = null | ||
try { | ||
parsed = JSON.parse(atob(token.split('.')[1])) | ||
parsed = JSON.parse(atob(tokenToSend.split('.')[1])) | ||
} catch (_error) {} | ||
@@ -364,19 +388,57 @@ if (parsed && parsed.exp) { | ||
) | ||
return | ||
return Promise.reject( | ||
`InvalidJWTToken: Invalid value for JWT claim "exp" with value ${parsed.exp}` | ||
) | ||
} | ||
} | ||
} | ||
this.accessToken = token | ||
this.accessTokenValue = tokenToSend | ||
this.channels.forEach((channel) => { | ||
tokenToSend && channel.updateJoinPayload({ access_token: tokenToSend }) | ||
this.channels.forEach((channel) => { | ||
token && channel.updateJoinPayload({ access_token: token }) | ||
if (channel.joinedOnce && channel._isJoined()) { | ||
channel._push(CHANNEL_EVENTS.access_token, { access_token: token }) | ||
} | ||
if (channel.joinedOnce && channel._isJoined()) { | ||
channel._push(CHANNEL_EVENTS.access_token, { | ||
access_token: tokenToSend, | ||
}) | ||
} | ||
}) | ||
} | ||
} | ||
/** | ||
* Sends a heartbeat message if the socket is connected. | ||
*/ | ||
async sendHeartbeat() { | ||
if (!this.isConnected()) { | ||
return | ||
} | ||
if (this.pendingHeartbeatRef) { | ||
this.pendingHeartbeatRef = null | ||
this.log( | ||
'transport', | ||
'heartbeat timeout. Attempting to re-establish connection' | ||
) | ||
this.conn?.close(WS_CLOSE_NORMAL, 'hearbeat timeout') | ||
return | ||
} | ||
this.pendingHeartbeatRef = this._makeRef() | ||
this.push({ | ||
topic: 'phoenix', | ||
event: 'heartbeat', | ||
payload: {}, | ||
ref: this.pendingHeartbeatRef, | ||
}) | ||
this.setAuth() | ||
} | ||
/** | ||
* Flushes send buffer | ||
*/ | ||
flushSendBuffer() { | ||
if (this.isConnected() && this.sendBuffer.length > 0) { | ||
this.sendBuffer.forEach((callback) => callback()) | ||
this.sendBuffer = [] | ||
} | ||
} | ||
/** | ||
* Use either custom fetch, if provided, or default fetch to make HTTP requests | ||
@@ -461,14 +523,2 @@ * | ||
/** | ||
* Returns the URL of the websocket. | ||
* | ||
* @internal | ||
*/ | ||
private _endPointURL(): string { | ||
return this._appendParams( | ||
this.endPoint, | ||
Object.assign({}, this.params, { vsn: VSN }) | ||
) | ||
} | ||
/** @internal */ | ||
@@ -479,6 +529,3 @@ private _onConnMessage(rawMessage: { data: any }) { | ||
if ( | ||
(ref && ref === this.pendingHeartbeatRef) || | ||
event === payload?.type | ||
) { | ||
if (ref && ref === this.pendingHeartbeatRef) { | ||
this.pendingHeartbeatRef = null | ||
@@ -505,4 +552,4 @@ } | ||
private async _onConnOpen() { | ||
this.log('transport', `connected to ${this._endPointURL()}`) | ||
this._flushSendBuffer() | ||
this.log('transport', `connected to ${this.endpointURL()}`) | ||
this.flushSendBuffer() | ||
this.reconnectTimer.reset() | ||
@@ -512,3 +559,3 @@ if (!this.worker) { | ||
this.heartbeatTimer = setInterval( | ||
() => this._sendHeartbeat(), | ||
() => this.sendHeartbeat(), | ||
this.heartbeatIntervalMs | ||
@@ -531,3 +578,3 @@ ) | ||
if (event.data.event === 'keepAlive') { | ||
this._sendHeartbeat() | ||
this.sendHeartbeat() | ||
} | ||
@@ -582,33 +629,2 @@ } | ||
/** @internal */ | ||
private _flushSendBuffer() { | ||
if (this.isConnected() && this.sendBuffer.length > 0) { | ||
this.sendBuffer.forEach((callback) => callback()) | ||
this.sendBuffer = [] | ||
} | ||
} | ||
/** @internal */ | ||
private _sendHeartbeat() { | ||
if (!this.isConnected()) { | ||
return | ||
} | ||
if (this.pendingHeartbeatRef) { | ||
this.pendingHeartbeatRef = null | ||
this.log( | ||
'transport', | ||
'heartbeat timeout. Attempting to re-establish connection' | ||
) | ||
this.conn?.close(WS_CLOSE_NORMAL, 'hearbeat timeout') | ||
return | ||
} | ||
this.pendingHeartbeatRef = this._makeRef() | ||
this.push({ | ||
topic: 'phoenix', | ||
event: 'heartbeat', | ||
payload: {}, | ||
ref: this.pendingHeartbeatRef, | ||
}) | ||
this.setAuth(this.accessToken) | ||
} | ||
private _workerObjectUrl(url: string | undefined): string { | ||
@@ -615,0 +631,0 @@ let result_url: string |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
389982
6945