centrifuge 2.8.5 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +332 -679
  2. package/build/centrifuge.d.ts +156 -0
  3. package/build/centrifuge.js +1512 -0
  4. package/build/centrifuge.js.map +1 -0
  5. package/build/codes.d.ts +35 -0
  6. package/build/codes.js +39 -0
  7. package/build/codes.js.map +1 -0
  8. package/build/index.d.ts +4 -0
  9. package/build/index.js +23 -0
  10. package/build/index.js.map +1 -0
  11. package/build/json.d.ts +1 -0
  12. package/build/json.js +18 -0
  13. package/build/json.js.map +1 -0
  14. package/build/protobuf/client.proto.json +791 -0
  15. package/build/protobuf/index.d.ts +18 -0
  16. package/build/protobuf/index.js +64 -0
  17. package/build/protobuf/index.js.map +1 -0
  18. package/build/subscription.d.ts +82 -0
  19. package/build/subscription.js +577 -0
  20. package/build/subscription.js.map +1 -0
  21. package/build/transport_http_stream.d.ts +1 -0
  22. package/build/transport_http_stream.js +201 -0
  23. package/build/transport_http_stream.js.map +1 -0
  24. package/build/transport_sockjs.d.ts +1 -0
  25. package/build/transport_sockjs.js +46 -0
  26. package/build/transport_sockjs.js.map +1 -0
  27. package/build/transport_sse.d.ts +1 -0
  28. package/build/transport_sse.js +89 -0
  29. package/build/transport_sse.js.map +1 -0
  30. package/build/transport_websocket.d.ts +1 -0
  31. package/build/transport_websocket.js +58 -0
  32. package/build/transport_websocket.js.map +1 -0
  33. package/build/transport_webtransport.d.ts +1 -0
  34. package/build/transport_webtransport.js +177 -0
  35. package/build/transport_webtransport.js.map +1 -0
  36. package/build/types.d.ts +276 -0
  37. package/build/types.js +18 -0
  38. package/build/types.js.map +1 -0
  39. package/build/utils.d.ts +1 -0
  40. package/build/utils.js +52 -0
  41. package/build/utils.js.map +1 -0
  42. package/dist/centrifuge.js +7 -3564
  43. package/dist/centrifuge.js.map +7 -1
  44. package/dist/centrifuge.protobuf.js +10 -11394
  45. package/dist/centrifuge.protobuf.js.map +7 -1
  46. package/package.json +48 -34
  47. package/.babelrc +0 -4
  48. package/.editorconfig +0 -12
  49. package/.eslintrc +0 -179
  50. package/.gitattributes +0 -1
  51. package/.github/FUNDING.yml +0 -3
  52. package/.github/workflows/ci.yml +0 -23
  53. package/.github/workflows/release.yml +0 -24
  54. package/.nvmrc +0 -1
  55. package/CHANGELOG.md +0 -473
  56. package/dist/centrifuge.d.ts +0 -221
  57. package/dist/centrifuge.min.js +0 -2
  58. package/dist/centrifuge.min.js.map +0 -1
  59. package/dist/centrifuge.protobuf.d.ts +0 -3
  60. package/dist/centrifuge.protobuf.min.js +0 -2
  61. package/dist/centrifuge.protobuf.min.js.map +0 -1
  62. package/make-proto +0 -3
  63. package/src/centrifuge.js +0 -1918
  64. package/src/client.proto.json +0 -577
  65. package/src/index.js +0 -2
  66. package/src/index_protobuf.js +0 -2
  67. package/src/json.js +0 -48
  68. package/src/protobuf.js +0 -257
  69. package/src/subscription.js +0 -281
  70. package/src/utils.js +0 -40
  71. package/test/index.spec.js +0 -47
  72. package/webpack.config.js +0 -52
package/README.md CHANGED
@@ -1,33 +1,42 @@
1
- # Centrifuge client for NodeJS and browser
1
+ # Centrifuge and Centrifugo bidirectional SDK for NodeJS, React-Native and browser
2
2
 
