@verdant-web/server 1.7.8
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/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/cjs/Baselines.d.ts +14 -0
- package/dist/cjs/Baselines.js +101 -0
- package/dist/cjs/Baselines.js.map +1 -0
- package/dist/cjs/ClientConnection.d.ts +18 -0
- package/dist/cjs/ClientConnection.js +106 -0
- package/dist/cjs/ClientConnection.js.map +1 -0
- package/dist/cjs/MessageSender.d.ts +5 -0
- package/dist/cjs/MessageSender.js +3 -0
- package/dist/cjs/MessageSender.js.map +1 -0
- package/dist/cjs/OperationHistory.d.ts +34 -0
- package/dist/cjs/OperationHistory.js +147 -0
- package/dist/cjs/OperationHistory.js.map +1 -0
- package/dist/cjs/Presence.d.ts +15 -0
- package/dist/cjs/Presence.js +60 -0
- package/dist/cjs/Presence.js.map +1 -0
- package/dist/cjs/Profiles.d.ts +10 -0
- package/dist/cjs/Profiles.js +20 -0
- package/dist/cjs/Profiles.js.map +1 -0
- package/dist/cjs/ReplicaKeepaliveTimers.d.ts +17 -0
- package/dist/cjs/ReplicaKeepaliveTimers.js +31 -0
- package/dist/cjs/ReplicaKeepaliveTimers.js.map +1 -0
- package/dist/cjs/Replicas.d.ts +23 -0
- package/dist/cjs/Replicas.js +174 -0
- package/dist/cjs/Replicas.js.map +1 -0
- package/dist/cjs/Server.d.ts +121 -0
- package/dist/cjs/Server.js +378 -0
- package/dist/cjs/Server.js.map +1 -0
- package/dist/cjs/ServerLibrary.d.ts +63 -0
- package/dist/cjs/ServerLibrary.js +468 -0
- package/dist/cjs/ServerLibrary.js.map +1 -0
- package/dist/cjs/ServerStorage.d.ts +35 -0
- package/dist/cjs/ServerStorage.js +60 -0
- package/dist/cjs/ServerStorage.js.map +1 -0
- package/dist/cjs/TokenProvider.d.ts +16 -0
- package/dist/cjs/TokenProvider.js +27 -0
- package/dist/cjs/TokenProvider.js.map +1 -0
- package/dist/cjs/TokenVerifier.d.ts +16 -0
- package/dist/cjs/TokenVerifier.js +29 -0
- package/dist/cjs/TokenVerifier.js.map +1 -0
- package/dist/cjs/files/FileMetadata.d.ts +28 -0
- package/dist/cjs/files/FileMetadata.js +69 -0
- package/dist/cjs/files/FileMetadata.js.map +1 -0
- package/dist/cjs/files/FileStorage.d.ts +35 -0
- package/dist/cjs/files/FileStorage.js +72 -0
- package/dist/cjs/files/FileStorage.js.map +1 -0
- package/dist/cjs/index.d.ts +9 -0
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/migrations/v0.sql.d.ts +2 -0
- package/dist/cjs/migrations/v0.sql.js +29 -0
- package/dist/cjs/migrations/v0.sql.js.map +1 -0
- package/dist/cjs/migrations/v1.sql.d.ts +2 -0
- package/dist/cjs/migrations/v1.sql.js +31 -0
- package/dist/cjs/migrations/v1.sql.js.map +1 -0
- package/dist/cjs/migrations/v2.sql.d.ts +2 -0
- package/dist/cjs/migrations/v2.sql.js +28 -0
- package/dist/cjs/migrations/v2.sql.js.map +1 -0
- package/dist/cjs/migrations/v3.sql.d.ts +2 -0
- package/dist/cjs/migrations/v3.sql.js +14 -0
- package/dist/cjs/migrations/v3.sql.js.map +1 -0
- package/dist/cjs/migrations/v4.sql.d.ts +2 -0
- package/dist/cjs/migrations/v4.sql.js +8 -0
- package/dist/cjs/migrations/v4.sql.js.map +1 -0
- package/dist/cjs/migrations.d.ts +2 -0
- package/dist/cjs/migrations.js +41 -0
- package/dist/cjs/migrations.js.map +1 -0
- package/dist/cjs/types.d.ts +22 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/Baselines.d.ts +14 -0
- package/dist/esm/Baselines.js +97 -0
- package/dist/esm/Baselines.js.map +1 -0
- package/dist/esm/ClientConnection.d.ts +18 -0
- package/dist/esm/ClientConnection.js +102 -0
- package/dist/esm/ClientConnection.js.map +1 -0
- package/dist/esm/MessageSender.d.ts +5 -0
- package/dist/esm/MessageSender.js +2 -0
- package/dist/esm/MessageSender.js.map +1 -0
- package/dist/esm/OperationHistory.d.ts +34 -0
- package/dist/esm/OperationHistory.js +143 -0
- package/dist/esm/OperationHistory.js.map +1 -0
- package/dist/esm/Presence.d.ts +15 -0
- package/dist/esm/Presence.js +53 -0
- package/dist/esm/Presence.js.map +1 -0
- package/dist/esm/Profiles.d.ts +10 -0
- package/dist/esm/Profiles.js +16 -0
- package/dist/esm/Profiles.js.map +1 -0
- package/dist/esm/ReplicaKeepaliveTimers.d.ts +17 -0
- package/dist/esm/ReplicaKeepaliveTimers.js +27 -0
- package/dist/esm/ReplicaKeepaliveTimers.js.map +1 -0
- package/dist/esm/Replicas.d.ts +23 -0
- package/dist/esm/Replicas.js +170 -0
- package/dist/esm/Replicas.js.map +1 -0
- package/dist/esm/Server.d.ts +121 -0
- package/dist/esm/Server.js +371 -0
- package/dist/esm/Server.js.map +1 -0
- package/dist/esm/ServerLibrary.d.ts +63 -0
- package/dist/esm/ServerLibrary.js +464 -0
- package/dist/esm/ServerLibrary.js.map +1 -0
- package/dist/esm/ServerStorage.d.ts +44 -0
- package/dist/esm/ServerStorage.js +58 -0
- package/dist/esm/ServerStorage.js.map +1 -0
- package/dist/esm/TokenProvider.d.ts +16 -0
- package/dist/esm/TokenProvider.js +20 -0
- package/dist/esm/TokenProvider.js.map +1 -0
- package/dist/esm/TokenVerifier.d.ts +16 -0
- package/dist/esm/TokenVerifier.js +22 -0
- package/dist/esm/TokenVerifier.js.map +1 -0
- package/dist/esm/files/FileMetadata.d.ts +28 -0
- package/dist/esm/files/FileMetadata.js +65 -0
- package/dist/esm/files/FileMetadata.js.map +1 -0
- package/dist/esm/files/FileStorage.d.ts +35 -0
- package/dist/esm/files/FileStorage.js +45 -0
- package/dist/esm/files/FileStorage.js.map +1 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/migrations/v0.sql.d.ts +2 -0
- package/dist/esm/migrations/v0.sql.js +27 -0
- package/dist/esm/migrations/v0.sql.js.map +1 -0
- package/dist/esm/migrations/v1.sql.d.ts +2 -0
- package/dist/esm/migrations/v1.sql.js +29 -0
- package/dist/esm/migrations/v1.sql.js.map +1 -0
- package/dist/esm/migrations/v2.sql.d.ts +2 -0
- package/dist/esm/migrations/v2.sql.js +26 -0
- package/dist/esm/migrations/v2.sql.js.map +1 -0
- package/dist/esm/migrations/v3.sql.d.ts +2 -0
- package/dist/esm/migrations/v3.sql.js +12 -0
- package/dist/esm/migrations/v3.sql.js.map +1 -0
- package/dist/esm/migrations/v4.sql.d.ts +2 -0
- package/dist/esm/migrations/v4.sql.js +6 -0
- package/dist/esm/migrations/v4.sql.js.map +1 -0
- package/dist/esm/migrations.d.ts +2 -0
- package/dist/esm/migrations.js +34 -0
- package/dist/esm/migrations.js.map +1 -0
- package/dist/esm/types.d.ts +22 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/tsconfig-cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +43 -0
- package/src/Baselines.ts +158 -0
- package/src/ClientConnection.ts +150 -0
- package/src/MessageSender.ts +10 -0
- package/src/OperationHistory.ts +206 -0
- package/src/Presence.ts +63 -0
- package/src/Profiles.ts +21 -0
- package/src/ReplicaKeepaliveTimers.ts +34 -0
- package/src/Replicas.ts +238 -0
- package/src/Server.ts +553 -0
- package/src/ServerLibrary.ts +727 -0
- package/src/TokenProvider.ts +39 -0
- package/src/TokenVerifier.ts +30 -0
- package/src/files/FileMetadata.ts +112 -0
- package/src/files/FileStorage.ts +87 -0
- package/src/index.ts +9 -0
- package/src/migrations/v0.sql.ts +26 -0
- package/src/migrations/v1.sql.ts +28 -0
- package/src/migrations/v2.sql.ts +26 -0
- package/src/migrations/v3.sql.ts +12 -0
- package/src/migrations/v4.sql.ts +6 -0
- package/src/migrations.ts +51 -0
- package/src/types.ts +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Grant Forrest
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# verdant server
|
|
2
|
+
|
|
3
|
+
This server is designed to sync data from clients. That includes replication between clients and realtime updates, including presence.
|
|
4
|
+
|
|
5
|
+
The server includes flexibility of transport - using either websockets or push/pull HTTP requests. To do this cleanly, the concept of a `clientKey` is employed. Each client connection (socket or individual request) is assigned a key and added to a collection (based on its associated library). When the server responds to a client, abstractly, it submits that response to the client's key. The connection management code then either sends that response as a socket message, or includes it in the HTTP response.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DocumentBaseline, Operation, Ref } from '@verdant-web/common';
|
|
2
|
+
import { Database } from 'better-sqlite3';
|
|
3
|
+
export declare class Baselines {
|
|
4
|
+
private db;
|
|
5
|
+
constructor(db: Database);
|
|
6
|
+
get: (libraryId: string, oid: string) => DocumentBaseline<any> | null;
|
|
7
|
+
getAllWithSubObjects: (libraryId: string, oid: string) => DocumentBaseline<any>[];
|
|
8
|
+
set: (libraryId: string, baseline: DocumentBaseline) => import("better-sqlite3").RunResult;
|
|
9
|
+
insertAll: (libraryId: string, baselines: DocumentBaseline[]) => void;
|
|
10
|
+
private hydrateSnapshot;
|
|
11
|
+
getAllAfter: (libraryId: string, timestamp: string | null) => DocumentBaseline<any>[];
|
|
12
|
+
applyOperations: (libraryId: string, oid: string, operations: Operation[], deletedRefs?: Ref[]) => never[] | undefined;
|
|
13
|
+
deleteAll: (libraryId: string) => void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Baselines = void 0;
|
|
4
|
+
const common_1 = require("@verdant-web/common");
|
|
5
|
+
class Baselines {
|
|
6
|
+
constructor(db) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
this.get = (libraryId, oid) => {
|
|
9
|
+
const row = this.db
|
|
10
|
+
.prepare(`
|
|
11
|
+
SELECT * FROM DocumentBaseline
|
|
12
|
+
WHERE libraryId = ? AND oid = ?
|
|
13
|
+
`)
|
|
14
|
+
.get(libraryId, oid);
|
|
15
|
+
if (!row) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return this.hydrateSnapshot(row);
|
|
19
|
+
};
|
|
20
|
+
this.getAllWithSubObjects = (libraryId, oid) => {
|
|
21
|
+
const rows = this.db
|
|
22
|
+
.prepare(`
|
|
23
|
+
SELECT * FROM DocumentBaseline
|
|
24
|
+
WHERE libraryId = ? AND oid LIKE ?
|
|
25
|
+
`)
|
|
26
|
+
.all(libraryId, `${oid}%`);
|
|
27
|
+
return rows.map(this.hydrateSnapshot);
|
|
28
|
+
};
|
|
29
|
+
this.set = (libraryId, baseline) => {
|
|
30
|
+
return this.db
|
|
31
|
+
.prepare(`
|
|
32
|
+
INSERT OR REPLACE INTO DocumentBaseline (libraryId, oid, snapshot, timestamp)
|
|
33
|
+
VALUES (?, ?, ?, ?)
|
|
34
|
+
`)
|
|
35
|
+
.run(libraryId, baseline.oid, JSON.stringify(baseline.snapshot), baseline.timestamp);
|
|
36
|
+
};
|
|
37
|
+
this.insertAll = (libraryId, baselines) => {
|
|
38
|
+
const tx = this.db.transaction(() => {
|
|
39
|
+
for (const baseline of baselines) {
|
|
40
|
+
this.db
|
|
41
|
+
.prepare(`
|
|
42
|
+
INSERT OR REPLACE INTO DocumentBaseline (libraryId, oid, snapshot, timestamp)
|
|
43
|
+
VALUES (?, ?, ?, ?)
|
|
44
|
+
`)
|
|
45
|
+
.run(libraryId, baseline.oid, JSON.stringify(baseline.snapshot), baseline.timestamp);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
tx();
|
|
49
|
+
};
|
|
50
|
+
this.hydrateSnapshot = (snapshot) => {
|
|
51
|
+
return Object.assign(Object.assign({}, snapshot), { snapshot: JSON.parse(snapshot.snapshot) });
|
|
52
|
+
};
|
|
53
|
+
this.getAllAfter = (libraryId, timestamp) => {
|
|
54
|
+
if (!timestamp) {
|
|
55
|
+
return this.db
|
|
56
|
+
.prepare(`
|
|
57
|
+
SELECT * FROM DocumentBaseline
|
|
58
|
+
WHERE libraryId = ?
|
|
59
|
+
ORDER BY timestamp ASC
|
|
60
|
+
`)
|
|
61
|
+
.all(libraryId)
|
|
62
|
+
.map(this.hydrateSnapshot);
|
|
63
|
+
}
|
|
64
|
+
return this.db
|
|
65
|
+
.prepare(`
|
|
66
|
+
SELECT * FROM DocumentBaseline
|
|
67
|
+
WHERE libraryId = ? AND timestamp > ?
|
|
68
|
+
ORDER BY timestamp ASC
|
|
69
|
+
`)
|
|
70
|
+
.all(libraryId, timestamp)
|
|
71
|
+
.map(this.hydrateSnapshot);
|
|
72
|
+
};
|
|
73
|
+
this.applyOperations = (libraryId, oid, operations, deletedRefs) => {
|
|
74
|
+
if (operations.length === 0)
|
|
75
|
+
return [];
|
|
76
|
+
let baseline = this.get(libraryId, oid);
|
|
77
|
+
if (!baseline) {
|
|
78
|
+
baseline = {
|
|
79
|
+
oid,
|
|
80
|
+
snapshot: {},
|
|
81
|
+
timestamp: operations[0].timestamp,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
for (const operation of operations) {
|
|
85
|
+
baseline.snapshot = (0, common_1.applyPatch)(baseline.snapshot, operation.data, deletedRefs);
|
|
86
|
+
}
|
|
87
|
+
baseline.timestamp = operations[operations.length - 1].timestamp;
|
|
88
|
+
this.set(libraryId, baseline);
|
|
89
|
+
};
|
|
90
|
+
this.deleteAll = (libraryId) => {
|
|
91
|
+
this.db
|
|
92
|
+
.prepare(`
|
|
93
|
+
DELETE FROM DocumentBaseline
|
|
94
|
+
WHERE libraryId = ?
|
|
95
|
+
`)
|
|
96
|
+
.run(libraryId);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.Baselines = Baselines;
|
|
101
|
+
//# sourceMappingURL=Baselines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Baselines.js","sourceRoot":"","sources":["../../src/Baselines.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ServerMessage } from '@verdant-web/common';
|
|
2
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
+
import { WebSocket } from 'ws';
|
|
4
|
+
import { TokenInfo } from './TokenVerifier.js';
|
|
5
|
+
export declare class ClientConnectionManager {
|
|
6
|
+
private readonly connections;
|
|
7
|
+
private getLibraryConnections;
|
|
8
|
+
addSocket: (libraryId: string, key: string, socket: WebSocket, info: TokenInfo) => void;
|
|
9
|
+
addRequest: (libraryId: string, key: string, request: IncomingMessage, response: ServerResponse, info: TokenInfo) => () => void;
|
|
10
|
+
remove: (libraryId: string, key: string) => void;
|
|
11
|
+
respond: (libraryId: string, key: string, message: ServerMessage) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Broadcasts a message to all live-connected clients in a library.
|
|
14
|
+
* This does not reach push/pull clients.
|
|
15
|
+
*/
|
|
16
|
+
broadcast: (libraryId: string, message: ServerMessage, omitKeys?: string[]) => void;
|
|
17
|
+
disconnectAll: (libraryId: string) => void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClientConnectionManager = void 0;
|
|
4
|
+
class ClientConnection {
|
|
5
|
+
constructor(key) {
|
|
6
|
+
this.key = key;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
class RequestClientConnection extends ClientConnection {
|
|
10
|
+
constructor(key, req, res, info) {
|
|
11
|
+
super(key);
|
|
12
|
+
this.req = req;
|
|
13
|
+
this.res = res;
|
|
14
|
+
this.info = info;
|
|
15
|
+
this.type = 'request';
|
|
16
|
+
this.responses = [];
|
|
17
|
+
this.respond = (message) => {
|
|
18
|
+
this.responses.push(message);
|
|
19
|
+
};
|
|
20
|
+
this.end = () => {
|
|
21
|
+
this.res.writeHead(200, {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
});
|
|
24
|
+
this.res.write(JSON.stringify({
|
|
25
|
+
messages: this.responses,
|
|
26
|
+
}));
|
|
27
|
+
this.res.end();
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
class WebSocketClientConnection extends ClientConnection {
|
|
32
|
+
constructor(key, ws, info) {
|
|
33
|
+
super(key);
|
|
34
|
+
this.ws = ws;
|
|
35
|
+
this.info = info;
|
|
36
|
+
this.type = 'websocket';
|
|
37
|
+
this.respond = (message) => {
|
|
38
|
+
this.ws.send(JSON.stringify(message));
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
class ClientConnectionManager {
|
|
43
|
+
constructor() {
|
|
44
|
+
this.connections = {};
|
|
45
|
+
this.getLibraryConnections = (libraryId) => {
|
|
46
|
+
if (!this.connections[libraryId]) {
|
|
47
|
+
this.connections[libraryId] = new Map();
|
|
48
|
+
}
|
|
49
|
+
return this.connections[libraryId];
|
|
50
|
+
};
|
|
51
|
+
this.addSocket = (libraryId, key, socket, info) => {
|
|
52
|
+
const lib = this.getLibraryConnections(libraryId);
|
|
53
|
+
const connection = new WebSocketClientConnection(key, socket, info);
|
|
54
|
+
lib.set(key, connection);
|
|
55
|
+
socket.on('close', () => {
|
|
56
|
+
lib.delete(key);
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
this.addRequest = (libraryId, key, request, response, info) => {
|
|
60
|
+
const lib = this.getLibraryConnections(libraryId);
|
|
61
|
+
const connection = new RequestClientConnection(key, request, response, info);
|
|
62
|
+
lib.set(key, connection);
|
|
63
|
+
return () => {
|
|
64
|
+
connection.end();
|
|
65
|
+
lib.delete(key);
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
this.remove = (libraryId, key) => {
|
|
69
|
+
const lib = this.getLibraryConnections(libraryId);
|
|
70
|
+
lib.delete(key);
|
|
71
|
+
};
|
|
72
|
+
this.respond = (libraryId, key, message) => {
|
|
73
|
+
const lib = this.getLibraryConnections(libraryId);
|
|
74
|
+
const connection = lib.get(key);
|
|
75
|
+
if (connection) {
|
|
76
|
+
connection.respond(message);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
throw new Error(`No connection found for library ${libraryId}, key ${key}`);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Broadcasts a message to all live-connected clients in a library.
|
|
84
|
+
* This does not reach push/pull clients.
|
|
85
|
+
*/
|
|
86
|
+
this.broadcast = (libraryId, message, omitKeys = []) => {
|
|
87
|
+
const lib = this.getLibraryConnections(libraryId);
|
|
88
|
+
for (const [key, connection] of lib) {
|
|
89
|
+
if (!omitKeys.includes(key)) {
|
|
90
|
+
connection.respond(message);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
this.disconnectAll = (libraryId) => {
|
|
95
|
+
const lib = this.getLibraryConnections(libraryId);
|
|
96
|
+
for (const connection of lib.values()) {
|
|
97
|
+
if (connection instanceof WebSocketClientConnection) {
|
|
98
|
+
connection.ws.close();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
lib.clear();
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.ClientConnectionManager = ClientConnectionManager;
|
|
106
|
+
//# sourceMappingURL=ClientConnection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClientConnection.js","sourceRoot":"","sources":["../../src/ClientConnection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageSender.js","sourceRoot":"","sources":["../../src/MessageSender.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Operation } from '@verdant-web/common';
|
|
2
|
+
import { Database } from 'better-sqlite3';
|
|
3
|
+
import { OperationSpec } from './types.js';
|
|
4
|
+
export declare class OperationHistory {
|
|
5
|
+
private db;
|
|
6
|
+
constructor(db: Database);
|
|
7
|
+
private hydratePatch;
|
|
8
|
+
getAllFor: (libraryId: string, oid: string) => OperationSpec[];
|
|
9
|
+
getEarliest: (libraryId: string, count: number) => OperationSpec[];
|
|
10
|
+
getAfter: (libraryId: string, timestamp?: string | null) => OperationSpec[];
|
|
11
|
+
/**
|
|
12
|
+
* Returns all operations before the given timestamp
|
|
13
|
+
* in ascending chronological order
|
|
14
|
+
*/
|
|
15
|
+
getBefore: (libraryId: string, before: string) => OperationSpec[];
|
|
16
|
+
/**
|
|
17
|
+
* Returns all operations after a given server order. Useful
|
|
18
|
+
* for retrieving operations that were created after a client
|
|
19
|
+
* last synced.
|
|
20
|
+
*/
|
|
21
|
+
getFromServerOrder: (libraryId: string, serverOrder: number) => OperationSpec[];
|
|
22
|
+
/**
|
|
23
|
+
* Adds a new operation or replaces an existing one.
|
|
24
|
+
* The server order will be set to the current max + 1
|
|
25
|
+
*/
|
|
26
|
+
insert: (libraryId: string, replicaId: string, item: Operation) => import("better-sqlite3").RunResult;
|
|
27
|
+
/**
|
|
28
|
+
* Inserts all operations and updates the server
|
|
29
|
+
* order for this library
|
|
30
|
+
*/
|
|
31
|
+
insertAll: (libraryId: string, replicaId: string, items: Operation[]) => Promise<void>;
|
|
32
|
+
dropAll: (libraryId: string, items: Pick<OperationSpec, 'replicaId' | 'oid' | 'timestamp'>[]) => Promise<void>;
|
|
33
|
+
deleteAll: (libraryId: string) => void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.OperationHistory = void 0;
|
|
15
|
+
class OperationHistory {
|
|
16
|
+
constructor(db) {
|
|
17
|
+
this.db = db;
|
|
18
|
+
this.hydratePatch = (_a) => {
|
|
19
|
+
var { libraryId: _, data } = _a, rest = __rest(_a, ["libraryId", "data"]);
|
|
20
|
+
return Object.assign(Object.assign({}, rest), { data: JSON.parse(data) });
|
|
21
|
+
};
|
|
22
|
+
this.getAllFor = (libraryId, oid) => {
|
|
23
|
+
return this.db
|
|
24
|
+
.prepare(`
|
|
25
|
+
SELECT * FROM OperationHistory
|
|
26
|
+
WHERE libraryId = ? AND oid = ?
|
|
27
|
+
ORDER BY timestamp ASC
|
|
28
|
+
`)
|
|
29
|
+
.all(libraryId, oid)
|
|
30
|
+
.map(this.hydratePatch);
|
|
31
|
+
};
|
|
32
|
+
this.getEarliest = (libraryId, count) => {
|
|
33
|
+
return this.db
|
|
34
|
+
.prepare(`
|
|
35
|
+
SELECT * FROM OperationHistory
|
|
36
|
+
WHERE libraryId = ?
|
|
37
|
+
ORDER BY timestamp ASC
|
|
38
|
+
LIMIT ?
|
|
39
|
+
`)
|
|
40
|
+
.all(libraryId, count)
|
|
41
|
+
.map(this.hydratePatch);
|
|
42
|
+
};
|
|
43
|
+
this.getAfter = (libraryId, timestamp = null) => {
|
|
44
|
+
if (timestamp === null) {
|
|
45
|
+
return this.db
|
|
46
|
+
.prepare(`
|
|
47
|
+
SELECT * FROM OperationHistory
|
|
48
|
+
WHERE libraryId = ?
|
|
49
|
+
ORDER BY timestamp ASC
|
|
50
|
+
`)
|
|
51
|
+
.all(libraryId)
|
|
52
|
+
.map(this.hydratePatch);
|
|
53
|
+
}
|
|
54
|
+
return this.db
|
|
55
|
+
.prepare(`
|
|
56
|
+
SELECT * FROM OperationHistory
|
|
57
|
+
WHERE libraryId = ? AND timestamp > ?
|
|
58
|
+
ORDER BY timestamp ASC
|
|
59
|
+
`)
|
|
60
|
+
.all(libraryId, timestamp)
|
|
61
|
+
.map(this.hydratePatch);
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Returns all operations before the given timestamp
|
|
65
|
+
* in ascending chronological order
|
|
66
|
+
*/
|
|
67
|
+
this.getBefore = (libraryId, before) => {
|
|
68
|
+
return this.db
|
|
69
|
+
.prepare(`
|
|
70
|
+
SELECT * FROM OperationHistory
|
|
71
|
+
WHERE libraryId = ? AND timestamp < ?
|
|
72
|
+
ORDER BY timestamp ASC
|
|
73
|
+
`)
|
|
74
|
+
.all(libraryId, before)
|
|
75
|
+
.map(this.hydratePatch);
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Returns all operations after a given server order. Useful
|
|
79
|
+
* for retrieving operations that were created after a client
|
|
80
|
+
* last synced.
|
|
81
|
+
*/
|
|
82
|
+
this.getFromServerOrder = (libraryId, serverOrder) => {
|
|
83
|
+
const result = this.db
|
|
84
|
+
.prepare(`
|
|
85
|
+
SELECT * FROM OperationHistory
|
|
86
|
+
WHERE libraryId = ? AND serverOrder > ?
|
|
87
|
+
`)
|
|
88
|
+
.all(libraryId, serverOrder);
|
|
89
|
+
return (result || []).map(this.hydratePatch);
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Adds a new operation or replaces an existing one.
|
|
93
|
+
* The server order will be set to the current max + 1
|
|
94
|
+
*/
|
|
95
|
+
this.insert = (libraryId, replicaId, item) => {
|
|
96
|
+
return this.db
|
|
97
|
+
.prepare(`
|
|
98
|
+
INSERT OR REPLACE INTO OperationHistory (libraryId, oid, data, timestamp, replicaId, serverOrder)
|
|
99
|
+
VALUES (?, ?, ?, ?, ?, (SELECT COALESCE(MAX(serverOrder), 0) FROM OperationHistory WHERE libraryId = ?) + 1)
|
|
100
|
+
`)
|
|
101
|
+
.run(libraryId, item.oid, JSON.stringify(item.data), item.timestamp, replicaId);
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Inserts all operations and updates the server
|
|
105
|
+
* order for this library
|
|
106
|
+
*/
|
|
107
|
+
this.insertAll = async (libraryId, replicaId, items) => {
|
|
108
|
+
const tx = this.db.transaction(() => {
|
|
109
|
+
let orderResult = this.db
|
|
110
|
+
.prepare(`
|
|
111
|
+
SELECT COALESCE(MAX(serverOrder), 0) AS serverOrder FROM OperationHistory WHERE libraryId = ?
|
|
112
|
+
`)
|
|
113
|
+
.get(libraryId);
|
|
114
|
+
let currentServerOrder = orderResult.serverOrder || 0;
|
|
115
|
+
const insertStatement = this.db.prepare(`
|
|
116
|
+
INSERT OR REPLACE INTO OperationHistory (libraryId, oid, data, timestamp, replicaId, serverOrder)
|
|
117
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
118
|
+
`);
|
|
119
|
+
for (const item of items) {
|
|
120
|
+
insertStatement.run(libraryId, item.oid, JSON.stringify(item.data), item.timestamp, replicaId, ++currentServerOrder);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
tx();
|
|
124
|
+
};
|
|
125
|
+
this.dropAll = async (libraryId, items) => {
|
|
126
|
+
const deleteStatement = this.db.prepare(`
|
|
127
|
+
DELETE FROM OperationHistory
|
|
128
|
+
WHERE libraryId = ? AND replicaId = ? AND oid = ? AND timestamp = ?
|
|
129
|
+
`);
|
|
130
|
+
this.db.transaction(() => {
|
|
131
|
+
for (const item of items) {
|
|
132
|
+
deleteStatement.run(libraryId, item.replicaId, item.oid, item.timestamp);
|
|
133
|
+
}
|
|
134
|
+
})();
|
|
135
|
+
};
|
|
136
|
+
this.deleteAll = (libraryId) => {
|
|
137
|
+
this.db
|
|
138
|
+
.prepare(`
|
|
139
|
+
DELETE FROM OperationHistory
|
|
140
|
+
WHERE libraryId = ?
|
|
141
|
+
`)
|
|
142
|
+
.run(libraryId);
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.OperationHistory = OperationHistory;
|
|
147
|
+
//# sourceMappingURL=OperationHistory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OperationHistory.js","sourceRoot":"","sources":["../../src/OperationHistory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import { UserInfo } from '@verdant-web/common';
|
|
3
|
+
import EventEmitter from 'events';
|
|
4
|
+
/**
|
|
5
|
+
* Stores client presence in-memory for connected
|
|
6
|
+
* clients
|
|
7
|
+
*/
|
|
8
|
+
export declare class Presence extends EventEmitter {
|
|
9
|
+
private presences;
|
|
10
|
+
private getLibrary;
|
|
11
|
+
set: (libraryId: string, userId: string, presence: UserInfo<any, any>) => void;
|
|
12
|
+
removeReplica: (libraryId: string, replicaId: string) => void;
|
|
13
|
+
all: (libraryId: string) => Record<string, UserInfo<any, any>>;
|
|
14
|
+
clear: (libraryId: string) => void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Presence = void 0;
|
|
7
|
+
const events_1 = __importDefault(require("events"));
|
|
8
|
+
/**
|
|
9
|
+
* Stores client presence in-memory for connected
|
|
10
|
+
* clients
|
|
11
|
+
*/
|
|
12
|
+
class Presence extends events_1.default {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.presences = new Map();
|
|
16
|
+
this.getLibrary = (libraryId) => {
|
|
17
|
+
let map = this.presences.get(libraryId);
|
|
18
|
+
if (!map) {
|
|
19
|
+
map = {
|
|
20
|
+
presences: {},
|
|
21
|
+
replicaToUser: {},
|
|
22
|
+
userToReplica: {},
|
|
23
|
+
};
|
|
24
|
+
this.presences.set(libraryId, map);
|
|
25
|
+
}
|
|
26
|
+
return map;
|
|
27
|
+
};
|
|
28
|
+
this.set = (libraryId, userId, presence) => {
|
|
29
|
+
const lib = this.getLibrary(libraryId);
|
|
30
|
+
lib.presences[userId] = presence;
|
|
31
|
+
lib.replicaToUser[presence.replicaId] = userId;
|
|
32
|
+
lib.userToReplica[userId] = lib.userToReplica[userId] || new Set();
|
|
33
|
+
lib.userToReplica[userId].add(presence.replicaId);
|
|
34
|
+
};
|
|
35
|
+
this.removeReplica = (libraryId, replicaId) => {
|
|
36
|
+
const lib = this.getLibrary(libraryId);
|
|
37
|
+
const userId = lib.replicaToUser[replicaId];
|
|
38
|
+
if (!userId)
|
|
39
|
+
return;
|
|
40
|
+
lib.userToReplica[userId].delete(replicaId);
|
|
41
|
+
if (lib.userToReplica[userId].size === 0) {
|
|
42
|
+
delete lib.presences[userId];
|
|
43
|
+
this.emit('lost', libraryId, replicaId, userId);
|
|
44
|
+
// memory cleanup
|
|
45
|
+
if (Object.keys(lib.presences).length === 0) {
|
|
46
|
+
this.clear(libraryId);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
this.all = (libraryId) => {
|
|
51
|
+
var _a;
|
|
52
|
+
return ((_a = this.presences.get(libraryId)) === null || _a === void 0 ? void 0 : _a.presences) || {};
|
|
53
|
+
};
|
|
54
|
+
this.clear = (libraryId) => {
|
|
55
|
+
this.presences.delete(libraryId);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.Presence = Presence;
|
|
60
|
+
//# sourceMappingURL=Presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Presence.js","sourceRoot":"","sources":["../../src/Presence.ts"],"names":[],"mappings":";;;;;;AACA,oDAAkC;AAElC;;;GAGG;AACH,MAAa,QAAS,SAAQ,gBAAY;IAA1C;;QACS,cAAS,GAOb,IAAI,GAAG,EAAE,CAAC;QAEN,eAAU,GAAG,CAAC,SAAiB,EAAE,EAAE;YAC1C,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACxC,IAAI,CAAC,GAAG,EAAE;gBACT,GAAG,GAAG;oBACL,SAAS,EAAE,EAAE;oBACb,aAAa,EAAE,EAAE;oBACjB,aAAa,EAAE,EAAE;iBACjB,CAAC;gBACF,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;aACnC;YACD,OAAO,GAAG,CAAC;QACZ,CAAC,CAAC;QAEF,QAAG,GAAG,CAAC,SAAiB,EAAE,MAAc,EAAE,QAA4B,EAAE,EAAE;YACzE,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YACvC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;YACjC,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC;YAC/C,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;YAC3E,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACnD,CAAC,CAAC;QAEF,kBAAa,GAAG,CAAC,SAAiB,EAAE,SAAiB,EAAE,EAAE;YACxD,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM;gBAAE,OAAO;YAEpB,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC5C,IAAI,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE;gBACzC,OAAO,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;gBAC7B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;gBAEhD,iBAAiB;gBACjB,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;oBAC5C,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;iBACtB;aACD;QACF,CAAC,CAAC;QAEF,QAAG,GAAG,CAAC,SAAiB,EAAE,EAAE;;YAC3B,OAAO,CAAA,MAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,0CAAE,SAAS,KAAI,EAAE,CAAC;QACvD,CAAC,CAAC;QAEF,UAAK,GAAG,CAAC,SAAiB,EAAE,EAAE;YAC7B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC,CAAC;IACH,CAAC;CAAA;AAvDD,4BAuDC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface UserProfiles<Profile> {
|
|
2
|
+
get(userId: string): Promise<Profile>;
|
|
3
|
+
}
|
|
4
|
+
export declare class UserProfileLoader<Profile> {
|
|
5
|
+
private source;
|
|
6
|
+
private cache;
|
|
7
|
+
constructor(source: UserProfiles<Profile>);
|
|
8
|
+
get: (userId: string) => Promise<Profile>;
|
|
9
|
+
evict: (userId: string) => void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserProfileLoader = void 0;
|
|
4
|
+
class UserProfileLoader {
|
|
5
|
+
constructor(source) {
|
|
6
|
+
this.source = source;
|
|
7
|
+
this.cache = new Map();
|
|
8
|
+
this.get = async (userId) => {
|
|
9
|
+
if (!this.cache.has(userId)) {
|
|
10
|
+
this.cache.set(userId, this.source.get(userId));
|
|
11
|
+
}
|
|
12
|
+
return this.cache.get(userId);
|
|
13
|
+
};
|
|
14
|
+
this.evict = (userId) => {
|
|
15
|
+
this.cache.delete(userId);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.UserProfileLoader = UserProfileLoader;
|
|
20
|
+
//# sourceMappingURL=Profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Profiles.js","sourceRoot":"","sources":["../../src/Profiles.ts"],"names":[],"mappings":";;;AAIA,MAAa,iBAAiB;IAG7B,YAAoB,MAA6B;QAA7B,WAAM,GAAN,MAAM,CAAuB;QAFzC,UAAK,GAAG,IAAI,GAAG,EAA4B,CAAC;QAIpD,QAAG,GAAG,KAAK,EAAE,MAAc,EAAE,EAAE;YAC9B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;gBAC5B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;aAChD;YAED,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;QAChC,CAAC,CAAC;QAEF,UAAK,GAAG,CAAC,MAAc,EAAE,EAAE;YAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC,CAAC;IAZkD,CAAC;CAarD;AAhBD,8CAgBC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EventSubscriber } from '@verdant-web/common';
|
|
2
|
+
/**
|
|
3
|
+
* A set of timers per-replica which are
|
|
4
|
+
* refreshed whenever the replica sends
|
|
5
|
+
* a message to the server. If a timer expires,
|
|
6
|
+
* the replica is removed from presence. This
|
|
7
|
+
* is a failsafe for socket-based replicas but
|
|
8
|
+
* essential for pull-based replicas.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ReplicaKeepaliveTimers extends EventSubscriber<{
|
|
11
|
+
lost: (libraryId: string, replicaId: string) => void;
|
|
12
|
+
}> {
|
|
13
|
+
private timeout;
|
|
14
|
+
private timers;
|
|
15
|
+
constructor(timeout?: number);
|
|
16
|
+
refresh: (libraryId: string, replicaId: string) => void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReplicaKeepaliveTimers = void 0;
|
|
4
|
+
const common_1 = require("@verdant-web/common");
|
|
5
|
+
/**
|
|
6
|
+
* A set of timers per-replica which are
|
|
7
|
+
* refreshed whenever the replica sends
|
|
8
|
+
* a message to the server. If a timer expires,
|
|
9
|
+
* the replica is removed from presence. This
|
|
10
|
+
* is a failsafe for socket-based replicas but
|
|
11
|
+
* essential for pull-based replicas.
|
|
12
|
+
*/
|
|
13
|
+
class ReplicaKeepaliveTimers extends common_1.EventSubscriber {
|
|
14
|
+
constructor(timeout = 30 * 1000) {
|
|
15
|
+
super();
|
|
16
|
+
this.timeout = timeout;
|
|
17
|
+
this.timers = new Map();
|
|
18
|
+
this.refresh = (libraryId, replicaId) => {
|
|
19
|
+
const existing = this.timers.get(replicaId);
|
|
20
|
+
if (existing) {
|
|
21
|
+
clearTimeout(existing);
|
|
22
|
+
}
|
|
23
|
+
this.timers.set(replicaId, setTimeout(() => {
|
|
24
|
+
this.emit('lost', libraryId, replicaId);
|
|
25
|
+
this.timers.delete(replicaId);
|
|
26
|
+
}, this.timeout));
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.ReplicaKeepaliveTimers = ReplicaKeepaliveTimers;
|
|
31
|
+
//# sourceMappingURL=ReplicaKeepaliveTimers.js.map
|