@sockethub/server 5.0.0-alpha.3 → 5.0.0-alpha.6

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.
Files changed (127) hide show
  1. package/README.md +54 -60
  2. package/bin/sockethub +4 -3
  3. package/package.json +42 -60
  4. package/res/socket.io.js +4908 -0
  5. package/res/sockethub-client.js +602 -0
  6. package/res/sockethub-client.min.js +19 -0
  7. package/sockethub.config.example.json +2 -3
  8. package/src/bootstrap/init.d.ts +20 -7
  9. package/src/bootstrap/init.test.ts +211 -0
  10. package/src/bootstrap/init.ts +152 -75
  11. package/src/bootstrap/load-platforms.ts +151 -0
  12. package/src/config.test.ts +27 -22
  13. package/src/config.ts +82 -78
  14. package/src/defaults.json +24 -16
  15. package/src/index.ts +67 -27
  16. package/src/janitor.test.ts +211 -0
  17. package/src/janitor.ts +145 -77
  18. package/src/listener.ts +151 -57
  19. package/src/middleware/create-activity-object.test.ts +28 -8
  20. package/src/middleware/create-activity-object.ts +17 -8
  21. package/src/middleware/expand-activity-stream.test.data.ts +332 -346
  22. package/src/middleware/expand-activity-stream.test.ts +65 -66
  23. package/src/middleware/expand-activity-stream.ts +29 -19
  24. package/src/middleware/store-credentials.test.ts +74 -62
  25. package/src/middleware/store-credentials.ts +15 -15
  26. package/src/middleware/validate.test.data.ts +240 -242
  27. package/src/middleware/validate.test.ts +39 -78
  28. package/src/middleware/validate.ts +63 -39
  29. package/src/middleware.test.ts +168 -138
  30. package/src/middleware.ts +62 -43
  31. package/src/platform-instance.test.ts +507 -213
  32. package/src/platform-instance.ts +337 -219
  33. package/src/platform.test.ts +375 -0
  34. package/src/platform.ts +306 -139
  35. package/src/process-manager.ts +75 -51
  36. package/src/routes.test.ts +43 -89
  37. package/src/routes.ts +40 -77
  38. package/src/sentry.test.ts +106 -0
  39. package/src/sentry.ts +19 -0
  40. package/src/sockethub.ts +186 -153
  41. package/src/util.ts +5 -0
  42. package/coverage/tmp/coverage-93126-1649152190997-0.json +0 -1
  43. package/dist/bootstrap/init.d.ts +0 -18
  44. package/dist/bootstrap/init.js +0 -63
  45. package/dist/bootstrap/init.js.map +0 -1
  46. package/dist/bootstrap/platforms.js +0 -75
  47. package/dist/common.d.ts +0 -3
  48. package/dist/common.js +0 -20
  49. package/dist/common.js.map +0 -1
  50. package/dist/config.d.ts +0 -6
  51. package/dist/config.js +0 -102
  52. package/dist/config.js.map +0 -1
  53. package/dist/crypto.d.ts +0 -10
  54. package/dist/crypto.js +0 -38
  55. package/dist/crypto.js.map +0 -1
  56. package/dist/defaults.json +0 -28
  57. package/dist/index.d.ts +0 -2
  58. package/dist/index.js +0 -25
  59. package/dist/index.js.map +0 -1
  60. package/dist/janitor.d.ts +0 -15
  61. package/dist/janitor.js +0 -89
  62. package/dist/janitor.js.map +0 -1
  63. package/dist/listener.d.ts +0 -28
  64. package/dist/listener.js +0 -91
  65. package/dist/listener.js.map +0 -1
  66. package/dist/middleware/create-activity-object.d.ts +0 -6
  67. package/dist/middleware/create-activity-object.js +0 -19
  68. package/dist/middleware/create-activity-object.js.map +0 -1
  69. package/dist/middleware/expand-activity-stream.d.ts +0 -2
  70. package/dist/middleware/expand-activity-stream.js +0 -33
  71. package/dist/middleware/expand-activity-stream.js.map +0 -1
  72. package/dist/middleware/expand-activity-stream.test.data.d.ts +0 -480
  73. package/dist/middleware/expand-activity-stream.test.data.js +0 -360
  74. package/dist/middleware/expand-activity-stream.test.data.js.map +0 -1
  75. package/dist/middleware/store-credentials.d.ts +0 -3
  76. package/dist/middleware/store-credentials.js +0 -19
  77. package/dist/middleware/store-credentials.js.map +0 -1
  78. package/dist/middleware/validate.d.ts +0 -2
  79. package/dist/middleware/validate.js +0 -58
  80. package/dist/middleware/validate.js.map +0 -1
  81. package/dist/middleware/validate.test.data.d.ts +0 -532
  82. package/dist/middleware/validate.test.data.js +0 -263
  83. package/dist/middleware/validate.test.data.js.map +0 -1
  84. package/dist/middleware.d.ts +0 -10
  85. package/dist/middleware.js +0 -54
  86. package/dist/middleware.js.map +0 -1
  87. package/dist/platform-instance.d.ts +0 -77
  88. package/dist/platform-instance.js +0 -211
  89. package/dist/platform-instance.js.map +0 -1
  90. package/dist/platform.d.ts +0 -6
  91. package/dist/platform.js +0 -187
  92. package/dist/platform.js.map +0 -1
  93. package/dist/process-manager.d.ts +0 -11
  94. package/dist/process-manager.js +0 -78
  95. package/dist/process-manager.js.map +0 -1
  96. package/dist/routes.d.ts +0 -13
  97. package/dist/routes.js +0 -83
  98. package/dist/routes.js.map +0 -1
  99. package/dist/sockethub.d.ts +0 -39
  100. package/dist/sockethub.js +0 -119
  101. package/dist/sockethub.js.map +0 -1
  102. package/dist/store.d.ts +0 -5
  103. package/dist/store.js +0 -17
  104. package/dist/store.js.map +0 -1
  105. package/src/bootstrap/platforms.js +0 -75
  106. package/src/common.test.ts +0 -54
  107. package/src/common.ts +0 -14
  108. package/src/config.d.ts +0 -2
  109. package/src/crypto.d.ts +0 -5
  110. package/src/crypto.test.ts +0 -41
  111. package/src/crypto.ts +0 -41
  112. package/src/janitor.d.ts +0 -8
  113. package/src/middleware/validate.d.ts +0 -1
  114. package/src/middleware.d.ts +0 -21
  115. package/src/sockethub.d.ts +0 -1
  116. package/src/store.test.ts +0 -28
  117. package/src/store.ts +0 -17
  118. package/test/init-suite.js +0 -41
  119. package/test/queue.functional.test.js +0 -0
  120. package/test/sockethub-suite.js +0 -25
  121. package/tsconfig.json +0 -18
  122. package/views/examples/dummy.ejs +0 -93
  123. package/views/examples/feeds.ejs +0 -90
  124. package/views/examples/irc.ejs +0 -239
  125. package/views/examples/shared.js +0 -72
  126. package/views/examples/xmpp.ejs +0 -191
  127. package/views/index.ejs +0 -17
