@verdant-web/server 3.3.11 → 4.0.0

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 (204) hide show
  1. package/bin/server.mjs +110 -107
  2. package/dist/esm/TokenVerifier.d.ts +1 -0
  3. package/dist/esm/TokenVerifier.js +3 -0
  4. package/dist/esm/TokenVerifier.js.map +1 -1
  5. package/dist/esm/adapters/node/errorHandler.d.ts +2 -0
  6. package/dist/esm/adapters/node/errorHandler.js +12 -0
  7. package/dist/esm/adapters/node/errorHandler.js.map +1 -0
  8. package/dist/esm/adapters/node/getRequestToken.d.ts +1 -0
  9. package/dist/esm/adapters/node/getRequestToken.js +25 -0
  10. package/dist/esm/adapters/node/getRequestToken.js.map +1 -0
  11. package/dist/esm/adapters/node/httpRouter.d.ts +47 -0
  12. package/dist/esm/adapters/node/httpRouter.js +88 -0
  13. package/dist/esm/adapters/node/httpRouter.js.map +1 -0
  14. package/dist/esm/adapters/node/index.d.ts +5 -0
  15. package/dist/esm/adapters/node/index.js +6 -0
  16. package/dist/esm/adapters/node/index.js.map +1 -0
  17. package/dist/esm/adapters/node/tokenMiddleware.d.ts +7 -0
  18. package/dist/esm/adapters/node/tokenMiddleware.js +22 -0
  19. package/dist/esm/adapters/node/tokenMiddleware.js.map +1 -0
  20. package/dist/esm/adapters/node/websocketHandler.d.ts +4 -0
  21. package/dist/esm/adapters/node/websocketHandler.js +54 -0
  22. package/dist/esm/adapters/node/websocketHandler.js.map +1 -0
  23. package/dist/esm/connections/ClientConnection.d.ts +25 -0
  24. package/dist/esm/{ClientConnection.js → connections/ClientConnection.js} +47 -38
  25. package/dist/esm/connections/ClientConnection.js.map +1 -0
  26. package/dist/esm/connections/MessageSender.d.ts +5 -0
  27. package/dist/esm/connections/MessageSender.js.map +1 -0
  28. package/dist/esm/connections/Presence.d.ts +23 -0
  29. package/dist/esm/connections/Presence.js +65 -0
  30. package/dist/esm/connections/Presence.js.map +1 -0
  31. package/dist/esm/core.d.ts +15 -0
  32. package/dist/esm/core.js +15 -0
  33. package/dist/esm/core.js.map +1 -0
  34. package/dist/esm/defaults.d.ts +2 -0
  35. package/dist/esm/defaults.js +3 -0
  36. package/dist/esm/defaults.js.map +1 -0
  37. package/dist/esm/files/FileStorage.d.ts +12 -3
  38. package/dist/esm/files/FileStorage.js +20 -0
  39. package/dist/esm/files/FileStorage.js.map +1 -1
  40. package/dist/esm/index.d.ts +7 -7
  41. package/dist/esm/index.js +2 -2
  42. package/dist/esm/index.js.map +1 -1
  43. package/dist/esm/internals.d.ts +14 -0
  44. package/dist/esm/internals.js +12 -0
  45. package/dist/esm/internals.js.map +1 -0
  46. package/dist/esm/libraries/Library.d.ts +78 -0
  47. package/dist/esm/{ServerLibrary.js → libraries/Library.js} +153 -144
  48. package/dist/esm/libraries/Library.js.map +1 -0
  49. package/dist/esm/{ServerLibrary.test.js → libraries/Library.test.js} +42 -33
  50. package/dist/esm/libraries/Library.test.js.map +1 -0
  51. package/dist/esm/libraries/generic.d.ts +8 -0
  52. package/dist/esm/libraries/generic.js +2 -0
  53. package/dist/esm/libraries/generic.js.map +1 -0
  54. package/dist/esm/libraries/singleNode.d.ts +53 -0
  55. package/dist/esm/libraries/singleNode.js +88 -0
  56. package/dist/esm/libraries/singleNode.js.map +1 -0
  57. package/dist/esm/logger.d.ts +1 -0
  58. package/dist/esm/logger.js +2 -0
  59. package/dist/esm/logger.js.map +1 -0
  60. package/dist/esm/storage/Storage.d.ts +39 -38
  61. package/dist/esm/storage/StorageManager.d.ts +11 -0
  62. package/dist/esm/storage/StorageManager.js +32 -0
  63. package/dist/esm/storage/StorageManager.js.map +1 -0
  64. package/dist/esm/storage/index.d.ts +1 -2
  65. package/dist/esm/storage/index.js +1 -2
  66. package/dist/esm/storage/index.js.map +1 -1
  67. package/dist/esm/storage/sqlShard/Databases.d.ts +5 -3
  68. package/dist/esm/storage/sqlShard/Databases.js +11 -7
  69. package/dist/esm/storage/sqlShard/Databases.js.map +1 -1
  70. package/dist/esm/storage/sqlShard/SqlBaselines.d.ts +16 -17
  71. package/dist/esm/storage/sqlShard/SqlBaselines.js +55 -59
  72. package/dist/esm/storage/sqlShard/SqlBaselines.js.map +1 -1
  73. package/dist/esm/storage/sqlShard/SqlFileMetadata.d.ts +13 -13
  74. package/dist/esm/storage/sqlShard/SqlFileMetadata.js +44 -57
  75. package/dist/esm/storage/sqlShard/SqlFileMetadata.js.map +1 -1
  76. package/dist/esm/storage/sqlShard/SqlOperations.d.ts +12 -12
  77. package/dist/esm/storage/sqlShard/SqlOperations.js +38 -73
  78. package/dist/esm/storage/sqlShard/SqlOperations.js.map +1 -1
  79. package/dist/esm/storage/sqlShard/SqlReplicas.d.ts +18 -17
  80. package/dist/esm/storage/sqlShard/SqlReplicas.js +63 -100
  81. package/dist/esm/storage/sqlShard/SqlReplicas.js.map +1 -1
  82. package/dist/esm/storage/sqlShard/database.d.ts +25 -5
  83. package/dist/esm/storage/sqlShard/database.js +47 -15
  84. package/dist/esm/storage/sqlShard/database.js.map +1 -1
  85. package/dist/esm/storage/sqlShard/migrations/v1.d.ts +2 -3
  86. package/dist/esm/storage/sqlShard/migrations/v1.js +42 -47
  87. package/dist/esm/storage/sqlShard/migrations/v1.js.map +1 -1
  88. package/dist/esm/storage/sqlShard/migrations/v2.d.ts +2 -3
  89. package/dist/esm/storage/sqlShard/migrations/v2.js +5 -15
  90. package/dist/esm/storage/sqlShard/migrations/v2.js.map +1 -1
  91. package/dist/esm/storage/sqlShard/migrations.d.ts +5 -7
  92. package/dist/esm/storage/sqlShard/migrations.js +48 -1
  93. package/dist/esm/storage/sqlShard/migrations.js.map +1 -1
  94. package/dist/esm/storage/sqlShard/sqlShardStorage.d.ts +25 -4
  95. package/dist/esm/storage/sqlShard/sqlShardStorage.js +43 -40
  96. package/dist/esm/storage/sqlShard/sqlShardStorage.js.map +1 -1
  97. package/dist/esm/storage/sqlShard/tables.d.ts +9 -6
  98. package/dist/esm/types.d.ts +1 -2
  99. package/package.json +24 -13
  100. package/src/TokenVerifier.ts +4 -0
  101. package/src/adapters/node/errorHandler.ts +18 -0
  102. package/src/adapters/node/getRequestToken.ts +34 -0
  103. package/src/adapters/node/httpRouter.ts +139 -0
  104. package/src/adapters/node/index.ts +5 -0
  105. package/src/adapters/node/tokenMiddleware.ts +32 -0
  106. package/src/adapters/node/websocketHandler.ts +79 -0
  107. package/src/{ClientConnection.ts → connections/ClientConnection.ts} +64 -57
  108. package/src/connections/MessageSender.ts +6 -0
  109. package/src/connections/Presence.ts +95 -0
  110. package/src/core.ts +36 -0
  111. package/src/defaults.ts +2 -0
  112. package/src/files/FileStorage.ts +36 -3
  113. package/src/index.ts +7 -7
  114. package/src/internals.ts +14 -0
  115. package/src/{ServerLibrary.test.ts → libraries/Library.test.ts} +51 -49
  116. package/src/{ServerLibrary.ts → libraries/Library.ts} +203 -206
  117. package/src/libraries/generic.ts +9 -0
  118. package/src/libraries/singleNode.ts +145 -0
  119. package/src/logger.ts +4 -0
  120. package/src/storage/Storage.ts +37 -66
  121. package/src/storage/StorageManager.ts +39 -0
  122. package/src/storage/index.ts +1 -2
  123. package/src/storage/sqlShard/Databases.ts +26 -13
  124. package/src/storage/sqlShard/SqlBaselines.ts +58 -67
  125. package/src/storage/sqlShard/SqlFileMetadata.ts +51 -72
  126. package/src/storage/sqlShard/SqlOperations.ts +57 -81
  127. package/src/storage/sqlShard/SqlReplicas.ts +80 -121
  128. package/src/storage/sqlShard/database.ts +82 -18
  129. package/src/storage/sqlShard/migrations/v1.ts +42 -52
  130. package/src/storage/sqlShard/migrations/v2.ts +5 -19
  131. package/src/storage/sqlShard/migrations.ts +66 -1
  132. package/src/storage/sqlShard/sqlShardStorage.ts +56 -55
  133. package/src/storage/sqlShard/tables.ts +6 -6
  134. package/src/types.ts +1 -2
  135. package/dist/esm/ClientConnection.d.ts +0 -19
  136. package/dist/esm/ClientConnection.js.map +0 -1
  137. package/dist/esm/MessageSender.d.ts +0 -5
  138. package/dist/esm/MessageSender.js.map +0 -1
  139. package/dist/esm/Presence.d.ts +0 -18
  140. package/dist/esm/Presence.js +0 -65
  141. package/dist/esm/Presence.js.map +0 -1
  142. package/dist/esm/ReplicaKeepaliveTimers.d.ts +0 -17
  143. package/dist/esm/ReplicaKeepaliveTimers.js +0 -27
  144. package/dist/esm/ReplicaKeepaliveTimers.js.map +0 -1
  145. package/dist/esm/Server.d.ts +0 -166
  146. package/dist/esm/Server.js +0 -572
  147. package/dist/esm/Server.js.map +0 -1
  148. package/dist/esm/ServerLibrary.d.ts +0 -64
  149. package/dist/esm/ServerLibrary.js.map +0 -1
  150. package/dist/esm/ServerLibrary.test.js.map +0 -1
  151. package/dist/esm/storage/sql/SqlBaselines.d.ts +0 -22
  152. package/dist/esm/storage/sql/SqlBaselines.js +0 -107
  153. package/dist/esm/storage/sql/SqlBaselines.js.map +0 -1
  154. package/dist/esm/storage/sql/SqlFileMetadata.d.ts +0 -18
  155. package/dist/esm/storage/sql/SqlFileMetadata.js +0 -69
  156. package/dist/esm/storage/sql/SqlFileMetadata.js.map +0 -1
  157. package/dist/esm/storage/sql/SqlOperations.d.ts +0 -19
  158. package/dist/esm/storage/sql/SqlOperations.js +0 -122
  159. package/dist/esm/storage/sql/SqlOperations.js.map +0 -1
  160. package/dist/esm/storage/sql/SqlReplicas.d.ts +0 -32
  161. package/dist/esm/storage/sql/SqlReplicas.js +0 -163
  162. package/dist/esm/storage/sql/SqlReplicas.js.map +0 -1
  163. package/dist/esm/storage/sql/database.d.ts +0 -9
  164. package/dist/esm/storage/sql/database.js +0 -24
  165. package/dist/esm/storage/sql/database.js.map +0 -1
  166. package/dist/esm/storage/sql/migrations/v1.d.ts +0 -3
  167. package/dist/esm/storage/sql/migrations/v1.js +0 -59
  168. package/dist/esm/storage/sql/migrations/v1.js.map +0 -1
  169. package/dist/esm/storage/sql/migrations/v2.d.ts +0 -3
  170. package/dist/esm/storage/sql/migrations/v2.js +0 -16
  171. package/dist/esm/storage/sql/migrations/v2.js.map +0 -1
  172. package/dist/esm/storage/sql/migrations.d.ts +0 -7
  173. package/dist/esm/storage/sql/migrations.js +0 -4
  174. package/dist/esm/storage/sql/migrations.js.map +0 -1
  175. package/dist/esm/storage/sql/sqlStorage.d.ts +0 -6
  176. package/dist/esm/storage/sql/sqlStorage.js +0 -35
  177. package/dist/esm/storage/sql/sqlStorage.js.map +0 -1
  178. package/dist/esm/storage/sql/tables.d.ts +0 -44
  179. package/dist/esm/storage/sql/tables.js +0 -2
  180. package/dist/esm/storage/sql/tables.js.map +0 -1
  181. package/dist/esm/storage/sqlShard/transfer.d.ts +0 -7
  182. package/dist/esm/storage/sqlShard/transfer.js +0 -90
  183. package/dist/esm/storage/sqlShard/transfer.js.map +0 -1
  184. package/dist/esm/storage/sqlShard/transfer.test.d.ts +0 -1
  185. package/dist/esm/storage/sqlShard/transfer.test.js +0 -109
  186. package/dist/esm/storage/sqlShard/transfer.test.js.map +0 -1
  187. package/src/MessageSender.ts +0 -10
  188. package/src/Presence.ts +0 -86
  189. package/src/ReplicaKeepaliveTimers.ts +0 -34
  190. package/src/Server.ts +0 -859
  191. package/src/storage/sql/SqlBaselines.ts +0 -147
  192. package/src/storage/sql/SqlFileMetadata.ts +0 -94
  193. package/src/storage/sql/SqlOperations.ts +0 -160
  194. package/src/storage/sql/SqlReplicas.ts +0 -234
  195. package/src/storage/sql/database.ts +0 -32
  196. package/src/storage/sql/migrations/v1.ts +0 -64
  197. package/src/storage/sql/migrations/v2.ts +0 -20
  198. package/src/storage/sql/migrations.ts +0 -4
  199. package/src/storage/sql/sqlStorage.ts +0 -57
  200. package/src/storage/sql/tables.ts +0 -51
  201. package/src/storage/sqlShard/transfer.test.ts +0 -149
  202. package/src/storage/sqlShard/transfer.ts +0 -123
  203. /package/dist/esm/{MessageSender.js → connections/MessageSender.js} +0 -0
  204. /package/dist/esm/{ServerLibrary.test.d.ts → libraries/Library.test.d.ts} +0 -0
