diff --git a/CHANGELOG.md b/CHANGELOG.md index d7e6a06d..0f8a08a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +5.0.0 +===== + +In v5 release we are moving to Rollup to build the library. For `centrifuge-js` this means both ESM and CommonJS support. The migration includes some changes in how we provide Protobuf version of Centrifuge client. That's why we are making a new major v5 release. + +For users which work with JSON-based Centrifuge client (default behaviour) the migration to v5 should be smooth and require no code changes. + +Users of Protobuf version of the client need to change how they import `Centrifuge` when using the library. Also, we removed `protocol` option of Centrifuge instance config object. Imported Protobuf client now automatically uses Protobuf protocol under the hood. + +For example, previously, when using Protobuf version of Centrifuge client, you have to import Protobuf client and then provide an option to constructor: + +```javascript +import Centrifuge from 'centrifuge/build/protobuf'; + +const centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket", { + protocol: 'protobuf' +}); +``` + +Now this simplifies to: + +```javascript +import { Centrifuge } from 'centrifuge/build/protobuf'; + +const centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket", {}); +``` + +Note - changed import and no need to pass `protocol: 'protobuf'`. See [readme](https://github.com/centrifugal/centrifuge-js#protobuf-support) for more information about using Protobuf client and constructing binary payloads. + 4.1.0 ===== diff --git a/README.md b/README.md index 962d06b2..4d7d4ae0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ -This SDK provides a client to connect to [Centrifugo](https://github.com/centrifugal/centrifugo) or any [Centrifuge-based](https://github.com/centrifugal/centrifuge) server using pure WebSocket or one of the fallback transports from web browser, ReactNative, or NodeJS environments. +This SDK provides a client to connect to [Centrifugo](https://github.com/centrifugal/centrifugo) or any [Centrifuge-based](https://github.com/centrifugal/centrifuge) server using pure WebSocket or one of the alternative transports (HTTP-streaming, SSE/EventSource, experimental WebTransport) from web browser, ReactNative, or NodeJS environments. -The client behaves according to a common [Centrifigo SDK spec](https://centrifugal.dev/docs/transports/client_api). It's recommended to read that before starting to work with this SDK as the spec covers common SDK behavior - describes client and subscription state transitions, main options and methods. Then proceed with this readme for more specifics about `centrifuge-js`. +> [!IMPORTANT] +> This library behaves according to a common [Centrifigo SDK spec](https://centrifugal.dev/docs/transports/client_api). It's recommended to read that before starting to work with this SDK as the spec covers common SDK behavior - describes client and subscription state transitions, main options and methods. Then proceed with this readme for more specifics about `centrifuge-js`. The features implemented by this SDK can be found in [SDK feature matrix](https://centrifugal.dev/docs/transports/client_sdk#sdk-feature-matrix). -> **`centrifuge-js` v4.x is compatible with [Centrifugo](https://github.com/centrifugal/centrifugo) server v4 and v5 and [Centrifuge](https://github.com/centrifugal/centrifuge) >= 0.25.0. For Centrifugo v2, Centrifugo v3 and Centrifuge < 0.25.0 you should use `centrifuge-js` v2.x.** +> `centrifuge-js` v5.x is compatible with [Centrifugo](https://github.com/centrifugal/centrifugo) server v4 and v5 and [Centrifuge](https://github.com/centrifugal/centrifuge) >= 0.25.0. For Centrifugo v2, Centrifugo v3 and Centrifuge < 0.25.0 you should use `centrifuge-js` v2.x. * [Install](#install) * [Quick start](#quick-start) @@ -41,13 +42,13 @@ And then in your project: import { Centrifuge } from 'centrifuge'; ``` -In browser, you can import SDK from CDN (replace `4.0.0` with a concrete version number you want to use, see [releases](https://github.com/centrifugal/centrifuge-js/releases)): +In browser, you can import SDK from CDN (replace `5.0.0` with a concrete version number you want to use, see [releases](https://github.com/centrifugal/centrifuge-js/releases)): ```html - + ``` -See also [centrifuge-js on cdnjs](https://cdnjs.com/libraries/centrifuge). Note that `centrifuge-js` browser builds target [ES6](https://caniuse.com/es6) at this point. +See also [centrifuge-js on cdnjs](https://cdnjs.com/libraries/centrifuge). Note that `centrifuge-js` browser builds target [ES6](https://caniuse.com/es6). **By default, library works with JSON only**, if you want to send binary payloads go to [Protobuf support](#protobuf-support) section to see how to import client with Protobuf support. @@ -142,7 +143,7 @@ If you want to use SockJS you must also import SockJS client before centrifuge.j ```html - + ``` Or provide it explicitly as a dependency: @@ -243,7 +244,7 @@ centrifuge.publish("channel", {"input": "hello"}).then(function(res) { #### send method -This is only valid for Centrifuge library and does not work for Centrifugo server at the moment. `send` method allows sending asynchronous message from a client to a server. +This is only valid for Centrifuge server library for Go and does not work for Centrifugo server at the moment. `send` method allows sending asynchronous message from a client to a server. ```javascript centrifuge.send({"input": "hello"}).then(function(res) { @@ -342,6 +343,8 @@ If the token sets connection expiration then the client SDK will keep the token An example of possible `getToken` function implementation: ```javascript +import { Centrifuge, UnauthorizedError } from 'centrifuge'; + async function getToken() { if (!loggedIn) { return ""; @@ -350,7 +353,7 @@ async function getToken() { if (!res.ok) { if (res.status === 403) { // Return special error to not proceed with token refreshes, client will be disconnected. - throw new Centrifuge.UnauthorizedError(); + throw new UnauthorizedError(); } // Any other error thrown will result into token refresh re-attempts. throw new Error(`Unexpected status code ${res.status}`); @@ -548,6 +551,8 @@ If token sets subscription expiration client SDK will keep token refreshed. It d An example: ```javascript +import { Centrifuge, UnauthorizedError } from 'centrifuge'; + async function getToken(ctx) { // ctx argument has a channel. const res = await fetch('/centrifuge/subscription_token', { @@ -558,7 +563,7 @@ async function getToken(ctx) { if (!res.ok) { if (res.status === 403) { // Return special error to not proceed with token refreshes, subscription will be unsubscribed. - throw new Centrifuge.UnauthorizedError(); + throw new UnauthorizedError(); } // Any other error thrown will result into token refresh re-attempts. throw new Error(`Unexpected status code ${res.status}`); @@ -626,7 +631,51 @@ This call will flush all collected commands to a network. ## Server-side subscriptions -TODO. +We encourage using client-side subscriptions where possible as they provide a better control and isolation from connection. But in some cases you may want to use [server-side subscriptions](https://centrifugal.dev/docs/server/server_subs) (i.e. subscriptions created by server upon connection establishment). + +Technically, client SDK keeps server-side subscriptions in internal registry (similar to client-side subscriptions but without possibility to control them). + +To listen for server-side subscription events use callbacks as shown in example below: + +```javascript +const client = new Centrifuge('ws://localhost:8000/connection/websocket', {}); + +client.on('subscribed', function(ctx) { + // Called when subscribed to a server-side channel upon Client moving to + // connected state or during connection lifetime if server sends Subscribe + // push message. + console.log('subscribed to server-side channel', ctx.channel); +}); + +client.on('subscribing', function(ctx) { + // Called when existing connection lost (Client reconnects) or Client + // explicitly disconnected. Client continue keeping server-side subscription + // registry with stream position information where applicable. + console.log('subscribing to server-side channel', ctx.channel); +}); + +client.on('unsubscribed', function(ctx) { + // Called when server sent unsubscribe push or server-side subscription + // previously existed in SDK registry disappeared upon Client reconnect. + console.log('unsubscribed from server-side channel', ctx.channel); +}); + +client.on('publication', function(ctx) { + // Called when server sends Publication over server-side subscription. + console.log('publication receive from server-side channel', ctx.channel, ctx.data); +}); + +client.connect(); +``` + +Server-side subscription events mostly mimic events of client-side subscriptions. But again – they do not provide control to the client and managed entirely by a server side. + +Additionally, Client has several top-level methods to call with server-side subscription related operations: + +* `publish(channel, data)` +* `history(channel, options)` +* `presence(channel)` +* `presenceStats(channel)` ## Configuration options @@ -652,10 +701,6 @@ When client disconnected from a server it will automatically try to reconnect us `maxServerPingDelay` sets the maximum delay of server pings after which connection is considered broken and client reconnects. In milliseconds. Default is `10000`. -### protocol - -By default, client works using `json` protocol. If you want to use binary transfer with Protobuf-based protocol this option must be set to `protobuf`. See more details about Protobuf communication in a special chapter. - ### token Set initial connection token. @@ -690,29 +735,21 @@ Timeout for operations in milliseconds. ## Protobuf support -To import client with Protobuf protocol support: +To import client which uses Protobuf protocol under the hood: ```html - + ``` Or if you are developing with npm: ```javascript -import Centrifuge from 'centrifuge/build/protobuf'; +import { Centrifuge } from 'centrifuge/build/protobuf'; ``` This client uses [protobuf.js](https://github.com/dcodeIO/ProtoBuf.js/) under the hood. -To enable binary websocket add `protocol: 'protobuf'` option to Centrifuge configuration options: - -```javascript -const centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket", { - protocol: 'protobuf' -}); -``` - -When running with Protobuf protocol, you can send and receive any binary data as `Uint8Array`. Make sure data is properly encoded when calling methods of Centrifuge Protobuf-based instance. For example, you can not just send JSON-like objects like in JSON protocol case, you need to encode data to `Uint8Array` first: +When running with Protobuf-based client, you can send and receive any binary data as `Uint8Array`. Make sure data is properly encoded when calling methods of Centrifuge Protobuf-based instance. For example, you can not just send JSON-like objects like in JSON protocol case, you need to encode data to `Uint8Array` first: ```javascript const data = new TextEncoder("utf-8").encode(JSON.stringify({"any": "data"})); @@ -732,8 +769,8 @@ npm install ws At this point you have 2 options. Explicitly pass WebSocket object to Centrifuge. ```javascript -const { Centrifuge } = require('centrifuge'); -const WebSocket = require('ws'); +import { Centrifuge } from 'centrifuge'; +import WebSocket from 'ws'; var centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket', { websocket: WebSocket @@ -743,8 +780,10 @@ var centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket', { Or define it globally: ```javascript -const { Centrifuge } = require('centrifuge'); -global.WebSocket = require('ws'); +import { Centrifuge } from 'centrifuge'; +import WebSocket from 'ws'; + +global.WebSocket = WebSocket; const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket'); ``` diff --git a/fixup.sh b/fixup.sh new file mode 100755 index 00000000..89b16d24 --- /dev/null +++ b/fixup.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cat >build/protobuf/package.json < TypedEventEmitter, options?: Partial) { super(); @@ -1833,3 +1837,7 @@ export class Centrifuge extends (EventEmitter as new () => TypedEventEmitter { /** Client events which can be emitted. */ export type ClientEvents = { + /** called when client state changes */ state: (ctx: StateContext) => void; + /** called when client goes to connecting state */ connecting: (ctx: ConnectingContext) => void; + /** called when client goes to connected state */ connected: (ctx: ConnectedContext) => void; + /** called when client goes to disconnected state */ disconnected: (ctx: DisconnectedContext) => void; // Async message coming from a server. @@ -48,16 +52,23 @@ export enum State { /** Events of Subscription. */ export type SubscriptionEvents = { + /** called when subscription state changes */ state: (ctx: SubscriptionStateContext) => void; + /** called when subscription state goes to subscribing */ subscribing: (ctx: SubscribingContext) => void; + /** called when subscription state goes to subscribed */ subscribed: (ctx: SubscribedContext) => void; + /** called when subscription state goes to unsubscribed */ unsubscribed: (ctx: UnsubscribedContext) => void; + /** called when publication from channel received */ publication: (ctx: PublicationContext) => void; + /** called when join event from channel received */ join: (ctx: JoinContext) => void; + /** called when leave event from channel received */ leave: (ctx: LeaveContext) => void; - // listen to errors happening internally. + /** listen to subscription errors happening internally */ error: (ctx: SubscriptionErrorContext) => void; }