3
- This client can connect to [Centrifuge](https://github.com/centrifugal/centrifuge) server (and [Centrifugo](https://github.com/centrifugal/centrifugo) in particular) using pure WebSocket or [SockJS](https://github.com/sockjs/sockjs-client) polyfill transports from web browser or NodeJS environments.
3
+ 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.
4
4
 
5
- * [Install and quick start](#install-and-quick-start)
6
- * [Connection Token](#connection-token)
7
- * [Configuration parameters](#configuration-parameters)
5
+ 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`.
6
+
7
+ The features implemented by this SDK can be found in [SDK feature matrix](https://centrifugal.dev/docs/transports/client_sdk#sdk-feature-matrix).
8
+
9
+ * [Install](#install)
10
+ * [Quick start](#quick-start)
11
+ * [WebSocket transport](#websocket-transport)
12
+ * [Using fallbacks](#using-fallbacks)
13
+ * [Bidirectional emulation](#bidirectional-emulation)
14
+ * [SockJS](#using-sockjs)
15
+ * [WebTransport (experimental)](#webtransport-experimental)
8
16
  * [Client API](#client-api)
9
- * [Private channels subscription](#private-channels-subscription)
17
+ * [Client methods and events](#client-methods-and-events)
18
+ * [Connection token](#connection-token)
19
+ * [Subscription API](#subscription-api)
20
+ * [Subscription methods and events](#client-methods-and-events)
21
+ * [Subscription token](#subscription-token)
22
+ * [Message batching](#message-batching)
10
23
  * [Server-side subscriptions](#server-side-subscriptions)
11
- * [Connection expiration](#connection-expiration)
24
+ * [Configuration options](#configuration-options)
12
25
  * [Protobuf support](#protobuf-support)
13
- * [Browser support](#browser-support)
14
26
  * [Using with NodeJS](#using-with-nodejs)
15
- * [Custom XMLHttpRequest](#custom-xmlhttprequest)
16
27
  * [Custom WebSocket constructor](#custom-websocket-constructor)
17
- * [Subscribe since known position](#subscribe-since-known-position)
18
- * [Feature Matrix](#feature-matrix)
19
28
 
20
- ## Install and quick start
29
+ ## Install
21
30
 
22
- The simplest way is to include `centrifuge-js` into your web page using `script` tag. For example, from CDN (replace `X` to concrete version number):
31
+ Using CDN (replace `3.0.0` to a concrete version number):
23
32
 
24
33
  ```html
25
- <script src="https://cdn.jsdelivr.net/gh/centrifugal/centrifuge-js@2.X.X/dist/centrifuge.min.js"></script>
34
+ <script src="https://unpkg.com/centrifuge@3.0.0/dist/centrifuge.js"></script>
26
35
  ```
27
36
 
28
- Or check out [centrifuge-js on cdnjs.com](https://cdnjs.com/libraries/centrifuge).
37
+ It's also [available on cdnjs](https://cdnjs.com/libraries/centrifuge).
29
38
 
30
- Client is also available via `npm`:
39
+ You can install this SDK via `npm`:
31
40
 
32
41
  ```bash
33
42
  npm install centrifuge
@@ -36,274 +45,186 @@ npm install centrifuge
36
45
  And then:
37
46
 
38
47
  ```javascript
39
- var Centrifuge = require("centrifuge");
40
- ```
41
-
42
- Default library works with JSON only, see `Protobuf support` section to see how to import client with Protobuf support.
43
-
44
- As soon as you installed and imported `centrifuge-js` you can create new `Centrifuge` object instance, subscribe on channel and call `.connect()` method to make actual connection to server:
45
-
46
- ```javascript
47
- var centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket');
48
-
49
- centrifuge.subscribe("news", function(message) {
50
- console.log(message);
51
- });
52
-
53
- centrifuge.connect();
54
- ```
55
-
56
- In example above we initialize `Centrifuge` object instance, subscribe on channel `news`, print all new messages received from channel `news` into console and actually make connection to server. And that's all for basic real-time messaging on client side!
57
-
58
- If you want to use SockJS you must also import SockJS client before centrifuge.js
59
-
60
- ```html
61
- <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1.3/dist/sockjs.min.js" type="text/javascript"></script>
62
- <script src="centrifuge.js" type="text/javascript"></script>
63
- ```
64
-
65
- Or provide it explicitly:
66
-
67
- ```javascript
68
- var Centrifuge = require("centrifuge");
69
- var SockJS = require('sockjs-client');
70
-
71
- var centrifuge = new Centrifuge("http://localhost:8000/connection/sockjs", {
72
- sockjs: SockJS
73
- })
48
+ import { Centrifuge } from 'centrifuge';
74
49
  ```
75
50
 
76
- **`Centrifuge` object is an instance of [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter).**
51
+ By default, library works with JSON only, see [Protobuf support](#protobuf-support) section to see how to import client with Protobuf support.
77
52
 
78
- ## Connection Token
53
+ ## Quick start
79
54
 
80
- If you are connecting to Centrifugo **you must also provide connection token**:
55
+ The basic usage example may look like this:
81
56
 
82
57
  ```javascript
83
- var centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket');
58
+ // Use WebSocket transport endpoint.
59
+ const centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket');
84
60
 
85
- centrifuge.setToken(YOUR_TOKEN);
61
+ // Allocate Subscription to a channel.
62
+ const sub = centrifuge.newSubscription('news');
86
63
 
87
- centrifuge.subscribe("news", function(message) {
88
- console.log(message);
64
+ // React on `news` channel real-time publications.
65
+ sub.on('publication', function(ctx) {
66
+ console.log(ctx.data);
89
67
  });
90
68
 
69
+ // Trigger subscribe process.
70
+ sub.subscribe();
71
+
72
+ // Trigger actual connection establishement.
91
73
  centrifuge.connect();
92
74
  ```
93
75
 
94
- This token contains information about user of your application that tries to connect. See [server authentication documentation](https://centrifugal.github.io/centrifugo/server/authentication/) for details on how to generate it on your backend side.
76
+ Note, that we explicitly call `.connect()` method to initiate connection establishement with a server and `.subscribe()` method to move Subscription to `subsribing` state (which should transform into `subscribed` state soon after connection with a server is established). The order of `.connect()` and `.subscribe` calls does not actually matter here.
95
77
 
96
- **Connection JWT comes to Javascript code from application backend - i.e. must be generated on backend**.
78
+ **`Centrifuge` object and `Subscription` object are both instances of [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter).** Below we will describe events that can be exposed in detail.
97
79
 
98
- ## Configuration parameters
80
+ ## Websocket transport
99
81
 
100
- Let's also look at optional configuration parameters available when initializing `Centrifuge` object instance.
101
-
102
- #### websocket
103
-
104
- `websocket` option allows to explicitly provide custom WebSocket client to use. By default centrifuge-js will try to use global WebSocket object, so if you are in web browser – it will just use native WebSocket implementation. See notes about using `centrifuge-js` with NodeJS below.
105
-
106
- #### sockjs
107
-
108
- `sockjs` option allows to explicitly provide SockJS client object to Centrifuge client.
109
-
110
- For example this can be useful if you develop in ES6 with imports:
111
-
112
- ```javascript
113
- import Centrifuge from 'centrifuge'
114
- import SockJS from 'sockjs-client'
82
+ WebSocket is the main protocol used by `centrifuge-js` to communicate with a server. In browser environment it's available globally, but if you want to connect from NodeJS env – then you need to provide WebSocket constructor to `centrifuge-js` explicitly. [See below](#using-with-nodejs) more information about this.
115
83
 
116
- var centrifuge = new Centrifuge('https://centrifuge.example.com/connection/sockjs', {
117
- sockjs: SockJS
118
- });
119
- ```
84
+ ## Using fallbacks
120
85
 
121
- #### sockjsTransports
86
+ In the quick start example above we used WebSocket endpoint to configure Centrifuge. WebSocket is the main transport – it's bidirectional out of the box.
122
87
 
123
- In case of using SockJS additional configuration parameter can be used - `sockjsTransports`.
88
+ In some cases though, WebSocket connection may not be established (for example, due to corporate firewalls and proxies). For such situations `centrifuge-js` offers several WebSocket fallback options.
124
89
 
125
- It defines allowed SockJS transports and by default equals
90
+ ### Bidirectional emulation
126
91
 
127
- ```javascript
128
- var centrifuge = new Centrifuge(
129
- 'http://centrifuge.example.com/connection/sockjs',
130
- {
131
- sockjsTransports: [
132
- 'websocket',
133
- 'xdr-streaming',
134
- 'xhr-streaming',
135
- 'eventsource',
136
- 'iframe-eventsource',
137
- 'iframe-htmlfile',
138
- 'xdr-polling',
139
- 'xhr-polling',
140
- 'iframe-xhr-polling',
141
- 'jsonp-polling'
142
- ]
143
- });
144
- ```
92
+ SockJS is robust and stable product, but it's an extra dependency, it's pretty old, comes with some overhead and sticky sessions requirement for a distributed backend case. In most scenarios these days clients are fine to use WebSocket protocol for messaging. There are rare connection issues though which are caused by corporate firewall and proxy software. To deal with users behind such proxies Centrifuge SDK offers its own bidirectional emulation layer. This layer uses two HTTP-based transports:
145
93
 
146
- i.e. all possible SockJS transports.
94
+ * HTTP-streaming based on ReadableStream API
95
+ * SSE (EventSource).
147
96
 
148
- So to say `centrifuge-js` to use only `websocket` and `xhr-streaming` transports when
149
- using SockJS endpoint:
97
+ Bidirectional emulation must be first enabled on a server-side. For example, see Centrifugo docs to find out how. Then in Javascript you can slightly change client initialization and point it to a list of endpoints and transports you want to use:
150
98
 
151
99
  ```javascript
152
- var centrifuge = new Centrifuge('http://centrifuge.example.com/connection/sockjs', {
153
- sockjsTransports: ["websocket", "xhr-streaming"]
154
- });
100
+ const transports = [
101
+ {
102
+ transport: 'websocket',
103
+ endpoint: 'ws://example.com/connection/websocket'
104
+ },
105
+ {
106
+ transport: 'http_stream',
107
+ endpoint: 'http://example.com/connection/http_stream'
108
+ },
109
+ {
110
+ transport: 'sse',
111
+ endpoint: 'http://example.com/connection/sse'
112
+ }
113
+ ];
114
+ const centrifuge = new Centrifuge(transports);
115
+ centrifuge.connect()
155
116
  ```
156
117
 
157
- #### sockjsServer
118
+ In this case, client will try transports in order, one by one, during the initial handshake. Until success. Then will only use a successful transport during reconnects.
158
119
 
159
- `sockjsServer` is SockJS specific option to set server name into connection urls instead
160
- of random chars. See SockJS docs for more info.
161
-
162
- #### debug
163
-
164
- `debug` is a boolean option which is `false` by default. When enabled lots of various debug
165
- messages will be logged into javascript console. Mostly useful for development or
166
- troubleshooting.
120
+ Supported transports are:
167
121
 
168
- #### minRetry
122
+ * `websocket`
123
+ * `http_stream`
124
+ * `sse`
125
+ * `sockjs` (yes, SockJS can also be used as a fallback in the bidirectional emulation layer, but sticky session must be used on the backend in distributed case, SockJS is currently in DEPRECATED status in Centrifugal ecosystem).
126
+ * `webtransport` (experimental, see details below)
169
127
 
170
- When client disconnected from server it will automatically try to reconnect using exponential
171
- backoff algorithm to get interval between reconnect attempts which value grows exponentially.
172
- `minRetry` option sets minimal interval value in milliseconds. Default is `1000` milliseconds.
128
+ If you want to use sticky sessions on a load balancer level as an optimimization for Centrifugal bidirectional emulation layer keep in mind that we currently use `same-origin` credentials policy for emulation requests in `http_stream` and `sse` transport cases. So cookies will only be passed in same-origin case. Please open an issue in case you need to configure more relaxed credentials. Though in most cases stickyness based on client's IP may be sufficient enough.
173
129
 
174
- #### maxRetry
130
+ ### Using SockJS
175
131
 
176
- `maxRetry` sets upper interval value limit when reconnecting. Or your clients will never reconnect
177
- as exponent grows very fast:) Default is `20000` milliseconds.
132
+ **SockJS usage is DEPRECATED in the Centrifugal ecosystem**
178
133
 
179
- #### subscribeEndpoint
134
+ If you want to use SockJS you must also import SockJS client before centrifuge.js
180
135
 
181
- `subscribeEndpoint` is url to use when sending auth request for authorizing subscription on private channel. By default `/centrifuge/subscribe`. See also useful related options:
136
+ ```html
137
+ <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js" type="text/javascript"></script>
138
+ <script src="https://unpkg.com/centrifuge@3.0.0/dist/centrifuge.js" type="text/javascript"></script>
139
+ ```
182
140
 
183
- * `subscribeHeaders` - map of headers to send with subscribe request (default `{}`)
184
- * `subscribeParams` - map of params to include in subscribe endpoint url (default `{}`)
141
+ Or provide it explicitly as a dependency:
185
142
 
186
- #### refreshEndpoint
143
+ ```javascript
144
+ import { Centrifuge } from 'centrifuge'
145
+ import SockJS from 'sockjs-client'
187
146
 
188
- `refreshEndpoint` is url to use when refreshing client connection parameters when connection check mechanism enabled in Centrifugo configuration. See also related options:
147
+ const transports = [{
148
+ transport: "sockjs",
149
+ endpoint: "http://localhost:8000/connection/sockjs"
150
+ }];
189
151
 
190
- * `refreshHeaders` - map of headers to send with refresh request (default `{}`)
191
- * `refreshParams` - map of params to include in refresh url (default `{}`)
192
- * `refreshData` - send extra data in body (as JSON payload) when sending AJAX POST refresh request.
193
- * `refreshAttempts` - limit amount of refresh requests before giving up (by default `null` - unlimited)
194
- * `onRefreshFailed` - callback function called when `refreshAttempts` came to the end. By default `null` - i.e. nothing called.
195
- * `onRefresh` - optional callback to fully control refresh behaviour. This function will ve called as soon as connection token needs to be refreshed. After this it's up to application to get new token in a way it needs. As soon as application got token it must call callback passed as argument with proper data - see example below. *In this case `centrifuge-js` will not send automatic AJAX requests to your application*.
152
+ const centrifuge = new Centrifuge(transports, {
153
+ sockjs: SockJS
154
+ })
155
+ ```
196
156
 
197
- Here is an example of using custom `onRefresh` function:
157
+ Note, that in SockJS case endpoint starts with `http://`, not with `ws://` as we used above when connecting to a pure WebSocket endpoint.
198
158
 
199
- ```javascript
200
- centrifuge = new Centrifuge("http://localhost:8000/connection/websocket", {
201
- debug: true,
202
- onRefresh: function(ctx, cb) {
203
- let promise = fetch("http://localhost:3000/centrifuge/refresh", {
204
- method: "POST"
205
- }).then(function(resp) {
206
- resp.json().then(function(data) {
207
- // Data must be like {"status": 200, "data": {"token": "JWT"}} - see
208
- // type definitions in dist folder. Note that setting status to 200 is
209
- // required at moment. Any other status will result in refresh process
210
- // failure so client will eventually be disconnected by server.
211
- cb(data);
212
- });
213
- });
214
- }
215
- });
216
- ```
159
+ ## WebTransport (experimental)
217
160
 
218
- #### disableWithCredentials
161
+ WebTransport is experimental and is only supported by Centrifugo at the moment (i.e. it's not available in Centrifuge library for Go out of the box).
219
162
 
220
- `disableWithCredentials` is a reverse boolean option for control
221
- [withCredentials](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials)
222
- property of XMLHttpRequest. By default `false` - i.e. `withCredentials` property is enabled.
163
+ See information about it in [Centrifugo WebTransport docs](https://centrifugal.dev/docs/transports/webtransport).
223
164
 
224
165
  ## Client API
225
166
 
226
- When `Centrifuge` object properly initialized then it is ready to start communicating with server.
167
+ Let's look at top-level API of `Centrifuge` client.
168
+
169
+ ### Client methods and events
227
170
 
228
171
  #### connect method
229
172
 
230
- As we showed before, we must call `connect()` method to make an actual connection
173
+ As we already showed above, we must call `connect()` method to make an actual connection
231
174
  request to Centrifugo server:
232
175
 
233
176
  ```javascript
234
- var centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket');
235
-
177
+ const centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket');
236
178
  centrifuge.connect();
237
179
  ```
238
180
 
239
181
  `connect()` triggers an actual connection request to server.
240
182
 
241
- #### connect event
183
+ #### connected event
242
184
 
243
- After connection will be established and client credentials you provided authorized
244
- then `connect` event on `Centrifuge` object instance will be called.
185
+ As soon as connection is established and client successfully authenticated `connected` event on `Centrifuge` object instance will be called.
245
186
 
246
- You can listen to this setting event listener function on `connect` event:
187
+ It's possible to listen to this event by setting event listener function on `connected` event:
247
188
 
248
189
  ```javascript
249
- centrifuge.on('connect', function(context) {
250
- // now client connected to Centrifugo and authorized
190
+ centrifuge.on('connected', function(ctx) {
191
+ // now client connected to Centrifugo and authenticated.
251
192
  });
252
193
  ```
253
194
 
254
- What's in `context`:
195
+ #### connecting event
196
+
197
+ `connecting` event fired when Centrifuge object goes to connecting state. This may be called during initial connect, or after being `connected` due to temporary connection loss.
255
198
 
256
199
  ```javascript
257
- {
258
- client: "79ec54fa-8348-4671-650b-d299c193a8a3",
259
- transport: "raw-websocket",
260
- latency: 21
261
- }
200
+ centrifuge.on('connecting', function(ctx) {
201
+ // do whatever you need in case of connecting to a server
202
+ });
262
203
  ```
263
204
 
264
- * `client` – client ID Centrifugo gave to this connection (string)
265
- * `transport` – name of transport used to establish connection with server (string)
266
- * `latency` – latency in milliseconds (int). This measures time passed between sending
267
- `connect` client protocol command and receiving connect response.
268
-
269
- #### disconnect event
205
+ #### disconnected event
270
206
 
271
- `disconnect` event fired on centrifuge object every time client disconnects for
272
- some reason. This can be network disconnect or disconnect initiated by Centrifugo server.
207
+ `disconnected` event fired on Centrifuge object every time client disconnects for some reason. This can be terminal disconnect due to advice from a server or disconnect initiated by client-side.
273
208
 
274
209
  ```javascript
275
- centrifuge.on('disconnect', function(context) {
210
+ centrifuge.on('disconnected', function(ctx) {
276
211
  // do whatever you need in case of disconnect from server
277
212
  });
278
213
  ```
279
214
 
280
- What's in `context`?
281
-
282
- ```javascript
283
- {
284
- reason: "connection closed",
285
- reconnect: true
286
- }
287
- ```
288
-
289
- * `reason` – the reason of client's disconnect (string)
290
- * `reconnect` – flag indicating if client will reconnect or not (boolean)
291
-
292
215
  #### disconnect method
293
216
 
294
- In some cases you may need to disconnect your client from server, use `disconnect` method to
295
- do this:
217
+ In some cases you may need to disconnect your client from server, use `.disconnect()` method to do this:
296
218
 
297
219
  ```javascript
298
220
  centrifuge.disconnect();
299
221
  ```
300
222
 
301
- After calling this client will not try to reestablish connection periodically. You must call
302
- `connect` method manually again.
223
+ After calling this client will not try to reestablish connection periodically. You must call `.connect()` method manually again.
303
224
 
304
225
  #### publish method
305
226
 
306
- Sometimes you need to publish into channel with `publish` option set to `true` without actually being subscribed to it. In this case you can use `publish` method:
227
+ Sometimes you need to publish into channel without actually being subscribed to it. In this case you can use `publish` method:
307
228
 
308
229
  ```javascript
309
230
  centrifuge.publish("channel", {"input": "hello"}).then(function(res) {
@@ -315,7 +236,7 @@ centrifuge.publish("channel", {"input": "hello"}).then(function(res) {
315
236
 
316
237
  #### send method
317
238
 
318
- This is only valid for Centrifuge library and does not work for Centrifugo server. `send` method allows to send asynchronous message from client to server.
239
+ 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.
319
240
 
320
241
  ```javascript
321
242
  centrifuge.send({"input": "hello"}).then(function(res) {
@@ -327,22 +248,10 @@ centrifuge.send({"input": "hello"}).then(function(res) {
327
248
 
328
249
  #### rpc method
329
250
 
330
- `rpc` method allows to send RPC request from client to server and wait for data response.
331
-
332
- ```javascript
333
- centrifuge.rpc({"input": "hello"}).then(function(res) {
334
- console.log('rpc result', res);
335
- }, function(err) {
336
- console.log('rpc error', err);
337
- });
338
- ```
339
-
340
- #### namedRPC method
341
-
342
- `namedRPC` method allows to send rpc request from client to server and wait for data response. Unlike `rpc` it additionally allows to provide method name string (which can be handy to have on RPC request top level).
251
+ `rpc` method allows to send rpc request from client to server and wait for data response.
343
252
 
344
253
  ```javascript
345
- centrifuge.namedRPC("my.method.name", {"input": "hello"}).then(function(res) {
254
+ centrifuge.rpc("my.method.name", {"input": "hello"}).then(function(res) {
346
255
  console.log('rpc result', res);
347
256
  }, function(err) {
348
257
  console.log('rpc error', err);
@@ -351,8 +260,6 @@ centrifuge.namedRPC("my.method.name", {"input": "hello"}).then(function(res) {
351
260
 
352
261
  #### history method
353
262
 
354
- Available since v2.7.0
355
-
356
263
  Allows to get history from a server. This is a top-level analogue of `Subscription.history` method. But accepts a channel as first argument.
357
264
 
358
265
  ```javascript
@@ -365,8 +272,6 @@ centrifuge.history("channel", {since: {offset: 0, epoch: "xyz"}, limit: 10}).the
365
272
 
366
273
  #### presence method
367
274
 
368
- Available since v2.7.0
369
-
370
275
  Allows to get presence info from a server. This is a top-level analogue of `Subscription.presence` method. But accepts a channel as first argument.
371
276
 
372
277
  ```javascript
@@ -379,8 +284,6 @@ centrifuge.presence("channel").then(function(resp) {
379
284
 
380
285
  #### presenceStats method
381
286
 
382
- Available since v2.7.0
383
-
384
287
  Allows to get presence stats from a server. This is a top-level analogue of `Subscription.presenceStats` method. But accepts a channel as first argument.
385
288
 
386
289
  ```javascript
@@ -391,243 +294,139 @@ centrifuge.presenceStats("channel").then(function(resp) {
391
294
  });
392
295
  ```
393
296
 
394
- ### setConnectData method
395
-
396
- Allows setting custom data sent to a server in first message. This data will be available on a server side in OnConnecting callback (if using Centrifugo library) or proxied to application backend (in using Centrifugo with connect proxy enabled).
397
-
398
- ```
399
- centrifuge.setConnectData({"any": "key"});
400
- ```
401
-
402
- ## Subscriptions
403
-
404
- Of course being just connected is useless. What we usually want from Centrifugo is to
405
- receive new messages published into channels. So our next step is `subscribe` on channel
406
- from which we want to receive real-time messages.
407
-
408
- ### subscribe method
409
-
410
- To subscribe on channel we must use `subscribe` method of `Centrifuge` object instance.
411
-
412
- The simplest usage that allow to subscribe on channel and listen to new messages is:
413
-
414
- ```javascript
415
- var subscription = centrifuge.subscribe("news", function(message) {
416
- // handle new message coming from channel "news"
417
- console.log(message);
418
- });
419
- ```
420
-
421
- And that's all! For lots of cases it's enough! But let's look at possible events that
422
- can happen with subscription:
423
-
424
- * `publish` – called when new publication message received (callback function in our previous example is `publish` event callback btw)
425
- * `join` – called when someone joined channel
426
- * `leave` – called when someone left channel
427
- * `subscribe` – called when subscription on channel successful and acknowledged by Centrifugo
428
- server. It can be called several times during lifetime as browser client automatically resubscribes on channels after successful reconnect (caused by temporary network disconnect for example or Centrifugo server restart)
429
- * `error` – called when subscription on channel failed with error. It can be called several times
430
- during lifetime as browser client automatically resubscribes on channels after successful reconnect
431
- (caused by temporary network disconnect for example or Centrifugo server restart)
432
- * `unsubscribe` – called every time subscription that was successfully subscribed
433
- unsubscribes from channel (can be caused by network disconnect or by calling
434
- `unsubscribe` method of subscription object)
435
-
436
- Don't be frightened by amount of events available. In most cases you only need some of them
437
- until you need full control to what happens with your subscriptions. We will look at format
438
- of messages for this event callbacks later below.
439
-
440
- There are 2 ways setting callback functions for events above.
297
+ #### ready method
441
298
 
442
- First is providing object containing event callbacks as second argument to `subscribe` method.
443
-
444
- ```javascript
445
- var callbacks = {
446
- "publish": function(message) {
447
- // See below description of message format
448
- console.log(message);
449
- },
450
- "join": function(message) {
451
- // See below description of join message format
452
- console.log(message);
453
- },
454
- "leave": function(message) {
455
- // See below description of leave message format
456
- console.log(message);
457
- },
458
- "subscribe": function(context) {
459
- // See below description of subscribe callback context format
460
- console.log(context);
461
- },
462
- "error": function(errContext) {
463
- // See below description of subscribe error callback context format
464
- console.log(err);
465
- },
466
- "unsubscribe": function(context) {
467
- // See below description of unsubscribe event callback context format
468
- console.log(context);
469
- }
470
- }
299
+ Returns a Promise which will be resolved upon connection establishement (i.e. when Client goes to `connected` state).
471
300
 
472
- var subscription = centrifuge.subscribe("news", callbacks);
473
- ```
301
+ #### error event
474
302
 
475
- Another way is setting callbacks using `on` method of subscription. Subscription object
476
- is event emitter so you can simply do the following:
303
+ To listen asynchronous error happening internally while Centrifuge client works you can set an `error` handler:
477
304
 
478
305
  ```javascript
479
- var subscription = centrifuge.subscribe("news");
306
+ const centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket');
480
307
 
481
- subscription.on("publish", publishHandlerFunction);
482
- subscription.on("subscribe", subscribeHandlerFunction);
483
- subscription.on("error", subscribeErrorHandlerFunction);
308
+ centrifuge.on('error', function(ctx) {
309
+ console.log(ctx);
310
+ });
484
311
  ```
485
312
 
486
- **`Subscription` objects are instances of [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter).**
313
+ This can help you to log failed connection attempts, or token refresh errors, etc.
487
314
 
488
- ### join and leave events of subscription
315
+ ### Connection Token
489
316
 
490
- As you know you can enable `join_leave` option for channel in Centrifugo configuration.
491
- This gives you an opportunity to listen to `join` and `leave` events in those channels.
492
- Just set event handlers on `join` and `leave` events of subscription.
317
+ Depending on authentication scheme used by a server you may also want to provide connection token:
493
318
 
494
319
  ```javascript
495
- var subscription = centrifuge.subscribe("news", function(message) {
496
- // handle message
497
- }).on("join", function(message) {
498
- console.log("Client joined channel", message);
499
- }).on("leave", function(message) {
500
- console.log("Client left channel", message);
320
+ const centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket', {
321
+ token: '<CONNECTION_TOKEN>'
501
322
  });
502
323
  ```
503
324
 
504
- *Note, that in order join/leave events to work corresponding options must be enabled in server channel configuration (on top level or for channel namespace)*
325
+ In case of Centrifugo on a server side this may be a JSON Web Token - see [authentication documentation](https://centrifugal.github.io/centrifugo/server/authentication/) for details on how to generate it on your backend side.
505
326
 
506
- ### subscription event context formats
327
+ **Connection token must come to the frontend from application backend - i.e. must be generated on the backend side**. The way to deliver token to the application frontend is up to the developer. Usually you can pass it in template rendering context or issue a separate call to request a connection token from the backend.
507
328
 
508
- We already know how to listen for events on subscription. Let's look at format of
509
- messages event callback functions receive as arguments.
329
+ If the token sets connection expiration then the client SDK will keep the token refreshed. It does this by calling a special callback function. This callback must return a new token. If a new token with updated connection expiration is returned from callback then it's sent to Centrifugo. If your callback returns an empty string – this means the user has no permission to connect to Centrifugo and the Client will move to a disconnected state. In case of error returned by your callback SDK will retry the operation after some jittered time.
510
330
 
511
- #### format of message event context
512
-
513
- Let's look at message format of new message received from channel:
331
+ An example of possible `getToken` function implementation:
514
332
 
515
333
  ```javascript
516
- {
517
- "data":{"input":"hello"},
334
+ function getToken(url, ctx) {
335
+ return new Promise((resolve, reject) => {
336
+ fetch(url, {
337
+ method: 'POST',
338
+ headers: new Headers({ 'Content-Type': 'application/json' }),
339
+ body: JSON.stringify(ctx)
340
+ })
341
+ .then(res => {
342
+ if (!res.ok) {
343
+ throw new Error(`Unexpected status code ${res.status}`);
344
+ }
345
+ return res.json();
346
+ })
347
+ .then(data => {
348
+ resolve(data.token);
349
+ })
350
+ .catch(err => {
351
+ reject(err);
352
+ });
353
+ });
518
354
  }
519
- ```
520
-
521
- I.e. `data` field contains actual data that was published.
522
355
 
523
- Message can optionally contain additional client `info` in case when this message was published by javascript client directly using `publish` method (see details below):
524
-
525
- ```javascript
526
- {
527
- "info":{
528
- "user":"2694",
529
- "client":"7080fd2a-bd69-4f1f-6648-5f3ceba4b643",
530
- "conn_info":{"name":"Alexandr"},
531
- "chan_info":{"extra":"extra JSON data when authorizing private channel"}
532
- },
533
- "data":{"input":"hello"}
534
- }
356
+ const client = new Centrifuge(
357
+ 'ws://localhost:8000/connection/websocket',
358
+ {
359
+ token: 'JWT-GENERATED-ON-BACKEND-SIDE',
360
+ getToken: function (ctx) {
361
+ return getToken('/centrifuge/connection_token', ctx);
362
+ }
363
+ }
364
+ );
535
365
  ```
536
366
 
537
- #### format of join/leave event message
367
+ :::tip
538
368
 
539
- I.e. `on("join", function(message) {...})` or `on("leave", function(message) {...})`
369
+ If initial token is not provided, but `getToken` is specified then SDK assumes that developer wants to use token authentication. In this case SDK attempts to get a connection token before establishing an initial connection.
540
370
 
541
- ```javascript
542
- {
543
- "info":{
544
- "user":"2694",
545
- "client":"2724adea-6e9b-460b-4430-a9f999e94c36",
546
- "conn_info":{"first_name":"Alexandr"},
547
- "chan_info":{"extra":"extra JSON data when authorizing"}
548
- }
549
- }
550
- ```
371
+ :::
551
372
 
552
- `conn_info` and `chan_info` exist in message only if not empty.
373
+ ## Subscription API
553
374
 
554
- #### format of subscribe event context
375
+ What we usually want from Centrifugo is to receive new messages published into channels. To do this we must create `Subscription` object.
555
376
 
556
- I.e. `on("subscribe", function(context) {...})`
377
+ ### Subscription methods and events
557
378
 
558
- ```javascript
559
- {
560
- "channel": "$public:chat",
561
- "isResubscribe": true,
562
- "recovered": false
563
- }
564
- ```
379
+ #### Subscribe to a channel
565
380
 
566
- `isResubscribe` boolean flag showing if this was initial subscribe (`false`) or resubscribe (`true`)
567
- `recovered` – boolean flag that indicated whether missed messages were recovered on reconnect or not (recovery works according to Centrifugo channel configuration)
381
+ The simplest usage that allow to subscribe on channel and listen to new messages is:
568
382
 
569
- #### format of subscription error event context
383
+ ```javascript
384
+ const sub = centrifuge.newSubscription('example');
570
385
 
571
- I.e. `on("error", function(err) {...})`
386
+ sub.on('publication', function(ctx) {
387
+ // handle new Publication data coming from channel "news".
388
+ console.log(ctx.data);
389
+ });
572
390
 
573
- ```javascript
574
- {
575
- "error": "permission denied",
576
- "channel": "$public:chat",
577
- "isResubscribe": true
578
- }
391
+ sub.subscribe();
579
392
  ```
580
393
 
581
- `error` - error description
582
- `isResubscribe` – flag showing if this was initial subscribe (`false`) or resubscribe (`true`)
394
+ #### Subscription events
583
395
 
584
- #### format of unsubscribe event context
396
+ Some events which can be listened on Subscription object are:
397
+
398
+ * `publication` – called when new publication received from a Subscription channel
399
+ * `join` – called when someone joined channel
400
+ * `leave` – called when someone left channel
401
+ * `subscribing` - called when Subscription goes to `subscribing` state (initial subscribe and re-subscribes)
402
+ * `subscribed` – called when Subscription goes to `subscribed` state
403
+ * `unsubscribed` – called when Subscription goes to `unsubscribed` state
404
+ * `error` – called when subscription on channel failed with error. It can be called several times
405
+ during lifetime as browser client automatically resubscribes on channels after successful reconnect
406
+ (caused by temporary network disconnect for example or Centrifugo server restart)
585
407
 
586
- I.e `on("unsubscribe", function(context) {...})`
408
+ Don't be frightened by amount of events available. In most cases you only need some of them until you need full control to what happens with your subscriptions.
587
409
 
588
- ```javascript
589
- {
590
- "channel": "$public:chat"
591
- }
592
- ```
410
+ **`Subscription` objects are instances of [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter).**
593
411
 
594
- ### presence method of subscription
412
+ #### presence method of Subscription
595
413
 
596
414
  `presence` allows to get information about clients which are subscribed on channel at
597
415
  this moment. Note that this information is only available if `presence` option enabled
598
416
  in Centrifugo configuration for all channels or for channel namespace.
599
417
 
600
418
  ```javascript
601
- var subscription = centrifuge.subscribe("news", function(message) {
602
- // handle message
603
- });
419
+ const sub = centrifuge.newSubscription("news");
420
+ sub.subscribe()
604
421
 
605
- subscription.presence().then(function(message) {
606
- // presence data received
422
+ sub.presence().then(function(ctx) {
423
+ console.log(ctx.clients);
607
424
  }, function(err) {
608
425
  // presence call failed with error
609
426
  });
610
427
  ```
611
428
 
612
- `presence` is internally a promise that will be resolved with data or error only
613
- when subscription actually subscribed.
614
-
615
- Format of success callback `message`:
616
-
617
- ```javascript
618
- {
619
- "presence":{
620
- "2724adea-6e9b-460b-4430-a9f999e94c36": {
621
- "user":"2694",
622
- "client":"2724adea-6e9b-460b-4430-a9f999e94c36"
623
- },
624
- "d274505c-ce63-4e24-77cf-971fd8a59f00":{
625
- "user":"2694",
626
- "client":"d274505c-ce63-4e24-77cf-971fd8a59f00"
627
- }
628
- }
629
- }
630
- ```
429
+ `presence` is internally a promise that will be waiting for subscription subscribe success if required.
631
430
 
632
431
  As you can see presence data is a map where keys are client IDs and values are objects
633
432
  with client information.
@@ -636,8 +435,8 @@ Format of `err` in error callback:
636
435
 
637
436
  ```javascript
638
437
  {
639
- "code": 0,
640
- "message": "timeout"
438
+ "code": 108,
439
+ "message": "not available"
641
440
  }
642
441
  ```
643
442
 
@@ -646,68 +445,37 @@ Format of `err` in error callback:
646
445
 
647
446
  *Note, that in order presence to work corresponding options must be enabled in server channel configuration (on top level or for channel namespace)*
648
447
 
649
- ### presenceStats method of subscription
448
+ #### presenceStats method of subscription
650
449
 
651
450
  `presenceStats` allows to get two counters from a server: number of total clients currently subscribed and number of unique users currently subscribed. Note that this information is only available if `presence` option enabled in server configuration for a channel.
652
451
 
653
452
  ```javascript
654
- var subscription = centrifuge.subscribe("news", function(message) {
655
- // handle message
656
- });
657
-
658
- subscription.presenceStats().then(function(resp) {
659
- // presence stats data received
453
+ sub.presenceStats().then(function(ctx) {
454
+ console.log(ctx.numClients);
660
455
  }, function(err) {
661
456
  // presence stats call failed with error
662
457
  });
663
458
  ```
664
459
 
665
- ### history method of subscription
460
+ #### history method of subscription
666
461
 
667
- `history` method allows to get last messages published into channel. Note that history
668
- for channel must be configured in Centrifugo to be available for `history` calls from
669
- client.
462
+ `history` method allows to get last messages published into channel. Note that history for channel must be configured in Centrifugo to be available for `history` calls from client.
670
463
 
671
464
  ```javascript
672
- var subscription = centrifuge.subscribe("news", function(message) {
673
- // handle message
674
- });
675
-
676
- subscription.history().then(function(response) {
677
- // history messages received
465
+ sub.history({limit: 100}).then(function(ctx) {
466
+ console.log(ctx.publications);
678
467
  }, function(err) {
679
468
  // history call failed with error
680
469
  });
681
470
  ```
682
471
 
683
- Success callback `response` format:
684
-
685
- ```javascript
686
- {
687
- "publications": [
688
- {
689
- "data": {"input": "hello2"},
690
- "offset": 1
691
- },
692
- {
693
- "data": {"input": "hello1"},
694
- "offset": 2
695
- }
696
- ],
697
- "offset": 2,
698
- "epoch": "xcf4w"
699
- }
700
- ```
701
-
702
- Where `publications` is an array of messages published into channel, `offset` is a current stream top offset (added in v2.7.0), `epoch` is a current stream epoch (added in v2.7.0).
703
-
704
- Note that also additional fields can be included in publication objects - `client`, `info` if those fields were set in original publications.
705
-
706
- `err` format – the same as for `presence` method.
707
-
708
472
  *Note, that in order history to work corresponding options must be enabled in server channel configuration (on top level or for channel namespace)*
709
473
 
710
- Starting from v2.7.0 it's possible to iterate over history stream:
474
+ Some history options available:
475
+
476
+ * `limit` (number)
477
+ * `since` (StreamPosition)
478
+ * `reverse` (boolean)
711
479
 
712
480
  ```javascript
713
481
  resp = await subscription.history({'since': {'offset': 2, 'epoch': 'xcf4w'}, limit: 100});
@@ -723,22 +491,16 @@ resp = await subscription.history({limit: 0});
723
491
 
724
492
  I.e. not providing `since` and using zero `limit`.
725
493
 
726
- **For now history pagination feature only works with [Centrifuge](https://github.com/centrifugal/centrifuge) library based server and not available in Centrifugo**.
727
-
728
- ### publish method of subscription
494
+ #### publish method of subscription
729
495
 
730
- `publish` method of subscription object allows to publish data into channel directly from client. The main idea of Centrifugo is server side only push. Usually your application backend receives new event (for example new comment created, someone clicked like button etc) and then backend posts that event into Centrifugo over API. But in some cases you may need to allow clients to publish data into channels themselves. This can be used for demo projects, when prototyping ideas for example, for personal usage. And this allow to make something with real-time features without any application backend at all. Just Javascript code and Centrifugo.
496
+ `publish` method of Subscription object allows publishing data into channel directly from a client.
731
497
 
732
- **So to emphasize: using client publish is not an idiomatic Centrifugo usage. It's not for production applications but in some cases (demos, personal usage, Centrifugo as backend microservice) can be justified and convenient. In most real-life apps you need to send new data to your application backend first (using the convenient way, for example AJAX request in web app) and then publish data to Centrifugo over Centrifugo API.**
498
+ **Using client-side publish is not an idiomatic Centrifugo usage in many cases. Centrifugo is standalone server and when publishing from a client you won't get the message on the backend side (except using publish proxy feature of Centrifugo). In most real-life apps you need to send new data to your application backend first (using the convenient way, for example AJAX request in web app) and then publish data to Centrifugo over Centrifugo API.**
733
499
 
734
- To do this you can use `publish` method. Note that just like presence and history publish must be allowed in Centrifugo configuration for all channels or for channel namespace. When using `publish` data will go through Centrifugo to all clients in channel. Your application backend won't receive this message.
500
+ *Just like presence and history publish must be allowed in Centrifugo configuration for all channels or for channel namespace.*
735
501
 
736
502
  ```javascript
737
- var subscription = centrifuge.subscribe("news", function(message) {
738
- // handle message
739
- });
740
-
741
- subscription.publish({"input": "hello world"}).then(function() {
503
+ sub.publish({"input": "hello world"}).then(function() {
742
504
  // success ack from Centrifugo received
743
505
  }, function(err) {
744
506
  // publish call failed with error
@@ -746,78 +508,91 @@ subscription.publish({"input": "hello world"}).then(function() {
746
508
  });
747
509
  ```
748
510
 
749
- `err` format the same as for `presence` method.
511
+ *Note, that in order publish to work in Centrifugo corresponding option must be enabled in server channel configuration or client should have capability to publish*.
750
512
 
751
- *Note, that in order publish to work corresponding option must be enabled in server channel configuration (on top level or for channel namespace), by default client can not publish into channel*
513
+ #### unsubscribe method of subscription
752
514
 
753
- ### unsubscribe method of subscription
754
-
755
- You can call `unsubscribe` method to unsubscribe from subscription:
515
+ You can call `unsubscribe` method to unsubscribe from a channel:
756
516
 
757
517
  ```javascript
758
- subscription.unsubscribe();
518
+ sub.unsubscribe();
759
519
  ```
760
520
 
761
- **Important thing to know** is that unsubscribing from subscription does not remove event hanlers you already set to that subscription object. This allows to simply subscribe to channel again later calling `.subscribe()` method of subscription (see below). But there are cases when your code structured in a way that you need to remove event handlers after unsubscribe **to prevent them be executed twice** in the future. To do this remove event listeners explicitly after calling `unsubscribe()`:
521
+ **Important thing to know** is that unsubscribing from subscription does not remove event handlers you already set to that Subscription object. This allows to simply subscribe to channel again later calling `.subscribe()` method of subscription (see below). But there are cases when your code structured in a way that you need to remove event handlers after unsubscribe **to prevent them be executed twice** in the future. To do this remove event listeners explicitly after calling `unsubscribe()`:
762
522
 
763
523
  ```javascript
764
- subscription.unsubscribe();
765
- subscription.removeAllListeners();
524
+ sub.unsubscribe();
525
+ sub.removeAllListeners();
766
526
  ```
767
527
 
768
- ### subscribe method of subscription
528
+ #### ready method of subscription
529
+
530
+ Returns a Promise which will be resolved upon subscription success (i.e. when Subscription goes to `subscribed` state).
769
531
 
770
- You can restore subscription after unsubscribing calling `.subscribe()` method:
532
+ ### Subscription token
533
+
534
+ You may want to provide subscription token:
771
535
 
772
536
  ```javascript
773
- subscription.subscribe();
537
+ const sub = centrifuge.newSubscription("news", {
538
+ token: '<SUBSCRIPTION_TOKEN>'
539
+ });
774
540
  ```
775
541
 
776
- ### ready method of subscription
542
+ In case of Centrifugo on a server side this may be a JSON Web Token - see [channel token auth documentation](https://centrifugal.github.io/centrifugo/server/channel_token_auth) for details on how to generate it on your backend side.
777
543
 
778
- A small drawback of setting event handlers on subscription using `on` method is that event
779
- handlers can be set after `subscribe` event of underlying subscription already fired. This
780
- is not a problem in general but can be actual if you use one subscription (i.e. subscription
781
- to the same channel) from different parts of your javascript application - so be careful.
544
+ **Subscription token must come to the frontend from application backend - i.e. must be generated on the backend side**. The way to deliver token to the application frontend is up to the developer. Usually you can pass it in template rendering context or issue a separate call to request a connection token from the backend.
782
545
 
783
- For this case one extra helper method `.ready(callback, errback)` exists. This method calls
784
- `callback` if subscription already subscribed and calls `errback` if subscription already
785
- failed to subscribe with some error (because you subscribed on this channel before). So
786
- when you want to call subscribe on channel already subscribed before you may find `ready()`
787
- method useful:
546
+ If token sets subscription expiration client SDK will keep token refreshed. It does this by calling special callback function. This callback must return a new token. If new token with updated subscription expiration returned from a calbback then it's sent to Centrifugo. If your callback returns an empty string – this means user has no permission to subscribe to a channel anymore and subscription will be unsubscribed. In case of error returned by your callback SDK will retry operation after some jittered time.
788
547
 
789
- ```javascript
790
- var subscription = centrifuge.subscribe("news", function(message) {
791
- // handle message;
792
- });
548
+ An example:
793
549
 
794
- // artificially model subscription to the same channel that happen after
795
- // first subscription successfully subscribed - subscribe on the same
796
- // channel after 5 seconds.
797
- setTimeout(function() {
798
- var anotherSubscription = centrifuge.subscribe("news", function(message) {
799
- // another listener of channel "news"
800
- }).on("subscribe", function() {
801
- // won't be called on first subscribe because subscription already subscribed!
802
- // but will be called every time automatic resubscribe after network disconnect
803
- // happens
550
+ ```javascript
551
+ function getToken(url, ctx) {
552
+ return new Promise((resolve, reject) => {
553
+ fetch(url, {
554
+ method: 'POST',
555
+ headers: new Headers({ 'Content-Type': 'application/json' }),
556
+ body: JSON.stringify(ctx)
557
+ })
558
+ .then(res => {
559
+ if (!res.ok) {
560
+ throw new Error(`Unexpected status code ${res.status}`);
561
+ }
562
+ return res.json();
563
+ })
564
+ .then(data => {
565
+ resolve(data.token);
566
+ })
567
+ .catch(err => {
568
+ reject(err);
569
+ });
804
570
  });
805
- // one of subscribeSuccessHandler (or subscribeErrorHandler) will be called
806
- // only if subscription already subscribed (or subscribe request already failed).
807
- anotherSubscription.ready(subscribeSuccessHandler, subscribeErrorHandler);
808
- }, 5000);
571
+ }
572
+
573
+ const client = new Centrifuge('ws://localhost:8000/connection/websocket', {});
574
+
575
+ const sub = centrifuge.newSubscription(channel, {
576
+ token: 'JWT-GENERATED-ON-BACKEND-SIDE',
577
+ getToken: function (ctx) {
578
+ // ctx has channel in the Subscription token case.
579
+ return getToken('/centrifuge/subscription_token', ctx);
580
+ },
581
+ });
582
+ sub.subscribe();
809
583
  ```
810
584
 
811
- When called `callback` and `errback` of `ready` method receive the same arguments as
812
- callback functions for `subscribe` and `error` events of subscription.
585
+ :::tip
586
+
587
+ If initial token is not provided, but `getToken` is specified – then SDK assumes that developer wants to use token authorization for a channel subscription. In this case SDK attempts to get a subscription token before initial subscribe.
813
588
 
814
- ### Message batching
589
+ :::
815
590
 
816
- There is also message batching support. It allows to send several messages to server
817
- in one request - this can be especially useful when connection established via one of
818
- SockJS polling transports.
591
+ ## Message batching
819
592
 
820
- You can start collecting messages to send calling `startBatching()` method:
593
+ There is also a command batching support. It allows to send several commands to a server in one request - may be especially useful when connection established via one of HTTP-based transports.
594
+
595
+ You can start collecting commands by calling `startBatching()` method:
821
596
 
822
597
  ```javascript
823
598
  centrifuge.startBatching();
@@ -829,145 +604,101 @@ Finally if you don't want batching anymore call `stopBatching()` method:
829
604
  centrifuge.stopBatching();
830
605
  ```
831
606
 
832
- This call will flush all collected messages to network.
607
+ This call will flush all collected commands to a network.
833
608
 
834
- ## Private channels subscription
609
+ ## Server-side subscriptions
835
610
 
836
- If channel name starts with `$` then subscription on this channel will be checked via AJAX POST request from Javascript client to your web application backend.
611
+ TODO.
837
612
 
838
- You can subscribe on private channel as usual:
613
+ ## Configuration options
839
614
 
840
- ```javascript
841
- centrifuge.subscribe('$private', function(message) {
842
- // process message
843
- });
844
- ```
615
+ You can check out all available options with description [in source code](https://github.com/centrifugal/centrifuge-js/blob/master/src/types.ts#L82).
845
616
 
846
- But in this case Javascript client will first check subscription via your backend sending AJAX POST request to `/centrifuge/subscribe` endpoint (by default, can be changed via configuration option `subscribeEndpoint`). As said this is a POST request with JSON body. Request will contain `client` field on top level of JSON which is your connection client ID and array `channels` field - one or multiple private channels client wants to subscribe to.
617
+ Let's look at available configuration parameters when initializing `Centrifuge` object instance.
847
618
 
848
- ```javascript
849
- {
850
- "client": "<CLIENT ID>",
851
- "channels": ["$chan1", "$chan2"]
852
- }
853
- ```
619
+ ### debug
854
620
 
855
- Your server should validate all these subscriptions and return properly constructed response.
621
+ `debug` is a boolean option which is `false` by default. When enabled lots of various debug
622
+ messages will be logged into javascript console. Mostly useful for development or
623
+ troubleshooting.
856
624
 
857
- Response is a JSON with array `channels` field on top level:
625
+ ### minReconnectDelay
858
626
 
859
- ```javascript
860
- {
861
- "channels": [
862
- {
863
- "channel": "$chan1",
864
- "token": "<SUBSCRIPTION JWT TOKEN>"
865
- },
866
- {
867
- "channel": "$chan2",
868
- "token": <SUBSCRIPTION JWT TOKEN>
869
- }
870
- ]
871
- }
872
- ```
627
+ When client disconnected from a server it will automatically try to reconnect using a backoff algorithm with jitter. `minReconnectDelay` option sets minimal interval value in milliseconds before first reconnect attempt. Default is `500` milliseconds.
873
628
 
874
- I.e. you need to return individual subscription tokens for each private channel in request. See [how to generate private channel tokens](https://centrifugal.github.io/centrifugo/server/private_channels/) in Centrifugo docs.
629
+ ### maxReconnectDelay
875
630
 
876
- If you don't want to give client access to channel then just do not include it into response.
631
+ `maxReconnectDelay` sets an upper reconnect delay value. Default is `20000` milliseconds - i.e. clients won't have delays between reconnect attempts which are larger than 20 seconds.
877
632
 
878
- There are also two public API methods which can help to subscribe to many private channels sending only one POST request to your web application backend: `startSubscribeBatching` and `stopSubscribeBatching`. When you `startSubscribeBatching` javascript client will collect private subscriptions until `stopSubscribeBatching()` called – and then send them all at once.
633
+ ### maxServerPingDelay
879
634
 
880
- As we just described when client subscribes on private channel by default AJAX request will be sent to `subscribeEndpoint` automatically if channel starts with `$`. In this case developer only needs to return proper response from server. But there is a way to override default behaviour and take full control on authorizing private channels. To do this it's possible to provide custom `onPrivateSubscribe` function in configuration options. This function will be called with all data required to authorize private channels client subscribes to and should call callback (will be provided by centrifuge-js as second argument) with authorization data when done. See our type declarations in `dist` folder to find out data format (**for `onPrivateSubscribe` it is slightly different** - like `{"status": 200, "data": {"channels": [...]}}`).
635
+ `maxServerPingDelay` sets the maximum delay of server pings after which connection is considered broken and client reconnects. In milliseconds. Default is `10000`.
881
636
 
882
- ## Server-side subscriptions
637
+ ### protocol
883
638
 
884
- `centrifuge-js` v2.4.0 added support for server-side subscriptions. This means several new event handlers have been added.
639
+ 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.
885
640
 
886
- The main one is `publish` event of Centrifuge instance to handle publications coming from server-side channels:
641
+ ### token
887
642
 
888
- ```javascript
889
- var centrifuge = new Centrifuge(address);
643
+ Set initial connection token.
890
644
 
891
- centrifuge.on('publish', function(ctx) {
892
- const channel = ctx.channel;
893
- const payload = JSON.stringify(ctx.data);
894
- console.log('Publication from server-side channel', channel, payload);
895
- });
645
+ ### getToken
896
646
 
897
- centrifuge.connect();
898
- ```
647
+ Set function for getting connection token. This may be used for initial token loading and token refresh mechanism (when initial token is going to expire).
899
648
 
900
- Also there are event handlers for `join`, `leave`, `subscribe` and `unsubscribe` events. Actually they work the same way as analogues from Subscription instance but binded to Centrifuge instance instead.
649
+ ### data
901
650
 
902
- For example:
651
+ Set custom data to send to a server withing every connect command.
903
652
 
904
- ```javascript
905
- centrifuge.on('subscribe', function(ctx) {
906
- console.log('Subscribe to server-side channel ' + ctx.channel);
907
- });
653
+ ### name
908
654
 
909
- centrifuge.on('unsubscribe', function(ctx) {
910
- console.log('Unsubscribe from server-side channel ' + ctx.channel);
911
- });
912
- ```
655
+ Set custom client name. By default, it's set to `js`. This is useful for analitycs and semantically must identify an environment from which client establishes a connection.
913
656
 
914
- ## Connection expiration
657
+ ### version
915
658
 
916
- When connection expiration mechanism is on on server client will automatically ask your backend for updated connection credentials sending AJAX HTTP POST request to `/centrifuge/refresh` endpoint (by default, can be changed using `refreshEndpoint` option). Client will send that request when connection ttl is close to the end. In response backend should return response with JSON like this:
659
+ Version of your application - useful for analitycs.
917
660
 
918
- ```javascript
919
- {
920
- "token": "<ACTUAL JWT TOKEN>"
921
- }
922
- ```
661
+ ### timeout
662
+
663
+ Timeout for operations in milliseconds.
664
+
665
+ ### websocket
666
+
667
+ `websocket` option allows to explicitly provide custom WebSocket client to use. By default centrifuge-js will try to use global WebSocket object, so if you are in web browser – it will just use native WebSocket implementation. See notes about using `centrifuge-js` with NodeJS below.
668
+
669
+ ### sockjs
670
+
671
+ `sockjs` option allows to explicitly provide SockJS client object to Centrifuge client.
923
672
 
924
673
  ## Protobuf support
925
674
 
926
675
  To import client with Protobuf protocol support:
927
676
 
928
677
  ```html
929
- <script src="https://cdn.jsdelivr.net/gh/centrifugal/centrifuge-js@2.X.X/dist/centrifuge.protobuf.min.js"></script>
678
+ <script src="https://unpkg.com/centrifuge@3.0.0/dist/centrifuge.protobuf.js"></script>
930
679
  ```
931
680
 
932
681
  Or if you are developing with npm:
933
682
 
934
683
  ```javascript
935
- import Centrifuge from 'centrifuge/dist/centrifuge.protobuf';
684
+ import Centrifuge from 'centrifuge/build/protobuf';
936
685
  ```
937
686
 
938
687
  This client uses [protobuf.js](https://github.com/dcodeIO/ProtoBuf.js/) under the hood.
939
688
 
940
- Centrifuge client with Protobuf support also works with JSON. To enable binary websocket add `format` query param with `protobuf` value to Websocket endpoint URL:
941
-
942
- ```javascript
943
- var centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket?format=protobuf');
944
- ```
945
-
946
- When using Centrifugo v3 or Centrifuge >= v0.18.0 on server side prefer using client options instead of setting format in URL (available in `centrifuge-js` >= v2.8.0):
689
+ Centrifuge client with Protobuf support also works with JSON. To enable binary websocket add `protocol: "protobuf"` option to Centrifuge configuration options:
947
690
 
948
691
  ```javascript
949
- var centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket', {
692
+ const centrifuge = new Centrifuge('ws://centrifuge.example.com/connection/websocket", {
950
693
  protocol: 'protobuf'
951
694
  });
952
695
  ```
953
696
 
954
- ## Browser support
955
-
956
- This client intended to work in all modern browsers with Websocket support: https://caniuse.com/#search=websocket.
957
-
958
- **To support IE 11** you must additionally polyfill `Promise` as this library uses `Promise`.
959
-
960
- You can easily polyfill `Promise` via CDN (example here uses [es6-promise](https://github.com/stefanpenner/es6-promise) library):
961
-
962
- ```html
963
- <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script>
964
- ```
965
-
966
- Or you can explicitly polyfill `Promise` in your code, see [auto-polyfill of es6-promise](https://github.com/stefanpenner/es6-promise#auto-polyfill)
967
-
968
697
  ## Using with NodeJS
969
698
 
970
- NodeJS does not have native WebSocket library in std lib. To use `centrifuge-js` on Node you need to provide WebSocket object. You need to install WebSocket dependency:
699
+ NodeJS does not have native WebSocket library in std lib. To use `centrifuge-js` on Node you need to explicitly provide WebSocket constructor to the library.
700
+
701
+ First, install WebSocket dependency:
971
702
 
972
703
  ```
973
704
  npm install ws
@@ -990,38 +721,10 @@ Or define it globally:
990
721
  const Centrifuge = require('centrifuge');
991
722
  global.WebSocket = require('ws');
992
723
 
993
- var centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket')
994
- ```
995
-
996
- The same if you want to use `SockJS`:
997
-
998
- ```javascript
999
- const Centrifuge = require('centrifuge');
1000
- const SockJS = require('sockjs-client');
1001
-
1002
- var centrifuge = new Centrifuge('ws://localhost:8000/connection/sockjs', {
1003
- sockjs: SockJS
1004
- })
1005
- ```
1006
-
1007
- ### Custom XMLHttpRequest
1008
-
1009
- To work with private channels you may need to pass `XMLHttpRequest` object to library:
1010
-
1011
- ```javascript
1012
- const Centrifuge = require('centrifuge');
1013
- const WebSocket = require('ws');
1014
- const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
1015
-
1016
- var centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket', {
1017
- websocket: WebSocket,
1018
- xmlhttprequest: XMLHttpRequest
1019
- })
724
+ const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
1020
725
  ```
1021
726
 
1022
- Or define XMLHttpRequest globally over `global.XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;`
1023
-
1024
- ### Custom WebSocket constructor
727
+ ## Custom WebSocket constructor
1025
728
 
1026
729
  If you are building a client for a non-browser environment and want to pass custom headers then you can use the following approach to wrap a WebSocket constructor and let custom options to be used on connection initialization:
1027
730
 
@@ -1045,53 +748,3 @@ var centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket', {
1045
748
  websocket: myWs({ headers: { Authorization: '<token or key>' } }),
1046
749
  });
1047
750
  ```
1048
-
1049
- ### Subscribe since known position
1050
-
1051
- Available in `centrifuge-js` >= v2.8.0.
1052
-
1053
- Subscribe API supports setting known StreamPosition object to use server recovery feature on the connection start (otherwise recovery only used upon client reconnections due to temporary connection problems).
1054
-
1055
- ```javascript
1056
- centrifuge.subscribe('channel', function(messageCtx) {
1057
- console.log('new message', messageCtx);
1058
- }, {'since': {'offset': 0, 'epoch': '<EPOCH>'}});
1059
- ```
1060
-
1061
- ## Feature matrix
1062
-
1063
- - [x] connect to server using JSON protocol format
1064
- - [x] connect to server using Protobuf protocol format
1065
- - [x] connect with token (JWT)
1066
- - [ ] connect with custom header (not supported by browser API, though [possible for a non-browser target env](https://github.com/centrifugal/centrifuge-js#custom-websocket-constructor))
1067
- - [x] automatic reconnect in case of errors, network problems etc
1068
- - [x] an exponential backoff for reconnect
1069
- - [x] connect and disconnect events
1070
- - [x] handle disconnect reason
1071
- - [x] subscribe on a channel and handle asynchronous Publications
1072
- - [x] handle Join and Leave messages
1073
- - [x] handle Unsubscribe notifications
1074
- - [x] reconnect on subscribe timeout
1075
- - [x] publish method of Subscription
1076
- - [x] unsubscribe method of Subscription
1077
- - [x] presence method of Subscription
1078
- - [x] presence stats method of Subscription
1079
- - [x] history method of Subscription
1080
- - [x] top-level publish method
1081
- - [x] top-level presence method
1082
- - [x] top-level presence stats method
1083
- - [x] top-level history method
1084
- - [ ] top-level unsubscribe method
1085
- - [x] send asynchronous messages to server
1086
- - [x] handle asynchronous messages from server
1087
- - [x] send RPC commands
1088
- - [x] subscribe to private channels with token (JWT)
1089
- - [x] connection token (JWT) refresh
1090
- - [x] private channel subscription token (JWT) refresh
1091
- - [x] handle connection expired error
1092
- - [x] handle subscription expired error
1093
- - [x] ping/pong to find broken connection
1094
- - [x] message recovery mechanism for client-side subscriptions
1095
- - [x] server-side subscriptions
1096
- - [x] message recovery mechanism for server-side subscriptions
1097
- - [x] history stream pagination