package/bin/server.mjs CHANGED
@@ -1,15 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { createReadStream } from 'fs';
3
- import { createServer } from 'http';
2
+ import { serve } from '@hono/node-server';
3
+ import { serveStatic } from '@hono/node-server/serve-static';
4
+ import { Hono } from 'hono';
5
+ import { cors } from 'hono/cors';
4
6
  import minimist from 'minimist';
5
- import { parse, URLSearchParams } from 'url';
6
7
  import {
8
+ createHttpRouter,
9
+ createNodeWebsocketHandler,
10
+ } from '../dist/esm/adapters/node/index.js';
11
+ import {
12
+ createVerdant,
7
13
  LocalFileStorage,
8
14
  ReplicaType,
9
- Server,
10
15
  TokenProvider,
11
16
  } from '../dist/esm/index.js';
12
- import { sqlStorage } from '../dist/esm/storage/index.js';
17
+ import { sqlShardStorage } from '../dist/esm/storage/index.js';
13
18
 
14
19
  const argv = minimist(process.argv.slice(1));
15
20
 
@@ -17,119 +22,117 @@ const PORT = argv.port || argv.p || process.env.VERDANT_PORT || 3242;
17
22
  const SECRET =
18
23
  argv.secret || process.env.VERDANT_SECRET || 'notsecretnotsecretnotsecret';
