@evolu/nodejs 1.0.1-preview.1 → 1.0.1-preview.11

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.
@@ -0,0 +1,3 @@
1
+ import type { TimingSafeEqual } from "@evolu/common";
2
+ export declare const createTimingSafeEqual: () => TimingSafeEqual;
3
+ //# sourceMappingURL=Crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Crypto.d.ts","sourceRoot":"","sources":["../src/Crypto.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,eAAO,MAAM,qBAAqB,QAAO,eAAkC,CAAC"}
package/dist/Crypto.js ADDED
@@ -0,0 +1,2 @@
1
+ import { timingSafeEqual } from "node:crypto";
2
+ export const createTimingSafeEqual = () => timingSafeEqual;
@@ -1,7 +1,22 @@
1
- import { ConsoleDep } from "@evolu/common";
1
+ import { ConsoleDep, CreateSqliteDriverDep, Result, SqliteError } from "@evolu/common";
2
2
  import { Relay, RelayConfig } from "@evolu/common/evolu";
3
3
  export interface NodeJsRelayConfig extends RelayConfig {
4
+ /** The port number for the HTTP server. */
4
5
  readonly port?: number;
5
6
  }
6
- export declare const createNodeJsRelay: (deps: ConsoleDep) => (config: NodeJsRelayConfig) => Promise<Relay>;
7
+ /**
8
+ * Creates an Evolu relay server.
9
+ *
10
+ * This implementation uses Node.js and better-sqlite3. Additional relay
11
+ * implementations will be provided for other platforms (Bun, Deno, Cloudflare
12
+ * Workers, Vercel Edge, etc.).
13
+ */
14
+ export declare const createNodeJsRelay: (deps: ConsoleDep) => (config: NodeJsRelayConfig) => Promise<Result<Relay, SqliteError>>;
15
+ /**
16
+ * Creates an Evolu relay server with a custom SQLite driver.
17
+ *
18
+ * Use this when you need to provide a different SQLite driver implementation
19
+ * (e.g., using alternative SQLite libraries).
20
+ */
21
+ export declare const createNodeJsRelayWithSqliteDriver: (deps: ConsoleDep & CreateSqliteDriverDep) => (config: NodeJsRelayConfig) => Promise<Result<Relay, SqliteError>>;
7
22
  //# sourceMappingURL=Relay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../src/Evolu/Relay.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAQX,MAAM,eAAe,CAAC;AACvB,OAAO,EAML,KAAK,EACL,WAAW,EAEZ,MAAM,qBAAqB,CAAC;AAI7B,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,iBAAiB,GAC3B,MAAM,UAAU,MACV,QAAQ,iBAAiB,KAAG,OAAO,CAAC,KAAK,CA0G/C,CAAC"}
1
+ {"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../src/Evolu/Relay.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAIV,qBAAqB,EAKrB,MAAM,EAEN,WAAW,EAGZ,MAAM,eAAe,CAAC;AACvB,OAAO,EASL,KAAK,EACL,WAAW,EACZ,MAAM,qBAAqB,CAAC;AAO7B,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAC3B,MAAM,UAAU,MAChB,QAAQ,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,CAInD,CAAC;AAEf;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC,GAC3C,MAAM,UAAU,GAAG,qBAAqB,MACxC,QAAQ,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,CAKnD,CAAC"}
@@ -1,80 +1,161 @@
1
- import { createManyToManyMap, createRandom, createSqlite, getOrThrow, SimpleName, Uint8Array, } from "@evolu/common";
2
- import { applyProtocolMessageAsRelay, createRelayStorage, maxProtocolMessageSize, protocolVersion, } from "@evolu/common/evolu";
1
+ import { createManyToManyMap, createRandom, createSqlite, isAsync, ok, SimpleName, Uint8Array, } from "@evolu/common";
2
+ import { applyProtocolMessageAsRelay, createBaseSqliteStorageTables, createRelayLogger, createRelaySqliteStorage, createRelayStorageTables, defaultProtocolMessageMaxSize, parseOwnerIdFromOwnerWebSocketTransportUrl, } from "@evolu/common/evolu";
3
+ import { existsSync } from "fs";
4
+ import { createServer } from "http";
3
5
  import { WebSocket, WebSocketServer } from "ws";
