@verdant-web/server 1.7.8 → 1.7.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.
- package/dist/cjs/Baselines.d.ts +14 -14
- package/dist/cjs/Baselines.js +77 -77
- package/dist/cjs/ClientConnection.d.ts +18 -18
- package/dist/cjs/ClientConnection.js +105 -105
- package/dist/cjs/MessageSender.d.ts +5 -5
- package/dist/cjs/MessageSender.js +2 -2
- package/dist/cjs/OperationHistory.d.ts +34 -34
- package/dist/cjs/OperationHistory.js +108 -108
- package/dist/cjs/Presence.d.ts +15 -15
- package/dist/cjs/Presence.js +59 -59
- package/dist/cjs/Profiles.d.ts +10 -10
- package/dist/cjs/Profiles.js +19 -19
- package/dist/cjs/ReplicaKeepaliveTimers.d.ts +17 -17
- package/dist/cjs/ReplicaKeepaliveTimers.js +30 -30
- package/dist/cjs/Replicas.d.ts +23 -23
- package/dist/cjs/Replicas.js +132 -132
- package/dist/cjs/Server.d.ts +121 -121
- package/dist/cjs/Server.js +385 -377
- package/dist/cjs/Server.js.map +1 -1
- package/dist/cjs/ServerLibrary.d.ts +63 -63
- package/dist/cjs/ServerLibrary.js +467 -467
- package/dist/cjs/TokenProvider.d.ts +16 -16
- package/dist/cjs/TokenProvider.js +26 -26
- package/dist/cjs/TokenVerifier.d.ts +16 -16
- package/dist/cjs/TokenVerifier.js +28 -28
- package/dist/cjs/files/FileMetadata.d.ts +28 -28
- package/dist/cjs/files/FileMetadata.js +46 -46
- package/dist/cjs/files/FileStorage.d.ts +35 -35
- package/dist/cjs/files/FileStorage.js +71 -71
- package/dist/cjs/index.d.ts +9 -9
- package/dist/cjs/index.js +11 -11
- package/dist/cjs/migrations/v0.sql.d.ts +2 -2
- package/dist/cjs/migrations/v0.sql.js +3 -3
- package/dist/cjs/migrations/v1.sql.d.ts +2 -2
- package/dist/cjs/migrations/v1.sql.js +3 -3
- package/dist/cjs/migrations/v2.sql.d.ts +2 -2
- package/dist/cjs/migrations/v2.sql.js +4 -4
- package/dist/cjs/migrations/v3.sql.d.ts +2 -2
- package/dist/cjs/migrations/v3.sql.js +4 -4
- package/dist/cjs/migrations/v4.sql.d.ts +2 -2
- package/dist/cjs/migrations/v4.sql.js +4 -4
- package/dist/cjs/migrations.d.ts +2 -2
- package/dist/cjs/migrations.js +32 -32
- package/dist/cjs/types.d.ts +22 -22
- package/dist/cjs/types.js +2 -2
- package/dist/esm/Baselines.d.ts +14 -14
- package/dist/esm/Baselines.js +73 -73
- package/dist/esm/ClientConnection.d.ts +18 -18
- package/dist/esm/ClientConnection.js +101 -101
- package/dist/esm/MessageSender.d.ts +5 -5
- package/dist/esm/MessageSender.js +1 -1
- package/dist/esm/OperationHistory.d.ts +34 -34
- package/dist/esm/OperationHistory.js +104 -104
- package/dist/esm/Presence.d.ts +15 -15
- package/dist/esm/Presence.js +52 -52
- package/dist/esm/Profiles.d.ts +10 -10
- package/dist/esm/Profiles.js +15 -15
- package/dist/esm/ReplicaKeepaliveTimers.d.ts +17 -17
- package/dist/esm/ReplicaKeepaliveTimers.js +26 -26
- package/dist/esm/Replicas.d.ts +23 -23
- package/dist/esm/Replicas.js +128 -128
- package/dist/esm/Server.d.ts +121 -121
- package/dist/esm/Server.js +378 -370
- package/dist/esm/Server.js.map +1 -1
- package/dist/esm/ServerLibrary.d.ts +63 -63
- package/dist/esm/ServerLibrary.js +463 -463
- package/dist/esm/TokenProvider.d.ts +16 -16
- package/dist/esm/TokenProvider.js +19 -19
- package/dist/esm/TokenVerifier.d.ts +16 -16
- package/dist/esm/TokenVerifier.js +21 -21
- package/dist/esm/files/FileMetadata.d.ts +28 -28
- package/dist/esm/files/FileMetadata.js +42 -42
- package/dist/esm/files/FileStorage.d.ts +35 -35
- package/dist/esm/files/FileStorage.js +44 -44
- package/dist/esm/index.d.ts +9 -9
- package/dist/esm/index.js +4 -4
- package/dist/esm/migrations/v0.sql.d.ts +2 -2
- package/dist/esm/migrations/v0.sql.js +1 -1
- package/dist/esm/migrations/v1.sql.d.ts +2 -2
- package/dist/esm/migrations/v1.sql.js +1 -1
- package/dist/esm/migrations/v2.sql.d.ts +2 -2
- package/dist/esm/migrations/v2.sql.js +2 -2
- package/dist/esm/migrations/v3.sql.d.ts +2 -2
- package/dist/esm/migrations/v3.sql.js +2 -2
- package/dist/esm/migrations/v4.sql.d.ts +2 -2
- package/dist/esm/migrations/v4.sql.js +2 -2
- package/dist/esm/migrations.d.ts +2 -2
- package/dist/esm/migrations.js +25 -25
- package/dist/esm/types.d.ts +22 -22
- package/dist/esm/types.js +1 -1
- package/dist/tsconfig-cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/Server.ts +9 -2
- package/dist/cjs/ServerStorage.d.ts +0 -35
- package/dist/cjs/ServerStorage.js +0 -60
- package/dist/cjs/ServerStorage.js.map +0 -1
- package/dist/esm/ServerStorage.d.ts +0 -44
- package/dist/esm/ServerStorage.js +0 -58
- package/dist/esm/ServerStorage.js.map +0 -1
package/dist/esm/Presence.js
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
import EventEmitter from 'events';
|
|
2
|
-
/**
|
|
3
|
-
* Stores client presence in-memory for connected
|
|
4
|
-
* clients
|
|
5
|
-
*/
|
|
6
|
-
export class Presence extends EventEmitter {
|
|
7
|
-
constructor() {
|
|
8
|
-
super(...arguments);
|
|
9
|
-
this.presences = new Map();
|
|
10
|
-
this.getLibrary = (libraryId) => {
|
|
11
|
-
let map = this.presences.get(libraryId);
|
|
12
|
-
if (!map) {
|
|
13
|
-
map = {
|
|
14
|
-
presences: {},
|
|
15
|
-
replicaToUser: {},
|
|
16
|
-
userToReplica: {},
|
|
17
|
-
};
|
|
18
|
-
this.presences.set(libraryId, map);
|
|
19
|
-
}
|
|
20
|
-
return map;
|
|
21
|
-
};
|
|
22
|
-
this.set = (libraryId, userId, presence) => {
|
|
23
|
-
const lib = this.getLibrary(libraryId);
|
|
24
|
-
lib.presences[userId] = presence;
|
|
25
|
-
lib.replicaToUser[presence.replicaId] = userId;
|
|
26
|
-
lib.userToReplica[userId] = lib.userToReplica[userId] || new Set();
|
|
27
|
-
lib.userToReplica[userId].add(presence.replicaId);
|
|
28
|
-
};
|
|
29
|
-
this.removeReplica = (libraryId, replicaId) => {
|
|
30
|
-
const lib = this.getLibrary(libraryId);
|
|
31
|
-
const userId = lib.replicaToUser[replicaId];
|
|
32
|
-
if (!userId)
|
|
33
|
-
return;
|
|
34
|
-
lib.userToReplica[userId].delete(replicaId);
|
|
35
|
-
if (lib.userToReplica[userId].size === 0) {
|
|
36
|
-
delete lib.presences[userId];
|
|
37
|
-
this.emit('lost', libraryId, replicaId, userId);
|
|
38
|
-
// memory cleanup
|
|
39
|
-
if (Object.keys(lib.presences).length === 0) {
|
|
40
|
-
this.clear(libraryId);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
this.all = (libraryId) => {
|
|
45
|
-
var _a;
|
|
46
|
-
return ((_a = this.presences.get(libraryId)) === null || _a === void 0 ? void 0 : _a.presences) || {};
|
|
47
|
-
};
|
|
48
|
-
this.clear = (libraryId) => {
|
|
49
|
-
this.presences.delete(libraryId);
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
}
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
/**
|
|
3
|
+
* Stores client presence in-memory for connected
|
|
4
|
+
* clients
|
|
5
|
+
*/
|
|
6
|
+
export class Presence extends EventEmitter {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.presences = new Map();
|
|
10
|
+
this.getLibrary = (libraryId) => {
|
|
11
|
+
let map = this.presences.get(libraryId);
|
|
12
|
+
if (!map) {
|
|
13
|
+
map = {
|
|
14
|
+
presences: {},
|
|
15
|
+
replicaToUser: {},
|
|
16
|
+
userToReplica: {},
|
|
17
|
+
};
|
|
18
|
+
this.presences.set(libraryId, map);
|
|
19
|
+
}
|
|
20
|
+
return map;
|
|
21
|
+
};
|
|
22
|
+
this.set = (libraryId, userId, presence) => {
|
|
23
|
+
const lib = this.getLibrary(libraryId);
|
|
24
|
+
lib.presences[userId] = presence;
|
|
25
|
+
lib.replicaToUser[presence.replicaId] = userId;
|
|
26
|
+
lib.userToReplica[userId] = lib.userToReplica[userId] || new Set();
|
|
27
|
+
lib.userToReplica[userId].add(presence.replicaId);
|
|
28
|
+
};
|
|
29
|
+
this.removeReplica = (libraryId, replicaId) => {
|
|
30
|
+
const lib = this.getLibrary(libraryId);
|
|
31
|
+
const userId = lib.replicaToUser[replicaId];
|
|
32
|
+
if (!userId)
|
|
33
|
+
return;
|
|
34
|
+
lib.userToReplica[userId].delete(replicaId);
|
|
35
|
+
if (lib.userToReplica[userId].size === 0) {
|
|
36
|
+
delete lib.presences[userId];
|
|
37
|
+
this.emit('lost', libraryId, replicaId, userId);
|
|
38
|
+
// memory cleanup
|
|
39
|
+
if (Object.keys(lib.presences).length === 0) {
|
|
40
|
+
this.clear(libraryId);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
this.all = (libraryId) => {
|
|
45
|
+
var _a;
|
|
46
|
+
return ((_a = this.presences.get(libraryId)) === null || _a === void 0 ? void 0 : _a.presences) || {};
|
|
47
|
+
};
|
|
48
|
+
this.clear = (libraryId) => {
|
|
49
|
+
this.presences.delete(libraryId);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
53
|
//# sourceMappingURL=Presence.js.map
|
package/dist/esm/Profiles.d.ts
CHANGED
|
@@ -1,10 +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
|
-
}
|
|
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
|
+
}
|
package/dist/esm/Profiles.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
export class UserProfileLoader {
|
|
2
|
-
constructor(source) {
|
|
3
|
-
this.source = source;
|
|
4
|
-
this.cache = new Map();
|
|
5
|
-
this.get = async (userId) => {
|
|
6
|
-
if (!this.cache.has(userId)) {
|
|
7
|
-
this.cache.set(userId, this.source.get(userId));
|
|
8
|
-
}
|
|
9
|
-
return this.cache.get(userId);
|
|
10
|
-
};
|
|
11
|
-
this.evict = (userId) => {
|
|
12
|
-
this.cache.delete(userId);
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
}
|
|
1
|
+
export class UserProfileLoader {
|
|
2
|
+
constructor(source) {
|
|
3
|
+
this.source = source;
|
|
4
|
+
this.cache = new Map();
|
|
5
|
+
this.get = async (userId) => {
|
|
6
|
+
if (!this.cache.has(userId)) {
|
|
7
|
+
this.cache.set(userId, this.source.get(userId));
|
|
8
|
+
}
|
|
9
|
+
return this.cache.get(userId);
|
|
10
|
+
};
|
|
11
|
+
this.evict = (userId) => {
|
|
12
|
+
this.cache.delete(userId);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
16
|
//# sourceMappingURL=Profiles.js.map
|
|
@@ -1,17 +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
|
-
}
|
|
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
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
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 class ReplicaKeepaliveTimers extends EventSubscriber {
|
|
11
|
-
constructor(timeout = 30 * 1000) {
|
|
12
|
-
super();
|
|
13
|
-
this.timeout = timeout;
|
|
14
|
-
this.timers = new Map();
|
|
15
|
-
this.refresh = (libraryId, replicaId) => {
|
|
16
|
-
const existing = this.timers.get(replicaId);
|
|
17
|
-
if (existing) {
|
|
18
|
-
clearTimeout(existing);
|
|
19
|
-
}
|
|
20
|
-
this.timers.set(replicaId, setTimeout(() => {
|
|
21
|
-
this.emit('lost', libraryId, replicaId);
|
|
22
|
-
this.timers.delete(replicaId);
|
|
23
|
-
}, this.timeout));
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
}
|
|
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 class ReplicaKeepaliveTimers extends EventSubscriber {
|
|
11
|
+
constructor(timeout = 30 * 1000) {
|
|
12
|
+
super();
|
|
13
|
+
this.timeout = timeout;
|
|
14
|
+
this.timers = new Map();
|
|
15
|
+
this.refresh = (libraryId, replicaId) => {
|
|
16
|
+
const existing = this.timers.get(replicaId);
|
|
17
|
+
if (existing) {
|
|
18
|
+
clearTimeout(existing);
|
|
19
|
+
}
|
|
20
|
+
this.timers.set(replicaId, setTimeout(() => {
|
|
21
|
+
this.emit('lost', libraryId, replicaId);
|
|
22
|
+
this.timers.delete(replicaId);
|
|
23
|
+
}, this.timeout));
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
27
|
//# sourceMappingURL=ReplicaKeepaliveTimers.js.map
|
package/dist/esm/Replicas.d.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { Database } from 'better-sqlite3';
|
|
2
|
-
import { TokenInfo } from './TokenVerifier.js';
|
|
3
|
-
import { ReplicaInfoSpec } from './types.js';
|
|
4
|
-
export declare class ReplicaInfos {
|
|
5
|
-
private db;
|
|
6
|
-
private readonly replicaTruancyMinutes;
|
|
7
|
-
constructor(db: Database, replicaTruancyMinutes: number);
|
|
8
|
-
private get truantCutoff();
|
|
9
|
-
get: (libraryId: string, replicaId: string) => ReplicaInfoSpec | null;
|
|
10
|
-
getOrCreate: (libraryId: string, replicaId: string, info: TokenInfo) => {
|
|
11
|
-
status: 'new' | 'existing' | 'truant';
|
|
12
|
-
replicaInfo: ReplicaInfoSpec;
|
|
13
|
-
};
|
|
14
|
-
getAllNonTruant: (libraryId: string) => ReplicaInfoSpec[];
|
|
15
|
-
updateLastSeen: (libraryId: string, replicaId: string) => import("better-sqlite3").RunResult;
|
|
16
|
-
updateAcknowledged: (libraryId: string, replicaId: string, timestamp: string) => void;
|
|
17
|
-
updateServerOrder: (libraryId: string, replicaId: string, serverOrder: number) => void;
|
|
18
|
-
acknowledgeOperation: (libraryId: string, replicaId: string, timestamp: string) => void;
|
|
19
|
-
getGlobalAck: (libraryId: string, onlineReplicaIds?: string[]) => string | undefined;
|
|
20
|
-
delete: (libraryId: string, replicaId: string) => import("better-sqlite3").RunResult;
|
|
21
|
-
deleteAll: (libraryId: string) => import("better-sqlite3").RunResult;
|
|
22
|
-
deleteAllForUser: (libraryId: string, userId: string) => import("better-sqlite3").RunResult;
|
|
23
|
-
}
|
|
1
|
+
import { Database } from 'better-sqlite3';
|
|
2
|
+
import { TokenInfo } from './TokenVerifier.js';
|
|
3
|
+
import { ReplicaInfoSpec } from './types.js';
|
|
4
|
+
export declare class ReplicaInfos {
|
|
5
|
+
private db;
|
|
6
|
+
private readonly replicaTruancyMinutes;
|
|
7
|
+
constructor(db: Database, replicaTruancyMinutes: number);
|
|
8
|
+
private get truantCutoff();
|
|
9
|
+
get: (libraryId: string, replicaId: string) => ReplicaInfoSpec | null;
|
|
10
|
+
getOrCreate: (libraryId: string, replicaId: string, info: TokenInfo) => {
|
|
11
|
+
status: 'new' | 'existing' | 'truant';
|
|
12
|
+
replicaInfo: ReplicaInfoSpec;
|
|
13
|
+
};
|
|
14
|
+
getAllNonTruant: (libraryId: string) => ReplicaInfoSpec[];
|
|
15
|
+
updateLastSeen: (libraryId: string, replicaId: string) => import("better-sqlite3").RunResult;
|
|
16
|
+
updateAcknowledged: (libraryId: string, replicaId: string, timestamp: string) => void;
|
|
17
|
+
updateServerOrder: (libraryId: string, replicaId: string, serverOrder: number) => void;
|
|
18
|
+
acknowledgeOperation: (libraryId: string, replicaId: string, timestamp: string) => void;
|
|
19
|
+
getGlobalAck: (libraryId: string, onlineReplicaIds?: string[]) => string | undefined;
|
|
20
|
+
delete: (libraryId: string, replicaId: string) => import("better-sqlite3").RunResult;
|
|
21
|
+
deleteAll: (libraryId: string) => import("better-sqlite3").RunResult;
|
|
22
|
+
deleteAllForUser: (libraryId: string, userId: string) => import("better-sqlite3").RunResult;
|
|
23
|
+
}
|
package/dist/esm/Replicas.js
CHANGED
|
@@ -1,122 +1,122 @@
|
|
|
1
|
-
export class ReplicaInfos {
|
|
2
|
-
constructor(db, replicaTruancyMinutes) {
|
|
3
|
-
this.db = db;
|
|
4
|
-
this.replicaTruancyMinutes = replicaTruancyMinutes;
|
|
5
|
-
this.get = (libraryId, replicaId) => {
|
|
6
|
-
const row = this.db
|
|
1
|
+
export class ReplicaInfos {
|
|
2
|
+
constructor(db, replicaTruancyMinutes) {
|
|
3
|
+
this.db = db;
|
|
4
|
+
this.replicaTruancyMinutes = replicaTruancyMinutes;
|
|
5
|
+
this.get = (libraryId, replicaId) => {
|
|
6
|
+
const row = this.db
|
|
7
7
|
.prepare(`
|
|
8
8
|
SELECT * FROM ReplicaInfo
|
|
9
9
|
WHERE id = ? AND libraryId = ?
|
|
10
|
-
`)
|
|
11
|
-
.get(replicaId, libraryId);
|
|
12
|
-
if (!row) {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
return {
|
|
16
|
-
id: row.id,
|
|
17
|
-
libraryId: row.libraryId,
|
|
18
|
-
clientId: row.clientId,
|
|
19
|
-
lastSeenWallClockTime: row.lastSeenWallClockTime,
|
|
20
|
-
ackedLogicalTime: row.ackedLogicalTime,
|
|
21
|
-
type: row.type,
|
|
22
|
-
ackedServerOrder: row.ackedServerOrder,
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
this.getOrCreate = (libraryId, replicaId, info) => {
|
|
26
|
-
const replicaInfo = this.db
|
|
10
|
+
`)
|
|
11
|
+
.get(replicaId, libraryId);
|
|
12
|
+
if (!row) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
id: row.id,
|
|
17
|
+
libraryId: row.libraryId,
|
|
18
|
+
clientId: row.clientId,
|
|
19
|
+
lastSeenWallClockTime: row.lastSeenWallClockTime,
|
|
20
|
+
ackedLogicalTime: row.ackedLogicalTime,
|
|
21
|
+
type: row.type,
|
|
22
|
+
ackedServerOrder: row.ackedServerOrder,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
this.getOrCreate = (libraryId, replicaId, info) => {
|
|
26
|
+
const replicaInfo = this.db
|
|
27
27
|
.prepare(`
|
|
28
28
|
SELECT * FROM ReplicaInfo
|
|
29
29
|
WHERE id = ? AND libraryId = ?
|
|
30
|
-
`)
|
|
31
|
-
.get(replicaId, libraryId);
|
|
32
|
-
if (!replicaInfo) {
|
|
33
|
-
this.db
|
|
30
|
+
`)
|
|
31
|
+
.get(replicaId, libraryId);
|
|
32
|
+
if (!replicaInfo) {
|
|
33
|
+
this.db
|
|
34
34
|
.prepare(`
|
|
35
35
|
INSERT INTO ReplicaInfo (id, clientId, libraryId, type)
|
|
36
36
|
VALUES (?, ?, ?, ?)
|
|
37
|
-
`)
|
|
38
|
-
.run(replicaId, info.userId, libraryId, info.type);
|
|
39
|
-
return {
|
|
40
|
-
status: 'new',
|
|
41
|
-
replicaInfo: {
|
|
42
|
-
id: replicaId,
|
|
43
|
-
clientId: info.userId,
|
|
44
|
-
ackedLogicalTime: null,
|
|
45
|
-
lastSeenWallClockTime: null,
|
|
46
|
-
libraryId: libraryId,
|
|
47
|
-
type: info.type,
|
|
48
|
-
ackedServerOrder: 0,
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
if (replicaInfo.type !== info.type) {
|
|
53
|
-
// type should be updated if a new token changes it
|
|
54
|
-
this.db
|
|
37
|
+
`)
|
|
38
|
+
.run(replicaId, info.userId, libraryId, info.type);
|
|
39
|
+
return {
|
|
40
|
+
status: 'new',
|
|
41
|
+
replicaInfo: {
|
|
42
|
+
id: replicaId,
|
|
43
|
+
clientId: info.userId,
|
|
44
|
+
ackedLogicalTime: null,
|
|
45
|
+
lastSeenWallClockTime: null,
|
|
46
|
+
libraryId: libraryId,
|
|
47
|
+
type: info.type,
|
|
48
|
+
ackedServerOrder: 0,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (replicaInfo.type !== info.type) {
|
|
53
|
+
// type should be updated if a new token changes it
|
|
54
|
+
this.db
|
|
55
55
|
.prepare(`
|
|
56
56
|
UPDATE ReplicaInfo
|
|
57
57
|
SET type = ?
|
|
58
58
|
WHERE id = ? AND libraryId = ?
|
|
59
|
-
`)
|
|
60
|
-
.run(info.type, replicaId, libraryId);
|
|
61
|
-
}
|
|
62
|
-
if (replicaInfo.clientId !== info.userId) {
|
|
63
|
-
throw new Error(`Replica ${replicaId} already exists with a different user ID`);
|
|
64
|
-
}
|
|
65
|
-
if (replicaInfo.lastSeenWallClockTime &&
|
|
66
|
-
replicaInfo.lastSeenWallClockTime < this.truantCutoff) {
|
|
67
|
-
return {
|
|
68
|
-
status: 'truant',
|
|
69
|
-
replicaInfo,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
return {
|
|
73
|
-
status: 'existing',
|
|
74
|
-
replicaInfo,
|
|
75
|
-
};
|
|
76
|
-
};
|
|
77
|
-
this.getAllNonTruant = (libraryId) => {
|
|
78
|
-
return this.db
|
|
59
|
+
`)
|
|
60
|
+
.run(info.type, replicaId, libraryId);
|
|
61
|
+
}
|
|
62
|
+
if (replicaInfo.clientId !== info.userId) {
|
|
63
|
+
throw new Error(`Replica ${replicaId} already exists with a different user ID`);
|
|
64
|
+
}
|
|
65
|
+
if (replicaInfo.lastSeenWallClockTime &&
|
|
66
|
+
replicaInfo.lastSeenWallClockTime < this.truantCutoff) {
|
|
67
|
+
return {
|
|
68
|
+
status: 'truant',
|
|
69
|
+
replicaInfo,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
status: 'existing',
|
|
74
|
+
replicaInfo,
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
this.getAllNonTruant = (libraryId) => {
|
|
78
|
+
return this.db
|
|
79
79
|
.prepare(`
|
|
80
80
|
SELECT * FROM ReplicaInfo
|
|
81
81
|
WHERE libraryId = ? AND lastSeenWallClockTime > ?
|
|
82
|
-
`)
|
|
83
|
-
.all(libraryId, this.truantCutoff);
|
|
84
|
-
};
|
|
85
|
-
this.updateLastSeen = (libraryId, replicaId) => {
|
|
86
|
-
const clockTime = new Date().getTime();
|
|
87
|
-
return this.db
|
|
82
|
+
`)
|
|
83
|
+
.all(libraryId, this.truantCutoff);
|
|
84
|
+
};
|
|
85
|
+
this.updateLastSeen = (libraryId, replicaId) => {
|
|
86
|
+
const clockTime = new Date().getTime();
|
|
87
|
+
return this.db
|
|
88
88
|
.prepare(`
|
|
89
89
|
UPDATE ReplicaInfo
|
|
90
90
|
SET lastSeenWallClockTime = ?
|
|
91
91
|
WHERE id = ? AND libraryId = ?
|
|
92
|
-
`)
|
|
93
|
-
.run(clockTime, replicaId, libraryId);
|
|
94
|
-
};
|
|
95
|
-
this.updateAcknowledged = (libraryId, replicaId, timestamp) => {
|
|
96
|
-
this.db
|
|
92
|
+
`)
|
|
93
|
+
.run(clockTime, replicaId, libraryId);
|
|
94
|
+
};
|
|
95
|
+
this.updateAcknowledged = (libraryId, replicaId, timestamp) => {
|
|
96
|
+
this.db
|
|
97
97
|
.prepare(`
|
|
98
98
|
UPDATE ReplicaInfo
|
|
99
99
|
SET ackedLogicalTime = ?
|
|
100
100
|
WHERE id = ? AND libraryId = ?
|
|
101
|
-
`)
|
|
102
|
-
.run(timestamp, replicaId, libraryId);
|
|
103
|
-
};
|
|
104
|
-
this.updateServerOrder = (libraryId, replicaId, serverOrder) => {
|
|
105
|
-
// only increment the server order, never set it to lower than before
|
|
106
|
-
this.db
|
|
101
|
+
`)
|
|
102
|
+
.run(timestamp, replicaId, libraryId);
|
|
103
|
+
};
|
|
104
|
+
this.updateServerOrder = (libraryId, replicaId, serverOrder) => {
|
|
105
|
+
// only increment the server order, never set it to lower than before
|
|
106
|
+
this.db
|
|
107
107
|
.prepare(`
|
|
108
108
|
UPDATE ReplicaInfo
|
|
109
109
|
SET ackedServerOrder = MAX(ackedServerOrder, ?)
|
|
110
110
|
WHERE id = ? AND libraryId = ?
|
|
111
|
-
`)
|
|
112
|
-
.run(serverOrder, replicaId, libraryId);
|
|
113
|
-
};
|
|
114
|
-
this.acknowledgeOperation = (libraryId, replicaId, timestamp) => {
|
|
115
|
-
if (!timestamp)
|
|
116
|
-
return;
|
|
117
|
-
// get the server order from the operation with the timestamp and update the replica's server order
|
|
118
|
-
// to the max of the current server order and the server order of the operation
|
|
119
|
-
this.db
|
|
111
|
+
`)
|
|
112
|
+
.run(serverOrder, replicaId, libraryId);
|
|
113
|
+
};
|
|
114
|
+
this.acknowledgeOperation = (libraryId, replicaId, timestamp) => {
|
|
115
|
+
if (!timestamp)
|
|
116
|
+
return;
|
|
117
|
+
// get the server order from the operation with the timestamp and update the replica's server order
|
|
118
|
+
// to the max of the current server order and the server order of the operation
|
|
119
|
+
this.db
|
|
120
120
|
.prepare(`
|
|
121
121
|
UPDATE ReplicaInfo
|
|
122
122
|
SET ackedLogicalTime = ?, ackedServerOrder = MAX(ackedServerOrder, COALESCE((
|
|
@@ -124,47 +124,47 @@ export class ReplicaInfos {
|
|
|
124
124
|
WHERE libraryId = ? AND timestamp = ?
|
|
125
125
|
), 0))
|
|
126
126
|
WHERE id = ? AND libraryId = ?
|
|
127
|
-
`)
|
|
128
|
-
.run(timestamp, libraryId, timestamp, replicaId, libraryId);
|
|
129
|
-
};
|
|
130
|
-
this.getGlobalAck = (libraryId, onlineReplicaIds) => {
|
|
131
|
-
const nonTruant = this.getAllNonTruant(libraryId);
|
|
132
|
-
const globalAckEligible = nonTruant.filter((replica) => replica.type < 2 || (onlineReplicaIds === null || onlineReplicaIds === void 0 ? void 0 : onlineReplicaIds.includes(replica.id)));
|
|
133
|
-
// get the earliest acked time
|
|
134
|
-
return globalAckEligible.reduce((acc, replica) => {
|
|
135
|
-
if (!replica.ackedLogicalTime) {
|
|
136
|
-
return acc;
|
|
137
|
-
}
|
|
138
|
-
if (acc === undefined) {
|
|
139
|
-
return replica.ackedLogicalTime;
|
|
140
|
-
}
|
|
141
|
-
return acc < replica.ackedLogicalTime ? acc : replica.ackedLogicalTime;
|
|
142
|
-
}, undefined);
|
|
143
|
-
};
|
|
144
|
-
this.delete = (libraryId, replicaId) => {
|
|
145
|
-
return this.db
|
|
127
|
+
`)
|
|
128
|
+
.run(timestamp, libraryId, timestamp, replicaId, libraryId);
|
|
129
|
+
};
|
|
130
|
+
this.getGlobalAck = (libraryId, onlineReplicaIds) => {
|
|
131
|
+
const nonTruant = this.getAllNonTruant(libraryId);
|
|
132
|
+
const globalAckEligible = nonTruant.filter((replica) => replica.type < 2 || (onlineReplicaIds === null || onlineReplicaIds === void 0 ? void 0 : onlineReplicaIds.includes(replica.id)));
|
|
133
|
+
// get the earliest acked time
|
|
134
|
+
return globalAckEligible.reduce((acc, replica) => {
|
|
135
|
+
if (!replica.ackedLogicalTime) {
|
|
136
|
+
return acc;
|
|
137
|
+
}
|
|
138
|
+
if (acc === undefined) {
|
|
139
|
+
return replica.ackedLogicalTime;
|
|
140
|
+
}
|
|
141
|
+
return acc < replica.ackedLogicalTime ? acc : replica.ackedLogicalTime;
|
|
142
|
+
}, undefined);
|
|
143
|
+
};
|
|
144
|
+
this.delete = (libraryId, replicaId) => {
|
|
145
|
+
return this.db
|
|
146
146
|
.prepare(`
|
|
147
147
|
DELETE FROM ReplicaInfo
|
|
148
148
|
WHERE id = ? AND libraryId = ?
|
|
149
|
-
`)
|
|
150
|
-
.run(replicaId, libraryId);
|
|
151
|
-
};
|
|
152
|
-
this.deleteAll = (libraryId) => {
|
|
153
|
-
return this.db
|
|
149
|
+
`)
|
|
150
|
+
.run(replicaId, libraryId);
|
|
151
|
+
};
|
|
152
|
+
this.deleteAll = (libraryId) => {
|
|
153
|
+
return this.db
|
|
154
154
|
.prepare(`
|
|
155
155
|
DELETE FROM ReplicaInfo
|
|
156
156
|
WHERE libraryId = ?
|
|
157
|
-
`)
|
|
158
|
-
.run(libraryId);
|
|
159
|
-
};
|
|
160
|
-
this.deleteAllForUser = (libraryId, userId) => {
|
|
161
|
-
return this.db
|
|
162
|
-
.prepare(`DELETE FROM ReplicaInfo WHERE libraryId = ? AND clientId = ?`)
|
|
163
|
-
.run(libraryId, userId);
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
get truantCutoff() {
|
|
167
|
-
return Date.now() - this.replicaTruancyMinutes * 60 * 1000;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
157
|
+
`)
|
|
158
|
+
.run(libraryId);
|
|
159
|
+
};
|
|
160
|
+
this.deleteAllForUser = (libraryId, userId) => {
|
|
161
|
+
return this.db
|
|
162
|
+
.prepare(`DELETE FROM ReplicaInfo WHERE libraryId = ? AND clientId = ?`)
|
|
163
|
+
.run(libraryId, userId);
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
get truantCutoff() {
|
|
167
|
+
return Date.now() - this.replicaTruancyMinutes * 60 * 1000;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
170
|
//# sourceMappingURL=Replicas.js.map
|