19
24
 
20
- const tokenProvider = new TokenProvider({ secret: SECRET });
25
+ const tokenProvider = new TokenProvider({
26
+ secret: SECRET,
27
+ });
21
28
 
22
- const http = createServer((req, res) => {
23
- const url = parse(req.url);
24
- // write cors headers for incoming host
25
- const origin = req.headers.origin;
26
- res.setHeader('Access-Control-Allow-Origin', origin ?? '*');
27
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
28
- res.setHeader(
29
- 'Access-Control-Allow-Headers',
30
- 'Content-Type, Authorization, X-Request-Id',
31
- );
32
- res.setHeader('Access-Control-Allow-Credentials', 'true');
33
- res.setHeader('Access-Control-Max-Age', '86400');
34
- if (req.method === 'OPTIONS') {
35
- res.writeHead(200);
36
- res.end();
37
- return;
38
- }
29
+ // the "core" is a service API for interacting with libraries.
30
+ // you could plug this into your own HTTP or other kind of interface,
31
+ // but further on are some Hono-based tools to help get started.
32
+ const core = createVerdant({
33
+ // SQLite databases for each library are stored in this directory
34
+ storage: sqlShardStorage({
35
+ databasesDirectory: 'verdant-dbs',
36
+ }),
37
+ // implement .get to retrieve detailed profile information for sync users
38
+ profiles: {
39
+ get: async (userId) => {
40
+ // you could fetch a profile record from a database here to augment
41
+ // this profile with name, image, etc.
42
+ // values will be cached, so don't worry too much about timing.
43
+ return { id: userId };
44
+ },
45
+ },
46
+ // required to validate sync access tokens generated from
47
+ // your API
48
+ tokenSecret: SECRET,
49
+ // optional; enables file sync
50
+ fileStorage: new LocalFileStorage({
51
+ rootDirectory: 'verdant-files',
52
+ // this matches an API you must host (see below)
53
+ host: `http://localhost:${PORT}/files`,
54
+ log: (level, ...args) => console.log(level, ...args),
55
+ }),
56
+ // optional logger
57
+ log: (level, ...args) => console.log(level, ...args),
58
+ });
39
59
 
40
- // serve files on the /files path
41
- if (url.pathname.startsWith('/files/')) {
42
- const file = decodeURIComponent(url.pathname.slice(7));
43
- console.log('Serving file', file);
44
- const fileStream = createReadStream(`files/${file}`);
45
- fileStream.pipe(res);
46
- fileStream.on('error', (err) => {
47
- res.writeHead(404);
48
- res.end();
49
- });
50
- return;
51
- }
60
+ // our verdant router will be mounted to a subpath
61
+ // and handle all HTTP sync requests
62
+ const verdantRouter = createHttpRouter(core);
52
63
 
53
- // provide tokens on the /auth path
54
- if (url.pathname.startsWith('/auth/')) {
55
- // userId is provided as a query param
56
- const params = new URLSearchParams(url.search);
57
- const userId = params.get('userId');
64
+ // this is "your" app -- Verdant lives in it,
65
+ // but you control the API.
66
+ const app = new Hono()
67
+ .use('*', async (c, next) => {
68
+ // simple request logger
69
+ console.log(`${c.req.method} ${c.req.url}`);
70
+ await next();
71
+ })
72
+ .use(
73
+ '*',
74
+ cors({
75
+ origin: (o) => o,
76
+ credentials: true,
77
+ allowHeaders: ['Content-Type', 'Authorization', 'X-Request-Id'],
78
+ }),
79
+ )
80
+ .route('/verdant', verdantRouter)
81
+ // issue sync tokens
82
+ .get('/auth/:libraryId', async (ctx) => {
83
+ // here you authenticate your user, authorize
84
+ // their access to a particular library, and
85
+ // issue a token.
86
+ const library = ctx.req.param('libraryId');
87
+
88
+ const user = ctx.req.query('userId');
89
+ const type = ctx.req.query('type') || ReplicaType.Realtime;
58
90
 
59
- // library ID is in path
60
- const libraryId = url.pathname.slice(6);
91
+ // remember to check that the user is allowed to sync
92
+ // to this library
61
93
 
62
94
  const token = tokenProvider.getToken({
63
- userId,
64
- libraryId,
65
- syncEndpoint: `http://localhost:${PORT}/sync`,
66
- role: 'user',
67
- type: ReplicaType.Realtime,
95
+ libraryId: library,
96
+ userId: user,
97
+ // this subpath matches the verdantRouter mount
98
+ syncEndpoint: `http://localhost:${PORT}/verdant`,
99
+ type,
68
100
  });
69
- res.writeHead(200, {
70
- 'Content-Type': 'application/json',
101
+ return ctx.json({
102
+ accessToken: token,
71
103
  });
72
- res.end(JSON.stringify({ accessToken: token }));
73
- return;
74
- } else if (url.pathname.startsWith('/sync')) {
75
- if (url.pathname.startsWith('/sync/files/')) {
76
- return verdant.handleFileRequest(req, res);
77
- }
78
- return verdant.handleRequest(req, res);
79
- } else {
80
- res.writeHead(404);
81
- res.end();
82
- }
83
- });
84
-
85
- const verdant = new Server({
86
- storage: sqlStorage({
87
- databaseFile: 'verdant.sqlite',
88
- }),
89
- tokenSecret: SECRET,
90
- fileStorage: new LocalFileStorage({
91
- rootDirectory: 'files',
92
- host: `http://localhost:${3242}/files`,
93
- }),
94
- profiles: {
95
- get: (userId) => ({
96
- id: userId,
104
+ })
105
+ // (optional) serve uploaded files
106
+ // if you use LocalFileStorage
107
+ .get(
108
+ '/files/*',
109
+ serveStatic({
110
+ root: 'verdant-files',
111
+ rewriteRequestPath: (path) => path.replace('/files/', ''),
97
112
  }),
98
- },
99
- httpServer: http,
100
- log: (level, ...rest) => {
101
- if (process.env.VERDANT_LOG === 'false') {
102
- return;
103
- }
104
- if (level === 'debug' && process.env.VERDANT_DEBUG !== 'true') {
105
- return;
106
- }
107
- console.log(level, ...rest);
108
- },
109
- });
113
+ );
110
114
 
111
- verdant.on('error', (err) => {
112
- console.error(err);
115
+ const server = serve({
116
+ fetch: app.fetch,
117
+ port: parseInt(PORT.toString(), 10),
113
118
  });
114
119
 
115
- export default new Promise((resolve) => {
116
- http.listen(PORT, () => {
117
- console.log(`🌿 Verdant standalone server listening on port ${PORT}`);
118
- console.log(``);
119
- console.log(
120
- `It's not recommended you use this server in production. It's a convenient way to test syncing locally, but has no authorization.`,
121
- );
122
- console.log(``);
123
- console.log(
124
- `To connect your client, supply "http://localhost:${PORT}/auth/<libraryId>?userId=<userId>" to sync.authEndpoint.`,
125
- );
126
- console.log(
127
- `<libraryId> and <userId> are up to you and your app. Clients on the same library ID will sync data.`,
128
- );
120
+ // handle websocket sync! must be done like this, not included
121
+ // in createHttpRouter!
122
+ const onUpgrade = createNodeWebsocketHandler(core);
123
+ server.addListener('upgrade', onUpgrade);
129
124
 
130
- resolve({
131
- httpServer: http,
132
- verdant,
133
- });
134
- });
125
+ server.addListener('listening', () => {
126
+ console.log(`🌿 Verdant standalone server listening on port ${PORT}`);
127
+ console.log(``);
128
+ console.log(
129
+ `Never use this server in production. It's a convenient way to test syncing locally, but has no authorization.`,
130
+ );
131
+ console.log(``);
132
+ console.log(
133
+ `To connect your client, import \`cliSync\` and pass \`cliSync(<libraryId>)\` to \`sync\`, or supply "http://localhost:${PORT}/auth/<libraryId>?userId=<userId>" to sync.authEndpoint.`,
134
+ );
135
+ console.log(
136
+ `<libraryId> and <userId> are up to you and your app. Clients on the same library ID will sync data.`,
137
+ );
135
138
  });
@@ -4,6 +4,7 @@ export declare class TokenVerifier {
4
4
  constructor(config: {
5
5
  secret: string;
6
6
  });
7
+ get secret(): string;
7
8
  verifyToken: (token: string) => TokenInfo;
8
9
  }
9
10
  export interface TokenInfo {
@@ -29,5 +29,8 @@ export class TokenVerifier {
29
29
  }
30
30
  };
31
31
  }
32
+ get secret() {
33
+ return this.config.secret;
34
+ }
32
35
  }
33
36
  //# sourceMappingURL=TokenVerifier.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TokenVerifier.js","sourceRoot":"","sources":["../../src/TokenVerifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,GAAmB,MAAM,cAAc,CAAC;AAE/C,MAAM,OAAO,aAAa;IACzB,YAAoB,MAA0B;QAA1B,WAAM,GAAN,MAAM,CAAoB;QAE9C,gBAAW,GAAG,CAAC,KAAa,EAAa,EAAE;YAC1C,IAAI,CAAC;gBACJ,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;oBACrD,QAAQ,EAAE,KAAK;iBACf,CAAe,CAAC;gBACjB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACpB,OAAO;oBACN,MAAM,EAAE,OAAO,CAAC,GAAG;oBACnB,SAAS,EAAE,OAAO,CAAC,GAAG;oBACtB,YAAY,EAAE,OAAO,CAAC,GAAG;oBACzB,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,KAAK;iBACL,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACZ,IAAI,CAAC,YAAY,GAAG,CAAC,iBAAiB,EAAE,CAAC;oBACxC,MAAM,IAAI,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACxD,CAAC;gBACD,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;oBACxB,MAAM,IAAI,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;gBACzD,CAAC;gBACD,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,UAAU,EAC5B,SAAS,EACT,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CACjB,CAAC;YACH,CAAC;QACF,CAAC,CAAC;IA7B+C,CAAC;CA8BlD"}
1
+ {"version":3,"file":"TokenVerifier.js","sourceRoot":"","sources":["../../src/TokenVerifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,GAAmB,MAAM,cAAc,CAAC;AAE/C,MAAM,OAAO,aAAa;IACzB,YAAoB,MAA0B;QAA1B,WAAM,GAAN,MAAM,CAAoB;QAM9C,gBAAW,GAAG,CAAC,KAAa,EAAa,EAAE;YAC1C,IAAI,CAAC;gBACJ,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;oBACrD,QAAQ,EAAE,KAAK;iBACf,CAAe,CAAC;gBACjB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACpB,OAAO;oBACN,MAAM,EAAE,OAAO,CAAC,GAAG;oBACnB,SAAS,EAAE,OAAO,CAAC,GAAG;oBACtB,YAAY,EAAE,OAAO,CAAC,GAAG;oBACzB,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,KAAK;iBACL,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACZ,IAAI,CAAC,YAAY,GAAG,CAAC,iBAAiB,EAAE,CAAC;oBACxC,MAAM,IAAI,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACxD,CAAC;gBACD,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;oBACxB,MAAM,IAAI,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;gBACzD,CAAC;gBACD,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,UAAU,EAC5B,SAAS,EACT,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CACjB,CAAC;YACH,CAAC;QACF,CAAC,CAAC;IAjC+C,CAAC;IAElD,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC3B,CAAC;CA8BD"}
@@ -0,0 +1,2 @@
1
+ import { ErrorHandler } from 'hono';
2
+ export declare const errorHandler: ErrorHandler;
@@ -0,0 +1,12 @@
1
+ import { VerdantError } from '@verdant-web/common';
2
+ export const errorHandler = (err, ctx) => {
3
+ if (err instanceof VerdantError) {
4
+ if (err.code > VerdantError.Code.Unexpected) {
5
+ console.error('Verdant error:', err);
6
+ }
7
+ return ctx.json(err.toResponse(), err.httpStatus);
8
+ }
9
+ console.error('Unexpected error:', err);
10
+ return ctx.json(new VerdantError(VerdantError.Code.Unexpected).toResponse(), 500);
11
+ };
12
+ //# sourceMappingURL=errorHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../../../src/adapters/node/errorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAInD,MAAM,CAAC,MAAM,YAAY,GAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACtD,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;QACjC,IAAI,GAAG,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YAC7C,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,GAAG,CAAC,UAAkC,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;IACxC,OAAO,GAAG,CAAC,IAAI,CACd,IAAI,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,UAAU,EAAE,EAC3D,GAAG,CACH,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function getRequestToken(req: Request): string | null;
@@ -0,0 +1,25 @@
1
+ import { VerdantError } from '@verdant-web/common';
2
+ export function getRequestToken(req) {
3
+ if (req.method === 'OPTIONS') {
4
+ // preflight request, np
5
+ return null;
6
+ }
7
+ // websocket compat - we only receive one header...
8
+ const protocolHeader = req.headers.get('Sec-WebSocket-Protocol');
9
+ if (protocolHeader) {
10
+ const [type, token] = protocolHeader.split(',');
11
+ if (type === 'Bearer') {
12
+ return token.trim();
13
+ }
14
+ }
15
+ const authHeader = req.headers.get('Authorization');
16
+ if (!authHeader) {
17
+ throw new VerdantError(VerdantError.Code.NoToken, undefined, 'Authorization header is required');
18
+ }
19
+ const [type, token] = authHeader.split(' ');
20
+ if (type !== 'Bearer') {
21
+ throw new VerdantError(VerdantError.Code.InvalidToken, undefined, 'Authorization type must be Bearer');
22
+ }
23
+ return token;
24
+ }
25
+ //# sourceMappingURL=getRequestToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getRequestToken.js","sourceRoot":"","sources":["../../../../src/adapters/node/getRequestToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,UAAU,eAAe,CAAC,GAAY;IAC3C,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,wBAAwB;QACxB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,mDAAmD;IACnD,MAAM,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACjE,IAAI,cAAc,EAAE,CAAC;QACpB,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;QACrB,CAAC;IACF,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACpD,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,OAAO,EACzB,SAAS,EACT,kCAAkC,CAClC,CAAC;IACH,CAAC;IACD,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACvB,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,YAAY,EAC9B,SAAS,EACT,mCAAmC,CACnC,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { SingleNodeLibraryManager } from '../../libraries/singleNode.js';
2
+ export declare function createHttpRouter(core: SingleNodeLibraryManager): import("hono/hono-base").HonoBase<{}, {
3
+ "*": {};
4
+ } & {
5
+ "/": {
6
+ $post: {
7
+ input: {};
8
+ output: {};
9
+ outputFormat: string;
10
+ status: import("hono/utils/http-status").StatusCode;
11
+ };
12
+ };
13
+ } & {
14
+ "/files/:fileId": {
15
+ $post: {
16
+ input: {
17
+ param: {
18
+ fileId: string;
19
+ };
20
+ };
21
+ output: {
22
+ success: true;
23
+ };
24
+ outputFormat: "json";
25
+ status: 200;
26
+ };
27
+ };
28
+ } & {
29
+ "/files/:fileId": {
30
+ $get: {
31
+ input: {
32
+ param: {
33
+ fileId: string;
34
+ };
35
+ };
36
+ output: {
37
+ libraryId: string;
38
+ id: string;
39
+ name: string;
40
+ type: string;
41
+ url: string | null;
42
+ };
43
+ outputFormat: "json";
44
+ status: 200;
45
+ };
46
+ };
47
+ }, "/">;
@@ -0,0 +1,88 @@
1
+ import { VerdantError } from '@verdant-web/common';
2
+ import { Hono } from 'hono';
3
+ import { Readable } from 'stream';
4
+ import { errorHandler } from './errorHandler.js';
5
+ import { tokenMiddleware } from './tokenMiddleware.js';
6
+ export function createHttpRouter(core) {
7
+ const mw = tokenMiddleware(core.tokenVerifier);
8
+ const app = new Hono()
9
+ .onError(errorHandler)
10
+ .use('*', async (c, next) => {
11
+ core.log('debug', 'Incoming request', {
12
+ method: c.req.method,
13
+ path: c.req.path,
14
+ });
15
+ await next();
16
+ })
17
+ .post('/', mw, async (ctx) => {
18
+ var _a;
19
+ const key = ctx.get('key');
20
+ const info = ctx.get('tokenInfo');
21
+ const microserver = await core.get(info.libraryId);
22
+ const finish = (_a = microserver.onRequest) === null || _a === void 0 ? void 0 : _a.call(microserver, key, ctx.req.raw, info);
23
+ const body = (await ctx.req.json());
24
+ if (!(body === null || body === void 0 ? void 0 : body.messages)) {
25
+ throw new VerdantError(VerdantError.Code.BodyRequired, undefined, 'Invalid request body');
26
+ }
27
+ for (const message of body.messages) {
28
+ await microserver.handleMessage(key, info, message);
29
+ }
30
+ const res = finish();
31
+ return res;
32
+ })
33
+ .post('/files/:fileId', mw, async (ctx) => {
34
+ const info = ctx.get('tokenInfo');
35
+ const id = ctx.req.param('fileId');
36
+ if (!id) {
37
+ throw new VerdantError(VerdantError.Code.NotFound, undefined, 'Supply a file ID as a path parameters');
38
+ }
39
+ // TODO: remove this once I figure out why request file body parsing is
40
+ // hanging indefinitely.
41
+ if (core.__testMode) {
42
+ const microserver = await core.get(info.libraryId);
43
+ core.log('warn', 'TEST MODE: FILE WILL NOT BE WRITTEN', '\n', 'This Verdant server was launched in Test Mode. This is only meant for use', 'in automated testing environments. The file just uploaded WILL NOT be', 'stored on disk or retrievable.');
44
+ // Fake a file upload for testing
45
+ await microserver.uploadFile(new Readable({
46
+ read() {
47
+ this.push('test');
48
+ this.push(null);
49
+ },
50
+ }), {
51
+ id,
52
+ libraryId: info.libraryId,
53
+ fileName: 'test.txt',
54
+ type: 'text/plain',
55
+ });
56
+ return ctx.json({ success: true }, 200);
57
+ }
58
+ const form = await ctx.req.parseBody();
59
+ const file = form.file;
60
+ if (!file || !(file instanceof File)) {
61
+ throw new VerdantError(VerdantError.Code.InvalidRequest, undefined, 'No file was uploaded to the `file` form key');
62
+ }
63
+ const microserver = await core.get(info.libraryId);
64
+ const fileStream = file.stream();
65
+ await microserver.uploadFile(fileStream, {
66
+ id,
67
+ libraryId: info.libraryId,
68
+ fileName: file.name,
69
+ type: file.type,
70
+ });
71
+ return ctx.json({ success: true }, 200);
72
+ })
73
+ .get('/files/:fileId', mw, async (ctx) => {
74
+ const info = ctx.get('tokenInfo');
75
+ const id = ctx.req.param('fileId');
76
+ if (!id) {
77
+ throw new VerdantError(VerdantError.Code.NotFound, undefined, 'Supply a file ID as a path parameters');
78
+ }
79
+ const microserver = await core.get(info.libraryId);
80
+ const file = await microserver.getFileInfo(id);
81
+ if (!file) {
82
+ throw new VerdantError(VerdantError.Code.NotFound, undefined, `File with ID ${id} not found`);
83
+ }
84
+ return ctx.json(file, 200);
85
+ });
86
+ return app;
87
+ }
88
+ //# sourceMappingURL=httpRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpRouter.js","sourceRoot":"","sources":["../../../../src/adapters/node/httpRouter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { errorHandler } from '../node/errorHandler.js';
2
+ export { getRequestToken } from '../node/getRequestToken.js';
3
+ export * from './httpRouter.js';
4
+ export { tokenMiddleware } from './tokenMiddleware.js';
5
+ export * from './websocketHandler.js';
@@ -0,0 +1,6 @@
1
+ export { errorHandler } from '../node/errorHandler.js';
2
+ export { getRequestToken } from '../node/getRequestToken.js';
3
+ export * from './httpRouter.js';
4
+ export { tokenMiddleware } from './tokenMiddleware.js';
5
+ export * from './websocketHandler.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/adapters/node/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { TokenInfo, TokenVerifier } from '../../TokenVerifier.js';
2
+ export declare const tokenMiddleware: (tokenVerifier: TokenVerifier) => import("hono").MiddlewareHandler<{
3
+ Variables: {
4
+ key: string;
5
+ tokenInfo: TokenInfo;
6
+ };
7
+ }, string, {}>;
@@ -0,0 +1,22 @@
1
+ import { generateId } from '@verdant-web/common';
2
+ import { createMiddleware } from 'hono/factory';
3
+ import { getRequestToken } from '../node/getRequestToken.js';
4
+ export const tokenMiddleware = (tokenVerifier) => createMiddleware((ctx, next) => {
5
+ if (ctx.req.method === 'OPTIONS') {
6
+ // preflight request, np
7
+ return next();
8
+ }
9
+ const requestToken = getRequestToken(ctx.req.raw);
10
+ const key = generateId();
11
+ const info = tokenVerifier.verifyToken(requestToken);
12
+ ctx.set('key', key);
13
+ ctx.set('tokenInfo', info);
14
+ // if a Sec-Websocket-Protocol header was sent, we need to echo it back
15
+ // so the websocket connection is accepted by the client
16
+ const protocolHeader = ctx.req.header('Sec-WebSocket-Protocol');
17
+ if (protocolHeader) {
18
+ ctx.res.headers.set('Sec-WebSocket-Protocol', requestToken);
19
+ }
20
+ return next();
21
+ });
22
+ //# sourceMappingURL=tokenMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenMiddleware.js","sourceRoot":"","sources":["../../../../src/adapters/node/tokenMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,aAA4B,EAAE,EAAE,CAC/D,gBAAgB,CAKb,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IAChB,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,wBAAwB;QACxB,OAAO,IAAI,EAAE,CAAC;IACf,CAAC;IACD,MAAM,YAAY,GAAG,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;IAEnD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,aAAa,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IACrD,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACpB,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAE3B,uEAAuE;IACvE,wDAAwD;IACxD,MAAM,cAAc,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;IAChE,IAAI,cAAc,EAAE,CAAC;QACpB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,IAAI,EAAE,CAAC;AACf,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { IncomingMessage } from 'node:http';
2
+ import internal from 'node:stream';
3
+ import { SingleNodeLibraryManager } from '../../libraries/singleNode.js';
4
+ export declare function createNodeWebsocketHandler(core: SingleNodeLibraryManager): (req: IncomingMessage, socket: internal.Duplex, head: Buffer) => void;
@@ -0,0 +1,54 @@
1
+ import { VerdantError, assert, generateId, } from '@verdant-web/common';
2
+ import { URL } from 'url';
3
+ import { WebSocketServer } from 'ws';
4
+ export function createNodeWebsocketHandler(core) {
5
+ const wss = new WebSocketServer({ noServer: true });
6
+ wss.on('connection', async (ws, req, info) => {
7
+ const key = generateId();
8
+ const microserver = await core.get(info.libraryId);
9
+ microserver.clientConnections.addSocket(key, ws, info);
10
+ ws.on('message', async (message) => {
11
+ const data = JSON.parse(message.toString());
12
+ await microserver.handleMessage(key, info, data);
13
+ });
14
+ ws.on('close', () => {
15
+ microserver.clientConnections.remove(key);
16
+ core.log('debug', 'Connection closed', { key, userId: info.userId });
17
+ });
18
+ });
19
+ function getRequestToken(req) {
20
+ if (req.headers['sec-websocket-protocol']) {
21
+ // hack: read the token from the websocket protocol header
22
+ const [type, token] = req.headers['sec-websocket-protocol'].split(',');
23
+ if (type === 'Bearer') {
24
+ return token.trim();
25
+ }
26
+ }
27
+ assert(req.url, 'Request URL is required');
28
+ const url = new URL(req.url, 'http://localhost');
29
+ const token = url.searchParams.get('token');
30
+ if (!token) {
31
+ throw new VerdantError(VerdantError.Code.NoToken, undefined, 'Token query parameter is required');
32
+ }
33
+ return token;
34
+ }
35
+ return (req, socket, head) => {
36
+ try {
37
+ const token = getRequestToken(req);
38
+ const info = core.tokenVerifier.verifyToken(token);
39
+ wss.handleUpgrade(req, socket, head, (ws) => {
40
+ wss.emit('connection', ws, req, info);
41
+ });
42
+ }
43
+ catch (err) {
44
+ if (err instanceof VerdantError && err.httpStatus === 401) {
45
+ socket.write('HTTP/1.1 401 Unauthorized\r\n' +
46
+ 'Connection: close\r\n' +
47
+ 'Content-Length: 0\r\n' +
48
+ '\r\n');
49
+ }
50
+ socket.destroy();
51
+ }
52
+ };
53
+ }
54
+ //# sourceMappingURL=websocketHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocketHandler.js","sourceRoot":"","sources":["../../../../src/adapters/node/websocketHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,YAAY,EACZ,MAAM,EACN,UAAU,GACV,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EAAa,eAAe,EAAE,MAAM,IAAI,CAAC;AAIhD,MAAM,UAAU,0BAA0B,CAAC,IAA8B;IACxE,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,GAAG,CAAC,EAAE,CACL,YAAY,EACZ,KAAK,EAAE,EAAa,EAAE,GAAoB,EAAE,IAAe,EAAE,EAAE;QAC9D,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QAEzB,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEnD,WAAW,CAAC,iBAAiB,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QAEvD,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,OAAY,EAAE,EAAE;YACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAkB,CAAC;YAC7D,MAAM,WAAW,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,WAAW,CAAC,iBAAiB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1C,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,mBAAmB,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;IACJ,CAAC,CACD,CAAC;IAEF,SAAS,eAAe,CAAC,GAAoB;QAC5C,IAAI,GAAG,CAAC,OAAO,CAAC,wBAAwB,CAAC,EAAE,CAAC;YAC3C,0DAA0D;YAC1D,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACvE,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;YACrB,CAAC;QACF,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,IAAI,YAAY,CACrB,YAAY,CAAC,IAAI,CAAC,OAAO,EACzB,SAAS,EACT,mCAAmC,CACnC,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACd,CAAC;IAED,OAAO,CAAC,GAAoB,EAAE,MAAuB,EAAE,IAAY,EAAE,EAAE;QACtE,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACnD,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE;gBAC3C,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YACvC,CAAC,CAAC,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,IAAI,GAAG,YAAY,YAAY,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;gBAC3D,MAAM,CAAC,KAAK,CACX,+BAA+B;oBAC9B,uBAAuB;oBACvB,uBAAuB;oBACvB,MAAM,CACP,CAAC;YACH,CAAC;YACD,MAAM,CAAC,OAAO,EAAE,CAAC;QAClB,CAAC;IACF,CAAC,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { ServerMessage } from '@verdant-web/common';
2
+ import { IncomingMessage, ServerResponse } from 'http';
3
+ import { WebSocket as WSWebSocket } from 'ws';
4
+ import { UserProfileLoader } from '../Profiles.js';
5
+ import { TokenInfo } from '../TokenVerifier.js';
6
+ import { MessageSender } from './MessageSender.js';
7
+ import { Presence } from './Presence.js';
8
+ export declare class ClientConnectionManager implements MessageSender {
9
+ private readonly connections;
10
+ readonly presence: Presence;
11
+ constructor({ profiles }: {
12
+ profiles: UserProfileLoader<any>;
13
+ });
14
+ addSocket: (key: string, socket: WebSocket | WSWebSocket, info: TokenInfo) => void;
15
+ addRequest: (key: string, request: IncomingMessage, response: ServerResponse, info: TokenInfo) => () => void;
16
+ addFetch: (key: string, req: Request, info: TokenInfo) => (() => Response);
17
+ remove: (key: string) => void;
18
+ respond: (key: string, message: ServerMessage) => void;
19
+ /**
20
+ * Broadcasts a message to all live-connected clients in a library.
21
+ * This does not reach push/pull clients.
22
+ */
23
+ broadcast: (message: ServerMessage, omitKeys?: string[]) => void;
24
+ disconnectAll: () => void;
25
+ }