@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.
Files changed (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/dist/cjs/Baselines.d.ts +14 -0
  4. package/dist/cjs/Baselines.js +101 -0
  5. package/dist/cjs/Baselines.js.map +1 -0
  6. package/dist/cjs/ClientConnection.d.ts +18 -0
  7. package/dist/cjs/ClientConnection.js +106 -0
  8. package/dist/cjs/ClientConnection.js.map +1 -0
  9. package/dist/cjs/MessageSender.d.ts +5 -0
  10. package/dist/cjs/MessageSender.js +3 -0
  11. package/dist/cjs/MessageSender.js.map +1 -0
  12. package/dist/cjs/OperationHistory.d.ts +34 -0
  13. package/dist/cjs/OperationHistory.js +147 -0
  14. package/dist/cjs/OperationHistory.js.map +1 -0
  15. package/dist/cjs/Presence.d.ts +15 -0
  16. package/dist/cjs/Presence.js +60 -0
  17. package/dist/cjs/Presence.js.map +1 -0
  18. package/dist/cjs/Profiles.d.ts +10 -0
  19. package/dist/cjs/Profiles.js +20 -0
  20. package/dist/cjs/Profiles.js.map +1 -0
  21. package/dist/cjs/ReplicaKeepaliveTimers.d.ts +17 -0
  22. package/dist/cjs/ReplicaKeepaliveTimers.js +31 -0
  23. package/dist/cjs/ReplicaKeepaliveTimers.js.map +1 -0
  24. package/dist/cjs/Replicas.d.ts +23 -0
  25. package/dist/cjs/Replicas.js +174 -0
  26. package/dist/cjs/Replicas.js.map +1 -0
  27. package/dist/cjs/Server.d.ts +121 -0
  28. package/dist/cjs/Server.js +378 -0
  29. package/dist/cjs/Server.js.map +1 -0
  30. package/dist/cjs/ServerLibrary.d.ts +63 -0
  31. package/dist/cjs/ServerLibrary.js +468 -0
  32. package/dist/cjs/ServerLibrary.js.map +1 -0
  33. package/dist/cjs/ServerStorage.d.ts +35 -0
  34. package/dist/cjs/ServerStorage.js +60 -0
  35. package/dist/cjs/ServerStorage.js.map +1 -0
  36. package/dist/cjs/TokenProvider.d.ts +16 -0
  37. package/dist/cjs/TokenProvider.js +27 -0
  38. package/dist/cjs/TokenProvider.js.map +1 -0
  39. package/dist/cjs/TokenVerifier.d.ts +16 -0
  40. package/dist/cjs/TokenVerifier.js +29 -0
  41. package/dist/cjs/TokenVerifier.js.map +1 -0
  42. package/dist/cjs/files/FileMetadata.d.ts +28 -0
  43. package/dist/cjs/files/FileMetadata.js +69 -0
  44. package/dist/cjs/files/FileMetadata.js.map +1 -0
  45. package/dist/cjs/files/FileStorage.d.ts +35 -0
  46. package/dist/cjs/files/FileStorage.js +72 -0
  47. package/dist/cjs/files/FileStorage.js.map +1 -0
  48. package/dist/cjs/index.d.ts +9 -0
  49. package/dist/cjs/index.js +12 -0
  50. package/dist/cjs/index.js.map +1 -0
  51. package/dist/cjs/migrations/v0.sql.d.ts +2 -0
  52. package/dist/cjs/migrations/v0.sql.js +29 -0
  53. package/dist/cjs/migrations/v0.sql.js.map +1 -0
  54. package/dist/cjs/migrations/v1.sql.d.ts +2 -0
  55. package/dist/cjs/migrations/v1.sql.js +31 -0
  56. package/dist/cjs/migrations/v1.sql.js.map +1 -0
  57. package/dist/cjs/migrations/v2.sql.d.ts +2 -0
  58. package/dist/cjs/migrations/v2.sql.js +28 -0
  59. package/dist/cjs/migrations/v2.sql.js.map +1 -0
  60. package/dist/cjs/migrations/v3.sql.d.ts +2 -0
  61. package/dist/cjs/migrations/v3.sql.js +14 -0
  62. package/dist/cjs/migrations/v3.sql.js.map +1 -0
  63. package/dist/cjs/migrations/v4.sql.d.ts +2 -0
  64. package/dist/cjs/migrations/v4.sql.js +8 -0
  65. package/dist/cjs/migrations/v4.sql.js.map +1 -0
  66. package/dist/cjs/migrations.d.ts +2 -0
  67. package/dist/cjs/migrations.js +41 -0
  68. package/dist/cjs/migrations.js.map +1 -0
  69. package/dist/cjs/types.d.ts +22 -0
  70. package/dist/cjs/types.js +3 -0
  71. package/dist/cjs/types.js.map +1 -0
  72. package/dist/esm/Baselines.d.ts +14 -0
  73. package/dist/esm/Baselines.js +97 -0
  74. package/dist/esm/Baselines.js.map +1 -0
  75. package/dist/esm/ClientConnection.d.ts +18 -0
  76. package/dist/esm/ClientConnection.js +102 -0
  77. package/dist/esm/ClientConnection.js.map +1 -0
  78. package/dist/esm/MessageSender.d.ts +5 -0
  79. package/dist/esm/MessageSender.js +2 -0
  80. package/dist/esm/MessageSender.js.map +1 -0
  81. package/dist/esm/OperationHistory.d.ts +34 -0
  82. package/dist/esm/OperationHistory.js +143 -0
  83. package/dist/esm/OperationHistory.js.map +1 -0
  84. package/dist/esm/Presence.d.ts +15 -0
  85. package/dist/esm/Presence.js +53 -0
  86. package/dist/esm/Presence.js.map +1 -0
  87. package/dist/esm/Profiles.d.ts +10 -0
  88. package/dist/esm/Profiles.js +16 -0
  89. package/dist/esm/Profiles.js.map +1 -0
  90. package/dist/esm/ReplicaKeepaliveTimers.d.ts +17 -0
  91. package/dist/esm/ReplicaKeepaliveTimers.js +27 -0
  92. package/dist/esm/ReplicaKeepaliveTimers.js.map +1 -0
  93. package/dist/esm/Replicas.d.ts +23 -0
  94. package/dist/esm/Replicas.js +170 -0
  95. package/dist/esm/Replicas.js.map +1 -0
  96. package/dist/esm/Server.d.ts +121 -0
  97. package/dist/esm/Server.js +371 -0
  98. package/dist/esm/Server.js.map +1 -0
  99. package/dist/esm/ServerLibrary.d.ts +63 -0
  100. package/dist/esm/ServerLibrary.js +464 -0
  101. package/dist/esm/ServerLibrary.js.map +1 -0
  102. package/dist/esm/ServerStorage.d.ts +44 -0
  103. package/dist/esm/ServerStorage.js +58 -0
  104. package/dist/esm/ServerStorage.js.map +1 -0
  105. package/dist/esm/TokenProvider.d.ts +16 -0
  106. package/dist/esm/TokenProvider.js +20 -0
  107. package/dist/esm/TokenProvider.js.map +1 -0
  108. package/dist/esm/TokenVerifier.d.ts +16 -0
  109. package/dist/esm/TokenVerifier.js +22 -0
  110. package/dist/esm/TokenVerifier.js.map +1 -0
  111. package/dist/esm/files/FileMetadata.d.ts +28 -0
  112. package/dist/esm/files/FileMetadata.js +65 -0
  113. package/dist/esm/files/FileMetadata.js.map +1 -0
  114. package/dist/esm/files/FileStorage.d.ts +35 -0
  115. package/dist/esm/files/FileStorage.js +45 -0
  116. package/dist/esm/files/FileStorage.js.map +1 -0
  117. package/dist/esm/index.d.ts +9 -0
  118. package/dist/esm/index.js +5 -0
  119. package/dist/esm/index.js.map +1 -0
  120. package/dist/esm/migrations/v0.sql.d.ts +2 -0
  121. package/dist/esm/migrations/v0.sql.js +27 -0
  122. package/dist/esm/migrations/v0.sql.js.map +1 -0
  123. package/dist/esm/migrations/v1.sql.d.ts +2 -0
  124. package/dist/esm/migrations/v1.sql.js +29 -0
  125. package/dist/esm/migrations/v1.sql.js.map +1 -0
  126. package/dist/esm/migrations/v2.sql.d.ts +2 -0
  127. package/dist/esm/migrations/v2.sql.js +26 -0
  128. package/dist/esm/migrations/v2.sql.js.map +1 -0
  129. package/dist/esm/migrations/v3.sql.d.ts +2 -0
  130. package/dist/esm/migrations/v3.sql.js +12 -0
  131. package/dist/esm/migrations/v3.sql.js.map +1 -0
  132. package/dist/esm/migrations/v4.sql.d.ts +2 -0
  133. package/dist/esm/migrations/v4.sql.js +6 -0
  134. package/dist/esm/migrations/v4.sql.js.map +1 -0
  135. package/dist/esm/migrations.d.ts +2 -0
  136. package/dist/esm/migrations.js +34 -0
  137. package/dist/esm/migrations.js.map +1 -0
  138. package/dist/esm/types.d.ts +22 -0
  139. package/dist/esm/types.js +2 -0
  140. package/dist/esm/types.js.map +1 -0
  141. package/dist/tsconfig-cjs.tsbuildinfo +1 -0
  142. package/dist/tsconfig.tsbuildinfo +1 -0
  143. package/package.json +43 -0
  144. package/src/Baselines.ts +158 -0
  145. package/src/ClientConnection.ts +150 -0
  146. package/src/MessageSender.ts +10 -0
  147. package/src/OperationHistory.ts +206 -0
  148. package/src/Presence.ts +63 -0
  149. package/src/Profiles.ts +21 -0
  150. package/src/ReplicaKeepaliveTimers.ts +34 -0
  151. package/src/Replicas.ts +238 -0
  152. package/src/Server.ts +553 -0
  153. package/src/ServerLibrary.ts +727 -0
  154. package/src/TokenProvider.ts +39 -0
  155. package/src/TokenVerifier.ts +30 -0
  156. package/src/files/FileMetadata.ts +112 -0
  157. package/src/files/FileStorage.ts +87 -0
  158. package/src/index.ts +9 -0
  159. package/src/migrations/v0.sql.ts +26 -0
  160. package/src/migrations/v1.sql.ts +28 -0
  161. package/src/migrations/v2.sql.ts +26 -0
  162. package/src/migrations/v3.sql.ts +12 -0
  163. package/src/migrations/v4.sql.ts +6 -0
  164. package/src/migrations.ts +51 -0
  165. package/src/types.ts +35 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Baselines.js","sourceRoot":"","sources":["../../src/Baselines.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,GAKV,MAAM,qBAAqB,CAAC;AAI7B,MAAM,OAAO,SAAS;IACrB,YAAoB,EAAY;QAAZ,OAAE,GAAF,EAAE,CAAU;QAEhC,QAAG,GAAG,CAAC,SAAiB,EAAE,GAAW,EAAgC,EAAE;YACtE,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;iBACjB,OAAO,CACP;;;KAGC,CACD;iBACA,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;YAEtB,IAAI,CAAC,GAAG,EAAE;gBACT,OAAO,IAAI,CAAC;aACZ;YAED,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAClC,CAAC,CAAC;QAEF,yBAAoB,GAAG,CACtB,SAAiB,EACjB,GAAW,EACe,EAAE;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;iBAClB,OAAO,CACP;;;GAGD,CACC;iBACA,GAAG,CAAC,SAAS,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;YAE5B,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACvC,CAAC,CAAC;QAEF,QAAG,GAAG,CAAC,SAAiB,EAAE,QAA0B,EAAE,EAAE;YACvD,OAAO,IAAI,CAAC,EAAE;iBACZ,OAAO,CACP;;;KAGC,CACD;iBACA,GAAG,CACH,SAAS,EACT,QAAQ,CAAC,GAAG,EACZ,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EACjC,QAAQ,CAAC,SAAS,CAClB,CAAC;QACJ,CAAC,CAAC;QAEF,cAAS,GAAG,CAAC,SAAiB,EAAE,SAA6B,EAAE,EAAE;YAChE,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;gBACnC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE;oBACjC,IAAI,CAAC,EAAE;yBACL,OAAO,CACP;;;IAGF,CACE;yBACA,GAAG,CACH,SAAS,EACT,QAAQ,CAAC,GAAG,EACZ,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EACjC,QAAQ,CAAC,SAAS,CAClB,CAAC;iBACH;YACF,CAAC,CAAC,CAAC;YACH,EAAE,EAAE,CAAC;QACN,CAAC,CAAC;QAEM,oBAAe,GAAG,CACzB,QAA8B,EACN,EAAE;YAC1B,uCACI,QAAQ,KACX,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,IACtC;QACH,CAAC,CAAC;QAEF,gBAAW,GAAG,CACb,SAAiB,EACjB,SAAwB,EACE,EAAE;YAC5B,IAAI,CAAC,SAAS,EAAE;gBACf,OAAO,IAAI,CAAC,EAAE;qBACZ,OAAO,CACP;;;;KAIA,CACA;qBACA,GAAG,CAAC,SAAS,CAAC;qBACd,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;aAC5B;YACD,OAAO,IAAI,CAAC,EAAE;iBACZ,OAAO,CACP;;;;KAIC,CACD;iBACA,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC;iBACzB,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7B,CAAC,CAAC;QAEF,oBAAe,GAAG,CACjB,SAAiB,EACjB,GAAW,EACX,UAAuB,EACvB,WAAmB,EAClB,EAAE;YACH,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YAEvC,IAAI,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;YACxC,IAAI,CAAC,QAAQ,EAAE;gBACd,QAAQ,GAAG;oBACV,GAAG;oBACH,QAAQ,EAAE,EAAE;oBACZ,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;iBAClC,CAAC;aACF;YACD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;gBACnC,QAAQ,CAAC,QAAQ,GAAG,UAAU,CAC7B,QAAQ,CAAC,QAAQ,EACjB,SAAS,CAAC,IAAI,EACd,WAAW,CACX,CAAC;aACF;YACD,QAAQ,CAAC,SAAS,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;YACjE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC/B,CAAC,CAAC;QAEF,cAAS,GAAG,CAAC,SAAiB,EAAE,EAAE;YACjC,IAAI,CAAC,EAAE;iBACL,OAAO,CACP;;;GAGD,CACC;iBACA,GAAG,CAAC,SAAS,CAAC,CAAC;QAClB,CAAC,CAAC;IAjJiC,CAAC;CAkJpC"}
@@ -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,102 @@
1
+ class ClientConnection {
2
+ constructor(key) {
3
+ this.key = key;
4
+ }
5
+ }
6
+ class RequestClientConnection extends ClientConnection {
7
+ constructor(key, req, res, info) {
8
+ super(key);
9
+ this.req = req;
10
+ this.res = res;
11
+ this.info = info;
12
+ this.type = 'request';
13
+ this.responses = [];
14
+ this.respond = (message) => {
15
+ this.responses.push(message);
16
+ };
17
+ this.end = () => {
18
+ this.res.writeHead(200, {
19
+ 'Content-Type': 'application/json',
20
+ });
21
+ this.res.write(JSON.stringify({
22
+ messages: this.responses,
23
+ }));
24
+ this.res.end();
25
+ };
26
+ }
27
+ }
28
+ class WebSocketClientConnection extends ClientConnection {
29
+ constructor(key, ws, info) {
30
+ super(key);
31
+ this.ws = ws;
32
+ this.info = info;
33
+ this.type = 'websocket';
34
+ this.respond = (message) => {
35
+ this.ws.send(JSON.stringify(message));
36
+ };
37
+ }
38
+ }
39
+ export class ClientConnectionManager {
40
+ constructor() {
41
+ this.connections = {};
42
+ this.getLibraryConnections = (libraryId) => {
43
+ if (!this.connections[libraryId]) {
44
+ this.connections[libraryId] = new Map();
45
+ }
46
+ return this.connections[libraryId];
47
+ };
48
+ this.addSocket = (libraryId, key, socket, info) => {
49
+ const lib = this.getLibraryConnections(libraryId);
50
+ const connection = new WebSocketClientConnection(key, socket, info);
51
+ lib.set(key, connection);
52
+ socket.on('close', () => {
53
+ lib.delete(key);
54
+ });
55
+ };
56
+ this.addRequest = (libraryId, key, request, response, info) => {
57
+ const lib = this.getLibraryConnections(libraryId);
58
+ const connection = new RequestClientConnection(key, request, response, info);
59
+ lib.set(key, connection);
60
+ return () => {
61
+ connection.end();
62
+ lib.delete(key);
63
+ };
64
+ };
65
+ this.remove = (libraryId, key) => {
66
+ const lib = this.getLibraryConnections(libraryId);
67
+ lib.delete(key);
68
+ };
69
+ this.respond = (libraryId, key, message) => {
70
+ const lib = this.getLibraryConnections(libraryId);
71
+ const connection = lib.get(key);
72
+ if (connection) {
73
+ connection.respond(message);
74
+ }
75
+ else {
76
+ throw new Error(`No connection found for library ${libraryId}, key ${key}`);
77
+ }
78
+ };
79
+ /**
80
+ * Broadcasts a message to all live-connected clients in a library.
81
+ * This does not reach push/pull clients.
82
+ */
83
+ this.broadcast = (libraryId, message, omitKeys = []) => {
84
+ const lib = this.getLibraryConnections(libraryId);
85
+ for (const [key, connection] of lib) {
86
+ if (!omitKeys.includes(key)) {
87
+ connection.respond(message);
88
+ }
89
+ }
90
+ };
91
+ this.disconnectAll = (libraryId) => {
92
+ const lib = this.getLibraryConnections(libraryId);
93
+ for (const connection of lib.values()) {
94
+ if (connection instanceof WebSocketClientConnection) {
95
+ connection.ws.close();
96
+ }
97
+ }
98
+ lib.clear();
99
+ };
100
+ }
101
+ }
102
+ //# 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,5 @@
1
+ import { ServerMessage } from '@verdant-web/common';
2
+ export interface MessageSender {
3
+ broadcast(libraryId: string, message: ServerMessage, omitKeys?: string[]): void;
4
+ send(libraryId: string, clientKey: string, message: ServerMessage): void;
5
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=MessageSender.js.map
@@ -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,143 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ export class OperationHistory {
13
+ constructor(db) {
14
+ this.db = db;
15
+ this.hydratePatch = (_a) => {
16
+ var { libraryId: _, data } = _a, rest = __rest(_a, ["libraryId", "data"]);
17
+ return Object.assign(Object.assign({}, rest), { data: JSON.parse(data) });
18
+ };
19
+ this.getAllFor = (libraryId, oid) => {
20
+ return this.db
21
+ .prepare(`
22
+ SELECT * FROM OperationHistory
23
+ WHERE libraryId = ? AND oid = ?
24
+ ORDER BY timestamp ASC
25
+ `)
26
+ .all(libraryId, oid)
27
+ .map(this.hydratePatch);
28
+ };
29
+ this.getEarliest = (libraryId, count) => {
30
+ return this.db
31
+ .prepare(`
32
+ SELECT * FROM OperationHistory
33
+ WHERE libraryId = ?
34
+ ORDER BY timestamp ASC
35
+ LIMIT ?
36
+ `)
37
+ .all(libraryId, count)
38
+ .map(this.hydratePatch);
39
+ };
40
+ this.getAfter = (libraryId, timestamp = null) => {
41
+ if (timestamp === null) {
42
+ return this.db
43
+ .prepare(`
44
+ SELECT * FROM OperationHistory
45
+ WHERE libraryId = ?
46
+ ORDER BY timestamp ASC
47
+ `)
48
+ .all(libraryId)
49
+ .map(this.hydratePatch);
50
+ }
51
+ return this.db
52
+ .prepare(`
53
+ SELECT * FROM OperationHistory
54
+ WHERE libraryId = ? AND timestamp > ?
55
+ ORDER BY timestamp ASC
56
+ `)
57
+ .all(libraryId, timestamp)
58
+ .map(this.hydratePatch);
59
+ };
60
+ /**
61
+ * Returns all operations before the given timestamp
62
+ * in ascending chronological order
63
+ */
64
+ this.getBefore = (libraryId, before) => {
65
+ return this.db
66
+ .prepare(`
67
+ SELECT * FROM OperationHistory
68
+ WHERE libraryId = ? AND timestamp < ?
69
+ ORDER BY timestamp ASC
70
+ `)
71
+ .all(libraryId, before)
72
+ .map(this.hydratePatch);
73
+ };
74
+ /**
75
+ * Returns all operations after a given server order. Useful
76
+ * for retrieving operations that were created after a client
77
+ * last synced.
78
+ */
79
+ this.getFromServerOrder = (libraryId, serverOrder) => {
80
+ const result = this.db
81
+ .prepare(`
82
+ SELECT * FROM OperationHistory
83
+ WHERE libraryId = ? AND serverOrder > ?
84
+ `)
85
+ .all(libraryId, serverOrder);
86
+ return (result || []).map(this.hydratePatch);
87
+ };
88
+ /**
89
+ * Adds a new operation or replaces an existing one.
90
+ * The server order will be set to the current max + 1
91
+ */
92
+ this.insert = (libraryId, replicaId, item) => {
93
+ return this.db
94
+ .prepare(`
95
+ INSERT OR REPLACE INTO OperationHistory (libraryId, oid, data, timestamp, replicaId, serverOrder)
96
+ VALUES (?, ?, ?, ?, ?, (SELECT COALESCE(MAX(serverOrder), 0) FROM OperationHistory WHERE libraryId = ?) + 1)
97
+ `)
98
+ .run(libraryId, item.oid, JSON.stringify(item.data), item.timestamp, replicaId);
99
+ };
100
+ /**
101
+ * Inserts all operations and updates the server
102
+ * order for this library
103
+ */
104
+ this.insertAll = async (libraryId, replicaId, items) => {
105
+ const tx = this.db.transaction(() => {
106
+ let orderResult = this.db
107
+ .prepare(`
108
+ SELECT COALESCE(MAX(serverOrder), 0) AS serverOrder FROM OperationHistory WHERE libraryId = ?
109
+ `)
110
+ .get(libraryId);
111
+ let currentServerOrder = orderResult.serverOrder || 0;
112
+ const insertStatement = this.db.prepare(`
113
+ INSERT OR REPLACE INTO OperationHistory (libraryId, oid, data, timestamp, replicaId, serverOrder)
114
+ VALUES (?, ?, ?, ?, ?, ?)
115
+ `);
116
+ for (const item of items) {
117
+ insertStatement.run(libraryId, item.oid, JSON.stringify(item.data), item.timestamp, replicaId, ++currentServerOrder);
118
+ }
119
+ });
120
+ tx();
121
+ };
122
+ this.dropAll = async (libraryId, items) => {
123
+ const deleteStatement = this.db.prepare(`
124
+ DELETE FROM OperationHistory
125
+ WHERE libraryId = ? AND replicaId = ? AND oid = ? AND timestamp = ?
126
+ `);
127
+ this.db.transaction(() => {
128
+ for (const item of items) {
129
+ deleteStatement.run(libraryId, item.replicaId, item.oid, item.timestamp);
130
+ }
131
+ })();
132
+ };
133
+ this.deleteAll = (libraryId) => {
134
+ this.db
135
+ .prepare(`
136
+ DELETE FROM OperationHistory
137
+ WHERE libraryId = ?
138
+ `)
139
+ .run(libraryId);
140
+ };
141
+ }
142
+ }
143
+ //# 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,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
+ }
53
+ //# sourceMappingURL=Presence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Presence.js","sourceRoot":"","sources":["../../src/Presence.ts"],"names":[],"mappings":"AACA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAElC;;;GAGG;AACH,MAAM,OAAO,QAAS,SAAQ,YAAY;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"}
@@ -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,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
+ }
16
+ //# sourceMappingURL=Profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Profiles.js","sourceRoot":"","sources":["../../src/Profiles.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,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"}
@@ -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,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
+ }
27
+ //# sourceMappingURL=ReplicaKeepaliveTimers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplicaKeepaliveTimers.js","sourceRoot":"","sources":["../../src/ReplicaKeepaliveTimers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,OAAO,sBAAuB,SAAQ,eAE1C;IAGD,YAAoB,UAAU,EAAE,GAAG,IAAI;QACtC,KAAK,EAAE,CAAC;QADW,YAAO,GAAP,OAAO,CAAY;QAF/B,WAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;QAMnD,YAAO,GAAG,CAAC,SAAiB,EAAE,SAAiB,EAAE,EAAE;YAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC5C,IAAI,QAAQ,EAAE;gBACb,YAAY,CAAC,QAAQ,CAAC,CAAC;aACvB;YAED,IAAI,CAAC,MAAM,CAAC,GAAG,CACd,SAAS,EACT,UAAU,CAAC,GAAG,EAAE;gBACf,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;gBACxC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC/B,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAChB,CAAC;QACH,CAAC,CAAC;IAfF,CAAC;CAgBD"}
@@ -0,0 +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
+ }