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

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,EAIrB,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,164 @@
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, 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, authenticateOwner, }) => {
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
+ });
49
+ const server = createServer();
23
50
  const wss = new WebSocketServer({
24
- maxPayload: maxProtocolMessageSize,
25
- port,
51
+ maxPayload: defaultProtocolMessageMaxSize,
52
+ noServer: true,
26
53
  });
27
54
  const ownerSocketsMap = createManyToManyMap();
55
+ server.on("upgrade", (request, socket, head) => {
56
+ socket.on("error", log.upgradeSocketError);
57
+ const completeUpgrade = () => {
58
+ socket.removeListener("error", log.upgradeSocketError);
59
+ wss.handleUpgrade(request, socket, head, (ws) => {
60
+ wss.emit("connection", ws, request);
61
+ });
62
+ };
63
+ if (!authenticateOwner) {
64
+ completeUpgrade();
65
+ return;
66
+ }
67
+ const ownerId = parseOwnerIdFromOwnerWebSocketTransportUrl(request.url ?? "");
68
+ if (!ownerId) {
69
+ log.invalidOrMissingOwnerIdInUrl(request.url);
70
+ socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
71
+ socket.destroy();
72
+ return;
73
+ }
74
+ authenticateOwner(ownerId)
75
+ .then((isAuthenticated) => {
76
+ if (!isAuthenticated) {
77
+ log.unauthorizedOwner(ownerId);
78
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
79
+ socket.destroy();
80
+ return;
81
+ }
82
+ completeUpgrade();
83
+ })
84
+ .catch((error) => {
85
+ log.authenticateOwnerError(error);
86
+ socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n");
87
+ socket.destroy();
88
+ });
89
+ });
28
90
  wss.on("connection", (ws) => {
29
- ws.on("error", deps.console.error);
91
+ log.connectionEstablished(wss.clients.size);
92
+ ws.on("error", (error) => {
93
+ log.connectionWebSocketError(error);
94
+ });
30
95
  const options = {
31
96
  subscribe: (ownerId) => {
32
97
  ownerSocketsMap.add(ownerId, ws);
98
+ log.relayOptionSubscribe(ownerId, () => ownerSocketsMap.getValues(ownerId)?.size ?? 0);
99
+ },
100
+ unsubscribe: (ownerId) => {
101
+ ownerSocketsMap.remove(ownerId, ws);
102
+ log.relayOptionUnsubscribe(ownerId, () => ownerSocketsMap.getValues(ownerId)?.size ?? 0);
33
103
  },
34
104
  broadcast: (ownerId, message) => {
35
105
  const sockets = ownerSocketsMap.getValues(ownerId);
106
+ if (!sockets)
107
+ return;
108
+ let broadcastCount = 0;
36
109
  for (const socket of sockets) {
37
110
  if (socket !== ws && socket.readyState === WebSocket.OPEN) {
38
111
  socket.send(message, { binary: true });
112
+ broadcastCount++;
39
113
  }
40
114
  }
115
+ log.relayOptionBroadcast(ownerId, broadcastCount, sockets.size);
41
116
  },
42
117
  };
43
118
  ws.on("message", (message) => {
44
119
  if (!Uint8Array.is(message))
45
120
  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
- }
121
+ log.messageLength(message.length);
122
+ applyProtocolMessageAsRelay({ storage })(message, options)
123
+ .then((response) => {
124
+ if (!response.ok) {
125
+ log.applyProtocolMessageAsRelayError(response.error);
126
+ return;
127
+ }
128
+ ws.send(response.value.message, { binary: true });
129
+ log.responseLength(response.value.message.length);
130
+ })
131
+ .catch(log.applyProtocolMessageAsRelayUnknownError);
54
132
  });
55
133
  ws.on("close", () => {
56
134
  ownerSocketsMap.deleteValue(ws);
135
+ log.connectionClosed(wss.clients.size);
57
136
  });
58
137
  });
59
- const disposeWss = () => {
60
- deps.console.log("Shutting down Evolu Relay...");
138
+ server.listen(port);
139
+ const dispose = () => {
140
+ log.shuttingDown();
61
141
  wss.clients.forEach((client) => {
62
142
  if (client.readyState === WebSocket.OPEN) {
63
143
  client.close(1000, "Evolu Relay shutting down");
64
144
  }
65
145
  });
66
146
  wss.close(() => {
67
- deps.console.log("Evolu Relay disposed");
147
+ log.webSocketServerDisposed();
148
+ });
149
+ server.close(() => {
150
+ log.httpServerDisposed();
68
151
  });
69
152
  };
70
153
  let isDisposed = false;
71
- return {
154
+ const relay = {
72
155
  [Symbol.dispose]: () => {
73
156
  if (isDisposed)
74
157
  return;
75
158
  isDisposed = true;
76
- sqlite[Symbol.dispose]();
77
- disposeWss();
159
+ sqlite.value[Symbol.dispose]();
160
+ dispose();
78
161
  },
79
162
  };
163
+ return ok(relay);
80
164
  };
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.10",
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.26",
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.26"
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
+ ok,
7
8
  OwnerId,
9
+ RandomDep,
10
+ Result,
8
11
  SimpleName,
12
+ SqliteError,
13
+ TimingSafeEqualDep,
9
14
  Uint8Array,
10
15
  } from "@evolu/common";