package/src/index.ts CHANGED
@@ -1,28 +1,68 @@
1
- const Sockethub = require('./sockethub').default;
2
- const sockethub = new Sockethub();
3
-
4
- module.exports = async () => {
5
- process.once('uncaughtException', function (err) {
6
- console.log('UNCAUGHT EXCEPTION');
7
- // eslint-disable-next-line security-node/detect-crlf
8
- console.log(err.stack);
9
- process.exit(1);
10
- });
11
-
12
- process.once('SIGTERM', function () {
13
- console.log('Received TERM signal. Exiting.');
14
- process.exit(0);
15
- });
16
-
17
- process.once('SIGINT', function () {
18
- console.log('Received INT signal. Exiting.');
19
- process.exit(0);
20
- });
21
-
22
- process.once('exit', async function () {
23
- console.log('destroying all platform instances');
24
- await sockethub.removeAllPlatformInstances();
25
- });
26
-
27
- sockethub.boot();
1
+ import debug from "debug";
2
+ import config from "./config";
3
+ import Sockethub from "./sockethub";
4
+
5
+ let sentry: { readonly reportError: (err: Error) => void } = {
6
+ reportError: (err: Error) => {},
28
7
  };
8
+
9
+ export async function server() {
10
+ let sockethub: Sockethub;
11
+ const log = debug("sockethub:init");
12
+
13
+ // conditionally initialize sentry
14
+ if (config.get("sentry:dsn")) {
15
+ log("initializing sentry");
16
+ sentry = await import("./sentry");
17
+ }
18
+
19
+ try {
20
+ sockethub = new Sockethub();
21
+ } catch (err) {
22
+ sentry.reportError(err);
23
+ console.error(err);
24
+ process.exit(1);
25
+ }
26
+
27
+ process.once("uncaughtException", (err: Error) => {
28
+ console.error(
29
+ `${(new Date()).toUTCString()} UNCAUGHT EXCEPTION\n`,
30
+ err.stack,
31
+ );
32
+ sentry.reportError(err);
33
+ process.exit(1);
34
+ });
35
+
36
+ process.once("unhandledRejection", (err: Error) => {
37
+ console.error(
38
+ `${(new Date()).toUTCString()} UNHANDLED REJECTION\n`,
39
+ err,
40
+ );
41
+ sentry.reportError(err);
42
+ process.exit(1);
43
+ });
44
+
45
+ process.once("SIGTERM", () => {
46
+ console.log("Received TERM signal. Exiting.");
47
+ process.exit(0);
48
+ });
49
+
50
+ process.once("SIGINT", () => {
51
+ console.log("Received INT signal. Exiting.");
52
+ process.exit(0);
53
+ });
54
+
55
+ process.once("exit", async () => {
56
+ console.log("sockethub shutdown...");
57
+ await sockethub.shutdown();
58
+ process.exit(0);
59
+ });
60
+
61
+ try {
62
+ await sockethub.boot();
63
+ } catch (err) {
64
+ sentry.reportError(err);
65
+ console.error(err);
66
+ process.exit(1);
67
+ }
68
+ }
@@ -0,0 +1,211 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
2
+ import * as sinon from "sinon";
3
+
4
+ import { Janitor } from "./janitor.js";
5
+
6
+ const sockets = [
7
+ { id: "socket foo", emit: () => {} },
8
+ { id: "socket bar", emit: () => {} },
9
+ ];
10
+
11
+ function getPlatformInstanceFake() {
12
+ return {
13
+ flaggedForTermination: false,
14
+ config: {
15
+ initialized: false,
16
+ persist: true,
17
+ requireCredentials: ["foo", "bar"],
18
+ },
19
+ global: false,
20
+ shutdown: sinon.stub(),
21
+ process: {
22
+ removeListener: sinon.stub(),
23
+ },
24
+ sessions: new Set(["session foo", "session bar"]),
25
+ sessionCallbacks: {
26
+ close: (() =>
27
+ new Map([
28
+ ["session foo", function sessionFooClose() {}],
29
+ ["session bar", function sessionBarClose() {}],
30
+ ]))(),
31
+ message: (() =>
32
+ new Map([
33
+ ["session foo", function sessionFooMessage() {}],
34
+ ["session bar", function sessionBarMessage() {}],
35
+ ]))(),
36
+ },
37
+ };
38
+ }
39
+
40
+ const cycleInterval = 10;
41
+
42
+ describe("Janitor", () => {
43
+ let sandbox, fetchSocketsFake, janitor;
44
+
45
+ beforeEach((done) => {
46
+ sandbox = sinon.createSandbox();
47
+ fetchSocketsFake = sandbox.stub().returns(sockets);
48
+
49
+ janitor = new Janitor();
50
+ janitor.getSockets = fetchSocketsFake;
51
+ expect(janitor.cycleInterval).not.toEqual(cycleInterval);
52
+ janitor.cycleInterval = cycleInterval;
53
+ expect(janitor.cycleInterval).toEqual(cycleInterval);
54
+ janitor.start();
55
+ setTimeout(() => {
56
+ expect(janitor.cycleCount).toEqual(1);
57
+ done();
58
+ }, cycleInterval);
59
+ });
60
+
61
+ afterEach((done) => {
62
+ sandbox.reset();
63
+ janitor.stop();
64
+ setTimeout(() => {
65
+ done();
66
+ }, janitor.cycleInterval * 2);
67
+ });
68
+
69
+ it("runs cycle at every cycleInterval", (done) => {
70
+ const currCycleCount = janitor.cycleCount;
71
+ expect(currCycleCount).not.toEqual(0);
72
+ setTimeout(() => {
73
+ expect(janitor.cycleCount).toEqual(currCycleCount + 1);
74
+ setTimeout(() => {
75
+ expect(janitor.cycleCount).toEqual(currCycleCount + 2);
76
+ done();
77
+ }, cycleInterval);
78
+ }, cycleInterval);
79
+ });
80
+
81
+ describe("removeSessionCallbacks", () => {
82
+ it("removes session listeners and callbacks for a given platform", () => {
83
+ const pi = getPlatformInstanceFake();
84
+ const barMessage = pi.sessionCallbacks.message.get("session bar");
85
+ const barClose = pi.sessionCallbacks.close.get("session bar");
86
+ pi.flaggedForTermination = true;
87
+ janitor.removeSessionCallbacks(pi, "session foo");
88
+ sinon.assert.calledTwice(pi.process.removeListener);
89
+ expect(
90
+ pi.sessionCallbacks.message.get("session foo"),
91
+ ).toBeUndefined();
92
+ expect(pi.sessionCallbacks.message.get("session bar")).toEqual(
93
+ barMessage,
94
+ );
95
+ expect(
96
+ pi.sessionCallbacks.close.get("session foo"),
97
+ ).toBeUndefined();
98
+ expect(pi.sessionCallbacks.close.get("session bar")).toEqual(
99
+ barClose,
100
+ );
101
+ });
102
+ });
103
+
104
+ describe("removeStaleSocketSessions", () => {
105
+ it("doesnt do anything if the socket is active and stop is not flagged", async () => {
106
+ const pi = getPlatformInstanceFake();
107
+ janitor.removeSessionCallbacks = sinon.stub();
108
+ janitor.socketExists = sinon.stub().returns(true);
109
+ expect(janitor.stopTriggered).toBeFalse();
110
+ await janitor.removeStaleSocketSessions(pi);
111
+ sinon.assert.notCalled(janitor.removeSessionCallbacks);
112
+ });
113
+
114
+ it("removes session if the socket is active and stop is flagged", async () => {
115
+ const pi = getPlatformInstanceFake();
116
+ janitor.removeSessionCallbacks = sinon.stub();
117
+ janitor.socketExists = sinon.stub().returns(true);
118
+ janitor.stop();
119
+ expect(janitor.stopTriggered).toBeTrue();
120
+ await janitor.removeStaleSocketSessions(pi);
121
+ sinon.assert.calledTwice(janitor.removeSessionCallbacks);
122
+ sinon.assert.calledWith(
123
+ janitor.removeSessionCallbacks,
124
+ pi,
125
+ "session foo",
126
+ );
127
+ sinon.assert.calledWith(
128
+ janitor.removeSessionCallbacks,
129
+ pi,
130
+ "session bar",
131
+ );
132
+ });
133
+
134
+ it("removes session if the socket is inactive", async () => {
135
+ const pi = getPlatformInstanceFake();
136
+ janitor.removeSessionCallbacks = sinon.stub();
137
+ janitor.socketExists = sinon
138
+ .stub()
139
+ .onFirstCall()
140
+ .returns(false)
141
+ .onSecondCall()
142
+ .returns(true);
143
+ expect(janitor.stopTriggered).toBeFalse();
144
+ await janitor.removeStaleSocketSessions(pi);
145
+ sinon.assert.calledOnce(janitor.removeSessionCallbacks);
146
+ sinon.assert.calledWith(
147
+ janitor.removeSessionCallbacks,
148
+ pi,
149
+ "session foo",
150
+ );
151
+ });
152
+ });
153
+
154
+ describe("performStaleCheck", () => {
155
+ it("removes flagged and uninitialized platform instances", async () => {
156
+ const pi = getPlatformInstanceFake();
157
+ pi.flaggedForTermination = true;
158
+ pi.config.initialized = false;
159
+ janitor.removeStaleSocketSessions = sandbox.stub();
160
+ janitor.removeStalePlatformInstance = sandbox.stub();
161
+ await janitor.performStaleCheck(pi);
162
+ sinon.assert.calledOnce(janitor.removeStaleSocketSessions);
163
+ sinon.assert.calledOnce(janitor.removeStalePlatformInstance);
164
+ expect(pi.flaggedForTermination).toBeTrue();
165
+ });
166
+
167
+ it("flags for termination when there are not sockets", async () => {
168
+ const pi = getPlatformInstanceFake();
169
+ pi.sessions = new Set();
170
+ pi.flaggedForTermination = false;
171
+ pi.config.initialized = true;
172
+ janitor.removeStaleSocketSessions = sandbox.stub();
173
+ janitor.removeStalePlatformInstance = sandbox.stub();
174
+ await janitor.performStaleCheck(pi);
175
+ sinon.assert.calledOnce(janitor.removeStaleSocketSessions);
176
+ sinon.assert.calledOnce(janitor.removeStalePlatformInstance);
177
+ });
178
+ });
179
+
180
+ describe("removeStalePlatformInstance", () => {
181
+ it("flags stale platform", async () => {
182
+ const pi = getPlatformInstanceFake();
183
+ expect(pi.flaggedForTermination).toBeFalse();
184
+ await janitor.removeStalePlatformInstance(pi);
185
+ sinon.assert.notCalled(pi.shutdown);
186
+ expect(pi.flaggedForTermination).toBeTrue();
187
+ });
188
+
189
+ it("removes flagged stale platform", async () => {
190
+ const pi = getPlatformInstanceFake();
191
+ pi.flaggedForTermination = true;
192
+ await janitor.removeStalePlatformInstance(pi);
193
+ sinon.assert.calledOnce(pi.shutdown);
194
+ });
195
+ });
196
+
197
+ it("closes all connections when stop() is called", (done) => {
198
+ const prevCycle = janitor.cycleCount;
199
+ janitor.stop();
200
+ setTimeout(() => {
201
+ expect(janitor.cycleCount).toEqual(prevCycle);
202
+ setTimeout(() => {
203
+ expect(janitor.cycleCount).toEqual(prevCycle);
204
+ setTimeout(() => {
205
+ expect(janitor.cycleCount).toEqual(prevCycle);
206
+ done();
207
+ }, cycleInterval);
208
+ }, cycleInterval);
209
+ }, cycleInterval);
210
+ });
211
+ });
package/src/janitor.ts CHANGED
@@ -1,89 +1,157 @@
1
- import debug from 'debug';
2
-
3
- import PlatformInstance, { platformInstances } from './platform-instance';
4
- import listener, { SocketInstance } from "./listener";
5
-
6
- const rmLog = debug('sockethub:server:janitor');
7
-
8
- const TICK = 15000;
9
- let alreadyCalled: boolean = false;
10
- let cycleCount: number = 0; // a counter for each setInterval call
11
- let reportCount: number = 0; // number of times a report is printed
12
-
13
- /**
14
- * Every TICK the Janitor will compare existing platform instances with socket.ids (aka. sessionId)
15
- * If all of the sessionIds associated with a platformInstance have no corresponding socket.id
16
- * (from the http.io socket.io instance), then the platformInstance will first be flagged, if after
17
- * the next TICK the same state is determined, the platform will be destroyed (this allows for page
18
- * refreshes not destroying platform instances)
19
- */
20
- function janitorCycle() {
21
- if (! alreadyCalled) { alreadyCalled = true; }
22
- else { return; }
23
- rmLog('initializing resource manager');
24
- setInterval(async () => {
25
- cycleCount++;
26
- const sockets: Array<SocketInstance> = await listener.io.fetchSockets();
27
-
28
- if (! (cycleCount % 4)) {
29
- reportCount++;
30
- rmLog(`socket sessions: ${sockets.length} platform instances: ${platformInstances.size}`);
1
+ import debug from "debug";
2
+
3
+ import listener, { type SocketInstance } from "./listener.js";
4
+ import type PlatformInstance from "./platform-instance.js";
5
+ import { platformInstances } from "./platform-instance.js";
6
+
7
+ const rmLog = debug("sockethub:server:janitor");
8
+
9
+ export class Janitor {
10
+ cycleInterval = 15000;
11
+ cycleCount = 0; // a counter for each cycleInterval
12
+ reportCount = 0; // number of times a report is printed
13
+ protected stopTriggered = false;
14
+ protected sockets: Array<SocketInstance>;
15
+ private cycleRunning = false;
16
+
17
+ /**
18
+ * Every TICK the `Janitor` will compare existing platform instances with `socket.ids`
19
+ * (aka. `sessionId`). If all the `sessionIds` associated with a `platformInstance` have
20
+ * no corresponding `socket.id` (from the `http.io` `socket.io` instance), then the
21
+ * `platformInstance` will first be flagged, if after the next `cycleInterval` the same
22
+ * state is determined, the platform will be destroyed (this allows for page
23
+ * refreshes not destroying platform instances)
24
+ */
25
+ start(): void {
26
+ rmLog("initializing");
27
+ this.clean().then(() => {
28
+ rmLog("cleaning cycle started");
29
+ });
31
30
  }
32
31
 
33
- for (let platformInstance of platformInstances.values()) {
34
- removeStaleSocketSessions(platformInstance, sockets);
35
- // Static platforms are for global use, not tied to a unique to session / eg. credentials)
36
- if ((! platformInstance.global) && (platformInstance.sessions.size === 0)) {
37
- removeStalePlatformInstance(platformInstance);
38
- } else {
39
- platformInstance.flaggedForTermination = false;
40
- }
32
+ async stop(): Promise<void> {
33
+ this.stopTriggered = true;
34
+ rmLog("stopping, terminating all sessions");
35
+ for (const platformInstance of platformInstances.values()) {
36
+ this.removeStaleSocketSessions(platformInstance);
37
+ await this.removeStalePlatformInstance(platformInstance);
38
+ await platformInstance.shutdown();
39
+ }
41
40
  }
42
- }, TICK);
43
- }
44
41
 
45
- function socketExists(sessionId: string, sockets: Array<SocketInstance>) {
46
- for (let socket of sockets) {
47
- if (socket.id === sessionId) {
48
- return true;
42
+ private removeSessionCallbacks(
43
+ platformInstance: PlatformInstance,
44
+ sessionId: string,
45
+ ): void {
46
+ for (const key in platformInstance.sessionCallbacks) {
47
+ platformInstance.process.removeListener(
48
+ key,
49
+ platformInstance.sessionCallbacks[key].get(sessionId),
50
+ );
51
+ platformInstance.sessionCallbacks[key].delete(sessionId);
52
+ }
49
53
  }
50
- }
51
- return false;
52
- }
53
54
 
54
- function removeSessionCallbacks(platformInstance: PlatformInstance, sessionId: string) {
55
- for (const key in platformInstance.sessionCallbacks) {
56
- platformInstance.sessionCallbacks[key].delete(sessionId);
57
- }
58
- }
55
+ private removeStaleSocketSessions(
56
+ platformInstance: PlatformInstance,
57
+ ): void {
58
+ for (const sessionId of platformInstance.sessions.values()) {
59
+ if (this.stopTriggered || !this.socketExists(sessionId)) {
60
+ this.removeStaleSocketSession(platformInstance, sessionId);
61
+ }
62
+ }
63
+ }
59
64
 
60
- function removeStaleSocketSessions(platformInstance: PlatformInstance,
61
- sockets: Array<SocketInstance>) {
62
- for (const sessionId of platformInstance.sessions.values()) {
63
- if (! socketExists(sessionId, sockets)) {
64
- rmLog('removing stale socket session reference ' + sessionId + ' in platform instance '
65
- + platformInstance.id);
66
- platformInstance.sessions.delete(sessionId);
67
- removeSessionCallbacks(platformInstance, sessionId);
65
+ private removeStaleSocketSession(
66
+ platformInstance: PlatformInstance,
67
+ sessionId: string,
68
+ ) {
69
+ rmLog(
70
+ `removing ${
71
+ !this.stopTriggered ? "stale " : ""
72
+ }socket session reference ${sessionId}
73
+ in platform instance ${platformInstance.id}`,
74
+ );
75
+ platformInstance.sessions.delete(sessionId);
76
+ this.removeSessionCallbacks(platformInstance, sessionId);
77
+ }
78
+
79
+ private async removeStalePlatformInstance(
80
+ platformInstance: PlatformInstance,
81
+ ): Promise<void> {
82
+ if (platformInstance.flaggedForTermination || this.stopTriggered) {
83
+ rmLog(`terminating platform instance ${platformInstance.id}`);
84
+ await platformInstance.shutdown(); // terminate
85
+ } else {
86
+ rmLog(
87
+ `flagging for termination platform instance ${platformInstance.id} (no registered sessions found)`,
88
+ );
89
+ platformInstance.flaggedForTermination = true;
90
+ }
68
91
  }
69
- }
70
- }
71
92
 
72
- function removeStalePlatformInstance(platformInstance: PlatformInstance) {
73
- if (platformInstance.flaggedForTermination) {
74
- rmLog(`terminating platform instance ${platformInstance.id}`);
75
- platformInstance.destroy(); // terminate
76
- } else {
77
- rmLog(`flagging for termination platform instance ${platformInstance.id} ` +
78
- `(no registered sessions found)`);
79
- platformInstance.flaggedForTermination = true;
80
- }
93
+ private socketExists(sessionId: string) {
94
+ for (const socket of this.sockets) {
95
+ if (socket.id === sessionId) {
96
+ return true;
97
+ }
98
+ }
99
+ return false;
100
+ }
101
+
102
+ private async delay(ms: number): Promise<void> {
103
+ return await new Promise((resolve) => setTimeout(resolve, ms));
104
+ }
105
+
106
+ private async getSockets(): Promise<Array<SocketInstance>> {
107
+ return listener.io.fetchSockets();
108
+ }
109
+
110
+ private async performStaleCheck(platformInstance: PlatformInstance) {
111
+ this.removeStaleSocketSessions(platformInstance);
112
+ // Static platforms are for global use, not tied to a unique to session
113
+ // (e.g. a stateful platform where credentials are supplied)
114
+ if (!platformInstance.global) {
115
+ if (
116
+ (platformInstance.config.persist &&
117
+ !platformInstance.config.initialized) ||
118
+ platformInstance.sessions.size === 0
119
+ ) {
120
+ // either the platform failed to initialize, or there are no more sessions linked to it
121
+ await this.removeStalePlatformInstance(platformInstance);
122
+ }
123
+ }
124
+ }
125
+
126
+ private async clean(): Promise<void> {
127
+ if (this.stopTriggered) {
128
+ this.cycleRunning = false;
129
+ return;
130
+ }
131
+ if (this.cycleRunning) {
132
+ throw new Error(
133
+ "janitor cleanup cycle called while already running",
134
+ );
135
+ }
136
+ this.cycleRunning = true;
137
+ this.cycleCount++;
138
+ this.sockets = await this.getSockets();
139
+
140
+ if (!(this.cycleCount % 4)) {
141
+ this.reportCount++;
142
+ rmLog(
143
+ `socket sessions: ${this.sockets.length} platform instances: ${platformInstances.size}`,
144
+ );
145
+ }
146
+
147
+ for (const platformInstance of platformInstances.values()) {
148
+ await this.performStaleCheck(platformInstance);
149
+ }
150
+ this.cycleRunning = false;
151
+ await this.delay(this.cycleInterval);
152
+ return this.clean();
153
+ }
81
154
  }
82
155
 
83
- const janitor = {
84
- clean: janitorCycle,
85
- alreadyCalled: alreadyCalled,
86
- cycleCount: cycleCount,
87
- reportCount: reportCount
88
- };
156
+ const janitor = new Janitor();
89
157
  export default janitor;