@rvoh/psychic-websockets 0.5.0 → 2.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/cable/index.js +26 -27
- package/dist/cjs/src/cable/redisWsKey.js +1 -4
- package/dist/cjs/src/cable/ws.js +18 -20
- package/dist/cjs/src/error/ws/InvalidWsPathError.js +1 -4
- package/dist/cjs/src/error/ws/MissingWsRedisConnection.js +1 -4
- package/dist/cjs/src/helpers/EnvInternal.js +3 -5
- package/dist/cjs/src/index.js +3 -9
- package/dist/cjs/src/psychic-app-websockets/cache.js +3 -8
- package/dist/cjs/src/psychic-app-websockets/index.js +6 -9
- package/dist/esm/src/cable/index.js +3 -1
- package/dist/esm/src/cable/ws.js +2 -1
- package/package.json +5 -4
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Cable {
|
|
1
|
+
import { DreamCLI } from '@rvoh/dream';
|
|
2
|
+
import { PsychicApp, PsychicServer } from '@rvoh/psychic';
|
|
3
|
+
import { PsychicLogos } from '@rvoh/psychic/internal';
|
|
4
|
+
import { colorize } from '@rvoh/psychic/utils';
|
|
5
|
+
import { createAdapter } from '@socket.io/redis-adapter';
|
|
6
|
+
import * as socketio from 'socket.io';
|
|
7
|
+
import MissingWsRedisConnection from '../error/ws/MissingWsRedisConnection.js';
|
|
8
|
+
import EnvInternal from '../helpers/EnvInternal.js';
|
|
9
|
+
import PsychicAppWebsockets from '../psychic-app-websockets/index.js';
|
|
10
|
+
export default class Cable {
|
|
11
11
|
app;
|
|
12
12
|
io;
|
|
13
13
|
httpServer;
|
|
@@ -28,7 +28,7 @@ class Cable {
|
|
|
28
28
|
return;
|
|
29
29
|
// for socket.io, we have to circumvent the normal process for starting a
|
|
30
30
|
// psychic server so that we can bind socket.io to the http instance.
|
|
31
|
-
this.httpServer =
|
|
31
|
+
this.httpServer = PsychicServer.createPsychicHttpInstance(this.app, this.config.psychicApp.sslCredentials);
|
|
32
32
|
this.io = new socketio.Server(this.httpServer, { cors: this.config.psychicApp.corsOptions });
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
@@ -47,7 +47,7 @@ class Cable {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
catch (error) {
|
|
50
|
-
if (
|
|
50
|
+
if (EnvInternal.boolean('PSYCHIC_DANGEROUSLY_PERMIT_WS_EXCEPTIONS'))
|
|
51
51
|
throw error;
|
|
52
52
|
else {
|
|
53
53
|
;
|
|
@@ -67,7 +67,7 @@ class Cable {
|
|
|
67
67
|
}
|
|
68
68
|
});
|
|
69
69
|
this.bindToRedis();
|
|
70
|
-
const psychicAppWebsockets =
|
|
70
|
+
const psychicAppWebsockets = PsychicAppWebsockets.getOrFail();
|
|
71
71
|
await this.listen({
|
|
72
72
|
port: parseInt((port || psychicAppWebsockets.psychicApp.port).toString()),
|
|
73
73
|
});
|
|
@@ -99,7 +99,7 @@ class Cable {
|
|
|
99
99
|
async listen({ port }) {
|
|
100
100
|
return new Promise(accept => {
|
|
101
101
|
this.httpServer.listen(port, () => {
|
|
102
|
-
if (!
|
|
102
|
+
if (!EnvInternal.isTest) {
|
|
103
103
|
welcomeMessage({ port });
|
|
104
104
|
}
|
|
105
105
|
accept(true);
|
|
@@ -115,39 +115,38 @@ class Cable {
|
|
|
115
115
|
const pubClient = this.config.websocketOptions.connection;
|
|
116
116
|
const subClient = this.config.websocketOptions.subConnection;
|
|
117
117
|
if (!pubClient || !subClient)
|
|
118
|
-
throw new
|
|
118
|
+
throw new MissingWsRedisConnection();
|
|
119
119
|
this.redisConnections.push(pubClient);
|
|
120
120
|
this.redisConnections.push(subClient);
|
|
121
121
|
pubClient.on('error', error => {
|
|
122
|
-
|
|
122
|
+
PsychicAppWebsockets.log('PUB CLIENT ERROR', error);
|
|
123
123
|
});
|
|
124
124
|
subClient.on('error', error => {
|
|
125
|
-
|
|
125
|
+
PsychicAppWebsockets.log('sub CLIENT ERROR', error);
|
|
126
126
|
});
|
|
127
127
|
try {
|
|
128
|
-
this.io.adapter(
|
|
128
|
+
this.io.adapter(createAdapter(pubClient, subClient));
|
|
129
129
|
}
|
|
130
130
|
catch (error) {
|
|
131
|
-
|
|
131
|
+
PsychicAppWebsockets.log('FAILED TO ADAPT', error);
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
-
exports.default = Cable;
|
|
136
135
|
function welcomeMessage({ port }) {
|
|
137
|
-
if (
|
|
138
|
-
|
|
136
|
+
if (EnvInternal.isDevelopment) {
|
|
137
|
+
DreamCLI.logger.log(colorize(PsychicLogos.asciiLogo(), { color: 'greenBright' }), {
|
|
139
138
|
logPrefix: '',
|
|
140
139
|
});
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
DreamCLI.logger.log('', { logPrefix: '' });
|
|
141
|
+
DreamCLI.logger.log(colorize('✺ ' + PsychicApp.getOrFail().appName, { color: 'greenBright' }), {
|
|
143
142
|
logPrefix: '',
|
|
144
143
|
});
|
|
145
|
-
|
|
144
|
+
DreamCLI.logger.log(colorize(`└─ http://localhost:${port.toString()}`, { color: 'greenBright' }), {
|
|
146
145
|
logPrefix: '',
|
|
147
146
|
});
|
|
148
|
-
|
|
147
|
+
DreamCLI.logger.log('', { logPrefix: '' });
|
|
149
148
|
}
|
|
150
149
|
else {
|
|
151
|
-
|
|
150
|
+
DreamCLI.logger.log(`psychic dev server started at port ${port} with websockets`);
|
|
152
151
|
}
|
|
153
152
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = redisWsKey;
|
|
4
|
-
function redisWsKey(websocketId, redisKeyPrefix) {
|
|
1
|
+
export default function redisWsKey(websocketId, redisKeyPrefix) {
|
|
5
2
|
return `${redisKeyPrefix}:${websocketId}:socket_ids`;
|
|
6
3
|
}
|
package/dist/cjs/src/cable/ws.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Ws {
|
|
1
|
+
import { DateTime, Dream } from '@rvoh/dream';
|
|
2
|
+
import { uniq } from '@rvoh/dream/utils';
|
|
3
|
+
import { Emitter } from '@socket.io/redis-emitter';
|
|
4
|
+
import InvalidWsPathError from '../error/ws/InvalidWsPathError.js';
|
|
5
|
+
import EnvInternal from '../helpers/EnvInternal.js';
|
|
6
|
+
import PsychicAppWebsockets from '../psychic-app-websockets/index.js';
|
|
7
|
+
import redisWsKey from './redisWsKey.js';
|
|
8
|
+
export default class Ws {
|
|
10
9
|
allowedPaths;
|
|
11
10
|
/**
|
|
12
11
|
* @internal
|
|
@@ -57,10 +56,10 @@ class Ws {
|
|
|
57
56
|
* @param redisKeyPrefix - (optional) the prefix you wish to use to couple to this id (defaults to 'user')
|
|
58
57
|
*/
|
|
59
58
|
static async register(socket, id, redisKeyPrefix = 'user') {
|
|
60
|
-
const psychicWebsocketsApp =
|
|
59
|
+
const psychicWebsocketsApp = PsychicAppWebsockets.getOrFail();
|
|
61
60
|
const redisClient = psychicWebsocketsApp.websocketOptions.connection;
|
|
62
61
|
const websocketId = idOrDreamToId(id);
|
|
63
|
-
const redisKey = (
|
|
62
|
+
const redisKey = redisWsKey(websocketId, redisKeyPrefix);
|
|
64
63
|
const socketIdsToKeep = await redisClient.lrange(redisKey, -2, -1);
|
|
65
64
|
await redisClient
|
|
66
65
|
.multi()
|
|
@@ -68,8 +67,8 @@ class Ws {
|
|
|
68
67
|
.rpush(redisKey, ...socketIdsToKeep, socket.id)
|
|
69
68
|
.expireat(redisKey,
|
|
70
69
|
// TODO: make this configurable in non-test environments
|
|
71
|
-
|
|
72
|
-
.plus(
|
|
70
|
+
DateTime.now()
|
|
71
|
+
.plus(EnvInternal.isTest ? { seconds: 15 } : { day: 1 })
|
|
73
72
|
.toSeconds())
|
|
74
73
|
.exec();
|
|
75
74
|
socket.on('disconnect', async () => {
|
|
@@ -111,9 +110,9 @@ class Ws {
|
|
|
111
110
|
boot() {
|
|
112
111
|
if (this.booted)
|
|
113
112
|
return;
|
|
114
|
-
const psychicWebsocketsApp =
|
|
113
|
+
const psychicWebsocketsApp = PsychicAppWebsockets.getOrFail();
|
|
115
114
|
this.redisClient = psychicWebsocketsApp.websocketOptions.connection;
|
|
116
|
-
this.io = new
|
|
115
|
+
this.io = new Emitter(this.redisClient).of(this.namespace);
|
|
117
116
|
this.booted = true;
|
|
118
117
|
}
|
|
119
118
|
/**
|
|
@@ -127,7 +126,7 @@ class Ws {
|
|
|
127
126
|
// eslint-disable-next-line
|
|
128
127
|
data = {}) {
|
|
129
128
|
if (this.allowedPaths.length && !this.allowedPaths.includes(path))
|
|
130
|
-
throw new
|
|
129
|
+
throw new InvalidWsPathError(path);
|
|
131
130
|
this.boot();
|
|
132
131
|
const socketIds = await this.findSocketIds(idOrDreamToId(id));
|
|
133
132
|
for (const socketId of socketIds) {
|
|
@@ -141,7 +140,7 @@ class Ws {
|
|
|
141
140
|
*/
|
|
142
141
|
async findSocketIds(userId) {
|
|
143
142
|
this.boot();
|
|
144
|
-
return
|
|
143
|
+
return uniq(await this.redisClient.lrange(this.redisKey(userId), 0, -1));
|
|
145
144
|
}
|
|
146
145
|
/**
|
|
147
146
|
* @internal
|
|
@@ -150,10 +149,9 @@ class Ws {
|
|
|
150
149
|
* when this Ws instance was constructed.
|
|
151
150
|
*/
|
|
152
151
|
redisKey(userId) {
|
|
153
|
-
return (
|
|
152
|
+
return redisWsKey(userId, this.redisKeyPrefix);
|
|
154
153
|
}
|
|
155
154
|
}
|
|
156
|
-
exports.default = Ws;
|
|
157
155
|
function idOrDreamToId(id) {
|
|
158
|
-
return id instanceof
|
|
156
|
+
return id instanceof Dream ? id.primaryKeyValue().toString() : id.toString();
|
|
159
157
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
class InvalidWsPathError extends Error {
|
|
1
|
+
export default class InvalidWsPathError extends Error {
|
|
4
2
|
invalidPath;
|
|
5
3
|
constructor(invalidPath) {
|
|
6
4
|
super();
|
|
@@ -12,4 +10,3 @@ class InvalidWsPathError extends Error {
|
|
|
12
10
|
`;
|
|
13
11
|
}
|
|
14
12
|
}
|
|
15
|
-
exports.default = InvalidWsPathError;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
class MissingWsRedisConnection extends Error {
|
|
1
|
+
export default class MissingWsRedisConnection extends Error {
|
|
4
2
|
get message() {
|
|
5
3
|
return `
|
|
6
4
|
No websocket redis connection was found, even though
|
|
@@ -29,4 +27,3 @@ export default async (psy: PsychicApp) => {
|
|
|
29
27
|
`;
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
|
-
exports.default = MissingWsRedisConnection;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const EnvInternal = new dream_1.Env();
|
|
5
|
-
exports.default = EnvInternal;
|
|
1
|
+
import { Env } from '@rvoh/dream';
|
|
2
|
+
const EnvInternal = new Env();
|
|
3
|
+
export default EnvInternal;
|
package/dist/cjs/src/index.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var index_js_1 = require("./cable/index.js");
|
|
5
|
-
Object.defineProperty(exports, "Cable", { enumerable: true, get: function () { return index_js_1.default; } });
|
|
6
|
-
var ws_js_1 = require("./cable/ws.js");
|
|
7
|
-
Object.defineProperty(exports, "Ws", { enumerable: true, get: function () { return ws_js_1.default; } });
|
|
8
|
-
var index_js_2 = require("./psychic-app-websockets/index.js");
|
|
9
|
-
Object.defineProperty(exports, "PsychicAppWebsockets", { enumerable: true, get: function () { return index_js_2.default; } });
|
|
1
|
+
export { default as Cable } from './cable/index.js';
|
|
2
|
+
export { default as Ws } from './cable/ws.js';
|
|
3
|
+
export { default as PsychicAppWebsockets } from './psychic-app-websockets/index.js';
|
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.cachePsychicAppWebsockets = cachePsychicAppWebsockets;
|
|
4
|
-
exports.getCachedPsychicAppWebsockets = getCachedPsychicAppWebsockets;
|
|
5
|
-
exports.getCachedPsychicAppWebsocketsOrFail = getCachedPsychicAppWebsocketsOrFail;
|
|
6
1
|
let _psychicAppWebsockets = undefined;
|
|
7
|
-
function cachePsychicAppWebsockets(psychicAppWebsockets) {
|
|
2
|
+
export function cachePsychicAppWebsockets(psychicAppWebsockets) {
|
|
8
3
|
_psychicAppWebsockets = psychicAppWebsockets;
|
|
9
4
|
}
|
|
10
|
-
function getCachedPsychicAppWebsockets() {
|
|
5
|
+
export function getCachedPsychicAppWebsockets() {
|
|
11
6
|
return _psychicAppWebsockets;
|
|
12
7
|
}
|
|
13
|
-
function getCachedPsychicAppWebsocketsOrFail() {
|
|
8
|
+
export function getCachedPsychicAppWebsocketsOrFail() {
|
|
14
9
|
if (!_psychicAppWebsockets)
|
|
15
10
|
throw new Error('must call `cachePsychicAppWebsockets` before loading cached psychic application websockets');
|
|
16
11
|
return _psychicAppWebsockets;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const cache_js_1 = require("./cache.js");
|
|
5
|
-
class PsychicAppWebsockets {
|
|
1
|
+
import Cable from '../cable/index.js';
|
|
2
|
+
import { cachePsychicAppWebsockets, getCachedPsychicAppWebsocketsOrFail } from './cache.js';
|
|
3
|
+
export default class PsychicAppWebsockets {
|
|
6
4
|
static async init(psychicApp, cb) {
|
|
7
5
|
const psychicWsApp = new PsychicAppWebsockets(psychicApp);
|
|
8
6
|
await cb(psychicWsApp);
|
|
@@ -15,12 +13,12 @@ class PsychicAppWebsockets {
|
|
|
15
13
|
}
|
|
16
14
|
});
|
|
17
15
|
psychicApp.override('server:start', async (psychicServer, { port }) => {
|
|
18
|
-
const cable = new
|
|
16
|
+
const cable = new Cable(psychicServer.expressApp, psychicWsApp);
|
|
19
17
|
await cable.start(port);
|
|
20
18
|
psychicServer.attach('cable', cable);
|
|
21
19
|
return cable.httpServer;
|
|
22
20
|
});
|
|
23
|
-
|
|
21
|
+
cachePsychicAppWebsockets(psychicWsApp);
|
|
24
22
|
return psychicWsApp;
|
|
25
23
|
}
|
|
26
24
|
/**
|
|
@@ -30,7 +28,7 @@ class PsychicAppWebsockets {
|
|
|
30
28
|
* The psychic application can be set by calling PsychicApp#init
|
|
31
29
|
*/
|
|
32
30
|
static getOrFail() {
|
|
33
|
-
return
|
|
31
|
+
return getCachedPsychicAppWebsocketsOrFail();
|
|
34
32
|
}
|
|
35
33
|
psychicApp;
|
|
36
34
|
static log(...args) {
|
|
@@ -83,4 +81,3 @@ class PsychicAppWebsockets {
|
|
|
83
81
|
}
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
|
-
exports.default = PsychicAppWebsockets;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { DreamCLI } from '@rvoh/dream';
|
|
2
|
-
import {
|
|
2
|
+
import { PsychicApp, PsychicServer } from '@rvoh/psychic';
|
|
3
|
+
import { PsychicLogos } from '@rvoh/psychic/internal';
|
|
4
|
+
import { colorize } from '@rvoh/psychic/utils';
|
|
3
5
|
import { createAdapter } from '@socket.io/redis-adapter';
|
|
4
6
|
import * as socketio from 'socket.io';
|
|
5
7
|
import MissingWsRedisConnection from '../error/ws/MissingWsRedisConnection.js';
|
package/dist/esm/src/cable/ws.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { DateTime, Dream
|
|
1
|
+
import { DateTime, Dream } from '@rvoh/dream';
|
|
2
|
+
import { uniq } from '@rvoh/dream/utils';
|
|
2
3
|
import { Emitter } from '@socket.io/redis-emitter';
|
|
3
4
|
import InvalidWsPathError from '../error/ws/InvalidWsPathError.js';
|
|
4
5
|
import EnvInternal from '../helpers/EnvInternal.js';
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@rvoh/psychic-websockets",
|
|
4
4
|
"description": "Websocket system for Psychic applications",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "2.0.0-alpha.1",
|
|
6
6
|
"author": "RVO Health",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"psy:js": "node ./dist/test-app/src/cli/index.js",
|
|
30
30
|
"psy:ts": "tsx ./test-app/src/cli/index.ts",
|
|
31
31
|
"build": "echo \"building cjs...\" && rm -rf dist && npx tsc -p ./tsconfig.cjs.build.json && echo \"building esm...\" && npx tsc -p ./tsconfig.esm.build.json",
|
|
32
|
+
"build:test-app": "rm -rf dist && echo \"building test app to esm...\" && npx tsc -p ./tsconfig.esm.build.test-app.json && echo \"building test app to cjs...\" && npx tsc -p ./tsconfig.cjs.build.test-app.json",
|
|
32
33
|
"uspec": "vitest --config ./spec/unit/vite.config.ts",
|
|
33
34
|
"fspec": "vitest run --config=./spec/features/vite.config.ts",
|
|
34
35
|
"fspec:hanging": "vitest run --config=./spec/features/vite.config.ts --reporter=hanging-process",
|
|
@@ -48,9 +49,9 @@
|
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@eslint/js": "=9.0.0",
|
|
51
|
-
"@rvoh/dream": "^
|
|
52
|
+
"@rvoh/dream": "^2.0.0-alpha.1",
|
|
52
53
|
"@rvoh/dream-spec-helpers": "^1.2.1",
|
|
53
|
-
"@rvoh/psychic": "^
|
|
54
|
+
"@rvoh/psychic": "^2.0.0-alpha.1",
|
|
54
55
|
"@rvoh/psychic-spec-helpers": "^1.1.3",
|
|
55
56
|
"@socket.io/redis-adapter": "^8.3.0",
|
|
56
57
|
"@socket.io/redis-emitter": "^5.1.0",
|
|
@@ -78,7 +79,7 @@
|
|
|
78
79
|
"typedoc": "^0.26.6",
|
|
79
80
|
"typescript": "^5.8.2",
|
|
80
81
|
"typescript-eslint": "=7.18.0",
|
|
81
|
-
"vitest": "^3.2.
|
|
82
|
+
"vitest": "^3.2.4"
|
|
82
83
|
},
|
|
83
84
|
"packageManager": "yarn@4.7.0",
|
|
84
85
|
"dependencies": {
|