UNPKG

30.2 kBPlain TextView Raw
1import { ITransaction } from './i-transaction.js';
2import { StompConfig } from './stomp-config.js';
3import { StompHandler } from './stomp-handler.js';
4import { StompHeaders } from './stomp-headers.js';
5import { StompSubscription } from './stomp-subscription.js';
6import {
7 ActivationState,
8 closeEventCallbackType,
9 debugFnType,
10 frameCallbackType,
11 IPublishParams,
12 IStompSocket,
13 messageCallbackType,
14 StompSocketState,
15 wsErrorCallbackType,
16} from './types.js';
17import { Versions } from './versions.js';
18
19/**
20 * @internal
21 */
22declare const WebSocket: {
23 prototype: IStompSocket;
24 new (url: string, protocols?: string | string[]): IStompSocket;
25};
26
27/**
28 * STOMP Client Class.
29 *
30 * Part of `@stomp/stompjs`.
31 */
32export class Client {
33 /**
34 * The URL for the STOMP broker to connect to.
35 * Typically like `"ws://broker.329broker.com:15674/ws"` or `"wss://broker.329broker.com:15674/ws"`.
36 *
37 * Only one of this or [Client#webSocketFactory]{@link Client#webSocketFactory} need to be set.
38 * If both are set, [Client#webSocketFactory]{@link Client#webSocketFactory} will be used.
39 *
40 * If your environment does not support WebSockets natively, please refer to
41 * [Polyfills]{@link https://stomp-js.github.io/guide/stompjs/rx-stomp/ng2-stompjs/pollyfils-for-stompjs-v5.html}.
42 */
43 public brokerURL: string | undefined;
44
45 /**
46 * STOMP versions to attempt during STOMP handshake. By default, versions `1.2`, `1.1`, and `1.0` are attempted.
47 *
48 * Example:
49 * ```javascript
50 * // Try only versions 1.1 and 1.0
51 * client.stompVersions = new Versions(['1.1', '1.0'])
52 * ```
53 */
54 public stompVersions = Versions.default;
55
56 /**
57 * This function should return a WebSocket or a similar (e.g. SockJS) object.
58 * If your environment does not support WebSockets natively, please refer to
59 * [Polyfills]{@link https://stomp-js.github.io/guide/stompjs/rx-stomp/ng2-stompjs/pollyfils-for-stompjs-v5.html}.
60 * If your STOMP Broker supports WebSockets, prefer setting [Client#brokerURL]{@link Client#brokerURL}.
61 *
62 * If both this and [Client#brokerURL]{@link Client#brokerURL} are set, this will be used.
63 *
64 * Example:
65 * ```javascript
66 * // use a WebSocket
67 * client.webSocketFactory= function () {
68 * return new WebSocket("wss://broker.329broker.com:15674/ws");
69 * };
70 *
71 * // Typical usage with SockJS
72 * client.webSocketFactory= function () {
73 * return new SockJS("http://broker.329broker.com/stomp");
74 * };
75 * ```
76 */
77 public webSocketFactory: (() => IStompSocket) | undefined;
78
79 /**
80 * Will retry if Stomp connection is not established in specified milliseconds.
81 * Default 0, which switches off automatic reconnection.
82 */
83 public connectionTimeout: number = 0;
84
85 // As per https://stackoverflow.com/questions/45802988/typescript-use-correct-version-of-settimeout-node-vs-window/56239226#56239226
86 private _connectionWatcher: ReturnType<typeof setTimeout> | undefined; // Timer
87
88 /**
89 * automatically reconnect with delay in milliseconds, set to 0 to disable.
90 */
91 public reconnectDelay: number = 5000;
92
93 /**
94 * Incoming heartbeat interval in milliseconds. Set to 0 to disable.
95 */
96 public heartbeatIncoming: number = 10000;
97
98 /**
99 * Outgoing heartbeat interval in milliseconds. Set to 0 to disable.
100 */
101 public heartbeatOutgoing: number = 10000;
102
103 /**
104 * This switches on a non-standard behavior while sending WebSocket packets.
105 * It splits larger (text) packets into chunks of [maxWebSocketChunkSize]{@link Client#maxWebSocketChunkSize}.
106 * Only Java Spring brokers seem to support this mode.
107 *
108 * WebSockets, by itself, split large (text) packets,
109 * so it is not needed with a truly compliant STOMP/WebSocket broker.
110 * Setting it for such a broker will cause large messages to fail.
111 *
112 * `false` by default.
113 *
114 * Binary frames are never split.
115 */
116 public splitLargeFrames: boolean = false;
117
118 /**
119 * See [splitLargeFrames]{@link Client#splitLargeFrames}.
120 * This has no effect if [splitLargeFrames]{@link Client#splitLargeFrames} is `false`.
121 */
122 public maxWebSocketChunkSize: number = 8 * 1024;
123
124 /**
125 * Usually the
126 * [type of WebSocket frame]{@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send#Parameters}
127 * is automatically decided by type of the payload.
128 * Default is `false`, which should work with all compliant brokers.
129 *
130 * Set this flag to force binary frames.
131 */
132 public forceBinaryWSFrames: boolean = false;
133
134 /**
135 * A bug in ReactNative chops a string on occurrence of a NULL.
136 * See issue [https://github.com/stomp-js/stompjs/issues/89]{@link https://github.com/stomp-js/stompjs/issues/89}.
137 * This makes incoming WebSocket messages invalid STOMP packets.
138 * Setting this flag attempts to reverse the damage by appending a NULL.
139 * If the broker splits a large message into multiple WebSocket messages,
140 * this flag will cause data loss and abnormal termination of connection.
141 *
142 * This is not an ideal solution, but a stop gap until the underlying issue is fixed at ReactNative library.
143 */
144 public appendMissingNULLonIncoming: boolean = false;
145
146 /**
147 * Underlying WebSocket instance, READONLY.
148 */
149 get webSocket(): IStompSocket | undefined {
150 return this._stompHandler?._webSocket;
151 }
152
153 /**
154 * Connection headers, important keys - `login`, `passcode`, `host`.
155 * Though STOMP 1.2 standard marks these keys to be present, check your broker documentation for
156 * details specific to your broker.
157 */
158 public connectHeaders: StompHeaders;
159
160 /**
161 * Disconnection headers.
162 */
163 get disconnectHeaders(): StompHeaders {
164 return this._disconnectHeaders;
165 }
166
167 set disconnectHeaders(value: StompHeaders) {
168 this._disconnectHeaders = value;
169 if (this._stompHandler) {
170 this._stompHandler.disconnectHeaders = this._disconnectHeaders;
171 }
172 }
173 private _disconnectHeaders: StompHeaders;
174
175 /**
176 * This function will be called for any unhandled messages.
177 * It is useful for receiving messages sent to RabbitMQ temporary queues.
178 *
179 * It can also get invoked with stray messages while the server is processing
180 * a request to [Client#unsubscribe]{@link Client#unsubscribe}
181 * from an endpoint.
182 *
183 * The actual {@link IMessage} will be passed as parameter to the callback.
184 */
185 public onUnhandledMessage: messageCallbackType;
186
187 /**
188 * STOMP brokers can be requested to notify when an operation is actually completed.
189 * Prefer using [Client#watchForReceipt]{@link Client#watchForReceipt}. See
190 * [Client#watchForReceipt]{@link Client#watchForReceipt} for examples.
191 *
192 * The actual {@link IFrame} will be passed as parameter to the callback.
193 */
194 public onUnhandledReceipt: frameCallbackType;
195
196 /**
197 * Will be invoked if {@link IFrame} of an unknown type is received from the STOMP broker.
198 *
199 * The actual {@link IFrame} will be passed as parameter to the callback.
200 */
201 public onUnhandledFrame: frameCallbackType;
202
203 /**
204 * `true` if there is an active connection to STOMP Broker
205 */
206 get connected(): boolean {
207 return !!this._stompHandler && this._stompHandler.connected;
208 }
209
210 /**
211 * Callback, invoked on before a connection to the STOMP broker.
212 *
213 * You can change options on the client, which will impact the immediate connecting.
214 * It is valid to call [Client#decativate]{@link Client#deactivate} in this callback.
215 *
216 * As of version 5.1, this callback can be
217 * [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
218 * (i.e., it can return a
219 * [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)).
220 * In that case, connect will be called only after the Promise is resolved.
221 * This can be used to reliably fetch credentials, access token etc. from some other service
222 * in an asynchronous way.
223 */
224 public beforeConnect: () => void | Promise<void>;
225
226 /**
227 * Callback, invoked on every successful connection to the STOMP broker.
228 *
229 * The actual {@link IFrame} will be passed as parameter to the callback.
230 * Sometimes clients will like to use headers from this frame.
231 */
232 public onConnect: frameCallbackType;
233
234 /**
235 * Callback, invoked on every successful disconnection from the STOMP broker. It will not be invoked if
236 * the STOMP broker disconnected due to an error.
237 *
238 * The actual Receipt {@link IFrame} acknowledging the DISCONNECT will be passed as parameter to the callback.
239 *
240 * The way STOMP protocol is designed, the connection may close/terminate without the client
241 * receiving the Receipt {@link IFrame} acknowledging the DISCONNECT.
242 * You might find [Client#onWebSocketClose]{@link Client#onWebSocketClose} more appropriate to watch
243 * STOMP broker disconnects.
244 */
245 public onDisconnect: frameCallbackType;
246
247 /**
248 * Callback, invoked on an ERROR frame received from the STOMP Broker.
249 * A compliant STOMP Broker will close the connection after this type of frame.
250 * Please check broker specific documentation for exact behavior.
251 *
252 * The actual {@link IFrame} will be passed as parameter to the callback.
253 */
254 public onStompError: frameCallbackType;
255
256 /**
257 * Callback, invoked when underlying WebSocket is closed.
258 *
259 * Actual [CloseEvent]{@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}
260 * is passed as parameter to the callback.
261 */
262 public onWebSocketClose: closeEventCallbackType;
263
264 /**
265 * Callback, invoked when underlying WebSocket raises an error.
266 *
267 * Actual [Event]{@link https://developer.mozilla.org/en-US/docs/Web/API/Event}
268 * is passed as parameter to the callback.
269 */
270 public onWebSocketError: wsErrorCallbackType;
271
272 /**
273 * Set it to log the actual raw communication with the broker.
274 * When unset, it logs headers of the parsed frames.
275 *
276 * Changes effect from the next broker reconnect.
277 *
278 * **Caution: this assumes that frames only have valid UTF8 strings.**
279 */
280 public logRawCommunication: boolean;
281
282 /**
283 * By default, debug messages are discarded. To log to `console` following can be used:
284 *
285 * ```javascript
286 * client.debug = function(str) {
287 * console.log(str);
288 * };
289 * ```
290 *
291 * Currently this method does not support levels of log. Be aware that the
292 * output can be quite verbose
293 * and may contain sensitive information (like passwords, tokens etc.).
294 */
295 public debug: debugFnType;
296
297 /**
298 * Browsers do not immediately close WebSockets when `.close` is issued.
299 * This may cause reconnection to take a significantly long time in case
300 * of some types of failures.
301 * In case of incoming heartbeat failure, this experimental flag instructs
302 * the library to discard the socket immediately
303 * (even before it is actually closed).
304 */
305 public discardWebsocketOnCommFailure: boolean = false;
306
307 /**
308 * version of STOMP protocol negotiated with the server, READONLY
309 */
310 get connectedVersion(): string | undefined {
311 return this._stompHandler ? this._stompHandler.connectedVersion : undefined;
312 }
313
314 private _stompHandler: StompHandler | undefined;
315
316 /**
317 * if the client is active (connected or going to reconnect)
318 */
319 get active(): boolean {
320 return this.state === ActivationState.ACTIVE;
321 }
322
323 /**
324 * It will be called on state change.
325 *
326 * When deactivating, it may go from ACTIVE to INACTIVE without entering DEACTIVATING.
327 */
328 public onChangeState: (state: ActivationState) => void;
329
330 private _changeState(state: ActivationState) {
331 this.state = state;
332 this.onChangeState(state);
333 }
334
335 /**
336 * Activation state.
337 *
338 * It will usually be ACTIVE or INACTIVE.
339 * When deactivating, it may go from ACTIVE to INACTIVE without entering DEACTIVATING.
340 */
341 public state: ActivationState = ActivationState.INACTIVE;
342
343 private _reconnector: any;
344
345 /**
346 * Create an instance.
347 */
348 constructor(conf: StompConfig = {}) {
349 // No op callbacks
350 const noOp = () => {};
351 this.debug = noOp;
352 this.beforeConnect = noOp;
353 this.onConnect = noOp;
354 this.onDisconnect = noOp;
355 this.onUnhandledMessage = noOp;
356 this.onUnhandledReceipt = noOp;
357 this.onUnhandledFrame = noOp;
358 this.onStompError = noOp;
359 this.onWebSocketClose = noOp;
360 this.onWebSocketError = noOp;
361 this.logRawCommunication = false;
362 this.onChangeState = noOp;
363
364 // These parameters would typically get proper values before connect is called
365 this.connectHeaders = {};
366 this._disconnectHeaders = {};
367
368 // Apply configuration
369 this.configure(conf);
370 }
371
372 /**
373 * Update configuration.
374 */
375 public configure(conf: StompConfig): void {
376 // bulk assign all properties to this
377 (Object as any).assign(this, conf);
378 }
379
380 /**
381 * Initiate the connection with the broker.
382 * If the connection breaks, as per [Client#reconnectDelay]{@link Client#reconnectDelay},
383 * it will keep trying to reconnect.
384 *
385 * Call [Client#deactivate]{@link Client#deactivate} to disconnect and stop reconnection attempts.
386 */
387 public activate(): void {
388 const _activate = () => {
389 if (this.active) {
390 this.debug('Already ACTIVE, ignoring request to activate');
391 return;
392 }
393
394 this._changeState(ActivationState.ACTIVE);
395
396 this._connect();
397 };
398
399 // if it is deactivating, wait for it to complete before activating.
400 if (this.state === ActivationState.DEACTIVATING) {
401 this.debug('Waiting for deactivation to finish before activating');
402 this.deactivate().then(() => {
403 _activate();
404 });
405 } else {
406 _activate();
407 }
408 }
409
410 private async _connect(): Promise<void> {
411 await this.beforeConnect();
412
413 if (this._stompHandler) {
414 this.debug('There is already a stompHandler, skipping the call to connect');
415 return;
416 }
417
418 if (!this.active) {
419 this.debug(
420 'Client has been marked inactive, will not attempt to connect'
421 );
422 return;
423 }
424
425 // setup connection watcher
426 if (this.connectionTimeout > 0) {
427 // clear first
428 if (this._connectionWatcher) {
429 clearTimeout(this._connectionWatcher);
430 }
431 this._connectionWatcher = setTimeout(() => {
432 if (this.connected) {
433 return;
434 }
435 // Connection not established, close the underlying socket
436 // a reconnection will be attempted
437 this.debug(
438 `Connection not established in ${this.connectionTimeout}ms, closing socket`
439 );
440 this.forceDisconnect();
441 }, this.connectionTimeout);
442 }
443
444 this.debug('Opening Web Socket...');
445
446 // Get the actual WebSocket (or a similar object)
447 const webSocket = this._createWebSocket();
448
449 this._stompHandler = new StompHandler(this, webSocket, {
450 debug: this.debug,
451 stompVersions: this.stompVersions,
452 connectHeaders: this.connectHeaders,
453 disconnectHeaders: this._disconnectHeaders,
454 heartbeatIncoming: this.heartbeatIncoming,
455 heartbeatOutgoing: this.heartbeatOutgoing,
456 splitLargeFrames: this.splitLargeFrames,
457 maxWebSocketChunkSize: this.maxWebSocketChunkSize,
458 forceBinaryWSFrames: this.forceBinaryWSFrames,
459 logRawCommunication: this.logRawCommunication,
460 appendMissingNULLonIncoming: this.appendMissingNULLonIncoming,
461 discardWebsocketOnCommFailure: this.discardWebsocketOnCommFailure,
462
463 onConnect: frame => {
464 // Successfully connected, stop the connection watcher
465 if (this._connectionWatcher) {
466 clearTimeout(this._connectionWatcher);
467 this._connectionWatcher = undefined;
468 }
469
470 if (!this.active) {
471 this.debug(
472 'STOMP got connected while deactivate was issued, will disconnect now'
473 );
474 this._disposeStompHandler();
475 return;
476 }
477 this.onConnect(frame);
478 },
479 onDisconnect: frame => {
480 this.onDisconnect(frame);
481 },
482 onStompError: frame => {
483 this.onStompError(frame);
484 },
485 onWebSocketClose: evt => {
486 this._stompHandler = undefined; // a new one will be created in case of a reconnect
487
488 if (this.state === ActivationState.DEACTIVATING) {
489 // Mark deactivation complete
490 this._changeState(ActivationState.INACTIVE);
491 }
492
493 // The callback is called before attempting to reconnect, this would allow the client
494 // to be `deactivated` in the callback.
495 this.onWebSocketClose(evt);
496
497 if (this.active) {
498 this._schedule_reconnect();
499 }
500 },
501 onWebSocketError: evt => {
502 this.onWebSocketError(evt);
503 },
504 onUnhandledMessage: message => {
505 this.onUnhandledMessage(message);
506 },
507 onUnhandledReceipt: frame => {
508 this.onUnhandledReceipt(frame);
509 },
510 onUnhandledFrame: frame => {
511 this.onUnhandledFrame(frame);
512 },
513 });
514
515 this._stompHandler.start();
516 }
517
518 private _createWebSocket(): IStompSocket {
519 let webSocket: IStompSocket;
520
521 if (this.webSocketFactory) {
522 webSocket = this.webSocketFactory();
523 } else if (this.brokerURL) {
524 webSocket = new WebSocket(
525 this.brokerURL,
526 this.stompVersions.protocolVersions()
527 );
528 } else {
529 throw new Error('Either brokerURL or webSocketFactory must be provided');
530 }
531 webSocket.binaryType = 'arraybuffer';
532 return webSocket;
533 }
534
535 private _schedule_reconnect(): void {
536 if (this.reconnectDelay > 0) {
537 this.debug(`STOMP: scheduling reconnection in ${this.reconnectDelay}ms`);
538
539 this._reconnector = setTimeout(() => {
540 this._connect();
541 }, this.reconnectDelay);
542 }
543 }
544
545 /**
546 * Disconnect if connected and stop auto reconnect loop.
547 * Appropriate callbacks will be invoked if there is an underlying STOMP connection.
548 *
549 * This call is async. It will resolve immediately if there is no underlying active websocket,
550 * otherwise, it will resolve after the underlying websocket is properly disposed of.
551 *
552 * It is not an error to invoke this method more than once.
553 * Each of those would resolve on completion of deactivation.
554 *
555 * To reactivate, you can call [Client#activate]{@link Client#activate}.
556 *
557 * Experimental: pass `force: true` to immediately discard the underlying connection.
558 * This mode will skip both the STOMP and the Websocket shutdown sequences.
559 * In some cases, browsers take a long time in the Websocket shutdown
560 * if the underlying connection had gone stale.
561 * Using this mode can speed up.
562 * When this mode is used, the actual Websocket may linger for a while
563 * and the broker may not realize that the connection is no longer in use.
564 *
565 * It is possible to invoke this method initially without the `force` option
566 * and subsequently, say after a wait, with the `force` option.
567 */
568 public async deactivate(options: { force?: boolean } = {}): Promise<void> {
569 const force: boolean = options.force || false;
570 const needToDispose = this.active;
571 let retPromise: Promise<void>;
572
573 if (this.state === ActivationState.INACTIVE) {
574 this.debug(`Already INACTIVE, nothing more to do`);
575 return Promise.resolve();
576 }
577
578 this._changeState(ActivationState.DEACTIVATING);
579
580 // Clear if a reconnection was scheduled
581 if (this._reconnector) {
582 clearTimeout(this._reconnector);
583 this._reconnector = undefined;
584 }
585
586 if (
587 this._stompHandler &&
588 // @ts-ignore - if there is a _stompHandler, there is the webSocket
589 this.webSocket.readyState !== StompSocketState.CLOSED
590 ) {
591 const origOnWebSocketClose = this._stompHandler.onWebSocketClose;
592 // we need to wait for the underlying websocket to close
593 retPromise = new Promise<void>((resolve, reject) => {
594 // @ts-ignore - there is a _stompHandler
595 this._stompHandler.onWebSocketClose = evt => {
596 origOnWebSocketClose(evt);
597 resolve();
598 };
599 });
600 } else {
601 // indicate that auto reconnect loop should terminate
602 this._changeState(ActivationState.INACTIVE);
603 return Promise.resolve();
604 }
605
606 if (force) {
607 this._stompHandler?.discardWebsocket();
608 } else if (needToDispose) {
609 this._disposeStompHandler();
610 }
611
612 return retPromise;
613 }
614
615 /**
616 * Force disconnect if there is an active connection by directly closing the underlying WebSocket.
617 * This is different from a normal disconnect where a DISCONNECT sequence is carried out with the broker.
618 * After forcing disconnect, automatic reconnect will be attempted.
619 * To stop further reconnects call [Client#deactivate]{@link Client#deactivate} as well.
620 */
621 public forceDisconnect() {
622 if (this._stompHandler) {
623 this._stompHandler.forceDisconnect();
624 }
625 }
626
627 private _disposeStompHandler() {
628 // Dispose STOMP Handler
629 if (this._stompHandler) {
630 this._stompHandler.dispose();
631 }
632 }
633
634 /**
635 * Send a message to a named destination. Refer to your STOMP broker documentation for types
636 * and naming of destinations.
637 *
638 * STOMP protocol specifies and suggests some headers and also allows broker-specific headers.
639 *
640 * `body` must be String.
641 * You will need to covert the payload to string in case it is not string (e.g. JSON).
642 *
643 * To send a binary message body, use `binaryBody` parameter. It should be a
644 * [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array).
645 * Sometimes brokers may not support binary frames out of the box.
646 * Please check your broker documentation.
647 *
648 * `content-length` header is automatically added to the STOMP Frame sent to the broker.
649 * Set `skipContentLengthHeader` to indicate that `content-length` header should not be added.
650 * For binary messages, `content-length` header is always added.
651 *
652 * Caution: The broker will, most likely, report an error and disconnect
653 * if the message body has NULL octet(s) and `content-length` header is missing.
654 *
655 * ```javascript
656 * client.publish({destination: "/queue/test", headers: {priority: 9}, body: "Hello, STOMP"});
657 *
658 * // Only destination is mandatory parameter
659 * client.publish({destination: "/queue/test", body: "Hello, STOMP"});
660 *
661 * // Skip content-length header in the frame to the broker
662 * client.publish({"/queue/test", body: "Hello, STOMP", skipContentLengthHeader: true});
663 *
664 * var binaryData = generateBinaryData(); // This need to be of type Uint8Array
665 * // setting content-type header is not mandatory, however a good practice
666 * client.publish({destination: '/topic/special', binaryBody: binaryData,
667 * headers: {'content-type': 'application/octet-stream'}});
668 * ```
669 */
670 public publish(params: IPublishParams) {
671 this._checkConnection();
672 // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
673 this._stompHandler.publish(params);
674 }
675
676 private _checkConnection() {
677 if (!this.connected) {
678 throw new TypeError('There is no underlying STOMP connection');
679 }
680 }
681
682 /**
683 * STOMP brokers may carry out operation asynchronously and allow requesting for acknowledgement.
684 * To request an acknowledgement, a `receipt` header needs to be sent with the actual request.
685 * The value (say receipt-id) for this header needs to be unique for each use.
686 * Typically, a sequence, a UUID, a random number or a combination may be used.
687 *
688 * A complaint broker will send a RECEIPT frame when an operation has actually been completed.
689 * The operation needs to be matched based on the value of the receipt-id.
690 *
691 * This method allows watching for a receipt and invoking the callback
692 * when the corresponding receipt has been received.
693 *
694 * The actual {@link IFrame} will be passed as parameter to the callback.
695 *
696 * Example:
697 * ```javascript
698 * // Subscribing with acknowledgement
699 * let receiptId = randomText();
700 *
701 * client.watchForReceipt(receiptId, function() {
702 * // Will be called after server acknowledges
703 * });
704 *
705 * client.subscribe(TEST.destination, onMessage, {receipt: receiptId});
706 *
707 *
708 * // Publishing with acknowledgement
709 * receiptId = randomText();
710 *
711 * client.watchForReceipt(receiptId, function() {
712 * // Will be called after server acknowledges
713 * });
714 * client.publish({destination: TEST.destination, headers: {receipt: receiptId}, body: msg});
715 * ```
716 */
717 public watchForReceipt(receiptId: string, callback: frameCallbackType): void {
718 this._checkConnection();
719 // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
720 this._stompHandler.watchForReceipt(receiptId, callback);
721 }
722
723 /**
724 * Subscribe to a STOMP Broker location. The callback will be invoked for each
725 * received message with the {@link IMessage} as argument.
726 *
727 * Note: The library will generate a unique ID if there is none provided in the headers.
728 * To use your own ID, pass it using the `headers` argument.
729 *
730 * ```javascript
731 * callback = function(message) {
732 * // called when the client receives a STOMP message from the server
733 * if (message.body) {
734 * alert("got message with body " + message.body)
735 * } else {
736 * alert("got empty message");
737 * }
738 * });
739 *
740 * var subscription = client.subscribe("/queue/test", callback);
741 *
742 * // Explicit subscription id
743 * var mySubId = 'my-subscription-id-001';
744 * var subscription = client.subscribe(destination, callback, { id: mySubId });
745 * ```
746 */
747 public subscribe(
748 destination: string,
749 callback: messageCallbackType,
750 headers: StompHeaders = {}
751 ): StompSubscription {
752 this._checkConnection();
753 // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
754 return this._stompHandler.subscribe(destination, callback, headers);
755 }
756
757 /**
758 * It is preferable to unsubscribe from a subscription by calling
759 * `unsubscribe()` directly on {@link StompSubscription} returned by `client.subscribe()`:
760 *
761 * ```javascript
762 * var subscription = client.subscribe(destination, onmessage);
763 * // ...
764 * subscription.unsubscribe();
765 * ```
766 *
767 * See: https://stomp.github.com/stomp-specification-1.2.html#UNSUBSCRIBE UNSUBSCRIBE Frame
768 */
769 public unsubscribe(id: string, headers: StompHeaders = {}): void {
770 this._checkConnection();
771 // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
772 this._stompHandler.unsubscribe(id, headers);
773 }
774
775 /**
776 * Start a transaction, the returned {@link ITransaction} has methods - [commit]{@link ITransaction#commit}
777 * and [abort]{@link ITransaction#abort}.
778 *
779 * `transactionId` is optional, if not passed the library will generate it internally.
780 */
781 public begin(transactionId?: string): ITransaction {
782 this._checkConnection();
783 // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
784 return this._stompHandler.begin(transactionId);
785 }
786
787 /**
788 * Commit a transaction.
789 *
790 * It is preferable to commit a transaction by calling [commit]{@link ITransaction#commit} directly on
791 * {@link ITransaction} returned by [client.begin]{@link Client#begin}.
792 *
793 * ```javascript
794 * var tx = client.begin(txId);
795 * //...
796 * tx.commit();
797 * ```
798 */
799 public commit(transactionId: string): void {
800 this._checkConnection();
801 // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
802 this._stompHandler.commit(transactionId);
803 }
804
805 /**
806 * Abort a transaction.
807 * It is preferable to abort a transaction by calling [abort]{@link ITransaction#abort} directly on
808 * {@link ITransaction} returned by [client.begin]{@link Client#begin}.
809 *
810 * ```javascript
811 * var tx = client.begin(txId);
812 * //...
813 * tx.abort();
814 * ```
815 */
816 public abort(transactionId: string): void {
817 this._checkConnection();
818 // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
819 this._stompHandler.abort(transactionId);
820 }
821
822 /**
823 * ACK a message. It is preferable to acknowledge a message by calling [ack]{@link IMessage#ack} directly
824 * on the {@link IMessage} handled by a subscription callback:
825 *
826 * ```javascript
827 * var callback = function (message) {
828 * // process the message
829 * // acknowledge it
830 * message.ack();
831 * };
832 * client.subscribe(destination, callback, {'ack': 'client'});
833 * ```
834 */
835 public ack(
836 messageId: string,
837 subscriptionId: string,
838 headers: StompHeaders = {}
839 ): void {
840 this._checkConnection();
841 // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
842 this._stompHandler.ack(messageId, subscriptionId, headers);
843 }
844
845 /**
846 * NACK a message. It is preferable to acknowledge a message by calling [nack]{@link IMessage#nack} directly
847 * on the {@link IMessage} handled by a subscription callback:
848 *
849 * ```javascript
850 * var callback = function (message) {
851 * // process the message
852 * // an error occurs, nack it
853 * message.nack();
854 * };
855 * client.subscribe(destination, callback, {'ack': 'client'});
856 * ```
857 */
858 public nack(
859 messageId: string,
860 subscriptionId: string,
861 headers: StompHeaders = {}
862 ): void {
863 this._checkConnection();
864 // @ts-ignore - we already checked that there is a _stompHandler, and it is connected
865 this._stompHandler.nack(messageId, subscriptionId, headers);
866 }
867}
868
\No newline at end of file