chore(deps): update socketcluster to v16 (major) (#1167)

* chore(deps): update socketcluster to v16

* Use the right packages

* stash

* stash

* stash

* Fixes

* Fixes

Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Nathan Bierema <nbierema@gmail.com>
This commit is contained in:
renovate[bot] 2022-06-13 11:35:11 +00:00 committed by GitHub
parent 1ee787e353
commit 6c9ae2713f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 644 additions and 810 deletions

View File

@ -64,7 +64,7 @@
"react-redux": "^8.0.2", "react-redux": "^8.0.2",
"redux": "^4.2.0", "redux": "^4.2.0",
"redux-persist": "^6.0.0", "redux-persist": "^6.0.0",
"socketcluster-client": "^14.3.2" "socketcluster-client": "^16.1.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.17.10", "@babel/cli": "^7.17.10",
@ -84,7 +84,7 @@
"@types/node": "^16.11.38", "@types/node": "^16.11.38",
"@types/react": "^18.0.12", "@types/react": "^18.0.12",
"@types/react-dom": "^18.0.5", "@types/react-dom": "^18.0.5",
"@types/socketcluster-client": "^13.0.5", "@types/socketcluster-client": "^16.0.0",
"@types/styled-components": "^5.1.25", "@types/styled-components": "^5.1.25",
"@types/testing-library__jest-dom": "^5.14.3", "@types/testing-library__jest-dom": "^5.14.3",
"@types/webpack-env": "^1.17.0", "@types/webpack-env": "^1.17.0",

View File

@ -1,5 +1,5 @@
import { SchemeName, ThemeName } from '@redux-devtools/ui'; import { SchemeName, ThemeName } from '@redux-devtools/ui';
import { AuthStates, States } from 'socketcluster-client/lib/scclientsocket'; import { AuthStates, States } from 'socketcluster-client/lib/clientsocket';
import { REHYDRATE } from 'redux-persist'; import { REHYDRATE } from 'redux-persist';
import { import {
CHANGE_SECTION, CHANGE_SECTION,

View File

@ -16,7 +16,7 @@ export const {
AUTHENTICATED, AUTHENTICATED,
PENDING, PENDING,
UNAUTHENTICATED, UNAUTHENTICATED,
} = socketCluster.SCClientSocket as unknown as States; } = socketCluster.AGClientSocket as unknown as States;
export const CONNECT_REQUEST = 'socket/CONNECT_REQUEST'; export const CONNECT_REQUEST = 'socket/CONNECT_REQUEST';
export const CONNECT_SUCCESS = 'socket/CONNECT_SUCCESS'; export const CONNECT_SUCCESS = 'socket/CONNECT_SUCCESS';
export const CONNECT_ERROR = 'socket/CONNECT_ERROR'; export const CONNECT_ERROR = 'socket/CONNECT_ERROR';

View File

@ -1,4 +1,4 @@
import socketCluster, { SCClientSocket } from 'socketcluster-client'; import socketClusterClient, { AGClientSocket } from 'socketcluster-client';
import { stringify } from 'jsan'; import { stringify } from 'jsan';
import { Dispatch, MiddlewareAPI } from 'redux'; import { Dispatch, MiddlewareAPI } from 'redux';
import * as actions from '../constants/socketActionTypes'; import * as actions from '../constants/socketActionTypes';
@ -25,11 +25,16 @@ import {
import { nonReduxDispatch } from '../utils/monitorActions'; import { nonReduxDispatch } from '../utils/monitorActions';
import { StoreState } from '../reducers'; import { StoreState } from '../reducers';
let socket: SCClientSocket; let socket: AGClientSocket;
let store: MiddlewareAPI<Dispatch<StoreAction>, StoreState>; let store: MiddlewareAPI<Dispatch<StoreAction>, StoreState>;
function emit({ message: type, id, instanceId, action, state }: EmitAction) { function emit({ message: type, id, instanceId, action, state }: EmitAction) {
socket.emit(id ? `sc-${id}` : 'respond', { type, action, state, instanceId }); void socket.transmit(id ? `sc-${id}` : 'respond', {
type,
action,
state,
instanceId,
});
} }
function startMonitoring(channel: string) { function startMonitoring(channel: string) {
@ -120,7 +125,7 @@ function monitoring(request: MonitoringRequest) {
instanceId === instances.selected && instanceId === instances.selected &&
(request.type === 'ACTION' || request.type === 'STATE') (request.type === 'ACTION' || request.type === 'STATE')
) { ) {
socket.emit('respond', { void socket.transmit('respond', {
type: 'SYNC', type: 'SYNC',
state: stringify(instances.states[instanceId]), state: stringify(instances.states[instanceId]),
id: request.id, id: request.id,
@ -134,65 +139,84 @@ function subscribe(
subscription: typeof UPDATE_STATE | typeof UPDATE_REPORTS subscription: typeof UPDATE_STATE | typeof UPDATE_REPORTS
) { ) {
const channel = socket.subscribe(channelName); const channel = socket.subscribe(channelName);
if (subscription === UPDATE_STATE) channel.watch(monitoring); if (subscription === UPDATE_STATE) {
else { void (async () => {
for await (const data of channel) {
monitoring(data as MonitoringRequest);
}
})();
} else {
const watcher = (request: UpdateReportsRequest) => { const watcher = (request: UpdateReportsRequest) => {
store.dispatch({ type: subscription, request }); store.dispatch({ type: subscription, request });
}; };
channel.watch(watcher); void (async () => {
socket.on(channelName, watcher); for await (const data of channel) {
watcher(data as UpdateReportsRequest);
}
})();
} }
} }
function handleConnection() { function handleConnection() {
socket.on('connect', (status) => { void (async () => {
store.dispatch({ for await (const data of socket.listener('connect')) {
type: actions.CONNECT_SUCCESS, store.dispatch({
payload: { type: actions.CONNECT_SUCCESS,
id: status.id, payload: {
authState: socket.authState, id: data.id,
socketState: socket.state, authState: socket.authState,
}, socketState: socket.state,
error: status.authError, },
}); // @ts-expect-error Is this legitimate?
if (socket.authState !== actions.AUTHENTICATED) { error: data.authError,
store.dispatch({ type: actions.AUTH_REQUEST }); });
if (socket.authState !== actions.AUTHENTICATED) {
store.dispatch({ type: actions.AUTH_REQUEST });
}
} }
}); })();
socket.on('disconnect', (code) => { void (async () => {
store.dispatch({ type: actions.DISCONNECTED, code }); for await (const data of socket.listener('disconnect')) {
}); store.dispatch({ type: actions.DISCONNECTED, code: data.code });
}
})();
socket.on('subscribe', (channel) => { void (async () => {
store.dispatch({ type: actions.SUBSCRIBE_SUCCESS, channel }); for await (const data of socket.listener('subscribe')) {
}); store.dispatch({
socket.on('unsubscribe', (channel) => { type: actions.SUBSCRIBE_SUCCESS,
socket.unsubscribe(channel); channel: data.channel,
socket.unwatch(channel); });
socket.off(channel); }
store.dispatch({ type: actions.UNSUBSCRIBE, channel }); })();
}); void (async () => {
socket.on('subscribeFail', (error) => { for await (const data of socket.listener('unsubscribe')) {
store.dispatch({ void socket.unsubscribe(data.channel);
type: actions.SUBSCRIBE_ERROR, store.dispatch({ type: actions.UNSUBSCRIBE, channel: data.channel });
error, }
status: 'subscribeFail', })();
}); void (async () => {
}); for await (const data of socket.listener('subscribeFail')) {
socket.on('dropOut', (error) => { store.dispatch({
store.dispatch({ type: actions.SUBSCRIBE_ERROR, error, status: 'dropOut' }); type: actions.SUBSCRIBE_ERROR,
}); error: data.error,
status: 'subscribeFail',
});
}
})();
socket.on('error', (error) => { void (async () => {
store.dispatch({ type: actions.CONNECT_ERROR, error }); for await (const data of socket.listener('error')) {
}); store.dispatch({ type: actions.CONNECT_ERROR, error: data.error });
}
})();
} }
function connect() { function connect() {
if (process.env.NODE_ENV === 'test') return; if (process.env.NODE_ENV === 'test') return;
const connection = store.getState().connection; const connection = store.getState().connection;
try { try {
socket = socketCluster.create(connection.options); socket = socketClusterClient.create(connection.options);
handleConnection(); handleConnection();
} catch (error) { } catch (error) {
store.dispatch({ type: actions.CONNECT_ERROR, error: error as Error }); store.dispatch({ type: actions.CONNECT_ERROR, error: error as Error });
@ -205,43 +229,42 @@ function connect() {
function disconnect() { function disconnect() {
if (socket) { if (socket) {
socket.disconnect(); socket.disconnect();
socket.off();
} }
} }
function login() { function login() {
socket.emit('login', {}, (error: Error, baseChannel: string) => { void (async () => {
if (error) { try {
store.dispatch({ type: actions.AUTH_ERROR, error }); const baseChannel = (await socket.invoke('login', {})) as string;
return; store.dispatch({ type: actions.AUTH_SUCCESS, baseChannel });
store.dispatch({
type: actions.SUBSCRIBE_REQUEST,
channel: baseChannel,
subscription: UPDATE_STATE,
});
store.dispatch({
type: actions.SUBSCRIBE_REQUEST,
channel: 'report',
subscription: UPDATE_REPORTS,
});
} catch (error) {
store.dispatch({ type: actions.AUTH_ERROR, error: error as Error });
} }
store.dispatch({ type: actions.AUTH_SUCCESS, baseChannel }); })();
store.dispatch({
type: actions.SUBSCRIBE_REQUEST,
channel: baseChannel,
subscription: UPDATE_STATE,
});
store.dispatch({
type: actions.SUBSCRIBE_REQUEST,
channel: 'report',
subscription: UPDATE_REPORTS,
});
});
} }
function getReport(reportId: unknown) { function getReport(reportId: unknown) {
socket.emit( void (async () => {
'getReport', try {
reportId, const data = (await socket.invoke('getReport', reportId)) as {
(error: Error, data: { payload: string }) => { payload: string;
if (error) { };
store.dispatch({ type: GET_REPORT_ERROR, error });
return;
}
store.dispatch({ type: GET_REPORT_SUCCESS, data }); store.dispatch({ type: GET_REPORT_SUCCESS, data });
store.dispatch(importState(data.payload)); store.dispatch(importState(data.payload));
} catch (error) {
store.dispatch({ type: GET_REPORT_ERROR, error: error as Error });
} }
); })();
} }
export function api(inStore: MiddlewareAPI<Dispatch<StoreAction>, StoreState>) { export function api(inStore: MiddlewareAPI<Dispatch<StoreAction>, StoreState>) {

View File

@ -1,4 +1,4 @@
import { AuthStates, States } from 'socketcluster-client/lib/scclientsocket'; import { AuthStates, States } from 'socketcluster-client/lib/clientsocket';
import * as actions from '../constants/socketActionTypes'; import * as actions from '../constants/socketActionTypes';
import { StoreAction } from '../actions'; import { StoreAction } from '../actions';

View File

@ -60,7 +60,7 @@
"react-dom": "^18.1.0", "react-dom": "^18.1.0",
"react-is": "^18.1.0", "react-is": "^18.1.0",
"semver": "^7.3.7", "semver": "^7.3.7",
"socketcluster": "^14.4.2", "socketcluster-server": "^16.2.1",
"sqlite3": "^5.0.8", "sqlite3": "^5.0.8",
"styled-components": "^5.3.5", "styled-components": "^5.3.5",
"uuid": "^8.3.2" "uuid": "^8.3.2"
@ -76,9 +76,8 @@
"@types/morgan": "^1.9.3", "@types/morgan": "^1.9.3",
"@types/node": "^16.11.38", "@types/node": "^16.11.38",
"@types/semver": "^7.3.9", "@types/semver": "^7.3.9",
"@types/socketcluster": "^14.0.4", "@types/socketcluster-client": "^16.0.0",
"@types/socketcluster-client": "^13.0.5", "@types/socketcluster-server": "^16.1.0",
"@types/socketcluster-server": "^14.2.6",
"@types/styled-components": "^5.1.25", "@types/styled-components": "^5.1.25",
"@types/supertest": "^2.0.12", "@types/supertest": "^2.0.12",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
@ -90,7 +89,7 @@
"jest": "^27.5.1", "jest": "^27.5.1",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"socketcluster-client": "^14.3.2", "socketcluster-client": "^16.1.1",
"supertest": "^6.2.3", "supertest": "^6.2.3",
"ts-jest": "^27.1.5", "ts-jest": "^27.1.5",
"typescript": "~4.7.3" "typescript": "~4.7.3"

View File

@ -88,10 +88,9 @@ if (argv.injectserver) {
} }
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
server(argv).then(function (r) { server(argv).then(async function (r) {
if (argv.open && argv.open !== 'false') { if (argv.open && argv.open !== 'false') {
r.on('ready', async function () { await r.listener('ready').once();
await openApp(argv.open as string, options); await openApp(argv.open as string, options);
});
} }
}); });

View File

@ -1,8 +1,8 @@
import path from 'path'; import path from 'path';
import knexModule, { Knex } from 'knex'; import knexModule, { Knex } from 'knex';
import { SCServer } from 'socketcluster-server'; import { AGServer } from 'socketcluster-server';
export default function connector(options: SCServer.SCServerOptions) { export default function connector(options: AGServer.AGServerOptions) {
const dbOptions = options.dbOptions as Knex.Config; const dbOptions = options.dbOptions as Knex.Config;
dbOptions.useNullAsDefault = true; dbOptions.useNullAsDefault = true;
if (!(dbOptions as any).migrate) { if (!(dbOptions as any).migrate) {

View File

@ -1,6 +1,10 @@
import express from 'express';
import http from 'http';
import getPort from 'getport'; import getPort from 'getport';
import SocketCluster from 'socketcluster'; import socketClusterServer from 'socketcluster-server';
import getOptions, { Options } from './options'; import getOptions, { Options } from './options';
import routes from './routes';
import createStore from './store';
// var LOG_LEVEL_NONE = 0; // var LOG_LEVEL_NONE = 0;
const LOG_LEVEL_ERROR = 1; const LOG_LEVEL_ERROR = 1;
@ -8,16 +12,14 @@ const LOG_LEVEL_WARN = 2;
const LOG_LEVEL_INFO = 3; const LOG_LEVEL_INFO = 3;
export interface ExtendedOptions extends Options { export interface ExtendedOptions extends Options {
workerController: string;
allowClientPublish: boolean; allowClientPublish: boolean;
} }
export default function (argv: { [arg: string]: any }): Promise<{ export default function (argv: { [arg: string]: any }): Promise<{
portAlreadyUsed?: boolean; portAlreadyUsed?: boolean;
on: (status: 'ready', cb: (() => void) | (() => Promise<void>)) => void; listener: (eventName: 'ready') => { once(): Promise<void> };
}> { }> {
const options = Object.assign(getOptions(argv), { const options = Object.assign(getOptions(argv), {
workerController: __dirname + '/worker.js',
allowClientPublish: false, allowClientPublish: false,
}); });
const port = options.port; const port = options.port;
@ -39,8 +41,12 @@ export default function (argv: { [arg: string]: any }): Promise<{
} }
resolve({ resolve({
portAlreadyUsed: true, portAlreadyUsed: true,
on: function (status: string, cb: () => void) { listener: function (eventName: 'ready') {
cb(); return {
once() {
return Promise.resolve();
},
};
}, },
}); });
} else { } else {
@ -48,7 +54,100 @@ export default function (argv: { [arg: string]: any }): Promise<{
console.log('[ReduxDevTools] Start server...'); console.log('[ReduxDevTools] Start server...');
console.log('-'.repeat(80) + '\n'); console.log('-'.repeat(80) + '\n');
} }
resolve(new SocketCluster(options)); const httpServer = http.createServer();
const agServer = socketClusterServer.attach(httpServer, options);
const app = express();
httpServer.on('request', app);
const store = createStore(options);
app.use(routes(options, store, agServer));
agServer.setMiddleware(
agServer.MIDDLEWARE_INBOUND,
// eslint-disable-next-line @typescript-eslint/no-misused-promises
async (middlewareStream) => {
for await (const action of middlewareStream) {
if (action.type === action.TRANSMIT) {
const channel = action.receiver;
const data = action.data;
if (
channel.substring(0, 3) === 'sc-' ||
channel === 'respond' ||
channel === 'log'
) {
void agServer.exchange.transmitPublish(channel, data);
} else if (channel === 'log-noid') {
void agServer.exchange.transmitPublish('log', {
id: action.socket.id,
data: data,
});
}
} else if (action.type === action.SUBSCRIBE) {
if (action.channel === 'report') {
store
.list()
.then(function (data) {
void agServer.exchange.transmitPublish('report', {
type: 'list',
data: data,
});
})
.catch(function (error) {
console.error(error); // eslint-disable-line no-console
});
}
}
action.allow();
}
}
);
void (async () => {
for await (const { socket } of agServer.listener('connection')) {
let channelToWatch: string, channelToEmit: string;
void (async () => {
for await (const request of socket.procedure('login')) {
const credentials = request.data;
if (credentials === 'master') {
channelToWatch = 'respond';
channelToEmit = 'log';
} else {
channelToWatch = 'log';
channelToEmit = 'respond';
}
request.end(channelToWatch);
}
})();
void (async () => {
for await (const request of socket.procedure('getReport')) {
const id = request.data as string;
store
.get(id)
.then(function (data) {
request.end(data);
})
.catch(function (error) {
console.error(error); // eslint-disable-line no-console
});
}
})();
void (async () => {
for await (const data of socket.listener('disconnect')) {
const channel = agServer.exchange.channel('sc-' + socket.id);
channel.unsubscribe();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
void agServer.exchange.transmitPublish(channelToEmit!, {
id: socket.id,
type: 'DISCONNECTED',
});
}
})();
}
})();
httpServer.listen(options.port);
// @ts-expect-error Shouldn't there be a 'ready' event?
resolve(agServer);
} }
/* eslint-enable no-console */ /* eslint-enable no-console */
}); });

View File

@ -5,7 +5,7 @@ import morgan from 'morgan';
import * as http from 'http'; import * as http from 'http';
import bodyParser from 'body-parser'; import bodyParser from 'body-parser';
import cors from 'cors'; import cors from 'cors';
import { SCServer } from 'socketcluster-server'; import { AGServer } from 'socketcluster-server';
import { ApolloServer } from 'apollo-server-express'; import { ApolloServer } from 'apollo-server-express';
import { AddData, ReportBaseFields, Store } from './store'; import { AddData, ReportBaseFields, Store } from './store';
import { resolvers, schema } from './api/schema'; import { resolvers, schema } from './api/schema';
@ -21,9 +21,9 @@ function serveUmdModule(name: string) {
} }
function routes( function routes(
options: SCServer.SCServerOptions, options: AGServer.AGServerOptions,
store: Store, store: Store,
scServer: SCServer scServer: AGServer
): Router { ): Router {
const limit = options.maxRequestBody; const limit = options.maxRequestBody;
const logHTTPRequests = options.logHTTPRequests; const logHTTPRequests = options.logHTTPRequests;
@ -65,7 +65,8 @@ function routes(
serveUmdModule('@redux-devtools/app'); serveUmdModule('@redux-devtools/app');
app.get('/port.js', function (req, res) { app.get('/port.js', function (req, res) {
res.send(`reduxDevToolsPort = ${options.port!}`); // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
res.send(`reduxDevToolsPort = ${options.port}`);
}); });
app.get('*', function (req, res) { app.get('*', function (req, res) {
res.sendFile(path.join(__dirname, '../app/index.html')); res.sendFile(path.join(__dirname, '../app/index.html'));
@ -108,7 +109,7 @@ function routes(
id: (r as ReportBaseFields).id, id: (r as ReportBaseFields).id,
error: (r as { error: string }).error, error: (r as { error: string }).error,
}); });
scServer.exchange.publish('report', { void scServer.exchange.transmitPublish('report', {
type: 'add', type: 'add',
data: r, data: r,
}); });

View File

@ -1,6 +1,6 @@
import { v4 as uuidV4 } from 'uuid'; import { v4 as uuidV4 } from 'uuid';
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import { SCServer } from 'socketcluster-server'; import { AGServer } from 'socketcluster-server';
import { Knex } from 'knex'; import { Knex } from 'knex';
import connector from './db/connector'; import connector from './db/connector';
@ -139,7 +139,7 @@ export interface Store {
add: (data: AddData) => Promise<ReportBaseFields | { error: string }>; add: (data: AddData) => Promise<ReportBaseFields | { error: string }>;
} }
function createStore(options: SCServer.SCServerOptions): Store { function createStore(options: AGServer.AGServerOptions): Store {
knex = connector(options); knex = connector(options);
return { return {

View File

@ -1,86 +0,0 @@
import SCWorker from 'socketcluster/scworker';
import express from 'express';
import routes from './routes';
import createStore from './store';
const app = express();
class Worker extends SCWorker {
run() {
const httpServer = this.httpServer;
const scServer = this.scServer;
const options = this.options;
const store = createStore(options);
httpServer.on('request', app);
app.use(routes(options, store, scServer));
scServer.addMiddleware(scServer.MIDDLEWARE_EMIT, function (req, next) {
const channel = req.event;
const data = req.data;
if (
channel.substr(0, 3) === 'sc-' ||
channel === 'respond' ||
channel === 'log'
) {
scServer.exchange.publish(channel, data);
} else if (channel === 'log-noid') {
scServer.exchange.publish('log', { id: req.socket.id, data: data });
}
next();
});
scServer.addMiddleware(scServer.MIDDLEWARE_SUBSCRIBE, function (req, next) {
next();
if (req.channel === 'report') {
store
.list()
.then(function (data) {
req.socket.emit(req.channel!, { type: 'list', data: data });
})
.catch(function (error) {
console.error(error); // eslint-disable-line no-console
});
}
});
scServer.on('connection', function (socket) {
let channelToWatch: string, channelToEmit: string;
socket.on('login', function (this: Worker, credentials, respond) {
if (credentials === 'master') {
channelToWatch = 'respond';
channelToEmit = 'log';
} else {
channelToWatch = 'log';
channelToEmit = 'respond';
}
this.exchange.subscribe('sc-' + socket.id).watch(function (msg) {
socket.emit(channelToWatch, msg);
});
respond(null, channelToWatch);
});
socket.on('getReport', function (id: string, respond) {
store
.get(id)
.then(function (data) {
respond(null, data);
})
.catch(function (error) {
console.error(error); // eslint-disable-line no-console
});
});
socket.on('disconnect', function (this: Worker) {
const channel = this.exchange.channel('sc-' + socket.id);
channel.unsubscribe();
channel.destroy();
scServer.exchange.publish(channelToEmit, {
id: socket.id,
type: 'DISCONNECTED',
});
});
});
}
}
new Worker();

View File

@ -1,6 +1,6 @@
import childProcess from 'child_process'; import childProcess from 'child_process';
import request from 'supertest'; import request from 'supertest';
import scClient from 'socketcluster-client'; import socketClusterClient from 'socketcluster-client';
jest.setTimeout(10000); jest.setTimeout(10000);
@ -44,20 +44,30 @@ describe('Server', function () {
}); });
describe('Realtime monitoring', function () { describe('Realtime monitoring', function () {
let socket: scClient.SCClientSocket, let socket: socketClusterClient.AGClientSocket,
socket2: scClient.SCClientSocket, socket2: socketClusterClient.AGClientSocket,
channel; channel;
beforeAll(function () { beforeAll(function () {
socket = scClient.connect({ hostname: 'localhost', port: 8000 }); socket = socketClusterClient.create({
hostname: 'localhost',
port: 8000,
});
socket.connect(); socket.connect();
socket.on('error', function (error) { void (async () => {
console.error('Socket1 error', error); // eslint-disable-line no-console for await (const data of socket.listener('error')) {
console.error('Socket1 error', data.error); // eslint-disable-line no-console
}
})();
socket2 = socketClusterClient.create({
hostname: 'localhost',
port: 8000,
}); });
socket2 = scClient.connect({ hostname: 'localhost', port: 8000 });
socket2.connect(); socket2.connect();
socket.on('error', function (error) { void (async () => {
console.error('Socket2 error', error); // eslint-disable-line no-console for await (const data of socket2.listener('error')) {
}); console.error('Socket2 error', data.error); // eslint-disable-line no-console
}
})();
}); });
afterAll(function () { afterAll(function () {
@ -65,73 +75,50 @@ describe('Server', function () {
socket2.disconnect(); socket2.disconnect();
}); });
it('should connect', function () { it('should connect', async function () {
return new Promise<void>((done) => { const data = await socket.listener('connect').once();
socket.on('connect', function (status) { expect(data.id).toBeTruthy();
expect(status.id).toBeTruthy();
done();
});
});
}); });
it('should login', function () { it('should login', async function () {
socket.emit( try {
'login', const channelName = (await socket.invoke('login', 'master')) as string;
'master', expect(channelName).toBe('respond');
function (error: Error | undefined, channelName: string) { channel = socket.subscribe(channelName);
if (error) { expect(channel.SUBSCRIBED).toBe('subscribed');
/* eslint-disable-next-line no-console */ } catch (error) {
console.log(error); console.log(error);
return; }
}
expect(channelName).toBe('respond');
channel = socket.subscribe(channelName);
expect(channel.SUBSCRIBED).toBe('subscribed');
}
);
}); });
it('should send message', function () { it('should send message', async function () {
return new Promise<void>((done) => { const data = {
const data = { type: 'ACTION',
type: 'ACTION', payload: {
payload: { todos: 'do some',
todos: 'do some', },
}, action: {
timestamp: 1483349708506,
action: { action: {
timestamp: 1483349708506, type: 'ADD_TODO',
action: { text: 'hggg',
type: 'ADD_TODO',
text: 'hggg',
},
}, },
instanceId: 'tAmA7H5fclyWhvizAAAi', },
name: 'LoggerInstance', instanceId: 'tAmA7H5fclyWhvizAAAi',
id: 'tAmA7H5fclyWhvizAAAi', name: 'LoggerInstance',
}; id: 'tAmA7H5fclyWhvizAAAi',
};
socket2.emit( try {
'login', const channelName = (await socket.invoke('login', '')) as string;
'', expect(channelName).toBe('log');
function (error: Error | undefined, channelName: string) { const channel2 = socket2.subscribe(channelName);
if (error) { expect(channel2.SUBSCRIBED).toBe('subscribed');
/* eslint-disable-next-line no-console */ const message = await channel2.listener('subscribe').once();
console.log(error); expect(message).toEqual(data);
return; } catch (error) {
} console.log(error);
expect(channelName).toBe('log'); }
const channel2 = socket2.subscribe(channelName);
expect(channel2.SUBSCRIBED).toBe('subscribed');
channel2.on('subscribe', function () {
channel2.watch(function (message) {
expect(message).toEqual(data);
done();
});
socket.emit(channelName, data);
});
}
);
});
}); });
}); });
@ -149,97 +136,61 @@ describe('Server', function () {
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) ' + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) ' +
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36', 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36',
}; };
it('should add a report', function () { it('should add a report', async function () {
return new Promise<void>((done) => { const res = await request('http://localhost:8000')
// eslint-disable-next-line @typescript-eslint/no-floating-promises .post('/')
request('http://localhost:8000') .send(report)
.post('/') .set('Accept', 'application/json')
.send(report) .expect('Content-Type', /application\/json/)
.set('Accept', 'application/json') .expect(200);
.expect('Content-Type', /application\/json/) id = res.body.id;
.expect(200) expect(id).toBeTruthy();
.then(function (res: { body: { id: string } }) {
id = res.body.id;
expect(id).toBeTruthy();
done();
});
});
}); });
it('should get the report', function () { it('should get the report', async function () {
return new Promise<void>((done) => { const res = await request('http://localhost:8000')
// eslint-disable-next-line @typescript-eslint/no-floating-promises .post('/')
request('http://localhost:8000') .send({
.post('/') op: 'get',
.send({ id: id,
op: 'get', })
id: id, .set('Accept', 'application/json')
}) .expect('Content-Type', /application\/json/)
.set('Accept', 'application/json') .expect(200);
.expect('Content-Type', /application\/json/) expect(res.body).toMatchObject(report);
.expect(200)
.then(function (res: { body: unknown }) {
expect(res.body).toMatchObject(report);
done();
});
});
}); });
it('should list reports', function () { it('should list reports', async function () {
return new Promise<void>((done) => { const res = await request('http://localhost:8000')
// eslint-disable-next-line @typescript-eslint/no-floating-promises .post('/')
request('http://localhost:8000') .send({
.post('/') op: 'list',
.send({ })
op: 'list', .set('Accept', 'application/json')
}) .expect('Content-Type', /application\/json/)
.set('Accept', 'application/json') .expect(200);
.expect('Content-Type', /application\/json/) expect(res.body).toHaveLength(1);
.expect(200) expect(res.body[0].id).toBe(id);
.then(function (res: { expect(res.body[0].title).toBe('Test report');
body: { id: string; title: string | null; added: string | null }[]; expect(res.body[0].added).toBeTruthy();
}) {
expect(res.body).toHaveLength(1);
expect(res.body[0].id).toBe(id);
expect(res.body[0].title).toBe('Test report');
expect(res.body[0].added).toBeTruthy();
done();
});
});
}); });
}); });
describe('GraphQL backend', function () { describe('GraphQL backend', function () {
it('should get the report', function () { it('should get the report', async function () {
return new Promise<void>((done) => { const res = await request('http://localhost:8000')
// eslint-disable-next-line @typescript-eslint/no-floating-promises .post('/graphql')
request('http://localhost:8000') .send({
.post('/graphql') query: '{ reports { id, type, title } }',
.send({ })
query: '{ reports { id, type, title } }', .set('Accept', 'application/json')
}) .expect('Content-Type', /application\/json/)
.set('Accept', 'application/json') .expect(200);
.expect('Content-Type', /application\/json/) const reports = res.body.data.reports;
.expect(200) expect(reports).toHaveLength(1);
.then(function (res: { expect(reports[0].id).toBeTruthy();
body: { expect(reports[0].title).toBe('Test report');
data: { expect(reports[0].type).toBe('ACTIONS');
reports: {
id: string;
title: string | null;
type: string | null;
}[];
};
};
}) {
const reports = res.body.data.reports;
expect(reports).toHaveLength(1);
expect(reports[0].id).toBeTruthy();
expect(reports[0].title).toBe('Test report');
expect(reports[0].type).toBe('ACTIONS');
done();
});
});
}); });
}); });
}); });

View File

@ -47,7 +47,7 @@
"jsan": "^3.1.14", "jsan": "^3.1.14",
"querystring": "^0.2.1", "querystring": "^0.2.1",
"rn-host-detect": "^1.2.0", "rn-host-detect": "^1.2.0",
"socketcluster-client": "^14.3.2" "socketcluster-client": "^16.1.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.17.10", "@babel/cli": "^7.17.10",
@ -59,7 +59,7 @@
"@types/jsan": "^3.1.2", "@types/jsan": "^3.1.2",
"@types/node": "^16.11.38", "@types/node": "^16.11.38",
"@types/rn-host-detect": "^1.2.0", "@types/rn-host-detect": "^1.2.0",
"@types/socketcluster-client": "^13.0.5", "@types/socketcluster-client": "^16.0.0",
"@typescript-eslint/eslint-plugin": "^5.27.0", "@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0", "@typescript-eslint/parser": "^5.27.0",
"eslint": "^8.17.0", "eslint": "^8.17.0",

View File

@ -1,5 +1,5 @@
import { stringify, parse } from 'jsan'; import { stringify, parse } from 'jsan';
import socketCluster, { SCClientSocket } from 'socketcluster-client'; import socketClusterClient, { AGClientSocket } from 'socketcluster-client';
import configureStore from './configureStore'; import configureStore from './configureStore';
import { defaultSocketOptions } from './constants'; import { defaultSocketOptions } from './constants';
import getHostForRN from 'rn-host-detect'; import getHostForRN from 'rn-host-detect';
@ -179,7 +179,7 @@ class DevToolsEnhancer<S, A extends Action<unknown>> {
store!: EnhancedStore<S, A, {}>; store!: EnhancedStore<S, A, {}>;
filters: LocalFilter | undefined; filters: LocalFilter | undefined;
instanceId?: string; instanceId?: string;
socket?: SCClientSocket; socket?: AGClientSocket;
sendTo?: string; sendTo?: string;
instanceName: string | undefined; instanceName: string | undefined;
appInstanceId!: string; appInstanceId!: string;
@ -241,7 +241,8 @@ class DevToolsEnhancer<S, A extends Action<unknown>> {
) { ) {
const message: MessageToRelay = { const message: MessageToRelay = {
type, type,
id: this.socket!.id, // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
id: this.socket!.id!,
name: this.instanceName, name: this.instanceName,
instanceId: this.appInstanceId, instanceId: this.appInstanceId,
}; };
@ -279,7 +280,8 @@ class DevToolsEnhancer<S, A extends Action<unknown>> {
} else if (action) { } else if (action) {
message.action = action as ActionCreatorObject[]; message.action = action as ActionCreatorObject[];
} }
this.socket!.emit(this.socket!.id ? 'log' : 'log-noid', message); // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
void this.socket!.transmit(this.socket!.id ? 'log' : 'log-noid', message);
} }
dispatchRemotely( dispatchRemotely(
@ -300,7 +302,9 @@ class DevToolsEnhancer<S, A extends Action<unknown>> {
if ( if (
message.type === 'IMPORT' || message.type === 'IMPORT' ||
(message.type === 'SYNC' && (message.type === 'SYNC' &&
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
this.socket!.id && this.socket!.id &&
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
message.id !== this.socket!.id) message.id !== this.socket!.id)
) { ) {
this.store.liftedStore.dispatch({ this.store.liftedStore.dispatch({
@ -387,15 +391,22 @@ class DevToolsEnhancer<S, A extends Action<unknown>> {
} }
login() { login() {
this.socket!.emit('login', 'master', (err: Error, channelName: string) => { void (async () => {
if (err) { try {
console.log(err); // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return; const channelName = (await this.socket!.invoke(
'login',
'master'
)) as string;
this.channel = channelName;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
for await (const data of this.socket!.subscribe(channelName)) {
this.handleMessages(data as Message<S, A>);
}
} catch (error) {
console.log(error);
} }
this.channel = channelName; })();
this.socket!.subscribe(channelName).watch(this.handleMessages);
this.socket!.on(channelName, this.handleMessages);
});
this.started = true; this.started = true;
this.relay('START'); this.relay('START');
} }
@ -404,11 +415,9 @@ class DevToolsEnhancer<S, A extends Action<unknown>> {
this.started = false; this.started = false;
this.isMonitored = false; this.isMonitored = false;
if (!this.socket) return; if (!this.socket) return;
this.socket.destroyChannel(this.channel!); void this.socket.unsubscribe(this.channel!);
if (keepConnected) { this.socket.closeChannel(this.channel!);
this.socket.off(this.channel, this.handleMessages); if (!keepConnected) {
} else {
this.socket.off();
this.socket.disconnect(); this.socket.disconnect();
} }
}; };
@ -420,36 +429,48 @@ class DevToolsEnhancer<S, A extends Action<unknown>> {
) )
return; return;
this.socket = socketCluster.create(this.socketOptions); this.socket = socketClusterClient.create(this.socketOptions);
this.socket.on('error', (err) => { void (async () => {
// if we've already had this error before, increment it's counter, otherwise assign it '1' since we've had the error once. // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
// eslint-disable-next-line no-prototype-builtins for await (const data of this.socket!.listener('error')) {
this.errorCounts[err.name] = this.errorCounts.hasOwnProperty(err.name) // if we've already had this error before, increment it's counter, otherwise assign it '1' since we've had the error once.
? this.errorCounts[err.name] + 1 // eslint-disable-next-line no-prototype-builtins,@typescript-eslint/no-unsafe-argument
: 1; this.errorCounts[data.error.name] = this.errorCounts.hasOwnProperty(
data.error.name
)
? this.errorCounts[data.error.name] + 1
: 1;
if (this.suppressConnectErrors) { if (this.suppressConnectErrors) {
if (this.errorCounts[err.name] === 1) { if (this.errorCounts[data.error.name] === 1) {
console.log( console.log(
'remote-redux-devtools: Socket connection errors are being suppressed. ' + 'remote-redux-devtools: Socket connection errors are being suppressed. ' +
'\n' + '\n' +
"This can be disabled by setting suppressConnectErrors to 'false'." "This can be disabled by setting suppressConnectErrors to 'false'."
); );
console.log(err); console.log(data.error);
}
} else {
console.log(data.error);
} }
} else {
console.log(err);
} }
}); })();
this.socket.on('connect', () => {
console.log('connected to remotedev-server'); void (async () => {
this.errorCounts = {}; // clear the errorCounts object, so that we'll log any new errors in the event of a disconnect // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
this.login(); for await (const data of this.socket!.listener('connect')) {
}); console.log('connected to remotedev-server');
this.socket.on('disconnect', () => { this.errorCounts = {}; // clear the errorCounts object, so that we'll log any new errors in the event of a disconnect
this.stop(true); this.login();
}); }
})();
void (async () => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
for await (const data of this.socket!.listener('disconnect')) {
this.stop(true);
}
})();
}; };
checkForReducerErrors = (liftedState = this.getLiftedStateRaw()) => { checkForReducerErrors = (liftedState = this.getLiftedStateRaw()) => {

File diff suppressed because it is too large Load Diff