4
6
  import { createBetterSqliteDriver } from "../BetterSqliteDriver.js";
5
- export const createNodeJsRelay = (deps) => async (config) => {
6
- const { port = 443, name = getOrThrow(SimpleName.from("evolu-relay")), enableLogging = false, } = config;
7
- deps.console.log(`Evolu Relay started on port ${port}`);
8
- deps.console.enabled = enableLogging;
9
- const versionedName = getOrThrow(SimpleName.from(`${name}-${protocolVersion}`));
10
- const sqlite = getOrThrow(await createSqlite({
11
- ...deps,
12
- createSqliteDriver: createBetterSqliteDriver,
13
- })(versionedName));
14
- const sqliteStorageDeps = {
15
- sqlite,
16
- random: createRandom(),
17
- };
18
- const storage = getOrThrow(createRelayStorage(sqliteStorageDeps)({
19
- onStorageError: (error) => {
20
- deps.console.error("[relay]", "[storage]", error);
21
- },
22
- }));
7
+ import { createTimingSafeEqual } from "../Crypto.js";
8
+ /**
9
+ * Creates an Evolu relay server.
10
+ *
11
+ * This implementation uses Node.js and better-sqlite3. Additional relay
12
+ * implementations will be provided for other platforms (Bun, Deno, Cloudflare
13
+ * Workers, Vercel Edge, etc.).
14
+ */
15
+ export const createNodeJsRelay = (deps) => (config) => createNodeJsRelayWithSqliteDriver({
16
+ ...deps,
17
+ createSqliteDriver: createBetterSqliteDriver,
18
+ })(config);
19
+ /**
20
+ * Creates an Evolu relay server with a custom SQLite driver.
21
+ *
22
+ * Use this when you need to provide a different SQLite driver implementation
23
+ * (e.g., using alternative SQLite libraries).
24
+ */
25
+ export const createNodeJsRelayWithSqliteDriver = (deps) => (config) => createNodeJsRelayWithDeps({
26
+ ...deps,
27
+ random: createRandom(),
28
+ timingSafeEqual: createTimingSafeEqual(),
29
+ })(config);
30
+ const createNodeJsRelayWithDeps = (deps) => async ({ port = 443, name = SimpleName.orThrow("evolu-relay"), enableLogging = false, isOwnerAllowed, isOwnerWithinQuota, }) => {
31
+ const log = createRelayLogger(deps);
32
+ log.started(enableLogging, port);
33
+ const dbFileExists = existsSync(`${name}.db`);
34
+ const sqlite = await createSqlite(deps)(name);
35
+ if (!sqlite.ok)
36
+ return sqlite;
37
+ const depsWithSqlite = { ...deps, sqlite: sqlite.value };
38
+ if (!dbFileExists) {
39
+ const baseTables = createBaseSqliteStorageTables(depsWithSqlite);
40
+ if (!baseTables.ok)
41
+ return baseTables;
42
+ const relayTables = createRelayStorageTables(depsWithSqlite);
43
+ if (!relayTables.ok)
44
+ return relayTables;
45
+ }
46
+ const storage = createRelaySqliteStorage(depsWithSqlite)({
47
+ onStorageError: log.storageError,
48
+ ...(isOwnerWithinQuota && { isOwnerWithinQuota }),
49
+ });
50
+ const server = createServer();
23
51
  const wss = new WebSocketServer({
24
- maxPayload: maxProtocolMessageSize,
25
- port,
52
+ maxPayload: defaultProtocolMessageMaxSize,
53
+ noServer: true,
26
54
  });
27
55
  const ownerSocketsMap = createManyToManyMap();
56
+ server.on("upgrade", (request, socket, head) => {
57
+ socket.on("error", log.upgradeSocketError);
58
+ const completeUpgrade = () => {
59
+ socket.removeListener("error", log.upgradeSocketError);
60
+ wss.handleUpgrade(request, socket, head, (ws) => {
61
+ wss.emit("connection", ws, request);
62
+ });
63
+ };
64
+ if (!isOwnerAllowed) {
65
+ completeUpgrade();
66
+ return;
67
+ }
68
+ const ownerId = parseOwnerIdFromOwnerWebSocketTransportUrl(request.url ?? "");
69
+ if (!ownerId) {
70
+ log.invalidOrMissingOwnerIdInUrl(request.url);
71
+ socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
72
+ socket.destroy();
73
+ return;
74
+ }
75
+ void (async () => {
76
+ const result = isOwnerAllowed(ownerId);
77
+ const isAllowed = isAsync(result) ? await result : result;
78
+ if (!isAllowed) {
79
+ log.unauthorizedOwner(ownerId);
80
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
81
+ socket.destroy();
82
+ return;
83
+ }
84
+ completeUpgrade();
85
+ })();
86
+ });
28
87
  wss.on("connection", (ws) => {
29
- ws.on("error", deps.console.error);
88
+ log.connectionEstablished(wss.clients.size);
89
+ ws.on("error", (error) => {
90
+ log.connectionWebSocketError(error);
91
+ });
30
92
  const options = {
31
93
  subscribe: (ownerId) => {
32
94
  ownerSocketsMap.add(ownerId, ws);
95
+ log.relayOptionSubscribe(ownerId, () => ownerSocketsMap.getValues(ownerId)?.size ?? 0);
96
+ },
97
+ unsubscribe: (ownerId) => {
98
+ ownerSocketsMap.remove(ownerId, ws);
99
+ log.relayOptionUnsubscribe(ownerId, () => ownerSocketsMap.getValues(ownerId)?.size ?? 0);
33
100
  },
34
101
  broadcast: (ownerId, message) => {
35
102
  const sockets = ownerSocketsMap.getValues(ownerId);
103
+ if (!sockets)
104
+ return;
105
+ let broadcastCount = 0;
36
106
  for (const socket of sockets) {
37
107
  if (socket !== ws && socket.readyState === WebSocket.OPEN) {
38
108
  socket.send(message, { binary: true });
109
+ broadcastCount++;
39
110
  }
40
111
  }
112
+ log.relayOptionBroadcast(ownerId, broadcastCount, sockets.size);
41
113
  },
42
114
  };
43
115
  ws.on("message", (message) => {
44
116
  if (!Uint8Array.is(message))
45
117
  return;
46
- const response = applyProtocolMessageAsRelay({ storage })(message, options);
47
- if (!response.ok) {
48
- deps.console.warn(response.error.type);
49
- return;
50
- }
51
- if (response.value) {
52
- ws.send(response.value, { binary: true });
53
- }
118
+ log.messageLength(message.length);
119
+ applyProtocolMessageAsRelay({ storage })(message, options)
120
+ .then((response) => {
121
+ if (!response.ok) {
122
+ log.applyProtocolMessageAsRelayError(response.error);
123
+ return;
124
+ }
125
+ ws.send(response.value.message, { binary: true });
126
+ log.responseLength(response.value.message.length);
127
+ })
128
+ .catch(log.applyProtocolMessageAsRelayUnknownError);
54
129
  });
55
130
  ws.on("close", () => {
56
131
  ownerSocketsMap.deleteValue(ws);
132
+ log.connectionClosed(wss.clients.size);
57
133
  });
58
134
  });
59
- const disposeWss = () => {
60
- deps.console.log("Shutting down Evolu Relay...");
135
+ server.listen(port);
136
+ const dispose = () => {
137
+ log.shuttingDown();
61
138
  wss.clients.forEach((client) => {
62
139
  if (client.readyState === WebSocket.OPEN) {
63
140
  client.close(1000, "Evolu Relay shutting down");
64
141
  }
65
142
  });
66
143
  wss.close(() => {
67
- deps.console.log("Evolu Relay disposed");
144
+ log.webSocketServerDisposed();
145
+ });
146
+ server.close(() => {
147
+ log.httpServerDisposed();
68
148
  });
69
149
  };
70
150
  let isDisposed = false;
71
- return {
151
+ const relay = {
72
152
  [Symbol.dispose]: () => {
73
153
  if (isDisposed)
74
154
  return;
75
155
  isDisposed = true;
76
- sqlite[Symbol.dispose]();
77
- disposeWss();
156
+ sqlite.value[Symbol.dispose]();
157
+ dispose();
78
158
  },
79
159
  };
160
+ return ok(relay);
80
161
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evolu/nodejs",
3
- "version": "1.0.1-preview.1",
3
+ "version": "1.0.1-preview.11",
4
4
  "description": "Evolu for Node.js",
5
5
  "author": "Daniel Steigerwald <daniel@steigerwald.cz>",
6
6
  "license": "MIT",
@@ -20,19 +20,21 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "better-sqlite3": "^11.10.0",
23
+ "better-sqlite3": "^12.1.1",
24
24
  "ws": "^8.18.2"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/better-sqlite3": "^7.6.13",
28
- "@types/node": "^22.15.30",
29
- "typescript": "^5.8.3",
30
- "vitest": "^3.2.2",
31
- "@evolu/common": "6.0.1-preview.2",
28
+ "@types/node": "^22.17.1",
29
+ "@types/ws": "^8.18.1",
30
+ "shx": "^0.4.0",
31
+ "typescript": "^5.9.2",
32
+ "vitest": "^4.0.4",
33
+ "@evolu/common": "6.0.1-preview.29",
32
34
  "@evolu/tsconfig": "0.0.2"
33
35
  },
34
36
  "peerDependencies": {
35
- "@evolu/common": "^6.0.1-preview.2"
37
+ "@evolu/common": "^6.0.1-preview.29"
36
38
  },
37
39
  "engines": {
38
40
  "node": ">=22.0.0"
package/src/Crypto.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { timingSafeEqual } from "node:crypto";
2
+ import type { TimingSafeEqual } from "@evolu/common";
3
+
4
+ export const createTimingSafeEqual = (): TimingSafeEqual => timingSafeEqual;
@@ -3,114 +3,215 @@ import {
3
3
  createManyToManyMap,
4
4
  createRandom,
5
5
  createSqlite,
6
- getOrThrow,
6
+ CreateSqliteDriverDep,
7
+ isAsync,
8
+ ok,
7
9
  OwnerId,
10
+ RandomDep,
11
+ Result,
8
12
  SimpleName,
13
+ SqliteError,
14
+ TimingSafeEqualDep,
9
15
  Uint8Array,
10
16
  } from "@evolu/common";
11
17
  import {
12
18
  applyProtocolMessageAsRelay,
13
19
  ApplyProtocolMessageAsRelayOptions,
14
- createRelayStorage,
15
- maxProtocolMessageSize,
16
- protocolVersion,
20
+ createBaseSqliteStorageTables,
21
+ createRelayLogger,
22
+ createRelaySqliteStorage,
23
+ createRelayStorageTables,
24
+ defaultProtocolMessageMaxSize,
25
+ parseOwnerIdFromOwnerWebSocketTransportUrl,
17
26
  Relay,
18
27
  RelayConfig,
19
- SqliteStorageDeps,
20
28
  } from "@evolu/common/evolu";
29
+ import { existsSync } from "fs";
30
+ import { createServer } from "http";
21
31
  import { WebSocket, WebSocketServer } from "ws";
22
32
  import { createBetterSqliteDriver } from "../BetterSqliteDriver.js";
33
+ import { createTimingSafeEqual } from "../Crypto.js";
23
34
 
24
35
  export interface NodeJsRelayConfig extends RelayConfig {
36
+ /** The port number for the HTTP server. */
25
37
  readonly port?: number;
26
38
  }
27
39
 
40
+ /**
41
+ * Creates an Evolu relay server.
42
+ *
43
+ * This implementation uses Node.js and better-sqlite3. Additional relay
44
+ * implementations will be provided for other platforms (Bun, Deno, Cloudflare
45
+ * Workers, Vercel Edge, etc.).
46
+ */
28
47
  export const createNodeJsRelay =
29
48
  (deps: ConsoleDep) =>
30
- async (config: NodeJsRelayConfig): Promise<Relay> => {
31
- const {
32
- port = 443,
33
- name = getOrThrow(SimpleName.from("evolu-relay")),
34
- enableLogging = false,
35
- } = config;
36
-
37
- deps.console.log(`Evolu Relay started on port ${port}`);
38
-
39
- deps.console.enabled = enableLogging;
40
-
41
- const versionedName = getOrThrow(
42
- SimpleName.from(`${name}-${protocolVersion}`),
43
- );
44
-
45
- const sqlite = getOrThrow(
46
- await createSqlite({
47
- ...deps,
48
- createSqliteDriver: createBetterSqliteDriver,
49
- })(versionedName),
50
- );
51
-
52
- const sqliteStorageDeps: SqliteStorageDeps = {
53
- sqlite,
49
+ (config: NodeJsRelayConfig): Promise<Result<Relay, SqliteError>> =>
50
+ createNodeJsRelayWithSqliteDriver({
51
+ ...deps,
52
+ createSqliteDriver: createBetterSqliteDriver,
53
+ })(config);
54
+
55
+ /**
56
+ * Creates an Evolu relay server with a custom SQLite driver.
57
+ *
58
+ * Use this when you need to provide a different SQLite driver implementation
59
+ * (e.g., using alternative SQLite libraries).
60
+ */
61
+ export const createNodeJsRelayWithSqliteDriver =
62
+ (deps: ConsoleDep & CreateSqliteDriverDep) =>
63
+ (config: NodeJsRelayConfig): Promise<Result<Relay, SqliteError>> =>
64
+ createNodeJsRelayWithDeps({
65
+ ...deps,
54
66
  random: createRandom(),
55
- };
56
-
57
- const storage = getOrThrow(
58
- createRelayStorage(sqliteStorageDeps)({
59
- onStorageError: (error) => {
60
- deps.console.error("[relay]", "[storage]", error);
61
- },
62
- }),
63
- );
67
+ timingSafeEqual: createTimingSafeEqual(),
68
+ })(config);
69
+
70
+ const createNodeJsRelayWithDeps =
71
+ (deps: ConsoleDep & CreateSqliteDriverDep & RandomDep & TimingSafeEqualDep) =>
72
+ async ({
73
+ port = 443,
74
+ name = SimpleName.orThrow("evolu-relay"),
75
+ enableLogging = false,
76
+ isOwnerAllowed,
77
+ isOwnerWithinQuota,
78
+ }: NodeJsRelayConfig): Promise<Result<Relay, SqliteError>> => {
79
+ const log = createRelayLogger(deps);
80
+ log.started(enableLogging, port);
81
+
82
+ const dbFileExists = existsSync(`${name}.db`);
83
+
84
+ const sqlite = await createSqlite(deps)(name);
85
+ if (!sqlite.ok) return sqlite;
86
+
87
+ const depsWithSqlite = { ...deps, sqlite: sqlite.value };
88
+
89
+ if (!dbFileExists) {
90
+ const baseTables = createBaseSqliteStorageTables(depsWithSqlite);
91
+ if (!baseTables.ok) return baseTables;
92
+
93
+ const relayTables = createRelayStorageTables(depsWithSqlite);
94
+ if (!relayTables.ok) return relayTables;
95
+ }
96
+
97
+ const storage = createRelaySqliteStorage(depsWithSqlite)({
98
+ onStorageError: log.storageError,
99
+ ...(isOwnerWithinQuota && { isOwnerWithinQuota }),
100
+ });
64
101
 
102
+ const server = createServer();
65
103
  const wss = new WebSocketServer({
66
- maxPayload: maxProtocolMessageSize,
67
- port,
104
+ maxPayload: defaultProtocolMessageMaxSize,
105
+ noServer: true,
68
106
  });
69
107
 
70
108
  const ownerSocketsMap = createManyToManyMap<OwnerId, WebSocket>();
71
109
 
110
+ server.on("upgrade", (request, socket, head) => {
111
+ socket.on("error", log.upgradeSocketError);
112
+
113
+ const completeUpgrade = () => {
114
+ socket.removeListener("error", log.upgradeSocketError);
115
+ wss.handleUpgrade(request, socket, head, (ws) => {
116
+ wss.emit("connection", ws, request);
117
+ });
118
+ };
119
+
120
+ if (!isOwnerAllowed) {
121
+ completeUpgrade();
122
+ return;
123
+ }
124
+
125
+ const ownerId = parseOwnerIdFromOwnerWebSocketTransportUrl(
126
+ request.url ?? "",
127
+ );
128
+
129
+ if (!ownerId) {
130
+ log.invalidOrMissingOwnerIdInUrl(request.url);
131
+ socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
132
+ socket.destroy();
133
+ return;
134
+ }
135
+
136
+ void (async () => {
137
+ const result = isOwnerAllowed(ownerId);
138
+ const isAllowed = isAsync(result) ? await result : result;
139
+ if (!isAllowed) {
140
+ log.unauthorizedOwner(ownerId);
141
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
142
+ socket.destroy();
143
+ return;
144
+ }
145
+ completeUpgrade();
146
+ })();
147
+ });
148
+
72
149
  wss.on("connection", (ws) => {
73
- ws.on("error", deps.console.error);
150
+ log.connectionEstablished(wss.clients.size);
151
+
152
+ ws.on("error", (error) => {
153
+ log.connectionWebSocketError(error);
154
+ });
74
155
 
75
156
  const options: ApplyProtocolMessageAsRelayOptions = {
76
157
  subscribe: (ownerId) => {
77
158
  ownerSocketsMap.add(ownerId, ws);
159
+ log.relayOptionSubscribe(
160
+ ownerId,
161
+ () => ownerSocketsMap.getValues(ownerId)?.size ?? 0,
162
+ );
163
+ },
164
+
165
+ unsubscribe: (ownerId) => {
166
+ ownerSocketsMap.remove(ownerId, ws);
167
+ log.relayOptionUnsubscribe(
168
+ ownerId,
169
+ () => ownerSocketsMap.getValues(ownerId)?.size ?? 0,
170
+ );
78
171
  },
172
+
79
173
  broadcast: (ownerId, message) => {
80
- const sockets = ownerSocketsMap.getValues(ownerId)!;
174
+ const sockets = ownerSocketsMap.getValues(ownerId);
175
+ if (!sockets) return;
176
+
177
+ let broadcastCount = 0;
81
178
  for (const socket of sockets) {
82
179
  if (socket !== ws && socket.readyState === WebSocket.OPEN) {
83
180
  socket.send(message, { binary: true });
181
+ broadcastCount++;
84
182
  }
85
183
  }
184
+
185
+ log.relayOptionBroadcast(ownerId, broadcastCount, sockets.size);
86
186
  },
87
187
  };
88
188
 
89
189
  ws.on("message", (message) => {
90
190
  if (!Uint8Array.is(message)) return;
191
+ log.messageLength(message.length);
91
192
 
92
- const response = applyProtocolMessageAsRelay({ storage })(
93
- message,
94
- options,
95
- );
96
-
97
- if (!response.ok) {
98
- deps.console.warn(response.error.type);
99
- return;
100
- }
101
-
102
- if (response.value) {
103
- ws.send(response.value, { binary: true });
104
- }
193
+ applyProtocolMessageAsRelay({ storage })(message, options)
194
+ .then((response) => {
195
+ if (!response.ok) {
196
+ log.applyProtocolMessageAsRelayError(response.error);
197
+ return;
198
+ }
199
+ ws.send(response.value.message, { binary: true });
200
+ log.responseLength(response.value.message.length);
201
+ })
202
+ .catch(log.applyProtocolMessageAsRelayUnknownError);
105
203
  });
106
204
 
107
205
  ws.on("close", () => {
108
206
  ownerSocketsMap.deleteValue(ws);
207
+ log.connectionClosed(wss.clients.size);
109
208
  });
110
209
  });
111
210
 
112
- const disposeWss = () => {
113
- deps.console.log("Shutting down Evolu Relay...");
211
+ server.listen(port);
212
+
213
+ const dispose = () => {
214
+ log.shuttingDown();
114
215
 
115
216
  wss.clients.forEach((client) => {
116
217
  if (client.readyState === WebSocket.OPEN) {
@@ -119,18 +220,24 @@ export const createNodeJsRelay =
119
220
  });
120
221
 
121
222
  wss.close(() => {
122
- deps.console.log("Evolu Relay disposed");
223
+ log.webSocketServerDisposed();
224
+ });
225
+
226
+ server.close(() => {
227
+ log.httpServerDisposed();
123
228
  });
124
229
  };
125
230
 
126
231
  let isDisposed = false;
127
232
 
128
- return {
233
+ const relay: Relay = {
129
234
  [Symbol.dispose]: () => {
130
235
  if (isDisposed) return;
131
236
  isDisposed = true;
132
- sqlite[Symbol.dispose]();
133
- disposeWss();
237
+ sqlite.value[Symbol.dispose]();
238
+ dispose();
134
239
  },
135
240
  };
241
+
242
+ return ok(relay);
136
243
  };