11
16
  import {
12
17
  applyProtocolMessageAsRelay,
13
18
  ApplyProtocolMessageAsRelayOptions,
14
- createRelayStorage,
15
- maxProtocolMessageSize,
16
- protocolVersion,
19
+ createBaseSqliteStorageTables,
20
+ createRelayLogger,
21
+ createRelaySqliteStorage,
22
+ createRelayStorageTables,
23
+ defaultProtocolMessageMaxSize,
24
+ parseOwnerIdFromOwnerWebSocketTransportUrl,
17
25
  Relay,
18
26
  RelayConfig,
19
- SqliteStorageDeps,
20
27
  } from "@evolu/common/evolu";
28
+ import { existsSync } from "fs";
29
+ import { createServer } from "http";
21
30
  import { WebSocket, WebSocketServer } from "ws";
22
31
  import { createBetterSqliteDriver } from "../BetterSqliteDriver.js";
32
+ import { createTimingSafeEqual } from "../Crypto.js";
23
33
 
24
34
  export interface NodeJsRelayConfig extends RelayConfig {
35
+ /** The port number for the HTTP server. */
25
36
  readonly port?: number;
26
37
  }
27
38
 
39
+ /**
40
+ * Creates an Evolu relay server.
41
+ *
42
+ * This implementation uses Node.js and better-sqlite3. Additional relay
43
+ * implementations will be provided for other platforms (Bun, Deno, Cloudflare
44
+ * Workers, Vercel Edge, etc.).
45
+ */
28
46
  export const createNodeJsRelay =
29
47
  (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,
48
+ (config: NodeJsRelayConfig): Promise<Result<Relay, SqliteError>> =>
49
+ createNodeJsRelayWithSqliteDriver({
50
+ ...deps,
51
+ createSqliteDriver: createBetterSqliteDriver,
52
+ })(config);
53
+
54
+ /**
55
+ * Creates an Evolu relay server with a custom SQLite driver.
56
+ *
57
+ * Use this when you need to provide a different SQLite driver implementation
58
+ * (e.g., using alternative SQLite libraries).
59
+ */
60
+ export const createNodeJsRelayWithSqliteDriver =
61
+ (deps: ConsoleDep & CreateSqliteDriverDep) =>
62
+ (config: NodeJsRelayConfig): Promise<Result<Relay, SqliteError>> =>
63
+ createNodeJsRelayWithDeps({
64
+ ...deps,
54
65
  random: createRandom(),
55
- };
66
+ timingSafeEqual: createTimingSafeEqual(),
67
+ })(config);
56
68
 
57
- const storage = getOrThrow(
58
- createRelayStorage(sqliteStorageDeps)({
59
- onStorageError: (error) => {
60
- deps.console.error("[relay]", "[storage]", error);
61
- },
62
- }),
63
- );
69
+ const createNodeJsRelayWithDeps =
70
+ (deps: ConsoleDep & CreateSqliteDriverDep & RandomDep & TimingSafeEqualDep) =>
71
+ async ({
72
+ port = 443,
73
+ name = SimpleName.orThrow("evolu-relay"),
74
+ enableLogging = false,
75
+ authenticateOwner,
76
+ }: NodeJsRelayConfig): Promise<Result<Relay, SqliteError>> => {
77
+ const log = createRelayLogger(deps);
78
+ log.started(enableLogging, port);
79
+
80
+ const dbFileExists = existsSync(`${name}.db`);
81
+
82
+ const sqlite = await createSqlite(deps)(name);
83
+ if (!sqlite.ok) return sqlite;
84
+
85
+ const depsWithSqlite = { ...deps, sqlite: sqlite.value };
86
+
87
+ if (!dbFileExists) {
88
+ const baseTables = createBaseSqliteStorageTables(depsWithSqlite);
89
+ if (!baseTables.ok) return baseTables;
90
+
91
+ const relayTables = createRelayStorageTables(depsWithSqlite);
92
+ if (!relayTables.ok) return relayTables;
93
+ }
94
+
95
+ const storage = createRelaySqliteStorage(depsWithSqlite)({
96
+ onStorageError: log.storageError,
97
+ });
64
98
 
99
+ const server = createServer();
65
100
  const wss = new WebSocketServer({
66
- maxPayload: maxProtocolMessageSize,
67
- port,
101
+ maxPayload: defaultProtocolMessageMaxSize,
102
+ noServer: true,
68
103
  });
69
104
 
70
105
  const ownerSocketsMap = createManyToManyMap<OwnerId, WebSocket>();
71
106
 
107
+ server.on("upgrade", (request, socket, head) => {
108
+ socket.on("error", log.upgradeSocketError);
109
+
110
+ const completeUpgrade = () => {
111
+ socket.removeListener("error", log.upgradeSocketError);
112
+ wss.handleUpgrade(request, socket, head, (ws) => {
113
+ wss.emit("connection", ws, request);
114
+ });
115
+ };
116
+
117
+ if (!authenticateOwner) {
118
+ completeUpgrade();
119
+ return;
120
+ }
121
+
122
+ const ownerId = parseOwnerIdFromOwnerWebSocketTransportUrl(
123
+ request.url ?? "",
124
+ );
125
+ if (!ownerId) {
126
+ log.invalidOrMissingOwnerIdInUrl(request.url);
127
+ socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
128
+ socket.destroy();
129
+ return;
130
+ }
131
+
132
+ authenticateOwner(ownerId)
133
+ .then((isAuthenticated) => {
134
+ if (!isAuthenticated) {
135
+ log.unauthorizedOwner(ownerId);
136
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
137
+ socket.destroy();
138
+ return;
139
+ }
140
+ completeUpgrade();
141
+ })
142
+ .catch((error: unknown) => {
143
+ log.authenticateOwnerError(error);
144
+ socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n");
145
+ socket.destroy();
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
  };