centrifuge 2.8.4 → 3.0.0

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