Несовместимость версий в экосистеме STOMP.js возникает из-за одновременного изменения нескольких компонентов:
Особенно часто проблемы появляются при миграции:
stompjs на @stomp/stompjs;Исторически существовало несколько несовместимых реализаций:
| Библиотека | Статус | Особенности |
|---|---|---|
stomp-websocket |
устарела | старый API |
stompjs |
устарела | callback-подход |
@stomp/stompjs |
актуальна | TypeScript, modern API |
@stomp/rx-stomp |
современная | RxJS-интеграция |
Старые проекты часто используют:
const client = Stomp.client(url);
Новые версии используют:
import { Client } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'ws://localhost:15674/ws'
});
Попытка переноса кода без адаптации почти всегда приводит к ошибкам совместимости.
Stomp.client()Старый API:
const client = Stomp.client(url);
Новый API:
const client = new Client({
brokerURL: url
});
В старых приложениях часто встречается код:
client.connect(headers, onConnect);
В новых версиях используется:
client.onConn ect = () => {
console.log('Connected');
};
client.activate();
Метод connect() либо отсутствует, либо работает
иначе.
client.connect({}, () => {
console.log('Connected');
});
client.onConn ect = () => {
console.log('Connected');
};
client.activate();
Теперь библиотека использует:
Старый код, ожидающий синхронного поведения, начинает работать нестабильно.
В старых версиях реконнект часто реализовывался вручную:
function connect() {
client.connect({}, connectCallback, errorCallback);
}
В новых версиях встроен параметр:
const client = new Client({
reconnectDelay: 5000
});
Проблемы возникают, когда:
Типичный результат:
В ранних версиях heartbeat иногда отключался по умолчанию.
Современный API:
const client = new Client({
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000
});
Старые брокеры могут:
Некоторые legacy STOMP-серверы работают только при:
heartbeatIncoming: 0,
heartbeatOutgoing: 0
Не поддерживает:
Добавляет:
Изменяет:
Современный STOMP.js ориентирован преимущественно на STOMP 1.2.
Старые брокеры:
ack: 'client'
Новые реализации могут ожидать:
ack: 'client-individual'
Поведение radically меняется:
| Режим | Поведение |
|---|---|
| client | ACK пачкой |
| client-individual | ACK по одному сообщению |
При несовместимости появляются:
Некоторые версии STOMP.js автоматически сериализуют JSON:
client.publish({
destination: '/topic/test',
body: JSON.stringify(data)
});
Старый код мог использовать:
client.send('/topic/test', {}, data);
Если data — объект:
{
id: 1
}
то старые версии могут отправить:
[object Object]
Новые версии ожидают строку.
const Stomp = require('stompjs');
import { Client } from '@stomp/stompjs';
Возможные ошибки:
TypeError: Client is not a constructor
или:
Cannot use import statement outside a module
Причины:
tsconfig;Старые версии STOMP.js ориентировались на:
Современные версии работают через:
Из-за этого появляются ошибки:
Module not found
или:
process is not defined
Старые реализации:
const socket = new SockJS(url);
const client = Stomp.over(socket);
Новые версии:
const client = new Client({
brokerURL: url
});
Если брокер не поддерживает WebSocket напрямую:
WebSocket connection failed
Некоторые старые инфраструктуры требуют исключительно SockJS.
Stomp.over()Ранее:
const client = Stomp.over(socket);
Современные версии могут использовать:
webSocketFactory: () => new SockJS(url)
Пример:
const client = new Client({
webSocketFactory: () => new SockJS(url)
});
Старый код становится несовместимым без переписывания.
Старые версии:
any;Современный STOMP.js активно использует TypeScript.
Появляются ошибки:
Property 'body' does not exist
или:
Type 'undefined' is not assignable
Особенно часто проблемы возникают после обновления TypeScript.
Старые версии:
client.connect(headers, connectCallback, errorCallback);
Новые версии:
client.onConn ect = frame => {};
client.onStompEr ror = frame => {};
Старые обработчики больше не вызываются автоматически.
Ранее ошибки часто игнорировались:
client.debug = null;
Теперь библиотека использует:
client.onWebSocketError
client.onStompError
client.onDisconnect
Отсутствие новых обработчиков делает диагностику крайне сложной.
Разные брокеры реализуют STOMP по-разному:
| Брокер | Особенности |
|---|---|
| RabbitMQ | частичная специфика STOMP |
| ActiveMQ | свои расширения |
| Apollo | устаревшая реализация |
| HornetQ | legacy STOMP |
| Artemis | современная реализация |
Некоторые старые серверы:
STOMP 1.2 требует escaping заголовков.
Например:
header:value\:test
Старые брокеры могут не понимать escaping.
Следствие:
Некоторые старые версии:
content-length.Современные версии строже работают с бинарными данными.
Появляются проблемы:
Malformed frame
или:
Invalid UTF-8 sequence
Старый STOMP.js ориентировался в основном на текст.
Новые версии поддерживают:
binaryBody: uint8Array
Но старые брокеры:
Старый подход:
client.debug = console.log;
Новый подход всё ещё поддерживает debug, но поведение логирования изменилось.
Некоторые старые интеграции:
После обновления формат логов может измениться.
Старые примеры создавали клиента напрямую:
const client = new Client(...);
В React 18 Strict Mode эффекты могут запускаться дважды.
Результат:
Legacy-код особенно уязвим к этой проблеме.
Современный STOMP.js использует:
Старые версии Node.js:
Старые версии могли зависеть от:
global
process
Buffer
Современные bundler-системы больше не добавляют polyfill автоматически.
Особенно часто это проявляется в Vite.
Типичный неудачный перенос:
const client = Stomp.client(url);
client.connect({}, () => {
client.subscribe('/topic/test', message => {
console.log(message.body);
});
});
Попытка обновления:
const client = new Client({
brokerURL: url
});
client.activate();
client.subscribe('/topic/test', callback);
Ошибка:
subscribe is not a function
Причина — подписка возможна только после onConnect.
Корректный вариант:
client.onConn ect = () => {
client.subscribe('/topic/test', message => {
console.log(message.body);
});
};
client.activate();
Опасно:
STOMP.js + Webpack + React + Broker
обновлять одновременно.
Безопаснее:
Перед обновлением важно проверить:
| Возможность | Проверка |
|---|---|
| STOMP 1.2 | поддерживается ли |
| heartbeat | работает ли |
| binary frames | поддерживаются ли |
| SockJS | требуется ли |
| ACK modes | какие доступны |
При больших legacy-проектах часто создаётся wrapper:
class LegacyStompAdapter {
connect(headers, callback) {
this.client.onConn ect = callback;
this.client.activate();
}
}
Это позволяет:
Даже при одинаковом API поведение может отличаться:
| Поведение | Старая версия | Новая версия |
|---|---|---|
| reconnect | ручной | автоматический |
| subscriptions | sync | async |
| disconnect | мгновенный | graceful |
| heartbeat | off | on |
| errors | silent | strict |
Именно behavioral compatibility чаще всего вызывает production-инциденты.
Характерные симптомы:
Connection closed
No messages received
Message delivered multiple times
STOMP protocol error
Connection timeout
Вызывает лавинообразное создание соединений.
Может привести к потере сообщений.
Вызывает случайные disconnect.
Полностью ломает transport layer.
Приложение перестаёт собираться.
Для production-систем часто фиксируют версии:
{
"@stomp/stompjs": "7.0.0"
}
а не:
{
"@stomp/stompjs": "^7.0.0"
}
Это предотвращает неожиданные breaking changes.
Не все breaking changes исторически сопровождались major-version bump.
Особенно в старых поколениях библиотек встречались:
Поэтому обновление даже minor-версии требует тестирования.
Критически важно тестировать:
Без integration-тестов несовместимости часто проявляются только в production.