├── .github ├── ISSUE_TEMPLATE │ └── issue---feature-request.md ├── dependabot.yml └── workflows │ ├── docs-refresh.yml │ ├── linux.yml │ ├── node-js.yml │ └── osx.yml ├── .gitignore ├── .npmignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.json ├── Change-log.md ├── Contribute.md ├── LICENSE ├── README.md ├── bin ├── osx │ └── setup-rabbit.sh ├── rx-stomp.sh ├── setup-rabbit.sh └── watch-build.sh ├── bundles ├── package.json ├── stomp.umd.js ├── stomp.umd.js.map └── stomp.umd.min.js ├── esm6 ├── augment-websocket.d.ts ├── augment-websocket.js ├── augment-websocket.js.map ├── byte.d.ts ├── byte.js ├── byte.js.map ├── client.d.ts ├── client.js ├── client.js.map ├── compatibility │ ├── compat-client.d.ts │ ├── compat-client.js │ ├── compat-client.js.map │ ├── heartbeat-info.d.ts │ ├── heartbeat-info.js │ ├── heartbeat-info.js.map │ ├── stomp.d.ts │ ├── stomp.js │ └── stomp.js.map ├── frame-impl.d.ts ├── frame-impl.js ├── frame-impl.js.map ├── i-frame.d.ts ├── i-frame.js ├── i-frame.js.map ├── i-message.d.ts ├── i-message.js ├── i-message.js.map ├── i-transaction.d.ts ├── i-transaction.js ├── i-transaction.js.map ├── index.d.ts ├── index.js ├── index.js.map ├── parser.d.ts ├── parser.js ├── parser.js.map ├── stomp-config.d.ts ├── stomp-config.js ├── stomp-config.js.map ├── stomp-handler.d.ts ├── stomp-handler.js ├── stomp-handler.js.map ├── stomp-headers.d.ts ├── stomp-headers.js ├── stomp-headers.js.map ├── stomp-subscription.d.ts ├── stomp-subscription.js ├── stomp-subscription.js.map ├── ticker.d.ts ├── ticker.js ├── ticker.js.map ├── types.d.ts ├── types.js ├── types.js.map ├── versions.d.ts ├── versions.js └── versions.js.map ├── index.d.ts ├── package-lock.json ├── package.json ├── rabbitmq └── Dockerfile ├── rollup.config.mjs ├── spec ├── config │ ├── browser-config.js │ └── node-config.js ├── helpers-src │ ├── tsconfig.json │ └── wrapper-ws.ts ├── helpers │ ├── connect-helpers.js │ ├── content-helpers.js │ ├── parse-frame.js │ ├── utils.js │ └── wrapper-ws.js ├── karma.conf.js ├── package.json ├── support │ └── jasmine.json └── unit │ ├── ack.spec.js │ ├── append-missing-null-on-incoming.spec.js │ ├── callbacks.spec.js │ ├── compatibility │ ├── connection.spec.js │ ├── heartbeat-options.spec.js │ ├── message.spec.js │ └── parse-connect.spec.js │ ├── config.spec.js │ ├── connection.spec.js │ ├── force-binary-ws-frames.js │ ├── frame.spec.js │ ├── heart-beat.spec.js │ ├── message.spec.js │ ├── parser.spec.js │ ├── receipts.spec.js │ ├── reconnect.spec.js │ ├── split-frames.spec.js │ ├── subscription.spec.js │ ├── transaction.spec.js │ └── web-socket-state.spec.js ├── src ├── augment-websocket.ts ├── byte.ts ├── client.ts ├── compatibility │ ├── compat-client.ts │ ├── heartbeat-info.ts │ └── stomp.ts ├── frame-impl.ts ├── i-frame.ts ├── i-message.ts ├── i-transaction.ts ├── index.ts ├── parser.ts ├── stomp-config.ts ├── stomp-handler.ts ├── stomp-headers.ts ├── stomp-subscription.ts ├── ticker.ts ├── types.ts └── versions.ts ├── tsconfig.json ├── tslint.json └── webpack.config.cjs /.github/ISSUE_TEMPLATE/issue---feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: issue / feature request 3 | about: Report an issue / feature request 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | Please see FAQs at https://stomp-js.github.io/faqs/2019/05/20/faqs.html before reporting. 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: '/' 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | target-branch: develop 9 | ignore: 10 | - dependency-name: karma 11 | versions: 12 | - 6.2.0 13 | - 6.3.0 14 | - 6.3.1 15 | - dependency-name: typescript 16 | versions: ['3.x', '4.x'] 17 | - dependency-name: webpack-cli 18 | versions: 19 | - 4.4.0 20 | - 4.5.0 21 | - dependency-name: karma-summary-reporter 22 | versions: 23 | - 2.0.0 24 | -------------------------------------------------------------------------------- /.github/workflows/docs-refresh.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow that is manually triggered 2 | 3 | name: API docs refresh 4 | 5 | on: 6 | push: 7 | branches: 8 | - master 9 | - develop 10 | 11 | jobs: 12 | build: 13 | # The type of runner that the job will run on 14 | runs-on: ubuntu-latest 15 | 16 | # Steps represent a sequence of tasks that will be executed as part of the job 17 | steps: 18 | - name: Trigger workflow to update Github pages 19 | uses: benc-uk/workflow-dispatch@v1 20 | with: 21 | workflow: API docs refresh 22 | repo: stomp-js/stomp-js.github.io 23 | ref: master 24 | token: ${{ secrets.WORKLOW_TOKEN }} 25 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow that is manually triggered 2 | 3 | name: Firefox, Chrome 4 | 5 | # Controls when the action will run. Workflow runs when manually triggered using the UI 6 | # or API. 7 | on: [push, pull_request] 8 | 9 | jobs: 10 | build: 11 | # The type of runner that the job will run on 12 | runs-on: ubuntu-latest 13 | 14 | # Steps represent a sequence of tasks that will be executed as part of the job 15 | steps: 16 | - uses: actions/checkout@v3 17 | - run: bin/setup-rabbit.sh 18 | - name: Use Node.js 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: '18.x' 22 | - run: npm ci 23 | - run: npm run lint 24 | - run: npm run build 25 | - run: npx karma start spec/karma.conf.js --single-run --browsers=ChromeNoSandboxHeadless 26 | - run: npx karma start spec/karma.conf.js --single-run --browsers=FirefoxHeadless 27 | - run: npm pack 28 | - run: bin/rx-stomp.sh 29 | -------------------------------------------------------------------------------- /.github/workflows/node-js.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow that is manually triggered 2 | 3 | name: NodeJS Test 4 | 5 | # Controls when the action will run. Workflow runs when manually triggered using the UI 6 | # or API. 7 | on: [push, pull_request] 8 | 9 | jobs: 10 | build: 11 | # The type of runner that the job will run on 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | version: [16, 18, 19] 17 | 18 | # Steps represent a sequence of tasks that will be executed as part of the job 19 | steps: 20 | - uses: actions/checkout@v3 21 | - run: bin/setup-rabbit.sh 22 | - name: Use Node.js 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: ${{ matrix.version }} 26 | - run: npm ci 27 | - run: npm i stomp-js/tcp-wrapper --save-dev 28 | - run: npm run build 29 | - run: npm run test 30 | - run: CONN_MODE=tcp npm run test 31 | -------------------------------------------------------------------------------- /.github/workflows/osx.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow that is manually triggered 2 | 3 | name: Safari 4 | 5 | # Controls when the action will run. Workflow runs when manually triggered using the UI 6 | # or API. 7 | on: [push, pull_request] 8 | 9 | jobs: 10 | build: 11 | # The type of runner that the job will run on 12 | runs-on: macos-latest 13 | 14 | # Steps represent a sequence of tasks that will be executed as part of the job 15 | steps: 16 | - uses: actions/checkout@v3 17 | - run: bin/osx/setup-rabbit.sh 18 | - name: Use Node.js 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: '18.x' 22 | - run: npm ci 23 | - run: npm run build 24 | - run: npx karma start spec/karma.conf.js --single-run --browsers=Safari 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE files 2 | .idea 3 | 4 | # Installed dependencies 5 | node_modules 6 | 7 | # Temporary build files 8 | tmp 9 | *.tgz 10 | 11 | # Temporary dist file 12 | spec/dist/*.js 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Ignore everything then include selectively 2 | * 3 | 4 | !bundles/**/* 5 | !esm6/**/* 6 | !index.d.ts 7 | !package.json 8 | !README.md 9 | !src/**/* 10 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v18 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | 2 | node_modules 3 | 4 | bundles 5 | esm6 6 | 7 | example 8 | tsconfig.json 9 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /Contribute.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## How to contribute 4 | 5 | - File issues. 6 | - Edit/write documentation. 7 | - Submit pull requests. 8 | - Test in different environments. 9 | - Raise awareness. 10 | 11 | ## Summary of tools 12 | 13 | Following tools are getting used: 14 | 15 | - `TypeScript` as primary language - https://www.typescriptlang.org/ 16 | - `compodoc` for API documentation - https://compodoc.app/ 17 | - `Jasmine` for test cases - https://jasmine.github.io/ 18 | - `Karma` for running test cases in browsers - http://karma-runner.github.io/ 19 | - `webpack` for build - https://webpack.js.org/ 20 | - `nodejs` during development - https://nodejs.org/ 21 | - `npm` for dependency management, packaging and distribution - https://www.npmjs.com/ 22 | - `git` for version control - https://git-scm.com/ 23 | 24 | ## Initial setup 25 | 26 | Instructions on setting up development environment: 27 | 28 | - Install `node` and `npm` - https://nodejs.org/ 29 | - Checkout code from GitHub - you may fork the code first into your GitHub account. 30 | - Use `npm i` to install dependencies: 31 | ```bash 32 | $ npm i 33 | ``` 34 | 35 | ## Project structure 36 | 37 | ```text 38 | 39 | ├── LICENSE.md 40 | ├── README.md 41 | ├── bin/ -- Scripts invoked from `npm` tasks 42 | ├── bundles/ -- Generated code browsers 43 | ├── docs/ -- Generated docs, root for GitHub pages 44 | ├── docs-src/ -- Source for guides 45 | ├── esm6/ -- Generated ES6 modules 46 | ├── index.d.ts 47 | ├── karma.conf.js 48 | ├── package-lock.json 49 | ├── package.json 50 | ├── rabbitmq/ 51 | │   └── Dockerfile -- This builds a docker image that is used to run test cases 52 | ├── spec/ -- These test cases run both for nodejs (using just Jasmine) and Chrome (Jasmine/Karma) 53 | │   ├── config/ -- Slightly different setups for nodejs and Karma 54 | │   ├── helpers/ 55 | │   ├── support/ 56 | │   │   └── jasmine.json -- Used only while running for nodejs 57 | │   └── unit/ -- Test cases using Jasmine 58 | │   └── compatibility/ -- Test cases to check compatibility mode 59 | ├── src/ -- Typescript sources 60 | │   └── compatibility/ -- Code for compatibility mode 61 | ├── tsconfig.json 62 | └── webpack.config.js 63 | ``` 64 | 65 | ## Setup a Stomp broker 66 | 67 | - A Stomp broker is used for running the tests. I have been using RabbitMQ. 68 | - Edit `spec/config/browser-config.js` and `spec/config/node-config.js` as per 69 | your setup. Defaults should work for as RabbitMQ default setup on localhost. 70 | - Please note that in RabbitMQ you will need to enable Stomp and WebStomp plugins. 71 | - By default RabbitMQ WebStomp will treat messages a text, you will need to tell 72 | it is use binary frames: 73 | ```bash 74 | $ echo 'web_stomp.ws_frame = binary' >> /etc/rabbitmq/rabbitmq.conf 75 | ``` 76 | - A RabbitMQ Dockerfile is provided with necessary plugins and configuration. To use it, run: 77 | ```bash 78 | $ docker build -t myrabbitmq rabbitmq/ # Needed only once 79 | $ docker run -d -p 15674:15674 myrabbitmq # to start the broker 80 | ``` 81 | 82 | ## Building and testing 83 | 84 | Key npm tasks: 85 | 86 | ```text 87 | clean - Remove generated built artifacts 88 | build-tsc - Internally used by `npm run build` 89 | build-webpack - Internally used by `npm run build` 90 | build - Build two variants - ES Modules and UMD 91 | doc - Generate docs 92 | doc-serve - Generate docs and watch for changes 93 | test - Run tests in NodeJS 94 | karma - Rune test in browsers 95 | ``` 96 | 97 | ### Basic development workflow 98 | 99 | 1. Checkout a new branch. 100 | 1. Make code changes (src/specs) 101 | 1. Build: 102 | ```bash 103 | $ npm run build 104 | ``` 105 | 1. Run tests: 106 | - To run tests using nodejs: 107 | ```bash 108 | $ npm run test 109 | ``` 110 | - To run tests using Chrome: 111 | ```bash 112 | $ npm run karma 113 | ``` 114 | - _**Caution:** As both browser and nodejs use same set of test cases and same queue 115 | names. So, running both together may cause unexpected failures._ 116 | 1. Update documentation - do update the docs-src/Change-log.md 117 | 1. Regenerate documentation: 118 | ```bash 119 | $ npm run doc 120 | ``` 121 | 1. Please follow GitHub guidelines. Raise an issue if you are unclear. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # STOMP.js 2 | 3 | [![Firefox, Chrome](https://github.com/stomp-js/stompjs/actions/workflows/linux.yml/badge.svg?branch=develop)](https://github.com/stomp-js/stompjs/actions/workflows/linux.yml) 4 | [![Safari, Edge](https://github.com/stomp-js/stompjs/actions/workflows/osx.yml/badge.svg?branch=develop)](https://github.com/stomp-js/stompjs/actions/workflows/osx.yml) 5 | [![NodeJS Test](https://github.com/stomp-js/stompjs/actions/workflows/node-js.yml/badge.svg?branch=develop)](https://github.com/stomp-js/stompjs/actions/workflows/node-js.yml) 6 | [![API docs refresh](https://github.com/stomp-js/stompjs/actions/workflows/docs-refresh.yml/badge.svg?branch=develop)](https://github.com/stomp-js/stompjs/actions/workflows/docs-refresh.yml) 7 | 8 | This library provides a STOMP over WebSocket client for Web browser and node.js applications. 9 | 10 | Please visit https://stomp-js.github.io/ for guides, FAQs and API docs. 11 | 12 | # Introduction 13 | 14 | This library allows you to connect to a STOMP broker over WebSocket. This library 15 | supports complete STOMP specifications including all current protocol variants. Most 16 | popular messaging brokers support STOMP and STOMP over WebSockets out-of-the-box 17 | or using plugins. 18 | 19 | ## Features 20 | 21 | - Simple API to interact with the Stomp protocol 22 | - Support for v1.2, v1.1 and v1.0 of the Stomp protocol 23 | - Support for fallback options in case of WebSocket unavailable 24 | - Browser and Node.js support 25 | - Option to use STOMP over TCP 26 | - Binary payload support 27 | 28 | ## Usage 29 | 30 | ### Browser 31 | 32 | ```html 33 | 37 | 44 | 45 | 46 | 51 | 52 | 67 | ``` 68 | 69 | ### NodeJS 70 | 71 | ```bash 72 | $ npm install @stomp/stompjs ws 73 | ``` 74 | 75 | ```javascript 76 | import { Client } from '@stomp/stompjs'; 77 | 78 | import { WebSocket } from 'ws'; 79 | Object.assign(global, { WebSocket }); 80 | 81 | const client = new Client({ 82 | brokerURL: 'ws://localhost:15674/ws', 83 | onConnect: () => { 84 | client.subscribe('/topic/test01', message => 85 | console.log(`Received: ${message.body}`) 86 | ); 87 | client.publish({ destination: '/topic/test01', body: 'First Message' }); 88 | }, 89 | }); 90 | 91 | client.activate(); 92 | ``` 93 | 94 | ## Further information 95 | 96 | The API documentation is hosted as GitHub pages for the StompJS family of libraries. 97 | You may head straight to the https://stomp-js.github.io/api-docs/latest/ 98 | 99 | This library comes with detailed usage instructions. Please find it at 100 | [Usage instructions](https://stomp-js.github.io/guide/stompjs/using-stompjs-v5.html). 101 | Check out other guides at https://stomp-js.github.io/. 102 | 103 | There is quite detailed API documentation, 104 | you should start at https://stomp-js.github.io/api-docs/latest/classes/Client.html. 105 | 106 | ## Upgrading 107 | 108 | if you were using an older version of this library, you would need to make changes 109 | to your code. Head to 110 | [Upgrading](https://stomp-js.github.io/#upgrading). 111 | 112 | ## Usage with RxJS 113 | 114 | https://github.com/stomp-js/rx-stomp is based on this library and exposes the entire functionality 115 | offered by this library as RxJS Observables. 116 | 117 | ## TypeScript definitions 118 | 119 | The npm package includes TypeScript definitions, so there is no need to install it separately. 120 | 121 | ## Change-log 122 | 123 | Please visit [Change Log](Change-log.md). 124 | 125 | ## Contributing 126 | 127 | If you want to understand the code, develop, or contribute. Please visit 128 | [How to contribute](Contribute.md). 129 | 130 | ## Authors 131 | 132 | - [Jeff Mesnil](http://jmesnil.net/) 133 | - [Jeff Lindsay](http://github.com/progrium) 134 | - [Vanessa Williams](http://github.com/fridgebuzz) 135 | - [Deepak Kumar](https://github.com/kum-deepak) 136 | - [Astha Deep](https://github.com/astha183) 137 | - [Dillon Sellars](https://github.com/dillon-sellars) 138 | - [Jimi Charalampidis](https://github.com/jimic) 139 | - [Raul](https://github.com/rulonder) 140 | - [Dimitar Georgiev](https://github.com/iMitaka) 141 | - [Genadi](https://github.com/genadis) 142 | - [Bobohuochai](https://github.com/bobohuochai) 143 | - [Sailai](https://github.com/sailai) 144 | - [Harsh Deep](https://github.com/harsh183) 145 | - [Nikos Epping](https://github.com/Nikos410) 146 | - [Tom Pachtner](https://github.com/tomamatics) 147 | - [David Nussio](https://github.com/davidnussio) 148 | 149 | ## License 150 | 151 | [License](LICENSE) - Apache-2.0 152 | -------------------------------------------------------------------------------- /bin/osx/setup-rabbit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | brew update 6 | 7 | brew install rabbitmq 8 | 9 | # Path depends on whether Apple or Intel silicon is in use, https://www.rabbitmq.com/docs/install-homebrew 10 | /opt/homebrew/sbin/rabbitmq-plugins enable --offline rabbitmq_web_stomp 11 | echo 'web_stomp.ws_frame = binary' >>/opt/homebrew/etc/rabbitmq/rabbitmq.conf 12 | 13 | brew services run rabbitmq 14 | brew services list 15 | -------------------------------------------------------------------------------- /bin/rx-stomp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | # Checkout 6 | mkdir -p tmp/rx-stomp 7 | cd tmp/rx-stomp 8 | git clone --depth 1 https://github.com/stomp-js/rx-stomp develop 9 | cd develop 10 | 11 | # npm install 12 | npm ci 13 | npm i ../../../stomp-stompjs-*.tgz 14 | 15 | # Test 16 | npm run karma 17 | 18 | # Pack 19 | npm pack 20 | -------------------------------------------------------------------------------- /bin/setup-rabbit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | docker build -t rabbitmq:official-alpine-with-webstomp rabbitmq/ 6 | docker run -d --hostname rabbitmq --name rabbitmq \ 7 | -p 15674:15674 -p 61613:61613 \ 8 | rabbitmq:official-alpine-with-webstomp 9 | -------------------------------------------------------------------------------- /bin/watch-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | npx onchange -i -k 'src/**/*' -- npm run build 4 | -------------------------------------------------------------------------------- /bundles/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spec", 3 | "version": "1.0.0", 4 | "description": "Just for running specs with commonjs modules", 5 | "type": "commonjs" 6 | } 7 | -------------------------------------------------------------------------------- /esm6/augment-websocket.d.ts: -------------------------------------------------------------------------------- 1 | import { IStompSocket } from './types.js'; 2 | /** 3 | * @internal 4 | */ 5 | export declare function augmentWebsocket(webSocket: IStompSocket, debug: (msg: string) => void): void; 6 | -------------------------------------------------------------------------------- /esm6/augment-websocket.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal 3 | */ 4 | export function augmentWebsocket(webSocket, debug) { 5 | webSocket.terminate = function () { 6 | const noOp = () => { }; 7 | // set all callbacks to no op 8 | this.onerror = noOp; 9 | this.onmessage = noOp; 10 | this.onopen = noOp; 11 | const ts = new Date(); 12 | const id = Math.random().toString().substring(2, 8); // A simulated id 13 | const origOnClose = this.onclose; 14 | // Track delay in actual closure of the socket 15 | this.onclose = closeEvent => { 16 | const delay = new Date().getTime() - ts.getTime(); 17 | debug(`Discarded socket (#${id}) closed after ${delay}ms, with code/reason: ${closeEvent.code}/${closeEvent.reason}`); 18 | }; 19 | this.close(); 20 | origOnClose?.call(webSocket, { 21 | code: 4001, 22 | reason: `Quick discarding socket (#${id}) without waiting for the shutdown sequence.`, 23 | wasClean: false, 24 | }); 25 | }; 26 | } 27 | //# sourceMappingURL=augment-websocket.js.map -------------------------------------------------------------------------------- /esm6/augment-websocket.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"augment-websocket.js","sourceRoot":"","sources":["../src/augment-websocket.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAuB,EACvB,KAA4B;IAE5B,SAAS,CAAC,SAAS,GAAG;QACpB,MAAM,IAAI,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;QAEtB,6BAA6B;QAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QAEnB,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,iBAAiB;QAEtE,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC;QAEjC,8CAA8C;QAC9C,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,EAAE;YAC1B,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;YAClD,KAAK,CACH,sBAAsB,EAAE,mBAAmB,KAAK,yBAAyB,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,MAAM,EAAE,CAChH,CAAC;QACJ,CAAC,CAAC;QAEF,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE;YAC3B,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,6BAA6B,EAAE,8CAA8C;YACrF,QAAQ,EAAE,KAAK;SAChB,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"} -------------------------------------------------------------------------------- /esm6/byte.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Some byte values, used as per STOMP specifications. 3 | * 4 | * Part of `@stomp/stompjs`. 5 | * 6 | * @internal 7 | */ 8 | export declare const BYTE: { 9 | LF: string; 10 | NULL: string; 11 | }; 12 | -------------------------------------------------------------------------------- /esm6/byte.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Some byte values, used as per STOMP specifications. 3 | * 4 | * Part of `@stomp/stompjs`. 5 | * 6 | * @internal 7 | */ 8 | export const BYTE = { 9 | // LINEFEED byte (octet 10) 10 | LF: '\x0A', 11 | // NULL byte (octet 0) 12 | NULL: '\x00', 13 | }; 14 | //# sourceMappingURL=byte.js.map -------------------------------------------------------------------------------- /esm6/byte.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"byte.js","sourceRoot":"","sources":["../src/byte.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,2BAA2B;IAC3B,EAAE,EAAE,MAAM;IACV,sBAAsB;IACtB,IAAI,EAAE,MAAM;CACb,CAAC"} -------------------------------------------------------------------------------- /esm6/compatibility/compat-client.d.ts: -------------------------------------------------------------------------------- 1 | import { Client } from '../client.js'; 2 | import { StompHeaders } from '../stomp-headers.js'; 3 | import { frameCallbackType, messageCallbackType } from '../types.js'; 4 | /** 5 | * Available for backward compatibility, please shift to using {@link Client}. 6 | * 7 | * **Deprecated** 8 | * 9 | * Part of `@stomp/stompjs`. 10 | * 11 | * To upgrade, please follow the [Upgrade Guide](https://stomp-js.github.io/guide/stompjs/upgrading-stompjs.html) 12 | */ 13 | export declare class CompatClient extends Client { 14 | /** 15 | * It is no op now. No longer needed. Large packets work out of the box. 16 | */ 17 | maxWebSocketFrameSize: number; 18 | /** 19 | * Available for backward compatibility, please shift to using {@link Client} 20 | * and [Client#webSocketFactory]{@link Client#webSocketFactory}. 21 | * 22 | * **Deprecated** 23 | * 24 | * @internal 25 | */ 26 | constructor(webSocketFactory: () => any); 27 | private _parseConnect; 28 | /** 29 | * Available for backward compatibility, please shift to using [Client#activate]{@link Client#activate}. 30 | * 31 | * **Deprecated** 32 | * 33 | * The `connect` method accepts different number of arguments and types. See the Overloads list. Use the 34 | * version with headers to pass your broker specific options. 35 | * 36 | * overloads: 37 | * - connect(headers, connectCallback) 38 | * - connect(headers, connectCallback, errorCallback) 39 | * - connect(login, passcode, connectCallback) 40 | * - connect(login, passcode, connectCallback, errorCallback) 41 | * - connect(login, passcode, connectCallback, errorCallback, closeEventCallback) 42 | * - connect(login, passcode, connectCallback, errorCallback, closeEventCallback, host) 43 | * 44 | * params: 45 | * - headers, see [Client#connectHeaders]{@link Client#connectHeaders} 46 | * - connectCallback, see [Client#onConnect]{@link Client#onConnect} 47 | * - errorCallback, see [Client#onStompError]{@link Client#onStompError} 48 | * - closeEventCallback, see [Client#onWebSocketClose]{@link Client#onWebSocketClose} 49 | * - login [String], see [Client#connectHeaders](../classes/Client.html#connectHeaders) 50 | * - passcode [String], [Client#connectHeaders](../classes/Client.html#connectHeaders) 51 | * - host [String], see [Client#connectHeaders](../classes/Client.html#connectHeaders) 52 | * 53 | * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html) 54 | */ 55 | connect(...args: any[]): void; 56 | /** 57 | * Available for backward compatibility, please shift to using [Client#deactivate]{@link Client#deactivate}. 58 | * 59 | * **Deprecated** 60 | * 61 | * See: 62 | * [Client#onDisconnect]{@link Client#onDisconnect}, and 63 | * [Client#disconnectHeaders]{@link Client#disconnectHeaders} 64 | * 65 | * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html) 66 | */ 67 | disconnect(disconnectCallback?: any, headers?: StompHeaders): void; 68 | /** 69 | * Available for backward compatibility, use [Client#publish]{@link Client#publish}. 70 | * 71 | * Send a message to a named destination. Refer to your STOMP broker documentation for types 72 | * and naming of destinations. The headers will, typically, be available to the subscriber. 73 | * However, there may be special purpose headers corresponding to your STOMP broker. 74 | * 75 | * **Deprecated**, use [Client#publish]{@link Client#publish} 76 | * 77 | * Note: Body must be String. You will need to covert the payload to string in case it is not string (e.g. JSON) 78 | * 79 | * ```javascript 80 | * client.send("/queue/test", {priority: 9}, "Hello, STOMP"); 81 | * 82 | * // If you want to send a message with a body, you must also pass the headers argument. 83 | * client.send("/queue/test", {}, "Hello, STOMP"); 84 | * ``` 85 | * 86 | * To upgrade, please follow the [Upgrade Guide](../additional-documentation/upgrading.html) 87 | */ 88 | send(destination: string, headers?: { 89 | [key: string]: any; 90 | }, body?: string): void; 91 | /** 92 | * Available for backward compatibility, renamed to [Client#reconnectDelay]{@link Client#reconnectDelay}. 93 | * 94 | * **Deprecated** 95 | */ 96 | set reconnect_delay(value: number); 97 | /** 98 | * Available for backward compatibility, renamed to [Client#webSocket]{@link Client#webSocket}. 99 | * 100 | * **Deprecated** 101 | */ 102 | get ws(): any; 103 | /** 104 | * Available for backward compatibility, renamed to [Client#connectedVersion]{@link Client#connectedVersion}. 105 | * 106 | * **Deprecated** 107 | */ 108 | get version(): string | undefined; 109 | /** 110 | * Available for backward compatibility, renamed to [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}. 111 | * 112 | * **Deprecated** 113 | */ 114 | get onreceive(): messageCallbackType; 115 | /** 116 | * Available for backward compatibility, renamed to [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}. 117 | * 118 | * **Deprecated** 119 | */ 120 | set onreceive(value: messageCallbackType); 121 | /** 122 | * Available for backward compatibility, renamed to [Client#onUnhandledReceipt]{@link Client#onUnhandledReceipt}. 123 | * Prefer using [Client#watchForReceipt]{@link Client#watchForReceipt}. 124 | * 125 | * **Deprecated** 126 | */ 127 | get onreceipt(): frameCallbackType; 128 | /** 129 | * Available for backward compatibility, renamed to [Client#onUnhandledReceipt]{@link Client#onUnhandledReceipt}. 130 | * 131 | * **Deprecated** 132 | */ 133 | set onreceipt(value: frameCallbackType); 134 | private _heartbeatInfo; 135 | /** 136 | * Available for backward compatibility, renamed to [Client#heartbeatIncoming]{@link Client#heartbeatIncoming} 137 | * [Client#heartbeatOutgoing]{@link Client#heartbeatOutgoing}. 138 | * 139 | * **Deprecated** 140 | */ 141 | get heartbeat(): { 142 | incoming: number; 143 | outgoing: number; 144 | }; 145 | /** 146 | * Available for backward compatibility, renamed to [Client#heartbeatIncoming]{@link Client#heartbeatIncoming} 147 | * [Client#heartbeatOutgoing]{@link Client#heartbeatOutgoing}. 148 | * 149 | * **Deprecated** 150 | */ 151 | set heartbeat(value: { 152 | incoming: number; 153 | outgoing: number; 154 | }); 155 | } 156 | -------------------------------------------------------------------------------- /esm6/compatibility/compat-client.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"compat-client.js","sourceRoot":"","sources":["../../src/compatibility/compat-client.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /esm6/compatibility/heartbeat-info.d.ts: -------------------------------------------------------------------------------- 1 | import { CompatClient } from './compat-client.js'; 2 | /** 3 | * Part of `@stomp/stompjs`. 4 | * 5 | * @internal 6 | */ 7 | export declare class HeartbeatInfo { 8 | private client; 9 | constructor(client: CompatClient); 10 | get outgoing(): number; 11 | set outgoing(value: number); 12 | get incoming(): number; 13 | set incoming(value: number); 14 | } 15 | -------------------------------------------------------------------------------- /esm6/compatibility/heartbeat-info.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Part of `@stomp/stompjs`. 3 | * 4 | * @internal 5 | */ 6 | export class HeartbeatInfo { 7 | constructor(client) { 8 | this.client = client; 9 | } 10 | get outgoing() { 11 | return this.client.heartbeatOutgoing; 12 | } 13 | set outgoing(value) { 14 | this.client.heartbeatOutgoing = value; 15 | } 16 | get incoming() { 17 | return this.client.heartbeatIncoming; 18 | } 19 | set incoming(value) { 20 | this.client.heartbeatIncoming = value; 21 | } 22 | } 23 | //# sourceMappingURL=heartbeat-info.js.map -------------------------------------------------------------------------------- /esm6/compatibility/heartbeat-info.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"heartbeat-info.js","sourceRoot":"","sources":["../../src/compatibility/heartbeat-info.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,OAAO,aAAa;IACxB,YAAoB,MAAoB;QAApB,WAAM,GAAN,MAAM,CAAc;IAAG,CAAC;IAE5C,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC;IACvC,CAAC;IAED,IAAI,QAAQ,CAAC,KAAa;QACxB,IAAI,CAAC,MAAM,CAAC,iBAAiB,GAAG,KAAK,CAAC;IACxC,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC;IACvC,CAAC;IAED,IAAI,QAAQ,CAAC,KAAa;QACxB,IAAI,CAAC,MAAM,CAAC,iBAAiB,GAAG,KAAK,CAAC;IACxC,CAAC;CACF"} -------------------------------------------------------------------------------- /esm6/compatibility/stomp.d.ts: -------------------------------------------------------------------------------- 1 | import { CompatClient } from './compat-client.js'; 2 | /** 3 | * STOMP Class, acts like a factory to create {@link Client}. 4 | * 5 | * Part of `@stomp/stompjs`. 6 | * 7 | * **Deprecated** 8 | * 9 | * It will be removed in next major version. Please switch to {@link Client}. 10 | */ 11 | export declare class Stomp { 12 | /** 13 | * In case you need to use a non standard class for WebSocket. 14 | * 15 | * For example when using within NodeJS environment: 16 | * 17 | * ```javascript 18 | * StompJs = require('../../esm5/'); 19 | * Stomp = StompJs.Stomp; 20 | * Stomp.WebSocketClass = require('websocket').w3cwebsocket; 21 | * ``` 22 | * 23 | * **Deprecated** 24 | * 25 | * 26 | * It will be removed in next major version. Please switch to {@link Client} 27 | * using [Client#webSocketFactory]{@link Client#webSocketFactory}. 28 | */ 29 | static WebSocketClass: any; 30 | /** 31 | * This method creates a WebSocket client that is connected to 32 | * the STOMP server located at the url. 33 | * 34 | * ```javascript 35 | * var url = "ws://localhost:61614/stomp"; 36 | * var client = Stomp.client(url); 37 | * ``` 38 | * 39 | * **Deprecated** 40 | * 41 | * It will be removed in next major version. Please switch to {@link Client} 42 | * using [Client#brokerURL]{@link Client#brokerURL}. 43 | */ 44 | static client(url: string, protocols?: string[]): CompatClient; 45 | /** 46 | * This method is an alternative to [Stomp#client]{@link Stomp#client} to let the user 47 | * specify the WebSocket to use (either a standard HTML5 WebSocket or 48 | * a similar object). 49 | * 50 | * In order to support reconnection, the function Client._connect should be callable more than once. 51 | * While reconnecting 52 | * a new instance of underlying transport (TCP Socket, WebSocket or SockJS) will be needed. So, this function 53 | * alternatively allows passing a function that should return a new instance of the underlying socket. 54 | * 55 | * ```javascript 56 | * var client = Stomp.over(function(){ 57 | * return new WebSocket('ws://localhost:15674/ws') 58 | * }); 59 | * ``` 60 | * 61 | * **Deprecated** 62 | * 63 | * It will be removed in next major version. Please switch to {@link Client} 64 | * using [Client#webSocketFactory]{@link Client#webSocketFactory}. 65 | */ 66 | static over(ws: any): CompatClient; 67 | } 68 | -------------------------------------------------------------------------------- /esm6/compatibility/stomp.js: -------------------------------------------------------------------------------- 1 | import { Versions } from '../versions.js'; 2 | import { CompatClient } from './compat-client.js'; 3 | /** 4 | * STOMP Class, acts like a factory to create {@link Client}. 5 | * 6 | * Part of `@stomp/stompjs`. 7 | * 8 | * **Deprecated** 9 | * 10 | * It will be removed in next major version. Please switch to {@link Client}. 11 | */ 12 | export class Stomp { 13 | /** 14 | * This method creates a WebSocket client that is connected to 15 | * the STOMP server located at the url. 16 | * 17 | * ```javascript 18 | * var url = "ws://localhost:61614/stomp"; 19 | * var client = Stomp.client(url); 20 | * ``` 21 | * 22 | * **Deprecated** 23 | * 24 | * It will be removed in next major version. Please switch to {@link Client} 25 | * using [Client#brokerURL]{@link Client#brokerURL}. 26 | */ 27 | static client(url, protocols) { 28 | // This is a hack to allow another implementation than the standard 29 | // HTML5 WebSocket class. 30 | // 31 | // It is possible to use another class by calling 32 | // 33 | // Stomp.WebSocketClass = MozWebSocket 34 | // 35 | // *prior* to call `Stomp.client()`. 36 | // 37 | // This hack is deprecated and `Stomp.over()` method should be used 38 | // instead. 39 | // See remarks on the function Stomp.over 40 | if (protocols == null) { 41 | protocols = Versions.default.protocolVersions(); 42 | } 43 | const wsFn = () => { 44 | const klass = Stomp.WebSocketClass || WebSocket; 45 | return new klass(url, protocols); 46 | }; 47 | return new CompatClient(wsFn); 48 | } 49 | /** 50 | * This method is an alternative to [Stomp#client]{@link Stomp#client} to let the user 51 | * specify the WebSocket to use (either a standard HTML5 WebSocket or 52 | * a similar object). 53 | * 54 | * In order to support reconnection, the function Client._connect should be callable more than once. 55 | * While reconnecting 56 | * a new instance of underlying transport (TCP Socket, WebSocket or SockJS) will be needed. So, this function 57 | * alternatively allows passing a function that should return a new instance of the underlying socket. 58 | * 59 | * ```javascript 60 | * var client = Stomp.over(function(){ 61 | * return new WebSocket('ws://localhost:15674/ws') 62 | * }); 63 | * ``` 64 | * 65 | * **Deprecated** 66 | * 67 | * It will be removed in next major version. Please switch to {@link Client} 68 | * using [Client#webSocketFactory]{@link Client#webSocketFactory}. 69 | */ 70 | static over(ws) { 71 | let wsFn; 72 | if (typeof ws === 'function') { 73 | wsFn = ws; 74 | } 75 | else { 76 | console.warn('Stomp.over did not receive a factory, auto reconnect will not work. ' + 77 | 'Please see https://stomp-js.github.io/api-docs/latest/classes/Stomp.html#over'); 78 | wsFn = () => ws; 79 | } 80 | return new CompatClient(wsFn); 81 | } 82 | } 83 | /** 84 | * In case you need to use a non standard class for WebSocket. 85 | * 86 | * For example when using within NodeJS environment: 87 | * 88 | * ```javascript 89 | * StompJs = require('../../esm5/'); 90 | * Stomp = StompJs.Stomp; 91 | * Stomp.WebSocketClass = require('websocket').w3cwebsocket; 92 | * ``` 93 | * 94 | * **Deprecated** 95 | * 96 | * 97 | * It will be removed in next major version. Please switch to {@link Client} 98 | * using [Client#webSocketFactory]{@link Client#webSocketFactory}. 99 | */ 100 | // tslint:disable-next-line:variable-name 101 | Stomp.WebSocketClass = null; 102 | //# sourceMappingURL=stomp.js.map -------------------------------------------------------------------------------- /esm6/compatibility/stomp.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"stomp.js","sourceRoot":"","sources":["../../src/compatibility/stomp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAWlD;;;;;;;;GAQG;AACH,MAAM,OAAO,KAAK;IAqBhB;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAC,MAAM,CAAC,GAAW,EAAE,SAAoB;QACpD,mEAAmE;QACnE,yBAAyB;QACzB,EAAE;QACF,iDAAiD;QACjD,EAAE;QACF,0CAA0C;QAC1C,EAAE;QACF,oCAAoC;QACpC,EAAE;QACF,mEAAmE;QACnE,WAAW;QAEX,yCAAyC;QACzC,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;YACtB,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC;QAClD,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,MAAM,KAAK,GAAG,KAAK,CAAC,cAAc,IAAI,SAAS,CAAC;YAChD,OAAO,IAAI,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACnC,CAAC,CAAC;QAEF,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACI,MAAM,CAAC,IAAI,CAAC,EAAO;QACxB,IAAI,IAAe,CAAC;QAEpB,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;YAC7B,IAAI,GAAG,EAAE,CAAC;QACZ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CACV,sEAAsE;gBACpE,+EAA+E,CAClF,CAAC;YACF,IAAI,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC;QAClB,CAAC;QAED,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;;AA9FD;;;;;;;;;;;;;;;;GAgBG;AACH,yCAAyC;AAC3B,oBAAc,GAAQ,IAAI,CAAC"} -------------------------------------------------------------------------------- /esm6/frame-impl.d.ts: -------------------------------------------------------------------------------- 1 | import type { IFrame } from './i-frame.js'; 2 | import { StompHeaders } from './stomp-headers.js'; 3 | import { IRawFrameType } from './types.js'; 4 | /** 5 | * Frame class represents a STOMP frame. 6 | * 7 | * @internal 8 | */ 9 | export declare class FrameImpl implements IFrame { 10 | /** 11 | * STOMP Command 12 | */ 13 | command: string; 14 | /** 15 | * Headers, key value pairs. 16 | */ 17 | headers: StompHeaders; 18 | /** 19 | * Is this frame binary (based on whether body/binaryBody was passed when creating this frame). 20 | */ 21 | isBinaryBody: boolean; 22 | /** 23 | * body of the frame 24 | */ 25 | get body(): string; 26 | private _body; 27 | /** 28 | * body as Uint8Array 29 | */ 30 | get binaryBody(): Uint8Array; 31 | private _binaryBody; 32 | private escapeHeaderValues; 33 | private skipContentLengthHeader; 34 | /** 35 | * Frame constructor. `command`, `headers` and `body` are available as properties. 36 | * 37 | * @internal 38 | */ 39 | constructor(params: { 40 | command: string; 41 | headers?: StompHeaders; 42 | body?: string; 43 | binaryBody?: Uint8Array; 44 | escapeHeaderValues?: boolean; 45 | skipContentLengthHeader?: boolean; 46 | }); 47 | /** 48 | * deserialize a STOMP Frame from raw data. 49 | * 50 | * @internal 51 | */ 52 | static fromRawFrame(rawFrame: IRawFrameType, escapeHeaderValues: boolean): FrameImpl; 53 | /** 54 | * @internal 55 | */ 56 | toString(): string; 57 | /** 58 | * serialize this Frame in a format suitable to be passed to WebSocket. 59 | * If the body is string the output will be string. 60 | * If the body is binary (i.e. of type Unit8Array) it will be serialized to ArrayBuffer. 61 | * 62 | * @internal 63 | */ 64 | serialize(): string | ArrayBuffer; 65 | private serializeCmdAndHeaders; 66 | private isBodyEmpty; 67 | private bodyLength; 68 | /** 69 | * Compute the size of a UTF-8 string by counting its number of bytes 70 | * (and not the number of characters composing the string) 71 | */ 72 | private static sizeOfUTF8; 73 | private static toUnit8Array; 74 | /** 75 | * Serialize a STOMP frame as per STOMP standards, suitable to be sent to the STOMP broker. 76 | * 77 | * @internal 78 | */ 79 | static marshall(params: { 80 | command: string; 81 | headers?: StompHeaders; 82 | body?: string; 83 | binaryBody?: Uint8Array; 84 | escapeHeaderValues?: boolean; 85 | skipContentLengthHeader?: boolean; 86 | }): string | ArrayBuffer; 87 | /** 88 | * Escape header values 89 | */ 90 | private static hdrValueEscape; 91 | /** 92 | * UnEscape header values 93 | */ 94 | private static hdrValueUnEscape; 95 | } 96 | -------------------------------------------------------------------------------- /esm6/frame-impl.js: -------------------------------------------------------------------------------- 1 | import { BYTE } from './byte.js'; 2 | /** 3 | * Frame class represents a STOMP frame. 4 | * 5 | * @internal 6 | */ 7 | export class FrameImpl { 8 | /** 9 | * body of the frame 10 | */ 11 | get body() { 12 | if (!this._body && this.isBinaryBody) { 13 | this._body = new TextDecoder().decode(this._binaryBody); 14 | } 15 | return this._body || ''; 16 | } 17 | /** 18 | * body as Uint8Array 19 | */ 20 | get binaryBody() { 21 | if (!this._binaryBody && !this.isBinaryBody) { 22 | this._binaryBody = new TextEncoder().encode(this._body); 23 | } 24 | // At this stage it will definitely have a valid value 25 | return this._binaryBody; 26 | } 27 | /** 28 | * Frame constructor. `command`, `headers` and `body` are available as properties. 29 | * 30 | * @internal 31 | */ 32 | constructor(params) { 33 | const { command, headers, body, binaryBody, escapeHeaderValues, skipContentLengthHeader, } = params; 34 | this.command = command; 35 | this.headers = Object.assign({}, headers || {}); 36 | if (binaryBody) { 37 | this._binaryBody = binaryBody; 38 | this.isBinaryBody = true; 39 | } 40 | else { 41 | this._body = body || ''; 42 | this.isBinaryBody = false; 43 | } 44 | this.escapeHeaderValues = escapeHeaderValues || false; 45 | this.skipContentLengthHeader = skipContentLengthHeader || false; 46 | } 47 | /** 48 | * deserialize a STOMP Frame from raw data. 49 | * 50 | * @internal 51 | */ 52 | static fromRawFrame(rawFrame, escapeHeaderValues) { 53 | const headers = {}; 54 | const trim = (str) => str.replace(/^\s+|\s+$/g, ''); 55 | // In case of repeated headers, as per standards, first value need to be used 56 | for (const header of rawFrame.headers.reverse()) { 57 | const idx = header.indexOf(':'); 58 | const key = trim(header[0]); 59 | let value = trim(header[1]); 60 | if (escapeHeaderValues && 61 | rawFrame.command !== 'CONNECT' && 62 | rawFrame.command !== 'CONNECTED') { 63 | value = FrameImpl.hdrValueUnEscape(value); 64 | } 65 | headers[key] = value; 66 | } 67 | return new FrameImpl({ 68 | command: rawFrame.command, 69 | headers, 70 | binaryBody: rawFrame.binaryBody, 71 | escapeHeaderValues, 72 | }); 73 | } 74 | /** 75 | * @internal 76 | */ 77 | toString() { 78 | return this.serializeCmdAndHeaders(); 79 | } 80 | /** 81 | * serialize this Frame in a format suitable to be passed to WebSocket. 82 | * If the body is string the output will be string. 83 | * If the body is binary (i.e. of type Unit8Array) it will be serialized to ArrayBuffer. 84 | * 85 | * @internal 86 | */ 87 | serialize() { 88 | const cmdAndHeaders = this.serializeCmdAndHeaders(); 89 | if (this.isBinaryBody) { 90 | return FrameImpl.toUnit8Array(cmdAndHeaders, this._binaryBody).buffer; 91 | } 92 | else { 93 | return cmdAndHeaders + this._body + BYTE.NULL; 94 | } 95 | } 96 | serializeCmdAndHeaders() { 97 | const lines = [this.command]; 98 | if (this.skipContentLengthHeader) { 99 | delete this.headers['content-length']; 100 | } 101 | for (const name of Object.keys(this.headers || {})) { 102 | const value = this.headers[name]; 103 | if (this.escapeHeaderValues && 104 | this.command !== 'CONNECT' && 105 | this.command !== 'CONNECTED') { 106 | lines.push(`${name}:${FrameImpl.hdrValueEscape(`${value}`)}`); 107 | } 108 | else { 109 | lines.push(`${name}:${value}`); 110 | } 111 | } 112 | if (this.isBinaryBody || 113 | (!this.isBodyEmpty() && !this.skipContentLengthHeader)) { 114 | lines.push(`content-length:${this.bodyLength()}`); 115 | } 116 | return lines.join(BYTE.LF) + BYTE.LF + BYTE.LF; 117 | } 118 | isBodyEmpty() { 119 | return this.bodyLength() === 0; 120 | } 121 | bodyLength() { 122 | const binaryBody = this.binaryBody; 123 | return binaryBody ? binaryBody.length : 0; 124 | } 125 | /** 126 | * Compute the size of a UTF-8 string by counting its number of bytes 127 | * (and not the number of characters composing the string) 128 | */ 129 | static sizeOfUTF8(s) { 130 | return s ? new TextEncoder().encode(s).length : 0; 131 | } 132 | static toUnit8Array(cmdAndHeaders, binaryBody) { 133 | const uint8CmdAndHeaders = new TextEncoder().encode(cmdAndHeaders); 134 | const nullTerminator = new Uint8Array([0]); 135 | const uint8Frame = new Uint8Array(uint8CmdAndHeaders.length + binaryBody.length + nullTerminator.length); 136 | uint8Frame.set(uint8CmdAndHeaders); 137 | uint8Frame.set(binaryBody, uint8CmdAndHeaders.length); 138 | uint8Frame.set(nullTerminator, uint8CmdAndHeaders.length + binaryBody.length); 139 | return uint8Frame; 140 | } 141 | /** 142 | * Serialize a STOMP frame as per STOMP standards, suitable to be sent to the STOMP broker. 143 | * 144 | * @internal 145 | */ 146 | static marshall(params) { 147 | const frame = new FrameImpl(params); 148 | return frame.serialize(); 149 | } 150 | /** 151 | * Escape header values 152 | */ 153 | static hdrValueEscape(str) { 154 | return str 155 | .replace(/\\/g, '\\\\') 156 | .replace(/\r/g, '\\r') 157 | .replace(/\n/g, '\\n') 158 | .replace(/:/g, '\\c'); 159 | } 160 | /** 161 | * UnEscape header values 162 | */ 163 | static hdrValueUnEscape(str) { 164 | return str 165 | .replace(/\\r/g, '\r') 166 | .replace(/\\n/g, '\n') 167 | .replace(/\\c/g, ':') 168 | .replace(/\\\\/g, '\\'); 169 | } 170 | } 171 | //# sourceMappingURL=frame-impl.js.map -------------------------------------------------------------------------------- /esm6/frame-impl.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"frame-impl.js","sourceRoot":"","sources":["../src/frame-impl.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /esm6/i-frame.d.ts: -------------------------------------------------------------------------------- 1 | import { StompHeaders } from './stomp-headers.js'; 2 | /** 3 | * It represents a STOMP frame. Many of the callbacks pass an IFrame received from 4 | * the STOMP broker. For advanced usage you might need to access [headers]{@link IFrame#headers}. 5 | * 6 | * Part of `@stomp/stompjs`. 7 | * 8 | * {@link IMessage} is an extended IFrame. 9 | */ 10 | export interface IFrame { 11 | /** 12 | * STOMP Command 13 | */ 14 | command: string; 15 | /** 16 | * Headers, key value pairs. 17 | */ 18 | headers: StompHeaders; 19 | /** 20 | * Is this frame binary (based on whether body/binaryBody was passed when creating this frame). 21 | */ 22 | isBinaryBody: boolean; 23 | /** 24 | * body of the frame as string 25 | */ 26 | readonly body: string; 27 | /** 28 | * body as Uint8Array 29 | */ 30 | readonly binaryBody: Uint8Array; 31 | } 32 | /** 33 | * Alias for {@link IFrame} 34 | */ 35 | export type Frame = IFrame; 36 | -------------------------------------------------------------------------------- /esm6/i-frame.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=i-frame.js.map -------------------------------------------------------------------------------- /esm6/i-frame.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"i-frame.js","sourceRoot":"","sources":["../src/i-frame.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /esm6/i-message.d.ts: -------------------------------------------------------------------------------- 1 | import type { IFrame } from './i-frame.js'; 2 | import { StompHeaders } from './stomp-headers.js'; 3 | /** 4 | * Instance of Message will be passed to [subscription callback]{@link Client#subscribe} 5 | * and [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}. 6 | * Since it is an extended {@link IFrame}, you can access [headers]{@link IFrame#headers} 7 | * and [body]{@link IFrame#body} as properties. 8 | * 9 | * Part of `@stomp/stompjs`. 10 | * 11 | * See [Client#subscribe]{@link Client#subscribe} for example. 12 | */ 13 | export interface IMessage extends IFrame { 14 | /** 15 | * When subscribing with manual acknowledgement, call this method on the message to ACK the message. 16 | * 17 | * See [Client#ack]{@link Client#ack} for an example. 18 | */ 19 | ack: (headers?: StompHeaders) => void; 20 | /** 21 | * When subscribing with manual acknowledgement, call this method on the message to NACK the message. 22 | * 23 | * See [Client#nack]{@link Client#nack} for an example. 24 | */ 25 | nack: (headers?: StompHeaders) => void; 26 | } 27 | /** 28 | * Aliased to {@link IMessage}. 29 | * 30 | * Part of `@stomp/stompjs`. 31 | */ 32 | export type Message = IMessage; 33 | -------------------------------------------------------------------------------- /esm6/i-message.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=i-message.js.map -------------------------------------------------------------------------------- /esm6/i-message.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"i-message.js","sourceRoot":"","sources":["../src/i-message.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /esm6/i-transaction.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A Transaction is created by calling [Client#begin]{@link Client#begin} 3 | * 4 | * Part of `@stomp/stompjs`. 5 | */ 6 | export interface ITransaction { 7 | /** 8 | * You will need to access this to send, ack, or nack within this transaction. 9 | */ 10 | id: string; 11 | /** 12 | * Commit this transaction. See [Client#commit]{@link Client#commit} for an example. 13 | */ 14 | commit: () => void; 15 | /** 16 | * Abort this transaction. See [Client#abort]{@link Client#abort} for an example. 17 | */ 18 | abort: () => void; 19 | } 20 | -------------------------------------------------------------------------------- /esm6/i-transaction.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=i-transaction.js.map -------------------------------------------------------------------------------- /esm6/i-transaction.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"i-transaction.js","sourceRoot":"","sources":["../src/i-transaction.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /esm6/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './client.js'; 2 | export * from './frame-impl.js'; 3 | export * from './i-frame.js'; 4 | export * from './i-message.js'; 5 | export * from './parser.js'; 6 | export * from './stomp-config.js'; 7 | export * from './stomp-headers.js'; 8 | export * from './stomp-subscription.js'; 9 | export * from './i-transaction.js'; 10 | export * from './types.js'; 11 | export * from './versions.js'; 12 | export * from './compatibility/compat-client.js'; 13 | export * from './compatibility/stomp.js'; 14 | -------------------------------------------------------------------------------- /esm6/index.js: -------------------------------------------------------------------------------- 1 | export * from './client.js'; 2 | export * from './frame-impl.js'; 3 | export * from './i-frame.js'; 4 | export * from './i-message.js'; 5 | export * from './parser.js'; 6 | export * from './stomp-config.js'; 7 | export * from './stomp-headers.js'; 8 | export * from './stomp-subscription.js'; 9 | export * from './i-transaction.js'; 10 | export * from './types.js'; 11 | export * from './versions.js'; 12 | // Compatibility code 13 | export * from './compatibility/compat-client.js'; 14 | export * from './compatibility/stomp.js'; 15 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /esm6/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAE9B,qBAAqB;AACrB,cAAc,kCAAkC,CAAC;AACjD,cAAc,0BAA0B,CAAC"} -------------------------------------------------------------------------------- /esm6/parser.d.ts: -------------------------------------------------------------------------------- 1 | import { IRawFrameType } from './types.js'; 2 | /** 3 | * This is an evented, rec descent parser. 4 | * A stream of Octets can be passed and whenever it recognizes 5 | * a complete Frame or an incoming ping it will invoke the registered callbacks. 6 | * 7 | * All incoming Octets are fed into _onByte function. 8 | * Depending on current state the _onByte function keeps changing. 9 | * Depending on the state it keeps accumulating into _token and _results. 10 | * State is indicated by current value of _onByte, all states are named as _collect. 11 | * 12 | * STOMP standards https://stomp.github.io/stomp-specification-1.2.html 13 | * imply that all lengths are considered in bytes (instead of string lengths). 14 | * So, before actual parsing, if the incoming data is String it is converted to Octets. 15 | * This allows faithful implementation of the protocol and allows NULL Octets to be present in the body. 16 | * 17 | * There is no peek function on the incoming data. 18 | * When a state change occurs based on an Octet without consuming the Octet, 19 | * the Octet, after state change, is fed again (_reinjectByte). 20 | * This became possible as the state change can be determined by inspecting just one Octet. 21 | * 22 | * There are two modes to collect the body, if content-length header is there then it by counting Octets 23 | * otherwise it is determined by NULL terminator. 24 | * 25 | * Following the standards, the command and headers are converted to Strings 26 | * and the body is returned as Octets. 27 | * Headers are returned as an array and not as Hash - to allow multiple occurrence of an header. 28 | * 29 | * This parser does not use Regular Expressions as that can only operate on Strings. 30 | * 31 | * It handles if multiple STOMP frames are given as one chunk, a frame is split into multiple chunks, or 32 | * any combination there of. The parser remembers its state (any partial frame) and continues when a new chunk 33 | * is pushed. 34 | * 35 | * Typically the higher level function will convert headers to Hash, handle unescaping of header values 36 | * (which is protocol version specific), and convert body to text. 37 | * 38 | * Check the parser.spec.js to understand cases that this parser is supposed to handle. 39 | * 40 | * Part of `@stomp/stompjs`. 41 | * 42 | * @internal 43 | */ 44 | export declare class Parser { 45 | onFrame: (rawFrame: IRawFrameType) => void; 46 | onIncomingPing: () => void; 47 | private readonly _encoder; 48 | private readonly _decoder; 49 | private _results; 50 | private _token; 51 | private _headerKey; 52 | private _bodyBytesRemaining; 53 | private _onByte; 54 | constructor(onFrame: (rawFrame: IRawFrameType) => void, onIncomingPing: () => void); 55 | parseChunk(segment: string | ArrayBuffer, appendMissingNULLonIncoming?: boolean): void; 56 | private _collectFrame; 57 | private _collectCommand; 58 | private _collectHeaders; 59 | private _reinjectByte; 60 | private _collectHeaderKey; 61 | private _collectHeaderValue; 62 | private _setupCollectBody; 63 | private _collectBodyNullTerminated; 64 | private _collectBodyFixedSize; 65 | private _retrievedBody; 66 | private _consumeByte; 67 | private _consumeTokenAsUTF8; 68 | private _consumeTokenAsRaw; 69 | private _initState; 70 | } 71 | -------------------------------------------------------------------------------- /esm6/parser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal 3 | */ 4 | const NULL = 0; 5 | /** 6 | * @internal 7 | */ 8 | const LF = 10; 9 | /** 10 | * @internal 11 | */ 12 | const CR = 13; 13 | /** 14 | * @internal 15 | */ 16 | const COLON = 58; 17 | /** 18 | * This is an evented, rec descent parser. 19 | * A stream of Octets can be passed and whenever it recognizes 20 | * a complete Frame or an incoming ping it will invoke the registered callbacks. 21 | * 22 | * All incoming Octets are fed into _onByte function. 23 | * Depending on current state the _onByte function keeps changing. 24 | * Depending on the state it keeps accumulating into _token and _results. 25 | * State is indicated by current value of _onByte, all states are named as _collect. 26 | * 27 | * STOMP standards https://stomp.github.io/stomp-specification-1.2.html 28 | * imply that all lengths are considered in bytes (instead of string lengths). 29 | * So, before actual parsing, if the incoming data is String it is converted to Octets. 30 | * This allows faithful implementation of the protocol and allows NULL Octets to be present in the body. 31 | * 32 | * There is no peek function on the incoming data. 33 | * When a state change occurs based on an Octet without consuming the Octet, 34 | * the Octet, after state change, is fed again (_reinjectByte). 35 | * This became possible as the state change can be determined by inspecting just one Octet. 36 | * 37 | * There are two modes to collect the body, if content-length header is there then it by counting Octets 38 | * otherwise it is determined by NULL terminator. 39 | * 40 | * Following the standards, the command and headers are converted to Strings 41 | * and the body is returned as Octets. 42 | * Headers are returned as an array and not as Hash - to allow multiple occurrence of an header. 43 | * 44 | * This parser does not use Regular Expressions as that can only operate on Strings. 45 | * 46 | * It handles if multiple STOMP frames are given as one chunk, a frame is split into multiple chunks, or 47 | * any combination there of. The parser remembers its state (any partial frame) and continues when a new chunk 48 | * is pushed. 49 | * 50 | * Typically the higher level function will convert headers to Hash, handle unescaping of header values 51 | * (which is protocol version specific), and convert body to text. 52 | * 53 | * Check the parser.spec.js to understand cases that this parser is supposed to handle. 54 | * 55 | * Part of `@stomp/stompjs`. 56 | * 57 | * @internal 58 | */ 59 | export class Parser { 60 | constructor(onFrame, onIncomingPing) { 61 | this.onFrame = onFrame; 62 | this.onIncomingPing = onIncomingPing; 63 | this._encoder = new TextEncoder(); 64 | this._decoder = new TextDecoder(); 65 | this._token = []; 66 | this._initState(); 67 | } 68 | parseChunk(segment, appendMissingNULLonIncoming = false) { 69 | let chunk; 70 | if (typeof segment === 'string') { 71 | chunk = this._encoder.encode(segment); 72 | } 73 | else { 74 | chunk = new Uint8Array(segment); 75 | } 76 | // See https://github.com/stomp-js/stompjs/issues/89 77 | // Remove when underlying issue is fixed. 78 | // 79 | // Send a NULL byte, if the last byte of a Text frame was not NULL.F 80 | if (appendMissingNULLonIncoming && chunk[chunk.length - 1] !== 0) { 81 | const chunkWithNull = new Uint8Array(chunk.length + 1); 82 | chunkWithNull.set(chunk, 0); 83 | chunkWithNull[chunk.length] = 0; 84 | chunk = chunkWithNull; 85 | } 86 | // tslint:disable-next-line:prefer-for-of 87 | for (let i = 0; i < chunk.length; i++) { 88 | const byte = chunk[i]; 89 | this._onByte(byte); 90 | } 91 | } 92 | // The following implements a simple Rec Descent Parser. 93 | // The grammar is simple and just one byte tells what should be the next state 94 | _collectFrame(byte) { 95 | if (byte === NULL) { 96 | // Ignore 97 | return; 98 | } 99 | if (byte === CR) { 100 | // Ignore CR 101 | return; 102 | } 103 | if (byte === LF) { 104 | // Incoming Ping 105 | this.onIncomingPing(); 106 | return; 107 | } 108 | this._onByte = this._collectCommand; 109 | this._reinjectByte(byte); 110 | } 111 | _collectCommand(byte) { 112 | if (byte === CR) { 113 | // Ignore CR 114 | return; 115 | } 116 | if (byte === LF) { 117 | this._results.command = this._consumeTokenAsUTF8(); 118 | this._onByte = this._collectHeaders; 119 | return; 120 | } 121 | this._consumeByte(byte); 122 | } 123 | _collectHeaders(byte) { 124 | if (byte === CR) { 125 | // Ignore CR 126 | return; 127 | } 128 | if (byte === LF) { 129 | this._setupCollectBody(); 130 | return; 131 | } 132 | this._onByte = this._collectHeaderKey; 133 | this._reinjectByte(byte); 134 | } 135 | _reinjectByte(byte) { 136 | this._onByte(byte); 137 | } 138 | _collectHeaderKey(byte) { 139 | if (byte === COLON) { 140 | this._headerKey = this._consumeTokenAsUTF8(); 141 | this._onByte = this._collectHeaderValue; 142 | return; 143 | } 144 | this._consumeByte(byte); 145 | } 146 | _collectHeaderValue(byte) { 147 | if (byte === CR) { 148 | // Ignore CR 149 | return; 150 | } 151 | if (byte === LF) { 152 | this._results.headers.push([ 153 | this._headerKey, 154 | this._consumeTokenAsUTF8(), 155 | ]); 156 | this._headerKey = undefined; 157 | this._onByte = this._collectHeaders; 158 | return; 159 | } 160 | this._consumeByte(byte); 161 | } 162 | _setupCollectBody() { 163 | const contentLengthHeader = this._results.headers.filter((header) => { 164 | return header[0] === 'content-length'; 165 | })[0]; 166 | if (contentLengthHeader) { 167 | this._bodyBytesRemaining = parseInt(contentLengthHeader[1], 10); 168 | this._onByte = this._collectBodyFixedSize; 169 | } 170 | else { 171 | this._onByte = this._collectBodyNullTerminated; 172 | } 173 | } 174 | _collectBodyNullTerminated(byte) { 175 | if (byte === NULL) { 176 | this._retrievedBody(); 177 | return; 178 | } 179 | this._consumeByte(byte); 180 | } 181 | _collectBodyFixedSize(byte) { 182 | // It is post decrement, so that we discard the trailing NULL octet 183 | if (this._bodyBytesRemaining-- === 0) { 184 | this._retrievedBody(); 185 | return; 186 | } 187 | this._consumeByte(byte); 188 | } 189 | _retrievedBody() { 190 | this._results.binaryBody = this._consumeTokenAsRaw(); 191 | try { 192 | this.onFrame(this._results); 193 | } 194 | catch (e) { 195 | console.log(`Ignoring an exception thrown by a frame handler. Original exception: `, e); 196 | } 197 | this._initState(); 198 | } 199 | // Rec Descent Parser helpers 200 | _consumeByte(byte) { 201 | this._token.push(byte); 202 | } 203 | _consumeTokenAsUTF8() { 204 | return this._decoder.decode(this._consumeTokenAsRaw()); 205 | } 206 | _consumeTokenAsRaw() { 207 | const rawResult = new Uint8Array(this._token); 208 | this._token = []; 209 | return rawResult; 210 | } 211 | _initState() { 212 | this._results = { 213 | command: undefined, 214 | headers: [], 215 | binaryBody: undefined, 216 | }; 217 | this._token = []; 218 | this._headerKey = undefined; 219 | this._onByte = this._collectFrame; 220 | } 221 | } 222 | //# sourceMappingURL=parser.js.map -------------------------------------------------------------------------------- /esm6/parser.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /esm6/stomp-config.d.ts: -------------------------------------------------------------------------------- 1 | import { StompHeaders } from './stomp-headers.js'; 2 | import { ActivationState, TickerStrategy, closeEventCallbackType, debugFnType, frameCallbackType, messageCallbackType, ReconnectionTimeMode, wsErrorCallbackType } from './types.js'; 3 | import { Versions } from './versions.js'; 4 | import { Client } from './client.js'; 5 | /** 6 | * Configuration options for STOMP Client, each key corresponds to 7 | * field by the same name in {@link Client}. This can be passed to 8 | * the constructor of {@link Client} or to [Client#configure]{@link Client#configure}. 9 | * 10 | * Part of `@stomp/stompjs`. 11 | */ 12 | export declare class StompConfig { 13 | /** 14 | * See [Client#brokerURL]{@link Client#brokerURL}. 15 | */ 16 | brokerURL?: string; 17 | /** 18 | * See [Client#stompVersions]{@link Client#stompVersions}. 19 | */ 20 | stompVersions?: Versions; 21 | /** 22 | * See [Client#webSocketFactory]{@link Client#webSocketFactory}. 23 | */ 24 | webSocketFactory?: () => any; 25 | /** 26 | * See [Client#connectionTimeout]{@link Client#connectionTimeout}. 27 | */ 28 | connectionTimeout?: number; 29 | /** 30 | * See [Client#reconnectDelay]{@link Client#reconnectDelay}. 31 | */ 32 | reconnectDelay?: number; 33 | /** 34 | * See [Client#maxReconnectDelay]{@link Client#maxReconnectDelay} 35 | */ 36 | maxReconnectDelay?: number; 37 | /** 38 | * See [Client#reconnectTimeMode]{@link Client#reconnectTimeMode} 39 | */ 40 | reconnectTimeMode?: ReconnectionTimeMode; 41 | /** 42 | * See [Client#heartbeatIncoming]{@link Client#heartbeatIncoming}. 43 | */ 44 | heartbeatIncoming?: number; 45 | /** 46 | * See [Client#heartbeatOutgoing]{@link Client#heartbeatOutgoing}. 47 | */ 48 | heartbeatOutgoing?: number; 49 | /** 50 | * See [Client#heartbeatStrategy]{@link Client#heartbeatStrategy}. 51 | */ 52 | heartbeatStrategy?: TickerStrategy; 53 | /** 54 | * See [Client#splitLargeFrames]{@link Client#splitLargeFrames}. 55 | */ 56 | splitLargeFrames?: boolean; 57 | /** 58 | * See [Client#forceBinaryWSFrames]{@link Client#forceBinaryWSFrames}. 59 | */ 60 | forceBinaryWSFrames?: boolean; 61 | /** 62 | * See [Client#appendMissingNULLonIncoming]{@link Client#appendMissingNULLonIncoming}. 63 | */ 64 | appendMissingNULLonIncoming?: boolean; 65 | /** 66 | * See [Client#maxWebSocketChunkSize]{@link Client#maxWebSocketChunkSize}. 67 | */ 68 | maxWebSocketChunkSize?: number; 69 | /** 70 | * See [Client#connectHeaders]{@link Client#connectHeaders}. 71 | */ 72 | connectHeaders?: StompHeaders; 73 | /** 74 | * See [Client#disconnectHeaders]{@link Client#disconnectHeaders}. 75 | */ 76 | disconnectHeaders?: StompHeaders; 77 | /** 78 | * See [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}. 79 | */ 80 | onUnhandledMessage?: messageCallbackType; 81 | /** 82 | * See [Client#onUnhandledReceipt]{@link Client#onUnhandledReceipt}. 83 | */ 84 | onUnhandledReceipt?: frameCallbackType; 85 | /** 86 | * See [Client#onUnhandledFrame]{@link Client#onUnhandledFrame}. 87 | */ 88 | onUnhandledFrame?: frameCallbackType; 89 | /** 90 | * See [Client#beforeConnect]{@link Client#beforeConnect}. 91 | */ 92 | beforeConnect?: (client: Client) => void | Promise; 93 | /** 94 | * See [Client#onConnect]{@link Client#onConnect}. 95 | */ 96 | onConnect?: frameCallbackType; 97 | /** 98 | * See [Client#onDisconnect]{@link Client#onDisconnect}. 99 | */ 100 | onDisconnect?: frameCallbackType; 101 | /** 102 | * See [Client#onStompError]{@link Client#onStompError}. 103 | */ 104 | onStompError?: frameCallbackType; 105 | /** 106 | * See [Client#onWebSocketClose]{@link Client#onWebSocketClose}. 107 | */ 108 | onWebSocketClose?: closeEventCallbackType; 109 | /** 110 | * See [Client#onWebSocketError]{@link Client#onWebSocketError}. 111 | */ 112 | onWebSocketError?: wsErrorCallbackType; 113 | /** 114 | * See [Client#logRawCommunication]{@link Client#logRawCommunication}. 115 | */ 116 | logRawCommunication?: boolean; 117 | /** 118 | * See [Client#debug]{@link Client#debug}. 119 | */ 120 | debug?: debugFnType; 121 | /** 122 | * See [Client#discardWebsocketOnCommFailure]{@link Client#discardWebsocketOnCommFailure}. 123 | */ 124 | discardWebsocketOnCommFailure?: boolean; 125 | /** 126 | * See [Client#onChangeState]{@link Client#onChangeState}. 127 | */ 128 | onChangeState?: (state: ActivationState) => void; 129 | } 130 | -------------------------------------------------------------------------------- /esm6/stomp-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration options for STOMP Client, each key corresponds to 3 | * field by the same name in {@link Client}. This can be passed to 4 | * the constructor of {@link Client} or to [Client#configure]{@link Client#configure}. 5 | * 6 | * Part of `@stomp/stompjs`. 7 | */ 8 | export class StompConfig { 9 | } 10 | //# sourceMappingURL=stomp-config.js.map -------------------------------------------------------------------------------- /esm6/stomp-config.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"stomp-config.js","sourceRoot":"","sources":["../src/stomp-config.ts"],"names":[],"mappings":"AAcA;;;;;;GAMG;AACH,MAAM,OAAO,WAAW;CAiJvB"} -------------------------------------------------------------------------------- /esm6/stomp-handler.d.ts: -------------------------------------------------------------------------------- 1 | import { Client } from './client.js'; 2 | import { ITransaction } from './i-transaction.js'; 3 | import { StompHeaders } from './stomp-headers.js'; 4 | import { StompSubscription } from './stomp-subscription.js'; 5 | import { closeEventCallbackType, debugFnType, frameCallbackType, IPublishParams, IStompSocket, IStomptHandlerConfig, messageCallbackType, wsErrorCallbackType } from './types.js'; 6 | import { Versions } from './versions.js'; 7 | /** 8 | * The STOMP protocol handler 9 | * 10 | * Part of `@stomp/stompjs`. 11 | * 12 | * @internal 13 | */ 14 | export declare class StompHandler { 15 | private _client; 16 | _webSocket: IStompSocket; 17 | debug: debugFnType; 18 | stompVersions: Versions; 19 | connectHeaders: StompHeaders; 20 | disconnectHeaders: StompHeaders; 21 | heartbeatIncoming: number; 22 | heartbeatOutgoing: number; 23 | onUnhandledMessage: messageCallbackType; 24 | onUnhandledReceipt: frameCallbackType; 25 | onUnhandledFrame: frameCallbackType; 26 | onConnect: frameCallbackType; 27 | onDisconnect: frameCallbackType; 28 | onStompError: frameCallbackType; 29 | onWebSocketClose: closeEventCallbackType; 30 | onWebSocketError: wsErrorCallbackType; 31 | logRawCommunication: boolean; 32 | splitLargeFrames: boolean; 33 | maxWebSocketChunkSize: number; 34 | forceBinaryWSFrames: boolean; 35 | appendMissingNULLonIncoming: boolean; 36 | discardWebsocketOnCommFailure: boolean; 37 | get connectedVersion(): string | undefined; 38 | private _connectedVersion; 39 | get connected(): boolean; 40 | private _connected; 41 | private readonly _subscriptions; 42 | private readonly _receiptWatchers; 43 | private _partialData; 44 | private _escapeHeaderValues; 45 | private _counter; 46 | private _pinger?; 47 | private _ponger; 48 | private _lastServerActivityTS; 49 | constructor(_client: Client, _webSocket: IStompSocket, config: IStomptHandlerConfig); 50 | start(): void; 51 | private readonly _serverFrameHandlers; 52 | private _setupHeartbeat; 53 | private _closeOrDiscardWebsocket; 54 | forceDisconnect(): void; 55 | _closeWebsocket(): void; 56 | discardWebsocket(): void; 57 | private _transmit; 58 | dispose(): void; 59 | private _cleanUp; 60 | publish(params: IPublishParams): void; 61 | watchForReceipt(receiptId: string, callback: frameCallbackType): void; 62 | subscribe(destination: string, callback: messageCallbackType, headers?: StompHeaders): StompSubscription; 63 | unsubscribe(id: string, headers?: StompHeaders): void; 64 | begin(transactionId: string): ITransaction; 65 | commit(transactionId: string): void; 66 | abort(transactionId: string): void; 67 | ack(messageId: string, subscriptionId: string, headers?: StompHeaders): void; 68 | nack(messageId: string, subscriptionId: string, headers?: StompHeaders): void; 69 | } 70 | -------------------------------------------------------------------------------- /esm6/stomp-headers.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * STOMP headers. Many functions calls will accept headers as parameters. 3 | * The headers sent by Broker will be available as [IFrame#headers]{@link IFrame#headers}. 4 | * 5 | * `key` and `value` must be valid strings. 6 | * In addition, `key` must not contain `CR`, `LF`, or `:`. 7 | * 8 | * Part of `@stomp/stompjs`. 9 | */ 10 | export declare class StompHeaders { 11 | [key: string]: string; 12 | } 13 | -------------------------------------------------------------------------------- /esm6/stomp-headers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * STOMP headers. Many functions calls will accept headers as parameters. 3 | * The headers sent by Broker will be available as [IFrame#headers]{@link IFrame#headers}. 4 | * 5 | * `key` and `value` must be valid strings. 6 | * In addition, `key` must not contain `CR`, `LF`, or `:`. 7 | * 8 | * Part of `@stomp/stompjs`. 9 | */ 10 | export class StompHeaders { 11 | } 12 | //# sourceMappingURL=stomp-headers.js.map -------------------------------------------------------------------------------- /esm6/stomp-headers.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"stomp-headers.js","sourceRoot":"","sources":["../src/stomp-headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,OAAO,YAAY;CAExB"} -------------------------------------------------------------------------------- /esm6/stomp-subscription.d.ts: -------------------------------------------------------------------------------- 1 | import { StompHeaders } from './stomp-headers.js'; 2 | /** 3 | * Call [Client#subscribe]{@link Client#subscribe} to create a StompSubscription. 4 | * 5 | * Part of `@stomp/stompjs`. 6 | */ 7 | export interface StompSubscription { 8 | /** 9 | * Id associated with this subscription. 10 | */ 11 | id: string; 12 | /** 13 | * Unsubscribe. See [Client#unsubscribe]{@link Client#unsubscribe} for an example. 14 | */ 15 | unsubscribe: (headers?: StompHeaders) => void; 16 | } 17 | -------------------------------------------------------------------------------- /esm6/stomp-subscription.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=stomp-subscription.js.map -------------------------------------------------------------------------------- /esm6/stomp-subscription.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"stomp-subscription.js","sourceRoot":"","sources":["../src/stomp-subscription.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /esm6/ticker.d.ts: -------------------------------------------------------------------------------- 1 | import { debugFnType, TickerStrategy } from './types.js'; 2 | export declare class Ticker { 3 | private readonly _interval; 4 | private readonly _strategy; 5 | private readonly _debug; 6 | private readonly _workerScript; 7 | private _worker?; 8 | private _timer?; 9 | constructor(_interval: number, _strategy: TickerStrategy | undefined, _debug: debugFnType); 10 | start(tick: (elapsedTime: number) => void): void; 11 | stop(): void; 12 | private shouldUseWorker; 13 | private runWorker; 14 | private runInterval; 15 | private disposeWorker; 16 | private disposeInterval; 17 | } 18 | -------------------------------------------------------------------------------- /esm6/ticker.js: -------------------------------------------------------------------------------- 1 | import { TickerStrategy } from './types.js'; 2 | export class Ticker { 3 | constructor(_interval, _strategy = TickerStrategy.Interval, _debug) { 4 | this._interval = _interval; 5 | this._strategy = _strategy; 6 | this._debug = _debug; 7 | this._workerScript = ` 8 | var startTime = Date.now(); 9 | setInterval(function() { 10 | self.postMessage(Date.now() - startTime); 11 | }, ${this._interval}); 12 | `; 13 | } 14 | start(tick) { 15 | this.stop(); 16 | if (this.shouldUseWorker()) { 17 | this.runWorker(tick); 18 | } 19 | else { 20 | this.runInterval(tick); 21 | } 22 | } 23 | stop() { 24 | this.disposeWorker(); 25 | this.disposeInterval(); 26 | } 27 | shouldUseWorker() { 28 | return typeof (Worker) !== 'undefined' && this._strategy === TickerStrategy.Worker; 29 | } 30 | runWorker(tick) { 31 | this._debug('Using runWorker for outgoing pings'); 32 | if (!this._worker) { 33 | this._worker = new Worker(URL.createObjectURL(new Blob([this._workerScript], { type: 'text/javascript' }))); 34 | this._worker.onmessage = (message) => tick(message.data); 35 | } 36 | } 37 | runInterval(tick) { 38 | this._debug('Using runInterval for outgoing pings'); 39 | if (!this._timer) { 40 | const startTime = Date.now(); 41 | this._timer = setInterval(() => { 42 | tick(Date.now() - startTime); 43 | }, this._interval); 44 | } 45 | } 46 | disposeWorker() { 47 | if (this._worker) { 48 | this._worker.terminate(); 49 | delete this._worker; 50 | this._debug('Outgoing ping disposeWorker'); 51 | } 52 | } 53 | disposeInterval() { 54 | if (this._timer) { 55 | clearInterval(this._timer); 56 | delete this._timer; 57 | this._debug('Outgoing ping disposeInterval'); 58 | } 59 | } 60 | } 61 | //# sourceMappingURL=ticker.js.map -------------------------------------------------------------------------------- /esm6/ticker.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"ticker.js","sourceRoot":"","sources":["../src/ticker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzD,MAAM,OAAO,MAAM;IAWjB,YACmB,SAAiB,EACjB,YAAY,cAAc,CAAC,QAAQ,EACnC,MAAmB;QAFnB,cAAS,GAAT,SAAS,CAAQ;QACjB,cAAS,GAAT,SAAS,CAA0B;QACnC,WAAM,GAAN,MAAM,CAAa;QAbrB,kBAAa,GAAG;;;;SAI1B,IAAI,CAAC,SAAS;GACpB,CAAC;IASF,CAAC;IAEM,KAAK,CAAC,IAAmC;QAC9C,IAAI,CAAC,IAAI,EAAE,CAAC;QAEZ,IAAI,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YAC3B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAEM,IAAI;QACT,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAEO,eAAe;QACrB,OAAO,OAAM,CAAC,MAAM,CAAC,KAAK,WAAW,IAAI,IAAI,CAAC,SAAS,KAAK,cAAc,CAAC,MAAM,CAAA;IACnF,CAAC;IAEO,SAAS,CAAC,IAAmC;QACnD,IAAI,CAAC,MAAM,CAAC,oCAAoC,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,GAAG,IAAI,MAAM,CACvB,GAAG,CAAC,eAAe,CACjB,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAC5D,CACF,CAAC;YACF,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAEO,WAAW,CAAC,IAAmC;QACrD,IAAI,CAAC,MAAM,CAAC,sCAAsC,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;gBAC7B,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC;YAC/B,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAEO,aAAa;QACnB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,OAAO,CAAC;YACpB,IAAI,CAAC,MAAM,CAAC,6BAA6B,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAEO,eAAe;QACrB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC3B,OAAO,IAAI,CAAC,MAAM,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,+BAA+B,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;CACF"} -------------------------------------------------------------------------------- /esm6/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { IFrame } from './i-frame.js'; 2 | import type { IMessage } from './i-message.js'; 3 | import { StompHeaders } from './stomp-headers.js'; 4 | import { Versions } from './versions.js'; 5 | /** 6 | * This callback will receive a `string` as a parameter. 7 | * 8 | * Part of `@stomp/stompjs`. 9 | */ 10 | export type debugFnType = (msg: string) => void; 11 | /** 12 | * This callback will receive a {@link IMessage} as parameter. 13 | * 14 | * Part of `@stomp/stompjs`. 15 | */ 16 | export type messageCallbackType = (message: IMessage) => void; 17 | /** 18 | * This callback will receive a {@link IFrame} as parameter. 19 | * 20 | * Part of `@stomp/stompjs`. 21 | */ 22 | export type frameCallbackType = ((frame: IFrame) => void) | (() => void); 23 | /** 24 | * This callback will receive a [CloseEvent]{@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent} 25 | * as parameter. 26 | * 27 | * Part of `@stomp/stompjs`. 28 | */ 29 | export type closeEventCallbackType = (evt: T) => void; 30 | /** 31 | * This callback will receive an [Event]{@link https://developer.mozilla.org/en-US/docs/Web/API/Event} 32 | * as parameter. 33 | * 34 | * Part of `@stomp/stompjs`. 35 | */ 36 | export type wsErrorCallbackType = (evt: T) => void; 37 | /** 38 | * Parameters for [Client#publish]{@link Client#publish}. 39 | * Aliased as publishParams as well. 40 | * 41 | * Part of `@stomp/stompjs`. 42 | */ 43 | export interface IPublishParams { 44 | /** 45 | * destination end point 46 | */ 47 | destination: string; 48 | /** 49 | * headers (optional) 50 | */ 51 | headers?: StompHeaders; 52 | /** 53 | * body (optional) 54 | */ 55 | body?: string; 56 | /** 57 | * binary body (optional) 58 | */ 59 | binaryBody?: Uint8Array; 60 | /** 61 | * By default, a `content-length` header will be added in the Frame to the broker. 62 | * Set it to `true` for the header to be skipped. 63 | */ 64 | skipContentLengthHeader?: boolean; 65 | } 66 | /** 67 | * Backward compatibility, switch to {@link IPublishParams}. 68 | */ 69 | export type publishParams = IPublishParams; 70 | /** 71 | * Used in {@link IRawFrameType} 72 | * 73 | * Part of `@stomp/stompjs`. 74 | * 75 | * @internal 76 | */ 77 | export type RawHeaderType = [string, string]; 78 | /** 79 | * The parser yield frames in this structure 80 | * 81 | * Part of `@stomp/stompjs`. 82 | * 83 | * @internal 84 | */ 85 | export interface IRawFrameType { 86 | command: string | undefined; 87 | headers: RawHeaderType[]; 88 | binaryBody: Uint8Array | undefined; 89 | } 90 | /** 91 | * @internal 92 | */ 93 | export interface IStompSocketMessageEvent { 94 | data?: string | ArrayBuffer; 95 | } 96 | /** 97 | * Copied from Websocket interface to avoid dom typelib dependency. 98 | * 99 | * @internal 100 | */ 101 | export interface IStompSocket { 102 | url: string; 103 | onclose: ((ev?: any) => any) | undefined | null; 104 | onerror: ((ev: any) => any) | undefined | null; 105 | onmessage: ((ev: IStompSocketMessageEvent) => any) | undefined | null; 106 | onopen: ((ev?: any) => any) | undefined | null; 107 | terminate?: (() => any) | undefined | null; 108 | /** 109 | * Returns a string that indicates how binary data from the socket is exposed to scripts: 110 | * We support only 'arraybuffer'. 111 | */ 112 | binaryType?: string; 113 | /** 114 | * Returns the state of the socket connection. It can have the values of StompSocketState. 115 | */ 116 | readonly readyState: number; 117 | /** 118 | * Closes the connection. 119 | */ 120 | close(): void; 121 | /** 122 | * Transmits data using the connection. data can be a string or an ArrayBuffer. 123 | */ 124 | send(data: string | ArrayBuffer): void; 125 | } 126 | /** 127 | * Possible states for the IStompSocket 128 | */ 129 | export declare enum StompSocketState { 130 | CONNECTING = 0, 131 | OPEN = 1, 132 | CLOSING = 2, 133 | CLOSED = 3 134 | } 135 | /** 136 | * Possible activation state 137 | */ 138 | export declare enum ActivationState { 139 | ACTIVE = 0, 140 | DEACTIVATING = 1, 141 | INACTIVE = 2 142 | } 143 | /** 144 | * Possible reconnection wait time modes 145 | */ 146 | export declare enum ReconnectionTimeMode { 147 | LINEAR = 0, 148 | EXPONENTIAL = 1 149 | } 150 | /** 151 | * Possible ticker strategies for outgoing heartbeat ping 152 | */ 153 | export declare enum TickerStrategy { 154 | Interval = "interval", 155 | Worker = "worker" 156 | } 157 | /** 158 | * @internal 159 | */ 160 | export interface IStomptHandlerConfig { 161 | debug: debugFnType; 162 | stompVersions: Versions; 163 | connectHeaders: StompHeaders; 164 | disconnectHeaders: StompHeaders; 165 | heartbeatIncoming: number; 166 | heartbeatOutgoing: number; 167 | heartbeatStrategy: TickerStrategy; 168 | splitLargeFrames: boolean; 169 | maxWebSocketChunkSize: number; 170 | forceBinaryWSFrames: boolean; 171 | logRawCommunication: boolean; 172 | appendMissingNULLonIncoming: boolean; 173 | discardWebsocketOnCommFailure: boolean; 174 | onConnect: frameCallbackType; 175 | onDisconnect: frameCallbackType; 176 | onStompError: frameCallbackType; 177 | onWebSocketClose: closeEventCallbackType; 178 | onWebSocketError: wsErrorCallbackType; 179 | onUnhandledMessage: messageCallbackType; 180 | onUnhandledReceipt: frameCallbackType; 181 | onUnhandledFrame: frameCallbackType; 182 | } 183 | -------------------------------------------------------------------------------- /esm6/types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Possible states for the IStompSocket 3 | */ 4 | export var StompSocketState; 5 | (function (StompSocketState) { 6 | StompSocketState[StompSocketState["CONNECTING"] = 0] = "CONNECTING"; 7 | StompSocketState[StompSocketState["OPEN"] = 1] = "OPEN"; 8 | StompSocketState[StompSocketState["CLOSING"] = 2] = "CLOSING"; 9 | StompSocketState[StompSocketState["CLOSED"] = 3] = "CLOSED"; 10 | })(StompSocketState || (StompSocketState = {})); 11 | /** 12 | * Possible activation state 13 | */ 14 | export var ActivationState; 15 | (function (ActivationState) { 16 | ActivationState[ActivationState["ACTIVE"] = 0] = "ACTIVE"; 17 | ActivationState[ActivationState["DEACTIVATING"] = 1] = "DEACTIVATING"; 18 | ActivationState[ActivationState["INACTIVE"] = 2] = "INACTIVE"; 19 | })(ActivationState || (ActivationState = {})); 20 | /** 21 | * Possible reconnection wait time modes 22 | */ 23 | export var ReconnectionTimeMode; 24 | (function (ReconnectionTimeMode) { 25 | ReconnectionTimeMode[ReconnectionTimeMode["LINEAR"] = 0] = "LINEAR"; 26 | ReconnectionTimeMode[ReconnectionTimeMode["EXPONENTIAL"] = 1] = "EXPONENTIAL"; 27 | })(ReconnectionTimeMode || (ReconnectionTimeMode = {})); 28 | /** 29 | * Possible ticker strategies for outgoing heartbeat ping 30 | */ 31 | export var TickerStrategy; 32 | (function (TickerStrategy) { 33 | TickerStrategy["Interval"] = "interval"; 34 | TickerStrategy["Worker"] = "worker"; 35 | })(TickerStrategy || (TickerStrategy = {})); 36 | //# sourceMappingURL=types.js.map -------------------------------------------------------------------------------- /esm6/types.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA4IA;;GAEG;AACH,MAAM,CAAN,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,mEAAU,CAAA;IACV,uDAAI,CAAA;IACJ,6DAAO,CAAA;IACP,2DAAM,CAAA;AACR,CAAC,EALW,gBAAgB,KAAhB,gBAAgB,QAK3B;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,yDAAM,CAAA;IACN,qEAAY,CAAA;IACZ,6DAAQ,CAAA;AACV,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,oBAGX;AAHD,WAAY,oBAAoB;IAC9B,mEAAM,CAAA;IACN,6EAAW,CAAA;AACb,CAAC,EAHW,oBAAoB,KAApB,oBAAoB,QAG/B;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,cAGX;AAHD,WAAY,cAAc;IACxB,uCAAqB,CAAA;IACrB,mCAAiB,CAAA;AACnB,CAAC,EAHW,cAAc,KAAd,cAAc,QAGzB"} -------------------------------------------------------------------------------- /esm6/versions.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Supported STOMP versions 3 | * 4 | * Part of `@stomp/stompjs`. 5 | */ 6 | export declare class Versions { 7 | versions: string[]; 8 | /** 9 | * Indicates protocol version 1.0 10 | */ 11 | static V1_0: string; 12 | /** 13 | * Indicates protocol version 1.1 14 | */ 15 | static V1_1: string; 16 | /** 17 | * Indicates protocol version 1.2 18 | */ 19 | static V1_2: string; 20 | /** 21 | * @internal 22 | */ 23 | static default: Versions; 24 | /** 25 | * Takes an array of versions, typical elements '1.2', '1.1', or '1.0' 26 | * 27 | * You will be creating an instance of this class if you want to override 28 | * supported versions to be declared during STOMP handshake. 29 | */ 30 | constructor(versions: string[]); 31 | /** 32 | * Used as part of CONNECT STOMP Frame 33 | */ 34 | supportedVersions(): string; 35 | /** 36 | * Used while creating a WebSocket 37 | */ 38 | protocolVersions(): string[]; 39 | } 40 | -------------------------------------------------------------------------------- /esm6/versions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Supported STOMP versions 3 | * 4 | * Part of `@stomp/stompjs`. 5 | */ 6 | export class Versions { 7 | /** 8 | * Takes an array of versions, typical elements '1.2', '1.1', or '1.0' 9 | * 10 | * You will be creating an instance of this class if you want to override 11 | * supported versions to be declared during STOMP handshake. 12 | */ 13 | constructor(versions) { 14 | this.versions = versions; 15 | } 16 | /** 17 | * Used as part of CONNECT STOMP Frame 18 | */ 19 | supportedVersions() { 20 | return this.versions.join(','); 21 | } 22 | /** 23 | * Used while creating a WebSocket 24 | */ 25 | protocolVersions() { 26 | return this.versions.map(x => `v${x.replace('.', '')}.stomp`); 27 | } 28 | } 29 | /** 30 | * Indicates protocol version 1.0 31 | */ 32 | Versions.V1_0 = '1.0'; 33 | /** 34 | * Indicates protocol version 1.1 35 | */ 36 | Versions.V1_1 = '1.1'; 37 | /** 38 | * Indicates protocol version 1.2 39 | */ 40 | Versions.V1_2 = '1.2'; 41 | /** 42 | * @internal 43 | */ 44 | Versions.default = new Versions([ 45 | Versions.V1_2, 46 | Versions.V1_1, 47 | Versions.V1_0, 48 | ]); 49 | //# sourceMappingURL=versions.js.map -------------------------------------------------------------------------------- /esm6/versions.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"versions.js","sourceRoot":"","sources":["../src/versions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,QAAQ;IAuBnB;;;;;OAKG;IACH,YAAmB,QAAkB;QAAlB,aAAQ,GAAR,QAAQ,CAAU;IAAG,CAAC;IAEzC;;OAEG;IACI,iBAAiB;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACI,gBAAgB;QACrB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC;IAChE,CAAC;;AA1CD;;GAEG;AACW,aAAI,GAAG,KAAK,CAAC;AAC3B;;GAEG;AACW,aAAI,GAAG,KAAK,CAAC;AAC3B;;GAEG;AACW,aAAI,GAAG,KAAK,CAAC;AAE3B;;GAEG;AACW,gBAAO,GAAG,IAAI,QAAQ,CAAC;IACnC,QAAQ,CAAC,IAAI;IACb,QAAQ,CAAC,IAAI;IACb,QAAQ,CAAC,IAAI;CACd,CAAC,CAAC"} -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './esm6/index.js'; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@stomp/stompjs", 3 | "version": "7.1.1", 4 | "description": "STOMP client for Javascript and Typescript", 5 | "scripts": { 6 | "clean": "rm -rf bundles esm6", 7 | "rollup": "rollup -c && rm -rf bundles/esm6/", 8 | "build": "npm run clean && npx tsc && npm run rollup && rm -rf bundles/compatibility bundles/*.d.ts && cp spec/package.json bundles && date", 9 | "test": "jasmine", 10 | "karma": "karma start spec/karma.conf.js --single-run", 11 | "lint": "tslint 'src/**/*.ts'", 12 | "prettier": "prettier --write .", 13 | "test-helpers": "tsc -p spec/helpers-src/tsconfig.json", 14 | "prepublishOnly": "npm run lint && npm run build && npm run karma && npm run test" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/stomp-js/stompjs.git" 19 | }, 20 | "keywords": [ 21 | "STOMP", 22 | "RabbitMQ", 23 | "ActiveMQ", 24 | "Websocket", 25 | "messaging", 26 | "queue", 27 | "SockJS" 28 | ], 29 | "author": "deepak@kreatio.com", 30 | "license": "Apache-2.0", 31 | "bugs": { 32 | "url": "https://github.com/stomp-js/stompjs/issues" 33 | }, 34 | "homepage": "https://github.com/stomp-js/stompjs#readme", 35 | "devDependencies": { 36 | "@chiragrupani/karma-chromium-edge-launcher": "^2.4.1", 37 | "@rollup/plugin-terser": "^0.4.4", 38 | "@rollup/plugin-typescript": "^12.1.2", 39 | "jasmine": "^5.6.0", 40 | "karma": "^6.4.4", 41 | "karma-chrome-launcher": "^3.2.0", 42 | "karma-firefox-launcher": "^2.1.3", 43 | "karma-jasmine": "^5.1.0", 44 | "karma-safari-launcher": "https://github.com/RLovelett/karma-safari-launcher.git#safari-webdriver", 45 | "karma-summary-reporter": "^4.0.1", 46 | "onchange": "^7.1.0", 47 | "prettier": "^3.5.3", 48 | "rollup": "^4.35.0", 49 | "text-encoding": "^0.7.0", 50 | "ts-loader": "^9.5.2", 51 | "tslint": "^6.1.3", 52 | "tslint-config-prettier": "^1.18.0", 53 | "typescript": "^5.8.2", 54 | "ws": "^8.18.1" 55 | }, 56 | "type": "module", 57 | "exports": { 58 | "import": "./esm6/index.js", 59 | "require": "./bundles/stomp.umd.js" 60 | }, 61 | "main": "./bundles/stomp.umd.js", 62 | "browser": "./bundles/stomp.umd.js", 63 | "typings": "index.d.ts", 64 | "sideEffects": false 65 | } 66 | -------------------------------------------------------------------------------- /rabbitmq/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rabbitmq:3.10.25-alpine 2 | 3 | RUN rabbitmq-plugins enable --offline rabbitmq_web_stomp 4 | 5 | RUN \ 6 | echo 'loopback_users.guest = false' >> /etc/rabbitmq/rabbitmq.conf && \ 7 | echo 'web_stomp.ws_frame = binary' >> /etc/rabbitmq/rabbitmq.conf 8 | 9 | EXPOSE 15674 10 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import typescript from '@rollup/plugin-typescript'; 2 | import terser from '@rollup/plugin-terser'; 3 | 4 | const umdConf = { 5 | file: 'bundles/stomp.umd.js', 6 | format: 'umd', 7 | name: 'StompJs', 8 | sourcemap: true, 9 | }; 10 | 11 | const umdMinConf = { 12 | ...umdConf, 13 | file: 'bundles/stomp.umd.min.js', 14 | sourcemap: false, 15 | plugins: [terser()], 16 | }; 17 | 18 | export default [ 19 | { 20 | input: 'src/index.ts', 21 | // @rollup/plugin-typescript@12 required the file output to be within the outDir 22 | // Accordingly after the build the npm build steps removes the *.d.ts as these are not needed 23 | // for the UJS bundle 24 | plugins: [typescript({compilerOptions: {outDir: 'bundles'}})], 25 | output: [umdConf, umdMinConf], 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /spec/config/browser-config.js: -------------------------------------------------------------------------------- 1 | TEST = { 2 | destination: '/topic/chat.general', 3 | login: 'guest', 4 | password: 'guest', 5 | url: 'ws://localhost:15674/ws', 6 | badUrl: 'ws://localhost:61625', 7 | timeout: 2000, 8 | largeMessageSize: 1024, // 1MB body 9 | testHeartBeatUsingWebWorkers: true, 10 | }; 11 | 12 | Stomp = StompJs.Stomp; 13 | 14 | // For ActiveMQ "ws://localhost:61614/stomp 15 | -------------------------------------------------------------------------------- /spec/config/node-config.js: -------------------------------------------------------------------------------- 1 | TEST = { 2 | destination: '/topic/chat.general', 3 | login: 'guest', 4 | password: 'guest', 5 | url: 'ws://localhost:15674/ws', 6 | badUrl: 'ws://localhost:61625', 7 | timeout: 2000, 8 | largeMessageSize: 1023, // in KB, in Node total WebSocket frames needs to be lesser than 1MB 9 | testHeartBeatUsingWebWorkers: false, 10 | }; 11 | 12 | WebSocket = require('ws'); 13 | StompJs = require('../../bundles/stomp.umd.js'); 14 | Stomp = StompJs.Stomp; 15 | 16 | // For ActiveMQ "ws://localhost:61614/stomp 17 | -------------------------------------------------------------------------------- /spec/helpers-src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ 5 | "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "lib": ["dom", "es2015"], /* Specify library files to be included in the compilation. */ 7 | // "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 12 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 13 | // "outFile": "./", /* Concatenate and emit output to single file. */ 14 | "outDir": "../helpers", /* Redirect output structure to the directory. */ 15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 16 | // "composite": true, /* Enable project compilation */ 17 | // "removeComments": false, /* Do not emit comments to output. */ 18 | // "noEmit": true, /* Do not emit outputs. */ 19 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 20 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 21 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 22 | 23 | /* Strict Type-Checking Options */ 24 | "strict": true, /* Enable all strict type-checking options. */ 25 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 26 | "strictNullChecks": false, /* Enable strict null checks. */ 27 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 28 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 29 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 30 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 31 | 32 | /* Additional Checks */ 33 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 34 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 35 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 36 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 37 | 38 | /* Module Resolution Options */ 39 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 40 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 41 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 42 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 43 | // "typeRoots": [], /* List of folders to include type definitions from. */ 44 | // "types": [], /* Type declaration files to be included in compilation. */ 45 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 46 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 47 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 48 | 49 | /* Source Map Options */ 50 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 51 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 52 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 53 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 54 | 55 | /* Experimental Options */ 56 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 57 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 58 | }, 59 | "include": ["./**/*.ts"] /* Only compile files in src, avoids confusion with code in node_modules or compiled code */ 60 | } 61 | -------------------------------------------------------------------------------- /spec/helpers-src/wrapper-ws.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A wrapper for WebSocket. 3 | * By default, it is a no op, i.e., exposes the underlying WebSocket without any changes. 4 | * However, by providing alternate implementations to methods (typically send and/or onmessage) 5 | * several error conditions can be simulated. See heart-beat.spec.js for examples. 6 | */ 7 | class WrapperWS { 8 | get url(): string { 9 | return this.ws.url; 10 | } 11 | get readyState(): number { 12 | return this.ws.readyState; 13 | } 14 | get protocol(): string { 15 | return this.ws.protocol; 16 | } 17 | get binaryType(): 'blob' | 'arraybuffer' { 18 | return this.ws.binaryType; 19 | } 20 | set binaryType(value: 'blob' | 'arraybuffer') { 21 | this.ws.binaryType = value; 22 | } 23 | 24 | constructor(public ws: WebSocket) { 25 | const noOp = () => {}; 26 | 27 | this.onclose = noOp; 28 | this.onerror = noOp; 29 | this.onmessage = noOp; 30 | this.onopen = noOp; 31 | 32 | this.ws.onclose = ev => { 33 | this.wrapOnClose(ev); 34 | }; 35 | this.ws.onerror = ev => { 36 | this.wrapOnError(ev); 37 | }; 38 | this.ws.onmessage = ev => { 39 | this.wrapOnMessage(ev); 40 | }; 41 | this.ws.onopen = ev => { 42 | this.wrapOnOpen(ev); 43 | }; 44 | } 45 | 46 | protected wrapOnOpen(ev: Event) { 47 | this.onopen(ev); 48 | } 49 | 50 | protected wrapOnMessage(ev: MessageEvent) { 51 | this.onmessage(ev); 52 | } 53 | 54 | protected wrapOnError(ev: Event) { 55 | this.onerror(ev); 56 | } 57 | 58 | protected wrapOnClose(ev: CloseEvent) { 59 | this.onclose(ev); 60 | } 61 | 62 | public onclose: ((ev: CloseEvent) => any) | null; 63 | public onerror: ((ev: Event) => any) | null; 64 | public onmessage: ((ev: MessageEvent) => any) | null; 65 | public onopen: ((ev: Event) => any) | null; 66 | 67 | public close(code?: number, reason?: string) { 68 | this.ws.close(code, reason); 69 | } 70 | 71 | public send(data: string | ArrayBuffer | Blob | ArrayBufferView) { 72 | this.ws.send(data); 73 | } 74 | } 75 | 76 | if (typeof global === 'object') { 77 | Object.assign(global, { 78 | WrapperWS: WrapperWS, 79 | }); 80 | } 81 | -------------------------------------------------------------------------------- /spec/helpers/connect-helpers.js: -------------------------------------------------------------------------------- 1 | id = 0; 2 | 3 | stompClient = function () { 4 | const myId = ++id; 5 | 6 | const stompConfig = { 7 | connectHeaders: { 8 | login: TEST.login, 9 | passcode: TEST.password, 10 | }, 11 | brokerURL: TEST.url, 12 | // To test STOMP over TCP 13 | // webSocketFactory: () => new TCPWrapper('127.0.0.1', 61613), 14 | debug: function (str) { 15 | console.log('CLIENT ' + myId + ': ' + str); 16 | }, 17 | reconnectDelay: 0, 18 | }; 19 | 20 | if (typeof process !== 'undefined' && process.env.CONN_MODE === 'tcp') { 21 | const TCPWrapper = require('@stomp/tcp-wrapper').TCPWrapper; 22 | stompConfig.debug('Using STOMP over TCP'); 23 | stompConfig.webSocketFactory = () => new TCPWrapper('127.0.0.1', 61613); 24 | } 25 | 26 | return new StompJs.Client(stompConfig); 27 | }; 28 | 29 | badStompClient = function () { 30 | const client = stompClient(); 31 | // brokerURL is also provided, in this case webSocketFactory should get used 32 | client.webSocketFactory = function () { 33 | return new WebSocket(TEST.badUrl); 34 | }; 35 | return client; 36 | }; 37 | 38 | // This itself is important, if for some reason, deactivate does not complete, the jasmine test will time out 39 | // Ensure this is called as await in an async function. 40 | disconnectStomp = async function (client) { 41 | if (client) { 42 | await client.deactivate(); 43 | } 44 | }; 45 | 46 | saveOrigFactory = client => { 47 | if (!client._origFactory) { 48 | client._origFactory = 49 | client.webSocketFactory || 50 | (() => 51 | new WebSocket( 52 | client.brokerURL, 53 | client.stompVersions.protocolVersions() 54 | )); 55 | } 56 | }; 57 | 58 | overRideFactory = (client, WrapperClass) => { 59 | saveOrigFactory(client); 60 | 61 | client.webSocketFactory = () => new WrapperClass(client._origFactory()); 62 | }; 63 | -------------------------------------------------------------------------------- /spec/helpers/content-helpers.js: -------------------------------------------------------------------------------- 1 | randomText = function () { 2 | return '' + Math.random(); 3 | }; 4 | 5 | // Generate k Kilo Bytes of binary data 6 | generateBinaryData = function (k) { 7 | let chunk = []; 8 | for (let i = 0; i < 4 * k; i++) { 9 | for (let j = 0; j < 256; j++) { 10 | chunk.push(j); 11 | } 12 | } 13 | return new Uint8Array(chunk); 14 | }; 15 | 16 | // Generate k Kilo Bytes of text; data 17 | generateTextData = function (k) { 18 | let chunk = // 256 Bytes 19 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed uta' + 20 | ' ornare arcu. Aenean vehicula, magna in viverra pulvinar, enimr ' + 21 | 'arcu maximus erat, ac malesuada elit libero sit amet dui. Donecl' + 22 | ' dignissim felis at neque viverra porttitor. Maecenas maximus po'; 23 | 24 | let data = []; 25 | for (let i = 0; i < 4 * k; i++) { 26 | data.push(chunk); 27 | } 28 | return data.join(''); 29 | }; 30 | -------------------------------------------------------------------------------- /spec/helpers/parse-frame.js: -------------------------------------------------------------------------------- 1 | parseFrame = function (chunk) { 2 | let frame; 3 | 4 | // ignore 5 | parser = new StompJs.Parser( 6 | f => { 7 | frame = f; 8 | }, 9 | () => {} 10 | ); 11 | parser.parseChunk(chunk); 12 | 13 | return frame; 14 | }; 15 | -------------------------------------------------------------------------------- /spec/helpers/utils.js: -------------------------------------------------------------------------------- 1 | wait = (timeToDelay) => 2 | new Promise(resolve => setTimeout(resolve, timeToDelay)); 3 | -------------------------------------------------------------------------------- /spec/helpers/wrapper-ws.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * A wrapper for WebSocket. 4 | * By default, it is a no op, i.e., exposes the underlying WebSocket without any changes. 5 | * However, by providing alternate implementations to methods (typically send and/or onmessage) 6 | * several error conditions can be simulated. See heart-beat.spec.js for examples. 7 | */ 8 | class WrapperWS { 9 | constructor(ws) { 10 | this.ws = ws; 11 | const noOp = () => { }; 12 | this.onclose = noOp; 13 | this.onerror = noOp; 14 | this.onmessage = noOp; 15 | this.onopen = noOp; 16 | this.ws.onclose = ev => { 17 | this.wrapOnClose(ev); 18 | }; 19 | this.ws.onerror = ev => { 20 | this.wrapOnError(ev); 21 | }; 22 | this.ws.onmessage = ev => { 23 | this.wrapOnMessage(ev); 24 | }; 25 | this.ws.onopen = ev => { 26 | this.wrapOnOpen(ev); 27 | }; 28 | } 29 | get url() { 30 | return this.ws.url; 31 | } 32 | get readyState() { 33 | return this.ws.readyState; 34 | } 35 | get protocol() { 36 | return this.ws.protocol; 37 | } 38 | get binaryType() { 39 | return this.ws.binaryType; 40 | } 41 | set binaryType(value) { 42 | this.ws.binaryType = value; 43 | } 44 | wrapOnOpen(ev) { 45 | this.onopen(ev); 46 | } 47 | wrapOnMessage(ev) { 48 | this.onmessage(ev); 49 | } 50 | wrapOnError(ev) { 51 | this.onerror(ev); 52 | } 53 | wrapOnClose(ev) { 54 | this.onclose(ev); 55 | } 56 | close(code, reason) { 57 | this.ws.close(code, reason); 58 | } 59 | send(data) { 60 | this.ws.send(data); 61 | } 62 | } 63 | if (typeof global === 'object') { 64 | Object.assign(global, { 65 | WrapperWS: WrapperWS, 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /spec/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Thu Jul 05 2018 16:43:26 GMT+0530 (IST) 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | // base path that will be used to resolve all patterns (eg. files, exclude) 7 | basePath: '', 8 | 9 | // frameworks to use 10 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 11 | frameworks: ['jasmine'], 12 | 13 | plugins: [ 14 | require('@chiragrupani/karma-chromium-edge-launcher'), 15 | require('karma-chrome-launcher'), 16 | require('karma-firefox-launcher'), 17 | require('karma-jasmine'), 18 | require('karma-safari-launcher'), 19 | require('karma-summary-reporter'), 20 | ], 21 | 22 | // list of files / patterns to load in the browser 23 | files: [ 24 | '../bundles/stomp.umd.js', 25 | 'config/browser-config.js', 26 | 'helpers/**/*.js', 27 | 'unit/**/*.js', 28 | ], 29 | 30 | // list of files / patterns to exclude 31 | exclude: [], 32 | 33 | // preprocess matching files before serving them to the browser 34 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 35 | preprocessors: {}, 36 | 37 | // test results reporter to use 38 | // possible values: 'dots', 'progress' 39 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 40 | reporters: ['progress'], 41 | 42 | // web server port 43 | port: 9876, 44 | 45 | // enable / disable colors in the output (reporters and logs) 46 | colors: true, 47 | 48 | // level of logging 49 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 50 | logLevel: config.LOG_INFO, 51 | 52 | // enable / disable watching file and executing tests whenever any file changes 53 | autoWatch: true, 54 | 55 | // start these browsers 56 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 57 | browsers: ['ChromeNoSandboxHeadless'], 58 | 59 | customLaunchers: { 60 | // See https://github.com/karma-runner/karma/issues/2603 61 | ChromeNoSandboxHeadless: { 62 | base: 'Chrome', 63 | flags: [ 64 | '--no-sandbox', 65 | // See https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md 66 | '--headless', 67 | '--disable-gpu', 68 | // Without a remote debugging port, Google Chrome exits immediately. 69 | ' --remote-debugging-port=9222', 70 | ], 71 | }, 72 | FirefoxHeadless: { 73 | base: 'Firefox', 74 | flags: ['-headless'], 75 | }, 76 | }, 77 | 78 | // Continuous Integration mode 79 | // if true, Karma captures browsers, runs the tests and exits 80 | singleRun: false, 81 | 82 | // Concurrency level 83 | // how many browser should be started simultaneous 84 | concurrency: 1, 85 | }); 86 | }; 87 | -------------------------------------------------------------------------------- /spec/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spec", 3 | "version": "1.0.0", 4 | "description": "Just for running specs with commonjs modules", 5 | "type": "commonjs" 6 | } 7 | -------------------------------------------------------------------------------- /spec/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "spec", 3 | "spec_files": ["**/*[sS]pec.js"], 4 | "helpers": ["config/node-config.js", "helpers/**/*.js"], 5 | "stopSpecOnExpectationFailure": false, 6 | "random": true 7 | } 8 | -------------------------------------------------------------------------------- /spec/unit/ack.spec.js: -------------------------------------------------------------------------------- 1 | describe('Stomp Acknowledgement (RabbitMQ specific queue destination)', function () { 2 | let client01; 3 | let client02; 4 | 5 | beforeEach(function (done) { 6 | client01 = stompClient(); 7 | client01.onConnect = () => done(); 8 | client01.activate(); 9 | }); 10 | 11 | beforeEach(function (done) { 12 | client02 = stompClient(); 13 | client02.onConnect = () => done(); 14 | client02.activate(); 15 | }); 16 | 17 | afterEach(async function () { 18 | await disconnectStomp(client01); 19 | await disconnectStomp(client02); 20 | }); 21 | 22 | it('Should deliver to other client if nacked from one', function (done) { 23 | const queueDestination = '/queue/test01'; 24 | let receivedCount = 0; 25 | const body = randomText(); 26 | 27 | const setUpSubscription = function (client) { 28 | const onMessage = function (message) { 29 | if (message.body !== body) { 30 | return; 31 | } 32 | 33 | receivedCount++; 34 | 35 | if (receivedCount < 3) { 36 | message.nack(); 37 | return; 38 | } 39 | 40 | message.ack(); 41 | done(); 42 | }; 43 | 44 | client.subscribe(queueDestination, onMessage, { ack: 'client' }); 45 | }; 46 | 47 | setUpSubscription(client01); 48 | setUpSubscription(client02); 49 | 50 | client01.publish({ destination: queueDestination, body: body }); 51 | }); 52 | 53 | it('Should deliver to other client if connection drops before ack', function (done) { 54 | const queueDestination = '/queue/test01'; 55 | let receivedCount = 0; 56 | const body = randomText(); 57 | 58 | const setUpSubscription = function (client) { 59 | const onMessage = function (message) { 60 | if (message.body !== body) { 61 | return; 62 | } 63 | 64 | receivedCount++; 65 | 66 | if (receivedCount === 1) { 67 | client.deactivate(); 68 | return; 69 | } 70 | 71 | message.ack(); 72 | done(); 73 | }; 74 | 75 | client.subscribe(queueDestination, onMessage, { ack: 'client' }); 76 | }; 77 | 78 | setUpSubscription(client01); 79 | setUpSubscription(client02); 80 | 81 | client01.publish({ destination: queueDestination, body: body }); 82 | }); 83 | 84 | it('Should not redeliver after ack', function (done) { 85 | const queueDestination = '/queue/test01'; 86 | let receivedCount = 0; 87 | const body = randomText(); 88 | 89 | const setUpSubscription = function (client) { 90 | const onMessage = function (message) { 91 | if (message.body !== body) { 92 | return; 93 | } 94 | 95 | receivedCount++; 96 | 97 | message.ack(); 98 | client.deactivate(); 99 | 100 | setTimeout(function () { 101 | expect(receivedCount).toEqual(1); 102 | done(); 103 | }, 100); 104 | }; 105 | 106 | client.subscribe(queueDestination, onMessage, { ack: 'client' }); 107 | }; 108 | 109 | setUpSubscription(client01); 110 | setUpSubscription(client02); 111 | 112 | client01.publish({ destination: queueDestination, body: body }); 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /spec/unit/append-missing-null-on-incoming.spec.js: -------------------------------------------------------------------------------- 1 | describe('appendMissingNULLonIncoming', () => { 2 | let client; 3 | 4 | beforeEach(() => { 5 | client = stompClient(); 6 | 7 | // Simulate incorrect behavior in React Native (see https://github.com/stomp-js/stompjs/issues/89) 8 | overRideFactory( 9 | client, 10 | class extends WrapperWS { 11 | wrapOnMessage(ev) { 12 | // Convert incoming data to string if not already string 13 | let data = ev.data; 14 | if (typeof data !== 'string') { 15 | data = new TextDecoder().decode(data); 16 | } 17 | 18 | // chop everything after '\0' 19 | data = data.replace(/\0.*/, ''); 20 | const updatedEv = { ...ev.data, ...{ data: data } }; 21 | 22 | super.wrapOnMessage(updatedEv); 23 | } 24 | } 25 | ); 26 | }); 27 | 28 | afterEach(async () => { 29 | await disconnectStomp(client); 30 | }); 31 | 32 | // Find length - 33 | const length = data => { 34 | return typeof data === 'string' ? data.length : data.byteLength; 35 | }; 36 | 37 | it('Should append missing null in incoming frames (bypass bug in React Native)', done => { 38 | client.appendMissingNULLonIncoming = true; 39 | 40 | const body = randomText(); 41 | client.onConnect = () => { 42 | client.subscribe(TEST.destination, message => { 43 | expect(message.body).toEqual(body); 44 | client.deactivate(); 45 | 46 | done(); 47 | }); 48 | 49 | client.publish({ destination: TEST.destination, body: body }); 50 | }; 51 | client.activate(); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /spec/unit/callbacks.spec.js: -------------------------------------------------------------------------------- 1 | describe('Callbacks', function () { 2 | let client; 3 | 4 | beforeEach(function () { 5 | client = stompClient(); 6 | }); 7 | 8 | afterEach(async function () { 9 | await disconnectStomp(client); 10 | }); 11 | 12 | describe('invokes in sequence', function () { 13 | it('during regular connect/disconnect', function (done) { 14 | const expectedSeq = ['before connect', 'on connect', 'websocket close']; 15 | let seq = []; 16 | 17 | client.onConnect = function () { 18 | seq.push('on connect'); 19 | client.deactivate(); 20 | }; 21 | client.beforeConnect = function () { 22 | seq.push('before connect'); 23 | }; 24 | client.onDisconnect = function () { 25 | console.log( 26 | 'Optional callback, not every broker will acknowledge DISCONNECT' 27 | ); 28 | // seq.push('on disconnect'); 29 | }; 30 | client.onWebSocketClose = function () { 31 | seq.push('websocket close'); 32 | 33 | expect(seq).toEqual(expectedSeq); 34 | done(); 35 | }; 36 | client.onStompError = function () { 37 | seq.push('stomp-error'); 38 | }; 39 | 40 | client.activate(); 41 | }); 42 | 43 | it('during forced disconnect', function (done) { 44 | const expectedSeq = ['before connect', 'on connect', 'websocket close']; 45 | let seq = []; 46 | 47 | client.onConnect = function () { 48 | seq.push('on connect'); 49 | client.forceDisconnect(); 50 | client.deactivate(); 51 | }; 52 | client.beforeConnect = function () { 53 | seq.push('before connect'); 54 | }; 55 | client.onDisconnect = function () { 56 | seq.push('on disconnect'); 57 | }; 58 | client.onWebSocketClose = function () { 59 | seq.push('websocket close'); 60 | 61 | expect(seq).toEqual(expectedSeq); 62 | done(); 63 | }; 64 | client.onStompError = function () { 65 | seq.push('stomp-error'); 66 | }; 67 | 68 | client.activate(); 69 | }); 70 | 71 | it('during auto reconnect', function (done) { 72 | const expectedSeq = [ 73 | 'before connect', 74 | 'on connect', 75 | 'websocket close', // first cycle 76 | 'before connect', 77 | 'on connect', 78 | 'websocket close', 79 | ]; // send cycle 80 | let seq = []; 81 | let count = 0; 82 | 83 | client.reconnectDelay = 20; 84 | 85 | client.onConnect = function () { 86 | seq.push('on connect'); 87 | if (++count === 1) { 88 | client.forceDisconnect(); 89 | return; 90 | } 91 | client.deactivate(); 92 | }; 93 | client.beforeConnect = function () { 94 | seq.push('before connect'); 95 | }; 96 | client.onDisconnect = function () { 97 | console.log( 98 | 'Optional callback, not every broker will acknowledge DISCONNECT' 99 | ); 100 | // seq.push('on disconnect'); 101 | }; 102 | client.onWebSocketClose = function () { 103 | seq.push('websocket close'); 104 | if (count === 1) { 105 | return; 106 | } 107 | 108 | expect(seq).toEqual(expectedSeq); 109 | done(); 110 | }; 111 | client.onStompError = function () { 112 | seq.push('stomp-error'); 113 | }; 114 | 115 | client.activate(); 116 | }); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /spec/unit/compatibility/connection.spec.js: -------------------------------------------------------------------------------- 1 | describe('Compat Stomp Connection', function () { 2 | let client; 3 | 4 | afterEach(async function () { 5 | await disconnectStomp(client); 6 | }); 7 | 8 | it('Connect to a valid Stomp server using URL', function (done) { 9 | client = StompJs.Stomp.client(TEST.url); 10 | client.connect(TEST.login, TEST.password, function () { 11 | done(); 12 | }); 13 | }); 14 | 15 | it('Connect to a valid Stomp server using Stomp.over (plain socket)', function (done) { 16 | const socket = new WebSocket(TEST.url); 17 | client = StompJs.Stomp.over(socket); 18 | client.connect(TEST.login, TEST.password, function () { 19 | done(); 20 | }); 21 | }); 22 | 23 | it('Connect to a valid Stomp server using Stomp.over (socket factory)', function (done) { 24 | const socketFactory = function () { 25 | return new WebSocket(TEST.url); 26 | }; 27 | client = StompJs.Stomp.over(socketFactory); 28 | client.connect(TEST.login, TEST.password, function () { 29 | done(); 30 | }); 31 | }); 32 | 33 | it('Should warn if factory was not supplied to Stomp.over', function () { 34 | const socket = new WebSocket(TEST.url); 35 | 36 | const spy = spyOn(console, 'warn').and.callThrough(); 37 | 38 | client = StompJs.Stomp.over(socket); 39 | 40 | expect(spy).toHaveBeenCalled(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /spec/unit/compatibility/heartbeat-options.spec.js: -------------------------------------------------------------------------------- 1 | describe('Compat mode', function () { 2 | let client; 3 | 4 | beforeEach(function () { 5 | client = StompJs.Stomp.client(); 6 | }); 7 | 8 | afterEach(async function () { 9 | await disconnectStomp(client); 10 | }); 11 | 12 | it('Should set incoming heartbeat interval', function () { 13 | client.heartbeat.incoming = 5200; 14 | expect(client.heartbeatIncoming).toEqual(5200); 15 | expect(client.heartbeat.incoming).toEqual(client.heartbeatIncoming); 16 | }); 17 | 18 | it('Should set outgoing heartbeat interval', function () { 19 | client.heartbeat.outgoing = 3100; 20 | expect(client.heartbeatOutgoing).toEqual(3100); 21 | expect(client.heartbeat.outgoing).toEqual(client.heartbeatOutgoing); 22 | }); 23 | 24 | it('Should set incoming/outgoing heartbeat interval', function () { 25 | client.heartbeat = { incoming: 2500, outgoing: 3750 }; 26 | 27 | expect(client.heartbeatIncoming).toEqual(2500); 28 | expect(client.heartbeatOutgoing).toEqual(3750); 29 | 30 | expect(client.heartbeat.incoming).toEqual(client.heartbeatIncoming); 31 | expect(client.heartbeat.outgoing).toEqual(client.heartbeatOutgoing); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /spec/unit/compatibility/message.spec.js: -------------------------------------------------------------------------------- 1 | describe('Compat Stomp Message', function () { 2 | let client; 3 | 4 | beforeEach(function () { 5 | client = StompJs.Stomp.client(TEST.url); 6 | }); 7 | 8 | afterEach(async function () { 9 | await disconnectStomp(client); 10 | }); 11 | 12 | it('Send and receive a message', function (done) { 13 | const body = randomText(); 14 | 15 | client.connect(TEST.login, TEST.password, function () { 16 | client.subscribe(TEST.destination, function (message) { 17 | expect(message.body).toEqual(body); 18 | client.disconnect(); 19 | 20 | done(); 21 | }); 22 | 23 | client.send(TEST.destination, {}, body); 24 | }); 25 | }); 26 | 27 | it('Send and receive a message with a JSON body', function (done) { 28 | const payload = { text: 'hello', bool: true, value: randomText() }; 29 | 30 | client.connect(TEST.login, TEST.password, function () { 31 | client.subscribe(TEST.destination, function (message) { 32 | const res = JSON.parse(message.body); 33 | expect(res.text).toEqual(payload.text); 34 | expect(res.bool).toEqual(payload.bool); 35 | expect(res.value).toEqual(payload.value); 36 | client.disconnect(); 37 | 38 | done(); 39 | }); 40 | 41 | client.send(TEST.destination, {}, JSON.stringify(payload)); 42 | }); 43 | }); 44 | 45 | it('Should allow skipping content length header', function (done) { 46 | const body = 'Hello, world'; 47 | 48 | client.connect(TEST.login, TEST.password, function () { 49 | client.subscribe(TEST.destination, function (message) { 50 | expect(message.body).toEqual(body); 51 | client.disconnect(); 52 | 53 | done(); 54 | }); 55 | 56 | const spy = spyOn(client.webSocket, 'send').and.callThrough(); 57 | 58 | client.send(TEST.destination, { 'content-length': false }, body); 59 | 60 | const rawChunk = spy.calls.first().args[0]; 61 | expect(rawChunk).not.toMatch('content-length'); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /spec/unit/compatibility/parse-connect.spec.js: -------------------------------------------------------------------------------- 1 | describe('Compat Parse connect method arguments', function () { 2 | // prepare something for all following tests 3 | const myConnectCallback = function () { 4 | // called back when the client is connected to STOMP broker 5 | }; 6 | 7 | const myErrorCallback = function () { 8 | // called back if the client can not connect to STOMP broker 9 | }; 10 | 11 | const myCloseEventCallback = function () { 12 | // called back if the connection was closed 13 | }; 14 | 15 | function checkArgs( 16 | args, 17 | expectedHeaders, 18 | expectedConnectCallback, 19 | expectedErrorCallback, 20 | expectedCloseEventCallback 21 | ) { 22 | const headers = args[0]; 23 | const connectCallback = args[1]; 24 | const errorCallback = args[2]; 25 | const closeEventCallback = args[3]; 26 | 27 | expect(headers).toEqual(expectedHeaders); 28 | expect(connectCallback).toBe(expectedConnectCallback); 29 | expect(errorCallback).toBe(expectedErrorCallback); 30 | expect(closeEventCallback).toBe(expectedCloseEventCallback); 31 | } 32 | 33 | let client; 34 | 35 | beforeEach(function () { 36 | client = StompJs.Stomp.client(); 37 | }); 38 | 39 | it('connect(login, passcode, connectCallback)', function () { 40 | checkArgs( 41 | client._parseConnect('jmesnil', 'wombats', myConnectCallback), 42 | 43 | { login: 'jmesnil', passcode: 'wombats' }, 44 | myConnectCallback, 45 | undefined 46 | ); 47 | }); 48 | 49 | it('connect(login, passcode, connectCallback, errorCallback)', function () { 50 | checkArgs( 51 | client._parseConnect( 52 | 'jmesnil', 53 | 'wombats', 54 | myConnectCallback, 55 | myErrorCallback 56 | ), 57 | 58 | { login: 'jmesnil', passcode: 'wombats' }, 59 | myConnectCallback, 60 | myErrorCallback 61 | ); 62 | }); 63 | 64 | it('connect(login, passcode, connectCallback, errorCallback, closeEventCallback)', function () { 65 | checkArgs( 66 | client._parseConnect( 67 | 'jmesnil', 68 | 'wombats', 69 | myConnectCallback, 70 | myErrorCallback, 71 | myCloseEventCallback 72 | ), 73 | 74 | { login: 'jmesnil', passcode: 'wombats' }, 75 | myConnectCallback, 76 | myErrorCallback, 77 | myCloseEventCallback 78 | ); 79 | }); 80 | 81 | it('connect(login, passcode, connectCallback, errorCallback, vhost)', function () { 82 | checkArgs( 83 | client._parseConnect( 84 | 'jmesnil', 85 | 'wombats', 86 | myConnectCallback, 87 | myErrorCallback, 88 | myCloseEventCallback, 89 | 'myvhost' 90 | ), 91 | 92 | { login: 'jmesnil', passcode: 'wombats', host: 'myvhost' }, 93 | myConnectCallback, 94 | myErrorCallback, 95 | myCloseEventCallback 96 | ); 97 | }); 98 | 99 | it('connect(headers, connectCallback)', function () { 100 | const headers = { login: 'jmesnil', passcode: 'wombats', host: 'myvhost' }; 101 | 102 | checkArgs( 103 | client._parseConnect(headers, myConnectCallback), 104 | 105 | headers, 106 | myConnectCallback, 107 | undefined 108 | ); 109 | }); 110 | 111 | it('connect(headers, connectCallback, errorCallback)', function () { 112 | const headers = { login: 'jmesnil', passcode: 'wombats', host: 'myvhost' }; 113 | 114 | checkArgs( 115 | client._parseConnect(headers, myConnectCallback, myErrorCallback), 116 | 117 | headers, 118 | myConnectCallback, 119 | myErrorCallback 120 | ); 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /spec/unit/config.spec.js: -------------------------------------------------------------------------------- 1 | describe('Configuration', function () { 2 | let client; 3 | 4 | beforeEach(function () { 5 | client = stompClient(); 6 | }); 7 | 8 | afterEach(async function () { 9 | await disconnectStomp(client); 10 | }); 11 | 12 | it('Updating disconnectHeaders should take effect from subsequent disconnect', function (done) { 13 | // Ref issue: https://github.com/stomp-js/stompjs/issues/27 14 | 15 | const headerBeforeConnect = 'Header Before Connect'; 16 | const headerAfterConnect = 'Header After Connect'; 17 | 18 | client.configure({ 19 | disconnectHeaders: { 20 | myheader: headerBeforeConnect, 21 | }, 22 | onConnect: function () { 23 | const spy = spyOn(client.webSocket, 'send').and.callThrough(); 24 | 25 | client.configure({ 26 | disconnectHeaders: { 27 | myheader: headerAfterConnect, 28 | }, 29 | onWebSocketClose: function () { 30 | const rawChunk = spy.calls.first().args[0]; 31 | expect(rawChunk).not.toMatch(headerBeforeConnect); 32 | expect(rawChunk).toMatch(headerAfterConnect); 33 | 34 | done(); 35 | }, 36 | }); 37 | 38 | // Now call deactivate 39 | client.deactivate(); 40 | }, 41 | }); 42 | 43 | client.activate(); 44 | }); 45 | 46 | it('should not alter connect headers', function (done) { 47 | // Keep a copy of original headers 48 | const connectHeaders = Object.assign({}, client.connectHeaders); 49 | 50 | client.onConnect = function () { 51 | expect(client.connectHeaders).toEqual(connectHeaders); 52 | done(); 53 | }; 54 | 55 | client.activate(); 56 | }); 57 | 58 | it('should not alter disconnect headers', function (done) { 59 | const disconnectHeaders = { 60 | myheader: 'My Header', 61 | }; 62 | 63 | // Keep a copy of original headers 64 | const disconnectHeadersOrig = Object.assign({}, disconnectHeaders); 65 | 66 | client.configure({ 67 | disconnectHeaders: disconnectHeaders, 68 | onConnect: function () { 69 | client.deactivate(); 70 | }, 71 | onWebSocketClose: function () { 72 | expect(disconnectHeaders).toEqual(disconnectHeadersOrig); 73 | done(); 74 | }, 75 | }); 76 | 77 | client.activate(); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /spec/unit/force-binary-ws-frames.js: -------------------------------------------------------------------------------- 1 | describe('forceBinaryWSFrames', function () { 2 | let client; 3 | 4 | beforeEach(function () { 5 | client = stompClient(); 6 | client.configure({ 7 | forceBinaryWSFrames: true, 8 | }); 9 | }); 10 | 11 | afterEach(async function () { 12 | await disconnectStomp(client); 13 | }); 14 | 15 | it('all binary packets', function (done) { 16 | const body = randomText(); 17 | client.onConnect = function () { 18 | const spyWebSocketSend = spyOn( 19 | client.webSocket, 20 | 'send' 21 | ).and.callThrough(); 22 | 23 | client.subscribe(TEST.destination, function (message) { 24 | expect(message.body).toEqual(body); 25 | client.deactivate(); 26 | 27 | // Usually all packets should have been Text, but with this flag each packet would be binary Uint8Array 28 | spyWebSocketSend.calls.allArgs().forEach(function (args) { 29 | const packet = args[0]; 30 | expect(packet instanceof Uint8Array).toBeTruthy(); 31 | }); 32 | 33 | done(); 34 | }); 35 | 36 | client.publish({ destination: TEST.destination, body: body }); 37 | }; 38 | client.activate(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /spec/unit/frame.spec.js: -------------------------------------------------------------------------------- 1 | describe('Stomp FrameImpl', function () { 2 | // un-marshall a data chunk, for ease of matching body is converted to string 3 | const unmarshall = function (data, escapeHeaderValues) { 4 | const onFrame = jasmine.createSpy('onFrame'); 5 | const onIncomingPing = jasmine.createSpy('onIncomingPing'); 6 | const parser = new StompJs.Parser(onFrame, onIncomingPing); 7 | 8 | parser.parseChunk(data); 9 | 10 | const rawFrame = onFrame.calls.first().args[0]; 11 | return StompJs.FrameImpl.fromRawFrame(rawFrame, escapeHeaderValues); 12 | }; 13 | 14 | it('escape header value', function () { 15 | const out = StompJs.FrameImpl.hdrValueEscape( 16 | 'anything\\a\nb\nc\rd\re:f:\\anything\\a\nb\nc\rd\re:f:\\' 17 | ); 18 | expect(out).toEqual( 19 | 'anything\\\\a\\nb\\nc\\rd\\re\\cf\\c\\\\anything\\\\a\\nb\\nc\\rd\\re\\cf\\c\\\\' 20 | ); 21 | }); 22 | 23 | it('escapes and then unescapes header value to give original string', function () { 24 | const orig = 'anything\\a\nb\nc\rd\re:f:\\anything\\a\nb\nc\rd\re:f:\\'; 25 | const out = StompJs.FrameImpl.hdrValueUnEscape( 26 | StompJs.FrameImpl.hdrValueEscape(orig) 27 | ); 28 | expect(out).toEqual(orig); 29 | }); 30 | 31 | it('marshall a CONNECT frame', function () { 32 | const out = StompJs.FrameImpl.marshall({ 33 | command: 'CONNECT', 34 | headers: { login: 'jmesnil', passcode: 'wombats' }, 35 | }); 36 | expect(out).toEqual('CONNECT\nlogin:jmesnil\npasscode:wombats\n\n\0'); 37 | }); 38 | 39 | it('marshall a SEND frame', function () { 40 | const out = StompJs.FrameImpl.marshall({ 41 | command: 'SEND', 42 | headers: { destination: '/queue/test' }, 43 | body: 'hello, world!', 44 | }); 45 | expect(out).toEqual( 46 | 'SEND\ndestination:/queue/test\ncontent-length:13\n\nhello, world!\0' 47 | ); 48 | }); 49 | 50 | it('marshall a SEND frame without content-length', function () { 51 | const out = StompJs.FrameImpl.marshall({ 52 | command: 'SEND', 53 | headers: { destination: '/queue/test' }, 54 | body: 'hello, world!', 55 | skipContentLengthHeader: true, 56 | }); 57 | expect(out).toEqual('SEND\ndestination:/queue/test\n\nhello, world!\0'); 58 | }); 59 | 60 | it('unmarshall a CONNECTED frame', function () { 61 | const data = 'CONNECTED\nsession-id: 1234\n\n\0'; 62 | const frame = unmarshall(data); 63 | expect(frame.command).toEqual('CONNECTED'); 64 | expect(frame.headers).toEqual({ 'session-id': '1234' }); 65 | expect(frame.body).toEqual(''); 66 | }); 67 | 68 | it('unmarshall a RECEIVE frame', function () { 69 | const data = 'RECEIVE\nfoo: abc\nbar: 1234\n\nhello, world!\0'; 70 | const frame = unmarshall(data); 71 | expect(frame.command).toEqual('RECEIVE'); 72 | expect(frame.headers).toEqual({ foo: 'abc', bar: '1234' }); 73 | expect(frame.body).toEqual('hello, world!'); 74 | }); 75 | 76 | it('unmarshall should not include the null byte in the body', function () { 77 | const body1 = 'Just the text please.', 78 | body2 = 'And the newline\n', 79 | msg = 'MESSAGE\ndestination: /queue/test\nmessage-id: 123\n\n'; 80 | 81 | expect(unmarshall(msg + body1 + '\0').body).toEqual(body1); 82 | expect(unmarshall(msg + body2 + '\0').body).toEqual(body2); 83 | }); 84 | 85 | it('unmarshall should support colons (:) in header values', function () { 86 | const dest = 'foo:bar:baz', 87 | msg = 'MESSAGE\ndestination: ' + dest + '\nmessage-id: 456\n\n\0'; 88 | 89 | expect(unmarshall(msg).headers.destination).toEqual(dest); 90 | }); 91 | 92 | it('unmarshall should support colons (:) in header values with escaping', function () { 93 | const dest = 'foo:bar:baz', 94 | msg = 95 | 'MESSAGE\ndestination: ' + 96 | 'foo\\cbar\\cbaz' + 97 | '\nmessage-id: 456\n\n\0'; 98 | 99 | expect(unmarshall(msg, true).headers.destination).toEqual(dest); 100 | }); 101 | 102 | it('unmarshall should support \\, \\n and \\r in header values with escaping', function () { 103 | const dest = 'f:o:o\nbar\rbaz\\foo\nbar\rbaz\\', 104 | msg = 105 | 'MESSAGE\ndestination: ' + 106 | 'f\\co\\co\\nbar\\rbaz\\\\foo\\nbar\\rbaz\\\\' + 107 | '\nmessage-id: 456\n\n\0'; 108 | 109 | expect(unmarshall(msg, true).headers.destination).toEqual(dest); 110 | }); 111 | 112 | it('marshall should support \\, \\n and \\r in header values with escaping', function () { 113 | const dest = 'f:o:o\nbar\rbaz\\foo\nbar\rbaz\\', 114 | msg = 115 | 'MESSAGE\ndestination:' + 116 | 'f\\co\\co\\nbar\\rbaz\\\\foo\\nbar\\rbaz\\\\' + 117 | '\nmessage-id:456\n\n\0'; 118 | 119 | expect( 120 | StompJs.FrameImpl.marshall({ 121 | command: 'MESSAGE', 122 | headers: { destination: dest, 'message-id': '456' }, 123 | body: '', 124 | escapeHeaderValues: true, 125 | }) 126 | ).toEqual(msg); 127 | }); 128 | 129 | it('marshal/unmarshall should support \\, \\n and \\r in header values with escaping', function () { 130 | const dest = 'f:o:o\nbar\rbaz\\foo\nbar\rbaz\\'; 131 | const command = 'MESSAGE'; 132 | const headers = { destination: dest, 'message-id': '456' }; 133 | const body = ''; 134 | 135 | const msg = StompJs.FrameImpl.marshall({ 136 | command: command, 137 | headers: headers, 138 | body: body, 139 | escapeHeaderValues: true, 140 | }); 141 | const frame = unmarshall(msg, true); 142 | 143 | expect(frame.headers).toEqual(headers); 144 | }); 145 | 146 | it('only the 1st value of repeated headers is used', function () { 147 | const msg = 'MESSAGE\ndestination: /queue/test\nfoo:World\nfoo:Hello\n\n\0'; 148 | 149 | expect(unmarshall(msg).headers['foo']).toEqual('World'); 150 | }); 151 | 152 | it('Content length of UTF-8 strings', function () { 153 | expect(0).toEqual(StompJs.FrameImpl.sizeOfUTF8()); 154 | expect(0).toEqual(StompJs.FrameImpl.sizeOfUTF8('')); 155 | expect(1).toEqual(StompJs.FrameImpl.sizeOfUTF8('a')); 156 | expect(2).toEqual(StompJs.FrameImpl.sizeOfUTF8('ф')); 157 | expect(3).toEqual(StompJs.FrameImpl.sizeOfUTF8('№')); 158 | expect(15).toEqual(StompJs.FrameImpl.sizeOfUTF8('1 a ф № @ ®')); 159 | }); 160 | }); 161 | -------------------------------------------------------------------------------- /spec/unit/heart-beat.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | These tests wrap a web socket and force introduces errors. 3 | In this case, the wrapper eats away pings. 4 | Typically, either side, when they are expecting pings, will wait for 2*heartbeat interval before closing. 5 | RabbitMQ does not support heartbeat intervals of less than 1000ms. 6 | So, altogether, these tests will each take slightly more than 2000ms each. 7 | 8 | The test cases in this file focus on verifying the behavior of a WebSocket client 9 | when handling heartbeats (ping mechanisms) in different scenarios. These tests aim 10 | to simulate various error conditions and edge cases surrounding heartbeats to ensure 11 | the client behaves correctly. 12 | 13 | Different modes for testing: 14 | - The tests are executed in two modes: 15 | - `native` mode (default WebSocket handling). 16 | - `web worker` mode, where heartbeats are managed by a web worker. 17 | - Each mode is validated by dynamically wrapping the WebSocket object to simulate 18 | conditions like missing pings or altered headers. 19 | */ 20 | 21 | function executeTestCases(useWebWorkerHeartbeats, mode) { 22 | describe(`Ping using (${mode})`, () => { 23 | let client; 24 | 25 | beforeEach(() => { 26 | client = stompClient(); 27 | if (useWebWorkerHeartbeats) { 28 | client.configure({ 29 | heartbeatStrategy: 'worker', 30 | }); 31 | } 32 | }); 33 | 34 | afterEach(async () => { 35 | await disconnectStomp(client); 36 | }); 37 | 38 | // Find length - 39 | const length = data => { 40 | return typeof data === 'string' ? data.length : data.byteLength; 41 | }; 42 | 43 | // See https://github.com/stomp-js/stompjs/issues/188 44 | it('Should allow server to not send heartbeat header', done => { 45 | overRideFactory( 46 | client, 47 | class extends WrapperWS { 48 | wrapOnMessage(ev) { 49 | const inComingFrame = parseFrame(ev.data); 50 | 51 | if (inComingFrame.command === 'CONNECTED') { 52 | const frame = StompJs.FrameImpl.fromRawFrame(inComingFrame, true); 53 | delete frame.headers['heart-beat']; 54 | ev = { data: frame.serialize() }; 55 | } 56 | 57 | super.wrapOnMessage(ev); 58 | } 59 | }, 60 | ); 61 | 62 | client.onConnect = () => { 63 | done(); 64 | }; 65 | 66 | client.activate(); 67 | }); 68 | 69 | const incomingPingTest = done => { 70 | client.heartbeatIncoming = 1000; 71 | client.heartbeatOutgoing = 0; 72 | 73 | overRideFactory( 74 | client, 75 | class extends WrapperWS { 76 | wrapOnMessage(ev) { 77 | // Eat away incoming ping 78 | if (length(ev.data) === 1) { 79 | console.log('Eating incoming ping'); 80 | return; 81 | } 82 | super.wrapOnMessage(ev); 83 | } 84 | }, 85 | ); 86 | 87 | client.onWebSocketClose = ev => { 88 | if (client.discardWebsocketOnCommFailure) { 89 | // Discarded socket is closed with a different set of codes. 90 | expect([1006, 4001]).toContain(ev.code); 91 | } 92 | done(); 93 | }; 94 | 95 | client.activate(); 96 | }; 97 | 98 | it('Should close connection when no incoming ping', incomingPingTest); 99 | 100 | describe('With discardWebsocketOnCommFailure', () => { 101 | beforeEach(() => { 102 | client.discardWebsocketOnCommFailure = true; 103 | }); 104 | 105 | it('Should close connection when no incoming ping', incomingPingTest); 106 | }); 107 | 108 | it('Should close connection when no outgoing ping', done => { 109 | client.heartbeatIncoming = 0; 110 | client.heartbeatOutgoing = 1000; 111 | 112 | overRideFactory( 113 | client, 114 | class extends WrapperWS { 115 | send(data) { 116 | // Eat away outgoing ping 117 | if (length(data) === 1) { 118 | console.log('Eating outgoing ping'); 119 | return; 120 | } 121 | super.send(data); 122 | } 123 | }, 124 | ); 125 | 126 | client.onWebSocketClose = ev => { 127 | done(); 128 | }; 129 | 130 | client.activate(); 131 | }); 132 | }); 133 | } 134 | 135 | executeTestCases(false, 'native'); 136 | 137 | if (TEST.testHeartBeatUsingWebWorkers) { 138 | executeTestCases(true, 'web worker'); 139 | } 140 | -------------------------------------------------------------------------------- /spec/unit/message.spec.js: -------------------------------------------------------------------------------- 1 | describe('Stomp Message', function () { 2 | let client; 3 | 4 | beforeEach(function () { 5 | client = stompClient(); 6 | }); 7 | 8 | afterEach(async function () { 9 | await disconnectStomp(client); 10 | }); 11 | 12 | it('Send and receive a message', function (done) { 13 | const body = randomText(); 14 | client.onConnect = function () { 15 | client.subscribe(TEST.destination, function (message) { 16 | expect(message.body).toEqual(body); 17 | client.deactivate(); 18 | 19 | done(); 20 | }); 21 | 22 | client.publish({ destination: TEST.destination, body: body }); 23 | }; 24 | client.activate(); 25 | }); 26 | 27 | it('Send and receive non-ASCII UTF8 text', function (done) { 28 | // Text picked up from https://github.com/stomp-js/stomp-websocket/pull/46 29 | const body = 'Älä sinä yhtään and السابق'; 30 | client.onConnect = function () { 31 | client.subscribe(TEST.destination, function (message) { 32 | expect(message.body).toEqual(body); 33 | client.deactivate(); 34 | 35 | done(); 36 | }); 37 | 38 | client.publish({ destination: TEST.destination, body: body }); 39 | }; 40 | client.activate(); 41 | }); 42 | 43 | it('Logs raw communication', function (done) { 44 | // Text picked up from https://github.com/stomp-js/stomp-websocket/pull/46 45 | const body = 'Älä sinä yhtään and السابق'; 46 | client.logRawCommunication = true; 47 | 48 | client.debug = jasmine.createSpy('debug'); 49 | 50 | client.onConnect = function () { 51 | client.subscribe(TEST.destination, function (message) { 52 | // matching entire frame is not feasible as the broker adds headers which will vary for each execution 53 | // So, just check presence of the body 54 | expect(client.debug.calls.mostRecent().args[0]).toMatch(body); 55 | 56 | client.deactivate(); 57 | 58 | done(); 59 | }); 60 | 61 | client.publish({ destination: TEST.destination, body: body }); 62 | expect(client.debug.calls.mostRecent().args[0]).toEqual( 63 | '>>> SEND\ndestination:/topic/chat.general\ncontent-length:37\n\nÄlä sinä yhtään and السابق' + 64 | '\0' 65 | ); 66 | }; 67 | client.activate(); 68 | }); 69 | 70 | it('Send and receive binary message', function (done) { 71 | const binaryBody = generateBinaryData(1); 72 | client.onConnect = function () { 73 | client.subscribe(TEST.destination, function (message) { 74 | expect(message.binaryBody.toString()).toEqual(binaryBody.toString()); 75 | client.deactivate(); 76 | 77 | done(); 78 | }); 79 | 80 | client.publish({ destination: TEST.destination, binaryBody: binaryBody }); 81 | }; 82 | client.activate(); 83 | }); 84 | 85 | it('Send and receive text/binary messages', function (done) { 86 | const binaryData = generateBinaryData(1); 87 | const textData = 'Hello World'; 88 | let numCalls = 0; 89 | 90 | client.onConnect = function () { 91 | client.subscribe(TEST.destination, function (message) { 92 | if (++numCalls === 1) { 93 | // First message should be binary 94 | expect(message.binaryBody.toString()).toEqual(binaryData.toString()); 95 | return; 96 | } 97 | // Second message should be text 98 | expect(message.body).toEqual(textData); 99 | 100 | client.deactivate(); 101 | 102 | done(); 103 | }); 104 | 105 | // First a binary message 106 | client.publish({ 107 | destination: TEST.destination, 108 | binaryBody: binaryData, 109 | headers: { 'content-type': 'application/octet-stream' }, 110 | }); 111 | 112 | // Followed by a text message with a little gap 113 | setTimeout(() => { 114 | client.publish({ destination: TEST.destination, body: textData }); 115 | }, 20); 116 | }; 117 | client.activate(); 118 | }); 119 | 120 | it('Send and receive a message with a JSON body', function (done) { 121 | const payload = { text: 'hello', bool: true, value: randomText() }; 122 | client.onConnect = function () { 123 | client.subscribe(TEST.destination, function (message) { 124 | const res = JSON.parse(message.body); 125 | expect(res.text).toEqual(payload.text); 126 | expect(res.bool).toEqual(payload.bool); 127 | expect(res.value).toEqual(payload.value); 128 | client.deactivate(); 129 | 130 | done(); 131 | }); 132 | 133 | client.publish({ 134 | destination: TEST.destination, 135 | body: JSON.stringify(payload), 136 | }); 137 | }; 138 | client.activate(); 139 | }); 140 | 141 | it('Should allow skipping content length header', function (done) { 142 | const body = 'Hello, world'; 143 | 144 | client.onConnect = function () { 145 | client.subscribe(TEST.destination, function (message) { 146 | expect(message.body).toEqual(body); 147 | client.deactivate(); 148 | 149 | done(); 150 | }); 151 | 152 | const spy = spyOn(client.webSocket, 'send').and.callThrough(); 153 | 154 | client.publish({ 155 | destination: TEST.destination, 156 | body: body, 157 | skipContentLengthHeader: true, 158 | }); 159 | 160 | const rawChunk = spy.calls.first().args[0]; 161 | expect(rawChunk).not.toMatch('content-length'); 162 | }; 163 | client.activate(); 164 | }); 165 | 166 | it('Should always add content length header for binary messages', function (done) { 167 | const binaryBody = new Uint8Array([0]); 168 | 169 | client.onConnect = function () { 170 | client.subscribe(TEST.destination, function (message) { 171 | client.deactivate(); 172 | 173 | done(); 174 | }); 175 | 176 | const spy = spyOn(client.webSocket, 'send').and.callThrough(); 177 | 178 | client.publish({ 179 | destination: TEST.destination, 180 | binaryBody: binaryBody, 181 | skipContentLengthHeader: true, 182 | }); 183 | 184 | const rawChunk = spy.calls.first().args[0]; 185 | // The frame is binary so needs to be converted to String before RegEx can be used 186 | const chunkAsString = new TextDecoder().decode(rawChunk); 187 | expect(chunkAsString).toMatch('content-length'); 188 | }; 189 | client.activate(); 190 | }); 191 | 192 | describe('Large data', function () { 193 | it('Large text message', function (done) { 194 | const body = generateTextData(TEST.largeMessageSize); 195 | client.debug = function () {}; // disable for this test 196 | client.onConnect = function () { 197 | client.subscribe(TEST.destination, function (message) { 198 | expect(message.body).toEqual(body); 199 | client.deactivate(); 200 | 201 | done(); 202 | }); 203 | 204 | client.publish({ destination: TEST.destination, body: body }); 205 | }; 206 | client.activate(); 207 | }); 208 | 209 | it('Large binary message', function (done) { 210 | const binaryBody = generateBinaryData(TEST.largeMessageSize); 211 | client.onConnect = function () { 212 | client.subscribe(TEST.destination, function (message) { 213 | expect(message.binaryBody.toString()).toEqual(binaryBody.toString()); 214 | client.deactivate(); 215 | 216 | done(); 217 | }); 218 | 219 | client.publish({ 220 | destination: TEST.destination, 221 | binaryBody: binaryBody, 222 | }); 223 | }; 224 | client.activate(); 225 | }); 226 | }); 227 | }); 228 | -------------------------------------------------------------------------------- /spec/unit/receipts.spec.js: -------------------------------------------------------------------------------- 1 | describe('Stomp Receipts', function () { 2 | let client; 3 | 4 | beforeEach(function () { 5 | client = stompClient(); 6 | }); 7 | 8 | afterEach(async function () { 9 | await disconnectStomp(client); 10 | }); 11 | 12 | it('Should confirm subscription using receipt', function (done) { 13 | const msg = 'Is anybody out there?'; 14 | 15 | client.onConnect = function () { 16 | const receiptId = randomText(); 17 | 18 | client.watchForReceipt(receiptId, function () { 19 | client.publish({ destination: TEST.destination, body: msg }); 20 | }); 21 | 22 | client.subscribe( 23 | TEST.destination, 24 | function (frame) { 25 | expect(frame.body).toEqual(msg); 26 | 27 | done(); 28 | }, 29 | { receipt: receiptId } 30 | ); 31 | }; 32 | client.activate(); 33 | }); 34 | 35 | it('Should confirm send using receipt', function (done) { 36 | const msg = 'Is anybody out there?'; 37 | 38 | client.onConnect = function () { 39 | const receiptId = randomText(); 40 | 41 | client.watchForReceipt(receiptId, function () { 42 | done(); 43 | }); 44 | client.publish({ 45 | destination: TEST.destination, 46 | headers: { receipt: receiptId }, 47 | body: msg, 48 | }); 49 | }; 50 | client.activate(); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /spec/unit/split-frames.spec.js: -------------------------------------------------------------------------------- 1 | describe('splitLargeFrames', function () { 2 | let client; 3 | 4 | beforeEach(function () { 5 | client = stompClient(); 6 | 7 | client.configure({ 8 | splitLargeFrames: true, 9 | }); 10 | }); 11 | 12 | afterEach(async function () { 13 | await disconnectStomp(client); 14 | }); 15 | 16 | /* 17 | This test is bit hacky. This mode does not work with RabbitMQ, so during the test 18 | the WebSocket's send function is hijacked with a spy, check for expectations 19 | and then restored back. 20 | */ 21 | it('Should split large text frames', function (done) { 22 | const body = generateTextData(20); 23 | 24 | client.onConnect = function () { 25 | const origSend = client.webSocket.send; 26 | const spyWebSocketSend = spyOn(client.webSocket, 'send'); 27 | 28 | client.publish({ destination: TEST.destination, body: body }); 29 | expect(spyWebSocketSend).toHaveBeenCalledTimes(3); 30 | expect(spyWebSocketSend.calls.first().args[0].length).toEqual( 31 | client.maxWebSocketChunkSize 32 | ); 33 | expect(spyWebSocketSend.calls.mostRecent().args[0].length).toEqual(4156); 34 | 35 | // restore original send 36 | client.webSocket.send = origSend; 37 | done(); 38 | }; 39 | client.activate(); 40 | }); 41 | 42 | it('Should not split large binary messages', function (done) { 43 | const binaryBody = generateBinaryData(20); 44 | client.onConnect = function () { 45 | client.subscribe(TEST.destination, function (message) { 46 | expect(message.binaryBody.toString()).toEqual(binaryBody.toString()); 47 | 48 | done(); 49 | }); 50 | 51 | const spyWebSocketSend = spyOn( 52 | client.webSocket, 53 | 'send' 54 | ).and.callThrough(); 55 | client.publish({ destination: TEST.destination, binaryBody: binaryBody }); 56 | expect(spyWebSocketSend).toHaveBeenCalledTimes(1); 57 | expect(spyWebSocketSend.calls.first().args[0].length).not.toBeLessThan( 58 | 20 * 1024 59 | ); 60 | }; 61 | client.activate(); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /spec/unit/subscription.spec.js: -------------------------------------------------------------------------------- 1 | describe('Stomp Subscription', function () { 2 | let client; 3 | 4 | beforeEach(function () { 5 | client = stompClient(); 6 | }); 7 | 8 | afterEach(async function () { 9 | await disconnectStomp(client); 10 | }); 11 | 12 | it('Should receive messages sent to destination after subscribing', function (done) { 13 | const msg = 'Is anybody out there?'; 14 | 15 | client.onConnect = function () { 16 | client.subscribe(TEST.destination, function (frame) { 17 | expect(frame.body).toEqual(msg); 18 | 19 | done(); 20 | }); 21 | 22 | client.publish({ destination: TEST.destination, body: msg }); 23 | }; 24 | client.activate(); 25 | }); 26 | 27 | it('Should tolerate exceptions thrown in a message handler', function (done) { 28 | const msg = 'Message'; 29 | let numMessages = 0; 30 | 31 | client.onConnect = function () { 32 | client.subscribe(TEST.destination, function (frame) { 33 | numMessages++; 34 | // cause an unhandled exception 35 | throw new Error('Special Error'); 36 | }); 37 | 38 | client.publish({ destination: TEST.destination, body: msg }); 39 | client.publish({ destination: TEST.destination, body: msg }); 40 | 41 | setTimeout(() => { 42 | expect(numMessages).toBe(2); 43 | done(); 44 | }, 1000); 45 | }; 46 | client.activate(); 47 | }); 48 | 49 | it('Should receive messages with special chars in headers', function (done) { 50 | const msg = 'Is anybody out there?'; 51 | const cust = 'f:o:o\nbar\rbaz\\foo\nbar\rbaz\\'; 52 | 53 | client.onConnect = function () { 54 | // This is a test intended for version 1.2 of STOMP client 55 | if (client.connectedVersion !== StompJs.Versions.V1_2) { 56 | client.debug( 57 | `Skipping 1.2 specific test, current STOMP version: ${client.version}` 58 | ); 59 | done(); 60 | return; 61 | } 62 | 63 | client.subscribe(TEST.destination, function (frame) { 64 | expect(frame.body).toEqual(msg); 65 | expect(frame.headers.cust).toEqual(cust); 66 | 67 | done(); 68 | }); 69 | 70 | client.publish({ 71 | destination: TEST.destination, 72 | headers: { cust: cust }, 73 | body: msg, 74 | }); 75 | }; 76 | client.activate(); 77 | }); 78 | 79 | it('Should no longer receive messages after unsubscribing to destination', function (done) { 80 | const msg1 = 'Calling all cars!'; 81 | let subscription1 = null, 82 | subscription2 = null; 83 | 84 | client.onConnect = function () { 85 | subscription1 = client.subscribe(TEST.destination, function (frame) { 86 | // Should not have received message 87 | expect(false).toBe(true); 88 | }); 89 | 90 | subscription2 = client.subscribe(TEST.destination, function (frame) { 91 | expect(frame.body).toEqual(msg1); 92 | 93 | done(); 94 | }); 95 | 96 | subscription1.unsubscribe(); 97 | client.publish({ destination: TEST.destination, body: msg1 }); 98 | }; 99 | client.activate(); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /spec/unit/transaction.spec.js: -------------------------------------------------------------------------------- 1 | describe('Stomp Transaction', function () { 2 | let client; 3 | 4 | beforeEach(function () { 5 | client = stompClient(); 6 | }); 7 | 8 | afterEach(async function () { 9 | await disconnectStomp(client); 10 | }); 11 | 12 | it('Send a message in a transaction and abort', function (done) { 13 | const body = randomText(); 14 | const body2 = randomText(); 15 | 16 | client.onConnect = function () { 17 | client.subscribe(TEST.destination, function (message) { 18 | // we should receive the 2nd message outside the transaction 19 | expect(message.body).toEqual(body2); 20 | 21 | done(); 22 | }); 23 | 24 | const tx = client.begin('txid_' + Math.random()); 25 | client.publish({ 26 | destination: TEST.destination, 27 | headers: { transaction: tx.id }, 28 | body: body, 29 | }); 30 | tx.abort(); 31 | client.publish({ destination: TEST.destination, body: body2 }); 32 | }; 33 | client.activate(); 34 | }); 35 | 36 | it('Send a message in a transaction and commit', function (done) { 37 | const body = randomText(); 38 | 39 | client.onConnect = function () { 40 | client.subscribe(TEST.destination, function (message) { 41 | expect(message.body).toEqual(body); 42 | 43 | done(); 44 | }); 45 | const tx = client.begin(); 46 | client.publish({ 47 | destination: TEST.destination, 48 | headers: { transaction: tx.id }, 49 | body: body, 50 | }); 51 | tx.commit(); 52 | }; 53 | client.activate(); 54 | }); 55 | 56 | it('Send a message outside a transaction and abort', function (done) { 57 | const body = randomText(); 58 | 59 | client.onConnect = function () { 60 | client.subscribe(TEST.destination, function (message) { 61 | // we should receive the message since it was sent outside the transaction 62 | expect(message.body).toEqual(body); 63 | 64 | done(); 65 | }); 66 | 67 | const tx = client.begin(); 68 | client.publish({ destination: TEST.destination, body: body }); 69 | tx.abort(); 70 | }; 71 | client.activate(); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /spec/unit/web-socket-state.spec.js: -------------------------------------------------------------------------------- 1 | describe('StompSocketState', function () { 2 | it('use same constant values as WebSocket', function () { 3 | const StompSocketState = StompJs.StompSocketState; 4 | 5 | expect(StompSocketState.CLOSED).toEqual(WebSocket.CLOSED); 6 | expect(StompSocketState.CLOSING).toEqual(WebSocket.CLOSING); 7 | expect(StompSocketState.CONNECTING).toEqual(WebSocket.CONNECTING); 8 | expect(StompSocketState.OPEN).toEqual(WebSocket.OPEN); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/augment-websocket.ts: -------------------------------------------------------------------------------- 1 | import { IStompSocket } from './types.js'; 2 | 3 | /** 4 | * @internal 5 | */ 6 | export function augmentWebsocket( 7 | webSocket: IStompSocket, 8 | debug: (msg: string) => void 9 | ) { 10 | webSocket.terminate = function () { 11 | const noOp = () => {}; 12 | 13 | // set all callbacks to no op 14 | this.onerror = noOp; 15 | this.onmessage = noOp; 16 | this.onopen = noOp; 17 | 18 | const ts = new Date(); 19 | const id = Math.random().toString().substring(2, 8); // A simulated id 20 | 21 | const origOnClose = this.onclose; 22 | 23 | // Track delay in actual closure of the socket 24 | this.onclose = closeEvent => { 25 | const delay = new Date().getTime() - ts.getTime(); 26 | debug( 27 | `Discarded socket (#${id}) closed after ${delay}ms, with code/reason: ${closeEvent.code}/${closeEvent.reason}` 28 | ); 29 | }; 30 | 31 | this.close(); 32 | 33 | origOnClose?.call(webSocket, { 34 | code: 4001, 35 | reason: `Quick discarding socket (#${id}) without waiting for the shutdown sequence.`, 36 | wasClean: false, 37 | }); 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /src/byte.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Some byte values, used as per STOMP specifications. 3 | * 4 | * Part of `@stomp/stompjs`. 5 | * 6 | * @internal 7 | */ 8 | export const BYTE = { 9 | // LINEFEED byte (octet 10) 10 | LF: '\x0A', 11 | // NULL byte (octet 0) 12 | NULL: '\x00', 13 | }; 14 | -------------------------------------------------------------------------------- /src/compatibility/heartbeat-info.ts: -------------------------------------------------------------------------------- 1 | import { CompatClient } from './compat-client.js'; 2 | 3 | /** 4 | * Part of `@stomp/stompjs`. 5 | * 6 | * @internal 7 | */ 8 | export class HeartbeatInfo { 9 | constructor(private client: CompatClient) {} 10 | 11 | get outgoing(): number { 12 | return this.client.heartbeatOutgoing; 13 | } 14 | 15 | set outgoing(value: number) { 16 | this.client.heartbeatOutgoing = value; 17 | } 18 | 19 | get incoming(): number { 20 | return this.client.heartbeatIncoming; 21 | } 22 | 23 | set incoming(value: number) { 24 | this.client.heartbeatIncoming = value; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/compatibility/stomp.ts: -------------------------------------------------------------------------------- 1 | import { Versions } from '../versions.js'; 2 | import { CompatClient } from './compat-client.js'; 3 | import { IStompSocket } from '../types.js'; 4 | 5 | /** 6 | * @internal 7 | */ 8 | declare const WebSocket: { 9 | prototype: IStompSocket; 10 | new (url: string, protocols?: string | string[]): IStompSocket; 11 | }; 12 | 13 | /** 14 | * STOMP Class, acts like a factory to create {@link Client}. 15 | * 16 | * Part of `@stomp/stompjs`. 17 | * 18 | * **Deprecated** 19 | * 20 | * It will be removed in next major version. Please switch to {@link Client}. 21 | */ 22 | export class Stomp { 23 | /** 24 | * In case you need to use a non standard class for WebSocket. 25 | * 26 | * For example when using within NodeJS environment: 27 | * 28 | * ```javascript 29 | * StompJs = require('../../esm5/'); 30 | * Stomp = StompJs.Stomp; 31 | * Stomp.WebSocketClass = require('websocket').w3cwebsocket; 32 | * ``` 33 | * 34 | * **Deprecated** 35 | * 36 | * 37 | * It will be removed in next major version. Please switch to {@link Client} 38 | * using [Client#webSocketFactory]{@link Client#webSocketFactory}. 39 | */ 40 | // tslint:disable-next-line:variable-name 41 | public static WebSocketClass: any = null; 42 | 43 | /** 44 | * This method creates a WebSocket client that is connected to 45 | * the STOMP server located at the url. 46 | * 47 | * ```javascript 48 | * var url = "ws://localhost:61614/stomp"; 49 | * var client = Stomp.client(url); 50 | * ``` 51 | * 52 | * **Deprecated** 53 | * 54 | * It will be removed in next major version. Please switch to {@link Client} 55 | * using [Client#brokerURL]{@link Client#brokerURL}. 56 | */ 57 | public static client(url: string, protocols?: string[]): CompatClient { 58 | // This is a hack to allow another implementation than the standard 59 | // HTML5 WebSocket class. 60 | // 61 | // It is possible to use another class by calling 62 | // 63 | // Stomp.WebSocketClass = MozWebSocket 64 | // 65 | // *prior* to call `Stomp.client()`. 66 | // 67 | // This hack is deprecated and `Stomp.over()` method should be used 68 | // instead. 69 | 70 | // See remarks on the function Stomp.over 71 | if (protocols == null) { 72 | protocols = Versions.default.protocolVersions(); 73 | } 74 | const wsFn = () => { 75 | const klass = Stomp.WebSocketClass || WebSocket; 76 | return new klass(url, protocols); 77 | }; 78 | 79 | return new CompatClient(wsFn); 80 | } 81 | 82 | /** 83 | * This method is an alternative to [Stomp#client]{@link Stomp#client} to let the user 84 | * specify the WebSocket to use (either a standard HTML5 WebSocket or 85 | * a similar object). 86 | * 87 | * In order to support reconnection, the function Client._connect should be callable more than once. 88 | * While reconnecting 89 | * a new instance of underlying transport (TCP Socket, WebSocket or SockJS) will be needed. So, this function 90 | * alternatively allows passing a function that should return a new instance of the underlying socket. 91 | * 92 | * ```javascript 93 | * var client = Stomp.over(function(){ 94 | * return new WebSocket('ws://localhost:15674/ws') 95 | * }); 96 | * ``` 97 | * 98 | * **Deprecated** 99 | * 100 | * It will be removed in next major version. Please switch to {@link Client} 101 | * using [Client#webSocketFactory]{@link Client#webSocketFactory}. 102 | */ 103 | public static over(ws: any): CompatClient { 104 | let wsFn: () => any; 105 | 106 | if (typeof ws === 'function') { 107 | wsFn = ws; 108 | } else { 109 | console.warn( 110 | 'Stomp.over did not receive a factory, auto reconnect will not work. ' + 111 | 'Please see https://stomp-js.github.io/api-docs/latest/classes/Stomp.html#over' 112 | ); 113 | wsFn = () => ws; 114 | } 115 | 116 | return new CompatClient(wsFn); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/frame-impl.ts: -------------------------------------------------------------------------------- 1 | import { BYTE } from './byte.js'; 2 | import type { IFrame } from './i-frame.js'; 3 | import { StompHeaders } from './stomp-headers.js'; 4 | import { IRawFrameType } from './types.js'; 5 | 6 | /** 7 | * Frame class represents a STOMP frame. 8 | * 9 | * @internal 10 | */ 11 | export class FrameImpl implements IFrame { 12 | /** 13 | * STOMP Command 14 | */ 15 | public command: string; 16 | 17 | /** 18 | * Headers, key value pairs. 19 | */ 20 | public headers: StompHeaders; 21 | 22 | /** 23 | * Is this frame binary (based on whether body/binaryBody was passed when creating this frame). 24 | */ 25 | public isBinaryBody: boolean; 26 | 27 | /** 28 | * body of the frame 29 | */ 30 | get body(): string { 31 | if (!this._body && this.isBinaryBody) { 32 | this._body = new TextDecoder().decode(this._binaryBody); 33 | } 34 | return this._body || ''; 35 | } 36 | private _body: string | undefined; 37 | 38 | /** 39 | * body as Uint8Array 40 | */ 41 | get binaryBody(): Uint8Array { 42 | if (!this._binaryBody && !this.isBinaryBody) { 43 | this._binaryBody = new TextEncoder().encode(this._body); 44 | } 45 | // At this stage it will definitely have a valid value 46 | return this._binaryBody as Uint8Array; 47 | } 48 | private _binaryBody: Uint8Array | undefined; 49 | 50 | private escapeHeaderValues: boolean; 51 | private skipContentLengthHeader: boolean; 52 | 53 | /** 54 | * Frame constructor. `command`, `headers` and `body` are available as properties. 55 | * 56 | * @internal 57 | */ 58 | constructor(params: { 59 | command: string; 60 | headers?: StompHeaders; 61 | body?: string; 62 | binaryBody?: Uint8Array; 63 | escapeHeaderValues?: boolean; 64 | skipContentLengthHeader?: boolean; 65 | }) { 66 | const { 67 | command, 68 | headers, 69 | body, 70 | binaryBody, 71 | escapeHeaderValues, 72 | skipContentLengthHeader, 73 | } = params; 74 | this.command = command; 75 | this.headers = (Object as any).assign({}, headers || {}); 76 | 77 | if (binaryBody) { 78 | this._binaryBody = binaryBody; 79 | this.isBinaryBody = true; 80 | } else { 81 | this._body = body || ''; 82 | this.isBinaryBody = false; 83 | } 84 | this.escapeHeaderValues = escapeHeaderValues || false; 85 | this.skipContentLengthHeader = skipContentLengthHeader || false; 86 | } 87 | 88 | /** 89 | * deserialize a STOMP Frame from raw data. 90 | * 91 | * @internal 92 | */ 93 | public static fromRawFrame( 94 | rawFrame: IRawFrameType, 95 | escapeHeaderValues: boolean 96 | ): FrameImpl { 97 | const headers: StompHeaders = {}; 98 | const trim = (str: string): string => str.replace(/^\s+|\s+$/g, ''); 99 | 100 | // In case of repeated headers, as per standards, first value need to be used 101 | for (const header of rawFrame.headers.reverse()) { 102 | const idx = header.indexOf(':'); 103 | 104 | const key = trim(header[0]); 105 | let value = trim(header[1]); 106 | 107 | if ( 108 | escapeHeaderValues && 109 | rawFrame.command !== 'CONNECT' && 110 | rawFrame.command !== 'CONNECTED' 111 | ) { 112 | value = FrameImpl.hdrValueUnEscape(value); 113 | } 114 | 115 | headers[key] = value; 116 | } 117 | 118 | return new FrameImpl({ 119 | command: rawFrame.command as string, 120 | headers, 121 | binaryBody: rawFrame.binaryBody, 122 | escapeHeaderValues, 123 | }); 124 | } 125 | 126 | /** 127 | * @internal 128 | */ 129 | public toString(): string { 130 | return this.serializeCmdAndHeaders(); 131 | } 132 | 133 | /** 134 | * serialize this Frame in a format suitable to be passed to WebSocket. 135 | * If the body is string the output will be string. 136 | * If the body is binary (i.e. of type Unit8Array) it will be serialized to ArrayBuffer. 137 | * 138 | * @internal 139 | */ 140 | public serialize(): string | ArrayBuffer { 141 | const cmdAndHeaders = this.serializeCmdAndHeaders(); 142 | 143 | if (this.isBinaryBody) { 144 | return FrameImpl.toUnit8Array( 145 | cmdAndHeaders, 146 | this._binaryBody as Uint8Array 147 | ).buffer; 148 | } else { 149 | return cmdAndHeaders + this._body + BYTE.NULL; 150 | } 151 | } 152 | 153 | private serializeCmdAndHeaders(): string { 154 | const lines = [this.command]; 155 | if (this.skipContentLengthHeader) { 156 | delete this.headers['content-length']; 157 | } 158 | 159 | for (const name of Object.keys(this.headers || {})) { 160 | const value = this.headers[name]; 161 | if ( 162 | this.escapeHeaderValues && 163 | this.command !== 'CONNECT' && 164 | this.command !== 'CONNECTED' 165 | ) { 166 | lines.push(`${name}:${FrameImpl.hdrValueEscape(`${value}`)}`); 167 | } else { 168 | lines.push(`${name}:${value}`); 169 | } 170 | } 171 | if ( 172 | this.isBinaryBody || 173 | (!this.isBodyEmpty() && !this.skipContentLengthHeader) 174 | ) { 175 | lines.push(`content-length:${this.bodyLength()}`); 176 | } 177 | return lines.join(BYTE.LF) + BYTE.LF + BYTE.LF; 178 | } 179 | 180 | private isBodyEmpty(): boolean { 181 | return this.bodyLength() === 0; 182 | } 183 | 184 | private bodyLength(): number { 185 | const binaryBody = this.binaryBody; 186 | return binaryBody ? binaryBody.length : 0; 187 | } 188 | 189 | /** 190 | * Compute the size of a UTF-8 string by counting its number of bytes 191 | * (and not the number of characters composing the string) 192 | */ 193 | private static sizeOfUTF8(s: string): number { 194 | return s ? new TextEncoder().encode(s).length : 0; 195 | } 196 | 197 | private static toUnit8Array( 198 | cmdAndHeaders: string, 199 | binaryBody: Uint8Array 200 | ): Uint8Array { 201 | const uint8CmdAndHeaders = new TextEncoder().encode(cmdAndHeaders); 202 | const nullTerminator = new Uint8Array([0]); 203 | const uint8Frame = new Uint8Array( 204 | uint8CmdAndHeaders.length + binaryBody.length + nullTerminator.length 205 | ); 206 | 207 | uint8Frame.set(uint8CmdAndHeaders); 208 | uint8Frame.set(binaryBody, uint8CmdAndHeaders.length); 209 | uint8Frame.set( 210 | nullTerminator, 211 | uint8CmdAndHeaders.length + binaryBody.length 212 | ); 213 | 214 | return uint8Frame; 215 | } 216 | /** 217 | * Serialize a STOMP frame as per STOMP standards, suitable to be sent to the STOMP broker. 218 | * 219 | * @internal 220 | */ 221 | public static marshall(params: { 222 | command: string; 223 | headers?: StompHeaders; 224 | body?: string; 225 | binaryBody?: Uint8Array; 226 | escapeHeaderValues?: boolean; 227 | skipContentLengthHeader?: boolean; 228 | }) { 229 | const frame = new FrameImpl(params); 230 | return frame.serialize(); 231 | } 232 | 233 | /** 234 | * Escape header values 235 | */ 236 | private static hdrValueEscape(str: string): string { 237 | return str 238 | .replace(/\\/g, '\\\\') 239 | .replace(/\r/g, '\\r') 240 | .replace(/\n/g, '\\n') 241 | .replace(/:/g, '\\c'); 242 | } 243 | 244 | /** 245 | * UnEscape header values 246 | */ 247 | private static hdrValueUnEscape(str: string): string { 248 | return str 249 | .replace(/\\r/g, '\r') 250 | .replace(/\\n/g, '\n') 251 | .replace(/\\c/g, ':') 252 | .replace(/\\\\/g, '\\'); 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/i-frame.ts: -------------------------------------------------------------------------------- 1 | import { StompHeaders } from './stomp-headers.js'; 2 | 3 | /** 4 | * It represents a STOMP frame. Many of the callbacks pass an IFrame received from 5 | * the STOMP broker. For advanced usage you might need to access [headers]{@link IFrame#headers}. 6 | * 7 | * Part of `@stomp/stompjs`. 8 | * 9 | * {@link IMessage} is an extended IFrame. 10 | */ 11 | export interface IFrame { 12 | /** 13 | * STOMP Command 14 | */ 15 | command: string; 16 | 17 | /** 18 | * Headers, key value pairs. 19 | */ 20 | headers: StompHeaders; 21 | 22 | /** 23 | * Is this frame binary (based on whether body/binaryBody was passed when creating this frame). 24 | */ 25 | isBinaryBody: boolean; 26 | 27 | /** 28 | * body of the frame as string 29 | */ 30 | readonly body: string; 31 | 32 | /** 33 | * body as Uint8Array 34 | */ 35 | readonly binaryBody: Uint8Array; 36 | } 37 | 38 | /** 39 | * Alias for {@link IFrame} 40 | */ 41 | export type Frame = IFrame; 42 | -------------------------------------------------------------------------------- /src/i-message.ts: -------------------------------------------------------------------------------- 1 | import type { IFrame } from './i-frame.js'; 2 | import { StompHeaders } from './stomp-headers.js'; 3 | 4 | /** 5 | * Instance of Message will be passed to [subscription callback]{@link Client#subscribe} 6 | * and [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}. 7 | * Since it is an extended {@link IFrame}, you can access [headers]{@link IFrame#headers} 8 | * and [body]{@link IFrame#body} as properties. 9 | * 10 | * Part of `@stomp/stompjs`. 11 | * 12 | * See [Client#subscribe]{@link Client#subscribe} for example. 13 | */ 14 | export interface IMessage extends IFrame { 15 | /** 16 | * When subscribing with manual acknowledgement, call this method on the message to ACK the message. 17 | * 18 | * See [Client#ack]{@link Client#ack} for an example. 19 | */ 20 | ack: (headers?: StompHeaders) => void; 21 | 22 | /** 23 | * When subscribing with manual acknowledgement, call this method on the message to NACK the message. 24 | * 25 | * See [Client#nack]{@link Client#nack} for an example. 26 | */ 27 | nack: (headers?: StompHeaders) => void; 28 | } 29 | 30 | /** 31 | * Aliased to {@link IMessage}. 32 | * 33 | * Part of `@stomp/stompjs`. 34 | */ 35 | export type Message = IMessage; 36 | -------------------------------------------------------------------------------- /src/i-transaction.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A Transaction is created by calling [Client#begin]{@link Client#begin} 3 | * 4 | * Part of `@stomp/stompjs`. 5 | */ 6 | export interface ITransaction { 7 | /** 8 | * You will need to access this to send, ack, or nack within this transaction. 9 | */ 10 | id: string; 11 | 12 | /** 13 | * Commit this transaction. See [Client#commit]{@link Client#commit} for an example. 14 | */ 15 | commit: () => void; 16 | 17 | /** 18 | * Abort this transaction. See [Client#abort]{@link Client#abort} for an example. 19 | */ 20 | abort: () => void; 21 | } 22 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client.js'; 2 | export * from './frame-impl.js'; 3 | export * from './i-frame.js'; 4 | export * from './i-message.js'; 5 | export * from './parser.js'; 6 | export * from './stomp-config.js'; 7 | export * from './stomp-headers.js'; 8 | export * from './stomp-subscription.js'; 9 | export * from './i-transaction.js'; 10 | export * from './types.js'; 11 | export * from './versions.js'; 12 | 13 | // Compatibility code 14 | export * from './compatibility/compat-client.js'; 15 | export * from './compatibility/stomp.js'; 16 | -------------------------------------------------------------------------------- /src/parser.ts: -------------------------------------------------------------------------------- 1 | import { IRawFrameType } from './types.js'; 2 | 3 | /** 4 | * @internal 5 | */ 6 | const NULL = 0; 7 | /** 8 | * @internal 9 | */ 10 | const LF = 10; 11 | /** 12 | * @internal 13 | */ 14 | const CR = 13; 15 | /** 16 | * @internal 17 | */ 18 | const COLON = 58; 19 | 20 | /** 21 | * This is an evented, rec descent parser. 22 | * A stream of Octets can be passed and whenever it recognizes 23 | * a complete Frame or an incoming ping it will invoke the registered callbacks. 24 | * 25 | * All incoming Octets are fed into _onByte function. 26 | * Depending on current state the _onByte function keeps changing. 27 | * Depending on the state it keeps accumulating into _token and _results. 28 | * State is indicated by current value of _onByte, all states are named as _collect. 29 | * 30 | * STOMP standards https://stomp.github.io/stomp-specification-1.2.html 31 | * imply that all lengths are considered in bytes (instead of string lengths). 32 | * So, before actual parsing, if the incoming data is String it is converted to Octets. 33 | * This allows faithful implementation of the protocol and allows NULL Octets to be present in the body. 34 | * 35 | * There is no peek function on the incoming data. 36 | * When a state change occurs based on an Octet without consuming the Octet, 37 | * the Octet, after state change, is fed again (_reinjectByte). 38 | * This became possible as the state change can be determined by inspecting just one Octet. 39 | * 40 | * There are two modes to collect the body, if content-length header is there then it by counting Octets 41 | * otherwise it is determined by NULL terminator. 42 | * 43 | * Following the standards, the command and headers are converted to Strings 44 | * and the body is returned as Octets. 45 | * Headers are returned as an array and not as Hash - to allow multiple occurrence of an header. 46 | * 47 | * This parser does not use Regular Expressions as that can only operate on Strings. 48 | * 49 | * It handles if multiple STOMP frames are given as one chunk, a frame is split into multiple chunks, or 50 | * any combination there of. The parser remembers its state (any partial frame) and continues when a new chunk 51 | * is pushed. 52 | * 53 | * Typically the higher level function will convert headers to Hash, handle unescaping of header values 54 | * (which is protocol version specific), and convert body to text. 55 | * 56 | * Check the parser.spec.js to understand cases that this parser is supposed to handle. 57 | * 58 | * Part of `@stomp/stompjs`. 59 | * 60 | * @internal 61 | */ 62 | export class Parser { 63 | private readonly _encoder = new TextEncoder(); 64 | private readonly _decoder = new TextDecoder(); 65 | 66 | // @ts-ignore - it always has a value 67 | private _results: IRawFrameType; 68 | 69 | private _token: number[] = []; 70 | private _headerKey: string | undefined; 71 | private _bodyBytesRemaining: number | undefined; 72 | 73 | // @ts-ignore - it always has a value 74 | private _onByte: (byte: number) => void; 75 | 76 | public constructor( 77 | public onFrame: (rawFrame: IRawFrameType) => void, 78 | public onIncomingPing: () => void 79 | ) { 80 | this._initState(); 81 | } 82 | 83 | public parseChunk( 84 | segment: string | ArrayBuffer, 85 | appendMissingNULLonIncoming: boolean = false 86 | ) { 87 | let chunk: Uint8Array; 88 | 89 | if (typeof segment === 'string') { 90 | chunk = this._encoder.encode(segment); 91 | } else { 92 | chunk = new Uint8Array(segment); 93 | } 94 | 95 | // See https://github.com/stomp-js/stompjs/issues/89 96 | // Remove when underlying issue is fixed. 97 | // 98 | // Send a NULL byte, if the last byte of a Text frame was not NULL.F 99 | if (appendMissingNULLonIncoming && chunk[chunk.length - 1] !== 0) { 100 | const chunkWithNull = new Uint8Array(chunk.length + 1); 101 | chunkWithNull.set(chunk, 0); 102 | chunkWithNull[chunk.length] = 0; 103 | chunk = chunkWithNull; 104 | } 105 | 106 | // tslint:disable-next-line:prefer-for-of 107 | for (let i = 0; i < chunk.length; i++) { 108 | const byte = chunk[i]; 109 | this._onByte(byte); 110 | } 111 | } 112 | 113 | // The following implements a simple Rec Descent Parser. 114 | // The grammar is simple and just one byte tells what should be the next state 115 | 116 | private _collectFrame(byte: number): void { 117 | if (byte === NULL) { 118 | // Ignore 119 | return; 120 | } 121 | if (byte === CR) { 122 | // Ignore CR 123 | return; 124 | } 125 | if (byte === LF) { 126 | // Incoming Ping 127 | this.onIncomingPing(); 128 | return; 129 | } 130 | 131 | this._onByte = this._collectCommand; 132 | this._reinjectByte(byte); 133 | } 134 | 135 | private _collectCommand(byte: number): void { 136 | if (byte === CR) { 137 | // Ignore CR 138 | return; 139 | } 140 | if (byte === LF) { 141 | this._results.command = this._consumeTokenAsUTF8(); 142 | this._onByte = this._collectHeaders; 143 | return; 144 | } 145 | 146 | this._consumeByte(byte); 147 | } 148 | 149 | private _collectHeaders(byte: number): void { 150 | if (byte === CR) { 151 | // Ignore CR 152 | return; 153 | } 154 | if (byte === LF) { 155 | this._setupCollectBody(); 156 | return; 157 | } 158 | this._onByte = this._collectHeaderKey; 159 | this._reinjectByte(byte); 160 | } 161 | 162 | private _reinjectByte(byte: number) { 163 | this._onByte(byte); 164 | } 165 | 166 | private _collectHeaderKey(byte: number): void { 167 | if (byte === COLON) { 168 | this._headerKey = this._consumeTokenAsUTF8(); 169 | this._onByte = this._collectHeaderValue; 170 | return; 171 | } 172 | this._consumeByte(byte); 173 | } 174 | 175 | private _collectHeaderValue(byte: number): void { 176 | if (byte === CR) { 177 | // Ignore CR 178 | return; 179 | } 180 | if (byte === LF) { 181 | this._results.headers.push([ 182 | this._headerKey as string, 183 | this._consumeTokenAsUTF8(), 184 | ]); 185 | this._headerKey = undefined; 186 | this._onByte = this._collectHeaders; 187 | return; 188 | } 189 | this._consumeByte(byte); 190 | } 191 | 192 | private _setupCollectBody() { 193 | const contentLengthHeader = this._results.headers.filter( 194 | (header: [string, string]) => { 195 | return header[0] === 'content-length'; 196 | } 197 | )[0]; 198 | 199 | if (contentLengthHeader) { 200 | this._bodyBytesRemaining = parseInt(contentLengthHeader[1], 10); 201 | this._onByte = this._collectBodyFixedSize; 202 | } else { 203 | this._onByte = this._collectBodyNullTerminated; 204 | } 205 | } 206 | 207 | private _collectBodyNullTerminated(byte: number): void { 208 | if (byte === NULL) { 209 | this._retrievedBody(); 210 | return; 211 | } 212 | this._consumeByte(byte); 213 | } 214 | 215 | private _collectBodyFixedSize(byte: number): void { 216 | // It is post decrement, so that we discard the trailing NULL octet 217 | if ((this._bodyBytesRemaining as number)-- === 0) { 218 | this._retrievedBody(); 219 | return; 220 | } 221 | this._consumeByte(byte); 222 | } 223 | 224 | private _retrievedBody() { 225 | this._results.binaryBody = this._consumeTokenAsRaw(); 226 | 227 | try { 228 | this.onFrame(this._results); 229 | } catch (e) { 230 | console.log( 231 | `Ignoring an exception thrown by a frame handler. Original exception: `, 232 | e 233 | ); 234 | } 235 | 236 | this._initState(); 237 | } 238 | 239 | // Rec Descent Parser helpers 240 | 241 | private _consumeByte(byte: number) { 242 | this._token.push(byte); 243 | } 244 | 245 | private _consumeTokenAsUTF8() { 246 | return this._decoder.decode(this._consumeTokenAsRaw()); 247 | } 248 | 249 | private _consumeTokenAsRaw() { 250 | const rawResult = new Uint8Array(this._token); 251 | this._token = []; 252 | return rawResult; 253 | } 254 | 255 | private _initState() { 256 | this._results = { 257 | command: undefined, 258 | headers: [], 259 | binaryBody: undefined, 260 | }; 261 | 262 | this._token = []; 263 | this._headerKey = undefined; 264 | 265 | this._onByte = this._collectFrame; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/stomp-config.ts: -------------------------------------------------------------------------------- 1 | import { StompHeaders } from './stomp-headers.js'; 2 | import { 3 | ActivationState, 4 | TickerStrategy, 5 | closeEventCallbackType, 6 | debugFnType, 7 | frameCallbackType, 8 | messageCallbackType, 9 | ReconnectionTimeMode, 10 | wsErrorCallbackType, 11 | } from './types.js'; 12 | import { Versions } from './versions.js'; 13 | import { Client } from './client.js'; 14 | 15 | /** 16 | * Configuration options for STOMP Client, each key corresponds to 17 | * field by the same name in {@link Client}. This can be passed to 18 | * the constructor of {@link Client} or to [Client#configure]{@link Client#configure}. 19 | * 20 | * Part of `@stomp/stompjs`. 21 | */ 22 | export class StompConfig { 23 | /** 24 | * See [Client#brokerURL]{@link Client#brokerURL}. 25 | */ 26 | public brokerURL?: string; 27 | 28 | /** 29 | * See [Client#stompVersions]{@link Client#stompVersions}. 30 | */ 31 | public stompVersions?: Versions; 32 | 33 | /** 34 | * See [Client#webSocketFactory]{@link Client#webSocketFactory}. 35 | */ 36 | public webSocketFactory?: () => any; 37 | 38 | /** 39 | * See [Client#connectionTimeout]{@link Client#connectionTimeout}. 40 | */ 41 | public connectionTimeout?: number; 42 | 43 | /** 44 | * See [Client#reconnectDelay]{@link Client#reconnectDelay}. 45 | */ 46 | public reconnectDelay?: number; 47 | 48 | /** 49 | * See [Client#maxReconnectDelay]{@link Client#maxReconnectDelay} 50 | */ 51 | public maxReconnectDelay?: number; 52 | 53 | /** 54 | * See [Client#reconnectTimeMode]{@link Client#reconnectTimeMode} 55 | */ 56 | public reconnectTimeMode?: ReconnectionTimeMode; 57 | 58 | /** 59 | * See [Client#heartbeatIncoming]{@link Client#heartbeatIncoming}. 60 | */ 61 | public heartbeatIncoming?: number; 62 | 63 | /** 64 | * See [Client#heartbeatOutgoing]{@link Client#heartbeatOutgoing}. 65 | */ 66 | public heartbeatOutgoing?: number; 67 | 68 | /** 69 | * See [Client#heartbeatStrategy]{@link Client#heartbeatStrategy}. 70 | */ 71 | public heartbeatStrategy?: TickerStrategy; 72 | 73 | /** 74 | * See [Client#splitLargeFrames]{@link Client#splitLargeFrames}. 75 | */ 76 | public splitLargeFrames?: boolean; 77 | 78 | /** 79 | * See [Client#forceBinaryWSFrames]{@link Client#forceBinaryWSFrames}. 80 | */ 81 | public forceBinaryWSFrames?: boolean; 82 | 83 | /** 84 | * See [Client#appendMissingNULLonIncoming]{@link Client#appendMissingNULLonIncoming}. 85 | */ 86 | public appendMissingNULLonIncoming?: boolean; 87 | 88 | /** 89 | * See [Client#maxWebSocketChunkSize]{@link Client#maxWebSocketChunkSize}. 90 | */ 91 | public maxWebSocketChunkSize?: number; 92 | 93 | /** 94 | * See [Client#connectHeaders]{@link Client#connectHeaders}. 95 | */ 96 | public connectHeaders?: StompHeaders; 97 | 98 | /** 99 | * See [Client#disconnectHeaders]{@link Client#disconnectHeaders}. 100 | */ 101 | public disconnectHeaders?: StompHeaders; 102 | 103 | /** 104 | * See [Client#onUnhandledMessage]{@link Client#onUnhandledMessage}. 105 | */ 106 | public onUnhandledMessage?: messageCallbackType; 107 | 108 | /** 109 | * See [Client#onUnhandledReceipt]{@link Client#onUnhandledReceipt}. 110 | */ 111 | public onUnhandledReceipt?: frameCallbackType; 112 | 113 | /** 114 | * See [Client#onUnhandledFrame]{@link Client#onUnhandledFrame}. 115 | */ 116 | public onUnhandledFrame?: frameCallbackType; 117 | 118 | /** 119 | * See [Client#beforeConnect]{@link Client#beforeConnect}. 120 | */ 121 | public beforeConnect?: (client: Client) => void | Promise; 122 | 123 | /** 124 | * See [Client#onConnect]{@link Client#onConnect}. 125 | */ 126 | public onConnect?: frameCallbackType; 127 | 128 | /** 129 | * See [Client#onDisconnect]{@link Client#onDisconnect}. 130 | */ 131 | public onDisconnect?: frameCallbackType; 132 | 133 | /** 134 | * See [Client#onStompError]{@link Client#onStompError}. 135 | */ 136 | public onStompError?: frameCallbackType; 137 | 138 | /** 139 | * See [Client#onWebSocketClose]{@link Client#onWebSocketClose}. 140 | */ 141 | public onWebSocketClose?: closeEventCallbackType; 142 | 143 | /** 144 | * See [Client#onWebSocketError]{@link Client#onWebSocketError}. 145 | */ 146 | public onWebSocketError?: wsErrorCallbackType; 147 | 148 | /** 149 | * See [Client#logRawCommunication]{@link Client#logRawCommunication}. 150 | */ 151 | public logRawCommunication?: boolean; 152 | 153 | /** 154 | * See [Client#debug]{@link Client#debug}. 155 | */ 156 | public debug?: debugFnType; 157 | 158 | /** 159 | * See [Client#discardWebsocketOnCommFailure]{@link Client#discardWebsocketOnCommFailure}. 160 | */ 161 | public discardWebsocketOnCommFailure?: boolean; 162 | 163 | /** 164 | * See [Client#onChangeState]{@link Client#onChangeState}. 165 | */ 166 | public onChangeState?: (state: ActivationState) => void; 167 | } 168 | -------------------------------------------------------------------------------- /src/stomp-headers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * STOMP headers. Many functions calls will accept headers as parameters. 3 | * The headers sent by Broker will be available as [IFrame#headers]{@link IFrame#headers}. 4 | * 5 | * `key` and `value` must be valid strings. 6 | * In addition, `key` must not contain `CR`, `LF`, or `:`. 7 | * 8 | * Part of `@stomp/stompjs`. 9 | */ 10 | export class StompHeaders { 11 | [key: string]: string; 12 | } 13 | -------------------------------------------------------------------------------- /src/stomp-subscription.ts: -------------------------------------------------------------------------------- 1 | import { StompHeaders } from './stomp-headers.js'; 2 | 3 | /** 4 | * Call [Client#subscribe]{@link Client#subscribe} to create a StompSubscription. 5 | * 6 | * Part of `@stomp/stompjs`. 7 | */ 8 | export interface StompSubscription { 9 | /** 10 | * Id associated with this subscription. 11 | */ 12 | id: string; 13 | 14 | /** 15 | * Unsubscribe. See [Client#unsubscribe]{@link Client#unsubscribe} for an example. 16 | */ 17 | unsubscribe: (headers?: StompHeaders) => void; 18 | } 19 | -------------------------------------------------------------------------------- /src/ticker.ts: -------------------------------------------------------------------------------- 1 | import { debugFnType, TickerStrategy } from './types.js'; 2 | 3 | export class Ticker { 4 | private readonly _workerScript = ` 5 | var startTime = Date.now(); 6 | setInterval(function() { 7 | self.postMessage(Date.now() - startTime); 8 | }, ${this._interval}); 9 | `; 10 | 11 | private _worker?: Worker; 12 | private _timer?: any; 13 | 14 | constructor( 15 | private readonly _interval: number, 16 | private readonly _strategy = TickerStrategy.Interval, 17 | private readonly _debug: debugFnType) { 18 | } 19 | 20 | public start(tick: (elapsedTime: number) => void): void { 21 | this.stop(); 22 | 23 | if (this.shouldUseWorker()) { 24 | this.runWorker(tick); 25 | } else { 26 | this.runInterval(tick); 27 | } 28 | } 29 | 30 | public stop(): void { 31 | this.disposeWorker(); 32 | this.disposeInterval(); 33 | } 34 | 35 | private shouldUseWorker(): boolean { 36 | return typeof(Worker) !== 'undefined' && this._strategy === TickerStrategy.Worker 37 | } 38 | 39 | private runWorker(tick: (elapsedTime: number) => void): void { 40 | this._debug('Using runWorker for outgoing pings'); 41 | if (!this._worker) { 42 | this._worker = new Worker( 43 | URL.createObjectURL( 44 | new Blob([this._workerScript], { type: 'text/javascript' }) 45 | ) 46 | ); 47 | this._worker.onmessage = (message) => tick(message.data); 48 | } 49 | } 50 | 51 | private runInterval(tick: (elapsedTime: number) => void): void { 52 | this._debug('Using runInterval for outgoing pings'); 53 | if (!this._timer) { 54 | const startTime = Date.now(); 55 | this._timer = setInterval(() => { 56 | tick(Date.now() - startTime); 57 | }, this._interval); 58 | } 59 | } 60 | 61 | private disposeWorker(): void { 62 | if (this._worker) { 63 | this._worker.terminate(); 64 | delete this._worker; 65 | this._debug('Outgoing ping disposeWorker'); 66 | } 67 | } 68 | 69 | private disposeInterval(): void { 70 | if (this._timer) { 71 | clearInterval(this._timer); 72 | delete this._timer; 73 | this._debug('Outgoing ping disposeInterval'); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { IFrame } from './i-frame.js'; 2 | import type { IMessage } from './i-message.js'; 3 | import { StompHeaders } from './stomp-headers.js'; 4 | import { Versions } from './versions.js'; 5 | 6 | /** 7 | * This callback will receive a `string` as a parameter. 8 | * 9 | * Part of `@stomp/stompjs`. 10 | */ 11 | export type debugFnType = (msg: string) => void; 12 | 13 | /** 14 | * This callback will receive a {@link IMessage} as parameter. 15 | * 16 | * Part of `@stomp/stompjs`. 17 | */ 18 | export type messageCallbackType = (message: IMessage) => void; 19 | 20 | /** 21 | * This callback will receive a {@link IFrame} as parameter. 22 | * 23 | * Part of `@stomp/stompjs`. 24 | */ 25 | export type frameCallbackType = ((frame: IFrame) => void) | (() => void); 26 | 27 | /** 28 | * This callback will receive a [CloseEvent]{@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent} 29 | * as parameter. 30 | * 31 | * Part of `@stomp/stompjs`. 32 | */ 33 | export type closeEventCallbackType = (evt: T) => void; 34 | 35 | /** 36 | * This callback will receive an [Event]{@link https://developer.mozilla.org/en-US/docs/Web/API/Event} 37 | * as parameter. 38 | * 39 | * Part of `@stomp/stompjs`. 40 | */ 41 | export type wsErrorCallbackType = (evt: T) => void; 42 | 43 | /** 44 | * Parameters for [Client#publish]{@link Client#publish}. 45 | * Aliased as publishParams as well. 46 | * 47 | * Part of `@stomp/stompjs`. 48 | */ 49 | export interface IPublishParams { 50 | /** 51 | * destination end point 52 | */ 53 | destination: string; 54 | /** 55 | * headers (optional) 56 | */ 57 | headers?: StompHeaders; 58 | /** 59 | * body (optional) 60 | */ 61 | body?: string; 62 | /** 63 | * binary body (optional) 64 | */ 65 | binaryBody?: Uint8Array; 66 | /** 67 | * By default, a `content-length` header will be added in the Frame to the broker. 68 | * Set it to `true` for the header to be skipped. 69 | */ 70 | skipContentLengthHeader?: boolean; 71 | } 72 | 73 | /** 74 | * Backward compatibility, switch to {@link IPublishParams}. 75 | */ 76 | export type publishParams = IPublishParams; 77 | 78 | /** 79 | * Used in {@link IRawFrameType} 80 | * 81 | * Part of `@stomp/stompjs`. 82 | * 83 | * @internal 84 | */ 85 | export type RawHeaderType = [string, string]; 86 | 87 | /** 88 | * The parser yield frames in this structure 89 | * 90 | * Part of `@stomp/stompjs`. 91 | * 92 | * @internal 93 | */ 94 | export interface IRawFrameType { 95 | command: string | undefined; 96 | headers: RawHeaderType[]; 97 | binaryBody: Uint8Array | undefined; 98 | } 99 | 100 | /** 101 | * @internal 102 | */ 103 | export interface IStompSocketMessageEvent { 104 | data?: string | ArrayBuffer; 105 | } 106 | 107 | /** 108 | * Copied from Websocket interface to avoid dom typelib dependency. 109 | * 110 | * @internal 111 | */ 112 | export interface IStompSocket { 113 | url: string; 114 | onclose: ((ev?: any) => any) | undefined | null; 115 | onerror: ((ev: any) => any) | undefined | null; 116 | onmessage: ((ev: IStompSocketMessageEvent) => any) | undefined | null; 117 | onopen: ((ev?: any) => any) | undefined | null; 118 | terminate?: (() => any) | undefined | null; 119 | 120 | /** 121 | * Returns a string that indicates how binary data from the socket is exposed to scripts: 122 | * We support only 'arraybuffer'. 123 | */ 124 | binaryType?: string; 125 | 126 | /** 127 | * Returns the state of the socket connection. It can have the values of StompSocketState. 128 | */ 129 | readonly readyState: number; 130 | 131 | /** 132 | * Closes the connection. 133 | */ 134 | close(): void; 135 | /** 136 | * Transmits data using the connection. data can be a string or an ArrayBuffer. 137 | */ 138 | send(data: string | ArrayBuffer): void; 139 | } 140 | 141 | /** 142 | * Possible states for the IStompSocket 143 | */ 144 | export enum StompSocketState { 145 | CONNECTING, 146 | OPEN, 147 | CLOSING, 148 | CLOSED, 149 | } 150 | 151 | /** 152 | * Possible activation state 153 | */ 154 | export enum ActivationState { 155 | ACTIVE, 156 | DEACTIVATING, 157 | INACTIVE, 158 | } 159 | 160 | /** 161 | * Possible reconnection wait time modes 162 | */ 163 | export enum ReconnectionTimeMode { 164 | LINEAR, 165 | EXPONENTIAL 166 | } 167 | 168 | /** 169 | * Possible ticker strategies for outgoing heartbeat ping 170 | */ 171 | export enum TickerStrategy { 172 | Interval = 'interval', 173 | Worker = 'worker' 174 | } 175 | 176 | /** 177 | * @internal 178 | */ 179 | export interface IStomptHandlerConfig { 180 | debug: debugFnType; 181 | stompVersions: Versions; 182 | connectHeaders: StompHeaders; 183 | disconnectHeaders: StompHeaders; 184 | heartbeatIncoming: number; 185 | heartbeatOutgoing: number; 186 | heartbeatStrategy: TickerStrategy; 187 | splitLargeFrames: boolean; 188 | maxWebSocketChunkSize: number; 189 | forceBinaryWSFrames: boolean; 190 | logRawCommunication: boolean; 191 | appendMissingNULLonIncoming: boolean; 192 | discardWebsocketOnCommFailure: boolean; 193 | onConnect: frameCallbackType; 194 | onDisconnect: frameCallbackType; 195 | onStompError: frameCallbackType; 196 | onWebSocketClose: closeEventCallbackType; 197 | onWebSocketError: wsErrorCallbackType; 198 | onUnhandledMessage: messageCallbackType; 199 | onUnhandledReceipt: frameCallbackType; 200 | onUnhandledFrame: frameCallbackType; 201 | } 202 | -------------------------------------------------------------------------------- /src/versions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Supported STOMP versions 3 | * 4 | * Part of `@stomp/stompjs`. 5 | */ 6 | export class Versions { 7 | /** 8 | * Indicates protocol version 1.0 9 | */ 10 | public static V1_0 = '1.0'; 11 | /** 12 | * Indicates protocol version 1.1 13 | */ 14 | public static V1_1 = '1.1'; 15 | /** 16 | * Indicates protocol version 1.2 17 | */ 18 | public static V1_2 = '1.2'; 19 | 20 | /** 21 | * @internal 22 | */ 23 | public static default = new Versions([ 24 | Versions.V1_2, 25 | Versions.V1_1, 26 | Versions.V1_0, 27 | ]); 28 | 29 | /** 30 | * Takes an array of versions, typical elements '1.2', '1.1', or '1.0' 31 | * 32 | * You will be creating an instance of this class if you want to override 33 | * supported versions to be declared during STOMP handshake. 34 | */ 35 | constructor(public versions: string[]) {} 36 | 37 | /** 38 | * Used as part of CONNECT STOMP Frame 39 | */ 40 | public supportedVersions() { 41 | return this.versions.join(','); 42 | } 43 | 44 | /** 45 | * Used while creating a WebSocket 46 | */ 47 | public protocolVersions() { 48 | return this.versions.map(x => `v${x.replace('.', '')}.stomp`); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ 5 | "module": "nodenext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "lib": ["dom", "es2015"], /* Specify library files to be included in the compilation. */ 7 | // "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 12 | "sourceMap": true, /* Generates corresponding '.map' file. */ 13 | // "outFile": "./", /* Concatenate and emit output to single file. */ 14 | "outDir": "esm6", /* Redirect output structure to the directory. */ 15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 16 | // "composite": true, /* Enable project compilation */ 17 | "removeComments": false, /* Do not emit comments to output. */ 18 | // "noEmit": true, /* Do not emit outputs. */ 19 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 20 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 21 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 22 | 23 | /* Strict Type-Checking Options */ 24 | "strict": true, /* Enable all strict type-checking options. */ 25 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 26 | // "strictNullChecks": true, /* Enable strict null checks. */ 27 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 28 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 29 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 30 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 31 | 32 | /* Additional Checks */ 33 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 34 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 35 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 36 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 37 | 38 | /* Module Resolution Options */ 39 | "moduleResolution": "nodenext", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 40 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 41 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 42 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 43 | // "typeRoots": [], /* List of folders to include type definitions from. */ 44 | // "types": [], /* Type declaration files to be included in compilation. */ 45 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 46 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 47 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 48 | 49 | /* Source Map Options */ 50 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 51 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 52 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 53 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 54 | 55 | /* Experimental Options */ 56 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 57 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 58 | }, 59 | "include": ["./src/**/*.ts"] /* Only compile files in src, avoids confusion with code in node_modules or compiled code */ 60 | } 61 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended", "tslint-config-prettier"], 4 | "jsRules": {}, 5 | "rules": { 6 | "member-ordering": [false], 7 | "no-console": false, 8 | "no-empty": [true, "allow-empty-catch", "allow-empty-functions"], 9 | "object-literal-sort-keys": false, 10 | "variable-name": [ 11 | true, 12 | "ban-keywords", 13 | "check-format", 14 | "allow-leading-underscore" 15 | ] 16 | }, 17 | "rulesDirectory": [] 18 | } 19 | -------------------------------------------------------------------------------- /webpack.config.cjs: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | const PATHS = { 5 | entryPoint: path.resolve(__dirname, 'src/index.ts'), 6 | bundles: path.resolve(__dirname, 'bundles'), 7 | }; 8 | 9 | const config = { 10 | // These are the entry point of our library. We tell webpack to use 11 | // the name we assign later, when creating the bundle. We also use 12 | // the name to filter the second entry point for applying code 13 | // minification via UglifyJS 14 | entry: { 15 | stomp: [PATHS.entryPoint], 16 | }, 17 | // The output defines how and where we want the bundles. The special 18 | // value `[name]` in `filename` tell Webpack to use the name we defined above. 19 | // We target a UMD and name it MyLib. When including the bundle in the browser 20 | // it will be accessible at `window.MyLib` 21 | output: { 22 | path: PATHS.bundles, 23 | filename: '[name].umd.js', 24 | libraryTarget: 'umd', 25 | library: 'StompJs', 26 | globalObject: `typeof self !== 'undefined' ? self : this`, 27 | umdNamedDefine: true, 28 | }, 29 | mode: 'development', 30 | // Add resolve for `tsx` and `ts` files, otherwise Webpack would 31 | // only look for common JavaScript file extension (.js) 32 | resolve: { 33 | extensions: ['.ts', '.tsx', '.js'], 34 | }, 35 | // Activate source maps for the bundles in order to preserve the original 36 | // source when the user debugs the application 37 | devtool: 'inline-source-map', 38 | module: { 39 | rules: [ 40 | { 41 | test: /\.tsx?$/, 42 | loader: 'ts-loader', 43 | exclude: /node_modules/, 44 | }, 45 | ], 46 | }, 47 | }; 48 | 49 | module.exports = config; 50 | --------------------------------------------------------------------------------