@rpcbase/server 0.593.0 → 0.595.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 (63) hide show
  1. package/dist/convertHeifToWebp-BT1n72DQ.js +148 -0
  2. package/dist/convertHeifToWebp-BT1n72DQ.js.map +1 -0
  3. package/dist/email-Co1GNjlT.js +7204 -0
  4. package/dist/email-Co1GNjlT.js.map +1 -0
  5. package/dist/handler-BqlcQ9sE.js +245 -0
  6. package/dist/handler-BqlcQ9sE.js.map +1 -0
  7. package/dist/handler-C92_gJkX.js +743 -0
  8. package/dist/handler-C92_gJkX.js.map +1 -0
  9. package/dist/handler-CapJTGzc.js +795 -0
  10. package/dist/handler-CapJTGzc.js.map +1 -0
  11. package/dist/handler-Fk-5fOSm.js +119 -0
  12. package/dist/handler-Fk-5fOSm.js.map +1 -0
  13. package/dist/htmlTemplate.d.ts +16 -0
  14. package/dist/htmlTemplate.d.ts.map +1 -0
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +5140 -5286
  18. package/dist/index.js.map +1 -1
  19. package/dist/notifications.js +273 -382
  20. package/dist/notifications.js.map +1 -1
  21. package/dist/postProcessors-iffHvje4.js +89 -0
  22. package/dist/postProcessors-iffHvje4.js.map +1 -0
  23. package/dist/queryWindow-DP8zD0lb.js +555 -0
  24. package/dist/queryWindow-DP8zD0lb.js.map +1 -0
  25. package/dist/render_resend-B1SSQ4f7.js +7 -0
  26. package/dist/render_resend-B1SSQ4f7.js.map +1 -0
  27. package/dist/rts/index.d.ts +2 -1
  28. package/dist/rts/index.d.ts.map +1 -1
  29. package/dist/rts/index.js +990 -1197
  30. package/dist/rts/index.js.map +1 -1
  31. package/dist/schemas-B2fUvgYo.js +4151 -0
  32. package/dist/schemas-B2fUvgYo.js.map +1 -0
  33. package/dist/shared-DfrVDSp0.js +79 -0
  34. package/dist/shared-DfrVDSp0.js.map +1 -0
  35. package/dist/ssrMiddleware.d.ts +7 -3
  36. package/dist/ssrMiddleware.d.ts.map +1 -1
  37. package/dist/uploads/worker.js +139 -168
  38. package/dist/uploads/worker.js.map +1 -1
  39. package/dist/uploads.js +12 -22
  40. package/dist/uploads.js.map +1 -1
  41. package/package.json +1 -1
  42. package/dist/convertHeifToWebp-C-DGXZ2k.js +0 -169
  43. package/dist/convertHeifToWebp-C-DGXZ2k.js.map +0 -1
  44. package/dist/email-BCf24GmK.js +0 -8071
  45. package/dist/email-BCf24GmK.js.map +0 -1
  46. package/dist/handler-BqoKvylN.js +0 -147
  47. package/dist/handler-BqoKvylN.js.map +0 -1
  48. package/dist/handler-CUOJ51-w.js +0 -903
  49. package/dist/handler-CUOJ51-w.js.map +0 -1
  50. package/dist/handler-D-XdgeG_.js +0 -986
  51. package/dist/handler-D-XdgeG_.js.map +0 -1
  52. package/dist/handler-F0gFTzvh.js +0 -275
  53. package/dist/handler-F0gFTzvh.js.map +0 -1
  54. package/dist/postProcessors-D27fGZP0.js +0 -107
  55. package/dist/postProcessors-D27fGZP0.js.map +0 -1
  56. package/dist/queryWindow-Cdr7K-S1.js +0 -714
  57. package/dist/queryWindow-Cdr7K-S1.js.map +0 -1
  58. package/dist/render_resend_false-MiC__Smr.js +0 -6
  59. package/dist/render_resend_false-MiC__Smr.js.map +0 -1
  60. package/dist/schemas-Drf83ni9.js +0 -4517
  61. package/dist/schemas-Drf83ni9.js.map +0 -1
  62. package/dist/shared-CJrm9Wjp.js +0 -104
  63. package/dist/shared-CJrm9Wjp.js.map +0 -1
package/dist/rts/index.js CHANGED
@@ -1,1235 +1,1028 @@
1
- import { randomUUID } from "node:crypto";
1
+ import { a as resolveRtsQueryDependencyModelNames, c as runRtsQuery, i as normalizeRtsQueryOptions, l as syncAuthenticatedSessionFromRequest, s as runRtsCount, t as RTS_QUERY_WINDOW_MAX_COUNT } from "../queryWindow-DP8zD0lb.js";
2
2
  import { models } from "@rpcbase/db";
3
+ import { randomUUID } from "node:crypto";
3
4
  import { buildAbilityFromSession } from "@rpcbase/db/acl";
4
5
  import { WebSocketServer } from "ws";
5
- import { d as syncAuthenticatedSessionFromRequest, n as normalizeRtsQueryOptions, e as resolveRtsQueryDependencyModelNames, R as RTS_QUERY_WINDOW_MAX_COUNT, a as runRtsQuery, c as runRtsCount } from "../queryWindow-Cdr7K-S1.js";
6
- const routes = Object.entries({
7
- .../* @__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("../handler-BqoKvylN.js") })
8
- }).reduce((acc, [path, mod]) => {
9
- acc[path.replace("./api/", "@rpcbase/server/rts/api/")] = mod;
10
- return acc;
6
+ //#region src/rts/routes.ts
7
+ var routes = Object.entries({ .../* #__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("../handler-Fk-5fOSm.js") }) }).reduce((acc, [path, mod]) => {
8
+ acc[path.replace("./api/", "@rpcbase/server/rts/api/")] = mod;
9
+ return acc;
11
10
  }, {});
12
- const QUERY_KEY_MAX_LEN = 4096;
13
- const INTERNAL_MODEL_NAMES = /* @__PURE__ */ new Set(["RBRtsChange", "RBRtsCounter"]);
14
- const DEFAULT_MAX_PAYLOAD_BYTES = 1024 * 1024;
15
- const DEFAULT_MAX_SUBSCRIPTIONS_PER_SOCKET = 256;
16
- const DEFAULT_DISPATCH_DEBOUNCE_MS = 25;
17
- const initializedServers = /* @__PURE__ */ new WeakSet();
18
- const customHandlers = [];
19
- const sockets = /* @__PURE__ */ new Map();
20
- const socketMeta = /* @__PURE__ */ new Map();
21
- const socketWrappers = /* @__PURE__ */ new Map();
22
- const socketCleanup = /* @__PURE__ */ new Map();
23
- const socketSubscriptions = /* @__PURE__ */ new Map();
24
- const socketCountSubscriptions = /* @__PURE__ */ new Map();
25
- const subscriptions = /* @__PURE__ */ new Map();
26
- const countSubscriptions = /* @__PURE__ */ new Map();
27
- const changeStreams = /* @__PURE__ */ new Map();
28
- const dispatchTimers = /* @__PURE__ */ new Map();
29
- const upgradeMeta = /* @__PURE__ */ new WeakMap();
30
- let maxPayloadBytes = DEFAULT_MAX_PAYLOAD_BYTES;
31
- let maxSubscriptionsPerSocket = DEFAULT_MAX_SUBSCRIPTIONS_PER_SOCKET;
32
- let dispatchDebounceMs = DEFAULT_DISPATCH_DEBOUNCE_MS;
33
- let allowInternalModels = false;
34
- const isValidQueryWindowCount = (value) => typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= RTS_QUERY_WINDOW_MAX_COUNT;
35
- class RtsSocket {
36
- id;
37
- tenantId;
38
- userId;
39
- ws;
40
- handlers = /* @__PURE__ */ new Map();
41
- messageHandlers = /* @__PURE__ */ new Map();
42
- constructor({
43
- id,
44
- ws,
45
- meta
46
- }) {
47
- this.id = id;
48
- this.ws = ws;
49
- this.tenantId = meta.tenantId;
50
- this.userId = meta.userId;
51
- }
52
- on(event, handler) {
53
- const set = this.handlers.get(event) ?? /* @__PURE__ */ new Set();
54
- set.add(handler);
55
- this.handlers.set(event, set);
56
- return () => this.off(event, handler);
57
- }
58
- off(event, handler) {
59
- const set = this.handlers.get(event);
60
- if (!set) return;
61
- set.delete(handler);
62
- if (!set.size) this.handlers.delete(event);
63
- }
64
- emit(event, payload) {
65
- sendWs(this.ws, {
66
- type: "event",
67
- event,
68
- payload
69
- });
70
- }
71
- onRtsMessage(channel, handler) {
72
- const set = this.messageHandlers.get(channel) ?? /* @__PURE__ */ new Set();
73
- set.add(handler);
74
- this.messageHandlers.set(channel, set);
75
- return () => this.offRtsMessage(channel, handler);
76
- }
77
- offRtsMessage(channel, handler) {
78
- const set = this.messageHandlers.get(channel);
79
- if (!set) return;
80
- set.delete(handler);
81
- if (!set.size) this.messageHandlers.delete(channel);
82
- }
83
- emitRtsMessage(channel, payload) {
84
- sendWs(this.ws, {
85
- type: "rts-message",
86
- channel,
87
- payload
88
- });
89
- }
90
- close() {
91
- try {
92
- this.ws.close();
93
- } catch {
94
- }
95
- }
96
- dispatch(event, payload) {
97
- const set = this.handlers.get(event);
98
- if (!set) return;
99
- for (const handler of set) {
100
- handler(payload);
101
- }
102
- }
103
- dispatchRtsMessage(channel, payload) {
104
- const set = this.messageHandlers.get(channel);
105
- if (!set) return;
106
- for (const handler of set) {
107
- handler(payload);
108
- }
109
- }
110
- }
111
- const rawToText = (raw) => {
112
- if (typeof raw === "string") return raw;
113
- if (raw instanceof ArrayBuffer) return Buffer.from(raw).toString();
114
- if (Array.isArray(raw)) return Buffer.concat(raw).toString();
115
- return raw.toString();
11
+ //#endregion
12
+ //#region src/rts/index.ts
13
+ var QUERY_KEY_MAX_LEN = 4096;
14
+ var INTERNAL_MODEL_NAMES = /* @__PURE__ */ new Set(["RBRtsChange", "RBRtsCounter"]);
15
+ var DEFAULT_MAX_PAYLOAD_BYTES = 1024 * 1024;
16
+ var DEFAULT_MAX_SUBSCRIPTIONS_PER_SOCKET = 256;
17
+ var DEFAULT_DISPATCH_DEBOUNCE_MS = 25;
18
+ var initializedServers = /* @__PURE__ */ new WeakSet();
19
+ var customHandlers = [];
20
+ var sockets = /* @__PURE__ */ new Map();
21
+ var socketMeta = /* @__PURE__ */ new Map();
22
+ var socketWrappers = /* @__PURE__ */ new Map();
23
+ var socketCleanup = /* @__PURE__ */ new Map();
24
+ var socketSubscriptions = /* @__PURE__ */ new Map();
25
+ var socketCountSubscriptions = /* @__PURE__ */ new Map();
26
+ var subscriptions = /* @__PURE__ */ new Map();
27
+ var countSubscriptions = /* @__PURE__ */ new Map();
28
+ var changeStreams = /* @__PURE__ */ new Map();
29
+ var dispatchTimers = /* @__PURE__ */ new Map();
30
+ var upgradeMeta = /* @__PURE__ */ new WeakMap();
31
+ var maxPayloadBytes = DEFAULT_MAX_PAYLOAD_BYTES;
32
+ var maxSubscriptionsPerSocket = DEFAULT_MAX_SUBSCRIPTIONS_PER_SOCKET;
33
+ var dispatchDebounceMs = DEFAULT_DISPATCH_DEBOUNCE_MS;
34
+ var allowInternalModels = false;
35
+ var isValidQueryWindowCount = (value) => typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 4096;
36
+ var RtsSocket = class {
37
+ id;
38
+ tenantId;
39
+ userId;
40
+ ws;
41
+ handlers = /* @__PURE__ */ new Map();
42
+ messageHandlers = /* @__PURE__ */ new Map();
43
+ constructor({ id, ws, meta }) {
44
+ this.id = id;
45
+ this.ws = ws;
46
+ this.tenantId = meta.tenantId;
47
+ this.userId = meta.userId;
48
+ }
49
+ on(event, handler) {
50
+ const set = this.handlers.get(event) ?? /* @__PURE__ */ new Set();
51
+ set.add(handler);
52
+ this.handlers.set(event, set);
53
+ return () => this.off(event, handler);
54
+ }
55
+ off(event, handler) {
56
+ const set = this.handlers.get(event);
57
+ if (!set) return;
58
+ set.delete(handler);
59
+ if (!set.size) this.handlers.delete(event);
60
+ }
61
+ emit(event, payload) {
62
+ sendWs(this.ws, {
63
+ type: "event",
64
+ event,
65
+ payload
66
+ });
67
+ }
68
+ onRtsMessage(channel, handler) {
69
+ const set = this.messageHandlers.get(channel) ?? /* @__PURE__ */ new Set();
70
+ set.add(handler);
71
+ this.messageHandlers.set(channel, set);
72
+ return () => this.offRtsMessage(channel, handler);
73
+ }
74
+ offRtsMessage(channel, handler) {
75
+ const set = this.messageHandlers.get(channel);
76
+ if (!set) return;
77
+ set.delete(handler);
78
+ if (!set.size) this.messageHandlers.delete(channel);
79
+ }
80
+ emitRtsMessage(channel, payload) {
81
+ sendWs(this.ws, {
82
+ type: "rts-message",
83
+ channel,
84
+ payload
85
+ });
86
+ }
87
+ close() {
88
+ try {
89
+ this.ws.close();
90
+ } catch {}
91
+ }
92
+ dispatch(event, payload) {
93
+ const set = this.handlers.get(event);
94
+ if (!set) return;
95
+ for (const handler of set) handler(payload);
96
+ }
97
+ dispatchRtsMessage(channel, payload) {
98
+ const set = this.messageHandlers.get(channel);
99
+ if (!set) return;
100
+ for (const handler of set) handler(payload);
101
+ }
116
102
  };
117
- const safeJsonParse = (raw) => JSON.parse(rawToText(raw));
118
- const sendWs = (ws, message) => {
119
- if (ws.readyState !== 1) return;
120
- ws.send(JSON.stringify(message));
103
+ var rawToText = (raw) => {
104
+ if (typeof raw === "string") return raw;
105
+ if (raw instanceof ArrayBuffer) return Buffer.from(raw).toString();
106
+ if (Array.isArray(raw)) return Buffer.concat(raw).toString();
107
+ return raw.toString();
121
108
  };
122
- const ensureSocketErrorHandler = (socket, context) => {
123
- if (socket.listenerCount("error") > 0) return;
124
- socket.on("error", (err) => {
125
- const message = err instanceof Error ? err.message : String(err);
126
- const name = err instanceof Error ? err.name : typeof err;
127
- const stack = err instanceof Error ? err.stack : void 0;
128
- const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
129
- if (code === "ECONNRESET" || code == null && message.includes("ECONNRESET")) return;
130
- console.warn("[rb/rts] socket error", {
131
- name,
132
- code,
133
- message,
134
- stack,
135
- remoteAddress: socket.remoteAddress,
136
- remotePort: socket.remotePort,
137
- localAddress: socket.localAddress,
138
- localPort: socket.localPort,
139
- ...context?.()
140
- });
141
- });
109
+ var safeJsonParse = (raw) => JSON.parse(rawToText(raw));
110
+ var sendWs = (ws, message) => {
111
+ if (ws.readyState !== 1) return;
112
+ ws.send(JSON.stringify(message));
142
113
  };
143
- const redactErrorMessage = (err) => {
144
- const raw = err instanceof Error ? err.message : "Unknown error";
145
- const trimmedModelList = raw.replace(/\.\s+Available models:[\s\S]*$/, "");
146
- const maxLen = 256;
147
- if (trimmedModelList.length <= maxLen) return trimmedModelList;
148
- return trimmedModelList.slice(0, maxLen);
114
+ var ensureSocketErrorHandler = (socket, context) => {
115
+ if (socket.listenerCount("error") > 0) return;
116
+ socket.on("error", (err) => {
117
+ const message = err instanceof Error ? err.message : String(err);
118
+ const name = err instanceof Error ? err.name : typeof err;
119
+ const stack = err instanceof Error ? err.stack : void 0;
120
+ const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
121
+ if (code === "ECONNRESET" || code == null && message.includes("ECONNRESET")) return;
122
+ console.warn("[rb/rts] socket error", {
123
+ name,
124
+ code,
125
+ message,
126
+ stack,
127
+ remoteAddress: socket.remoteAddress,
128
+ remotePort: socket.remotePort,
129
+ localAddress: socket.localAddress,
130
+ localPort: socket.localPort,
131
+ ...context?.()
132
+ });
133
+ });
149
134
  };
150
- const unauthorized = (socket, message = "Unauthorized") => {
151
- ensureSocketErrorHandler(socket);
152
- try {
153
- socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
154
- socket.write(`Error: ${message}\r
155
- `);
156
- socket.end();
157
- } catch {
158
- socket.destroy();
159
- }
135
+ var redactErrorMessage = (err) => {
136
+ const trimmedModelList = (err instanceof Error ? err.message : "Unknown error").replace(/\.\s+Available models:[\s\S]*$/, "");
137
+ const maxLen = 256;
138
+ if (trimmedModelList.length <= maxLen) return trimmedModelList;
139
+ return trimmedModelList.slice(0, maxLen);
160
140
  };
161
- const badRequest = (socket, message = "Bad Request") => {
162
- ensureSocketErrorHandler(socket);
163
- try {
164
- socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
165
- socket.write(`Error: ${message}\r
166
- `);
167
- socket.end();
168
- } catch {
169
- socket.destroy();
170
- }
141
+ var unauthorized = (socket, message = "Unauthorized") => {
142
+ ensureSocketErrorHandler(socket);
143
+ try {
144
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
145
+ socket.write(`Error: ${message}\r\n`);
146
+ socket.end();
147
+ } catch {
148
+ socket.destroy();
149
+ }
171
150
  };
172
- const runSessionMiddleware = async (sessionMiddleware, req) => {
173
- await new Promise((resolve, reject) => {
174
- const next = (err) => {
175
- if (err) reject(err);
176
- else resolve();
177
- };
178
- sessionMiddleware(req, {}, next);
179
- });
151
+ var badRequest = (socket, message = "Bad Request") => {
152
+ ensureSocketErrorHandler(socket);
153
+ try {
154
+ socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
155
+ socket.write(`Error: ${message}\r\n`);
156
+ socket.end();
157
+ } catch {
158
+ socket.destroy();
159
+ }
180
160
  };
181
- const parseUpgradeMeta = async ({
182
- req,
183
- url,
184
- sessionMiddleware
185
- }) => {
186
- const upgradeReq = req;
187
- try {
188
- await runSessionMiddleware(sessionMiddleware, upgradeReq);
189
- } catch {
190
- throw new Error("Failed to load session for RTS");
191
- }
192
- await syncAuthenticatedSessionFromRequest(upgradeReq);
193
- const sessionUser = upgradeReq.session?.user;
194
- const sessionUserId = sessionUser?.id?.trim();
195
- if (!sessionUserId) {
196
- throw new Error("Not signed in (missing session.user.id)");
197
- }
198
- const tenantId = sessionUser?.currentTenantId?.trim();
199
- if (!tenantId) {
200
- throw new Error("Missing current tenant in session");
201
- }
202
- const ability = buildAbilityFromSession({
203
- tenantId,
204
- session: upgradeReq.session
205
- });
206
- return {
207
- tenantId,
208
- userId: sessionUserId,
209
- ability
210
- };
161
+ var runSessionMiddleware = async (sessionMiddleware, req) => {
162
+ await new Promise((resolve, reject) => {
163
+ const next = (err) => {
164
+ if (err) reject(err);
165
+ else resolve();
166
+ };
167
+ sessionMiddleware(req, {}, next);
168
+ });
211
169
  };
212
- const getTenantModel = async (tenantId, modelName) => {
213
- const ctx = {
214
- req: {
215
- session: {
216
- user: {
217
- currentTenantId: tenantId
218
- }
219
- }
220
- }
221
- };
222
- return models.getUnsafe(modelName, ctx);
170
+ var parseUpgradeMeta = async ({ req, url, sessionMiddleware, postSyncSessionMiddleware }) => {
171
+ const upgradeReq = req;
172
+ try {
173
+ await runSessionMiddleware(sessionMiddleware, upgradeReq);
174
+ } catch {
175
+ throw new Error("Failed to load session for RTS");
176
+ }
177
+ await syncAuthenticatedSessionFromRequest(upgradeReq);
178
+ if (postSyncSessionMiddleware) try {
179
+ await runSessionMiddleware(postSyncSessionMiddleware, upgradeReq);
180
+ } catch {
181
+ throw new Error("RTS session authorization failed");
182
+ }
183
+ const sessionUser = upgradeReq.session?.user;
184
+ const sessionUserId = sessionUser?.id?.trim();
185
+ if (!sessionUserId) throw new Error("Not signed in (missing session.user.id)");
186
+ const tenantId = sessionUser?.currentTenantId?.trim();
187
+ if (!tenantId) throw new Error("Missing current tenant in session");
188
+ return {
189
+ tenantId,
190
+ userId: sessionUserId,
191
+ ability: buildAbilityFromSession({
192
+ tenantId,
193
+ session: upgradeReq.session
194
+ })
195
+ };
223
196
  };
224
- const makeDispatchKey = (tenantId, modelName) => `${tenantId}:${modelName}`;
225
- const clearDispatchTimer = (tenantId, modelName) => {
226
- const key = makeDispatchKey(tenantId, modelName);
227
- const timer = dispatchTimers.get(key);
228
- if (!timer) return;
229
- clearTimeout(timer);
230
- dispatchTimers.delete(key);
197
+ var getTenantModel = async (tenantId, modelName) => {
198
+ const ctx = { req: { session: { user: { currentTenantId: tenantId } } } };
199
+ return models.getUnsafe(modelName, ctx);
231
200
  };
232
- const scheduleDispatchSubscriptionsForModel = (tenantId, modelName) => {
233
- const key = makeDispatchKey(tenantId, modelName);
234
- if (dispatchTimers.has(key)) return;
235
- const delay = Math.max(0, Math.min(1e3, Math.floor(dispatchDebounceMs)));
236
- dispatchTimers.set(key, setTimeout(() => {
237
- dispatchTimers.delete(key);
238
- void Promise.all([dispatchSubscriptionsForModel(tenantId, modelName), dispatchCountSubscriptionsForModel(tenantId, modelName)]).catch(() => {
239
- });
240
- }, delay));
201
+ var makeDispatchKey = (tenantId, modelName) => `${tenantId}:${modelName}`;
202
+ var clearDispatchTimer = (tenantId, modelName) => {
203
+ const key = makeDispatchKey(tenantId, modelName);
204
+ const timer = dispatchTimers.get(key);
205
+ if (!timer) return;
206
+ clearTimeout(timer);
207
+ dispatchTimers.delete(key);
241
208
  };
242
- const runAndSendQuery = async ({
243
- tenantId,
244
- targetSocketIds,
245
- ability,
246
- modelName,
247
- queryKey,
248
- query,
249
- options
250
- }) => {
251
- const result = await runRtsQuery({
252
- tenantId,
253
- ability,
254
- modelName,
255
- query,
256
- options,
257
- allowInternalModels
258
- });
259
- const payload = {
260
- type: "query-payload",
261
- modelName,
262
- queryKey,
263
- data: result.data,
264
- ...result.pageInfo ? {
265
- pageInfo: result.pageInfo
266
- } : {},
267
- ...typeof result.totalCount === "number" ? {
268
- totalCount: result.totalCount
269
- } : {}
270
- };
271
- for (const socketId of targetSocketIds) {
272
- const ws = sockets.get(socketId);
273
- if (!ws) continue;
274
- sendWs(ws, payload);
275
- }
209
+ var scheduleDispatchSubscriptionsForModel = (tenantId, modelName) => {
210
+ const key = makeDispatchKey(tenantId, modelName);
211
+ if (dispatchTimers.has(key)) return;
212
+ const delay = Math.max(0, Math.min(1e3, Math.floor(dispatchDebounceMs)));
213
+ dispatchTimers.set(key, setTimeout(() => {
214
+ dispatchTimers.delete(key);
215
+ Promise.all([dispatchSubscriptionsForModel(tenantId, modelName), dispatchCountSubscriptionsForModel(tenantId, modelName)]).catch(() => {});
216
+ }, delay));
276
217
  };
277
- const getHeadWindowOptions = (options, requestedCount) => {
278
- if (!options.pagination) return options;
279
- const pagination = {
280
- ...options.pagination,
281
- limit: requestedCount
282
- };
283
- delete pagination.cursor;
284
- delete pagination.direction;
285
- return {
286
- ...options,
287
- pagination
288
- };
218
+ var runAndSendQuery = async ({ tenantId, targetSocketIds, ability, modelName, queryKey, query, options }) => {
219
+ const result = await runRtsQuery({
220
+ tenantId,
221
+ ability,
222
+ modelName,
223
+ query,
224
+ options,
225
+ allowInternalModels
226
+ });
227
+ const payload = {
228
+ type: "query-payload",
229
+ modelName,
230
+ queryKey,
231
+ data: result.data,
232
+ ...result.pageInfo ? { pageInfo: result.pageInfo } : {},
233
+ ...typeof result.totalCount === "number" ? { totalCount: result.totalCount } : {}
234
+ };
235
+ for (const socketId of targetSocketIds) {
236
+ const ws = sockets.get(socketId);
237
+ if (!ws) continue;
238
+ sendWs(ws, payload);
239
+ }
289
240
  };
290
- const runAndSendSubscriptionQuery = async ({
291
- tenantId,
292
- targetSocketIds,
293
- ability,
294
- modelName,
295
- queryKey,
296
- subscription
297
- }) => {
298
- if (!subscription.options.pagination) {
299
- await runAndSendQuery({
300
- tenantId,
301
- targetSocketIds,
302
- ability,
303
- modelName,
304
- queryKey,
305
- query: subscription.query,
306
- options: subscription.options
307
- });
308
- return;
309
- }
310
- const groups = /* @__PURE__ */ new Map();
311
- for (const socketId of targetSocketIds) {
312
- const state = subscription.windows.get(socketId);
313
- if (!state) continue;
314
- state.version += 1;
315
- const targets = groups.get(state.requestedCount) ?? [];
316
- targets.push({
317
- socketId,
318
- window: {
319
- requestedCount: state.requestedCount,
320
- version: state.version
321
- }
322
- });
323
- groups.set(state.requestedCount, targets);
324
- }
325
- await Promise.all(Array.from(groups.entries()).map(async ([requestedCount, targets]) => {
326
- try {
327
- const result = await runRtsQuery({
328
- tenantId,
329
- ability,
330
- modelName,
331
- query: subscription.query,
332
- options: getHeadWindowOptions(subscription.options, requestedCount),
333
- allowInternalModels,
334
- paginationMaxLimit: RTS_QUERY_WINDOW_MAX_COUNT,
335
- includePaginationCursors: false
336
- });
337
- for (const target of targets) {
338
- const current = subscription.windows.get(target.socketId);
339
- if (!current || current.requestedCount !== target.window.requestedCount || current.version !== target.window.version) {
340
- continue;
341
- }
342
- const ws = sockets.get(target.socketId);
343
- if (!ws) continue;
344
- sendWs(ws, {
345
- type: "query-payload",
346
- modelName,
347
- queryKey,
348
- data: result.data,
349
- ...result.pageInfo ? {
350
- pageInfo: result.pageInfo
351
- } : {},
352
- ...typeof result.totalCount === "number" ? {
353
- totalCount: result.totalCount
354
- } : {},
355
- window: target.window
356
- });
357
- }
358
- } catch (err) {
359
- const error = redactErrorMessage(err);
360
- for (const target of targets) {
361
- const current = subscription.windows.get(target.socketId);
362
- if (!current || current.requestedCount !== target.window.requestedCount || current.version !== target.window.version) {
363
- continue;
364
- }
365
- const ws = sockets.get(target.socketId);
366
- if (!ws) continue;
367
- sendWs(ws, {
368
- type: "query-payload",
369
- modelName,
370
- queryKey,
371
- error,
372
- window: target.window
373
- });
374
- }
375
- }
376
- }));
241
+ var getHeadWindowOptions = (options, requestedCount) => {
242
+ if (!options.pagination) return options;
243
+ const pagination = {
244
+ ...options.pagination,
245
+ limit: requestedCount
246
+ };
247
+ delete pagination.cursor;
248
+ delete pagination.direction;
249
+ return {
250
+ ...options,
251
+ pagination
252
+ };
377
253
  };
378
- const runAndSendCount = async ({
379
- tenantId,
380
- targetSocketIds,
381
- ability,
382
- modelName,
383
- queryKey,
384
- query
385
- }) => {
386
- const count = await runRtsCount({
387
- tenantId,
388
- ability,
389
- modelName,
390
- query,
391
- allowInternalModels
392
- });
393
- const payload = {
394
- type: "count-payload",
395
- modelName,
396
- queryKey,
397
- count
398
- };
399
- for (const socketId of targetSocketIds) {
400
- const ws = sockets.get(socketId);
401
- if (!ws) continue;
402
- sendWs(ws, payload);
403
- }
254
+ var runAndSendSubscriptionQuery = async ({ tenantId, targetSocketIds, ability, modelName, queryKey, subscription }) => {
255
+ if (!subscription.options.pagination) {
256
+ await runAndSendQuery({
257
+ tenantId,
258
+ targetSocketIds,
259
+ ability,
260
+ modelName,
261
+ queryKey,
262
+ query: subscription.query,
263
+ options: subscription.options
264
+ });
265
+ return;
266
+ }
267
+ const groups = /* @__PURE__ */ new Map();
268
+ for (const socketId of targetSocketIds) {
269
+ const state = subscription.windows.get(socketId);
270
+ if (!state) continue;
271
+ state.version += 1;
272
+ const targets = groups.get(state.requestedCount) ?? [];
273
+ targets.push({
274
+ socketId,
275
+ window: {
276
+ requestedCount: state.requestedCount,
277
+ version: state.version
278
+ }
279
+ });
280
+ groups.set(state.requestedCount, targets);
281
+ }
282
+ await Promise.all(Array.from(groups.entries()).map(async ([requestedCount, targets]) => {
283
+ try {
284
+ const result = await runRtsQuery({
285
+ tenantId,
286
+ ability,
287
+ modelName,
288
+ query: subscription.query,
289
+ options: getHeadWindowOptions(subscription.options, requestedCount),
290
+ allowInternalModels,
291
+ paginationMaxLimit: RTS_QUERY_WINDOW_MAX_COUNT,
292
+ includePaginationCursors: false
293
+ });
294
+ for (const target of targets) {
295
+ const current = subscription.windows.get(target.socketId);
296
+ if (!current || current.requestedCount !== target.window.requestedCount || current.version !== target.window.version) continue;
297
+ const ws = sockets.get(target.socketId);
298
+ if (!ws) continue;
299
+ sendWs(ws, {
300
+ type: "query-payload",
301
+ modelName,
302
+ queryKey,
303
+ data: result.data,
304
+ ...result.pageInfo ? { pageInfo: result.pageInfo } : {},
305
+ ...typeof result.totalCount === "number" ? { totalCount: result.totalCount } : {},
306
+ window: target.window
307
+ });
308
+ }
309
+ } catch (err) {
310
+ const error = redactErrorMessage(err);
311
+ for (const target of targets) {
312
+ const current = subscription.windows.get(target.socketId);
313
+ if (!current || current.requestedCount !== target.window.requestedCount || current.version !== target.window.version) continue;
314
+ const ws = sockets.get(target.socketId);
315
+ if (!ws) continue;
316
+ sendWs(ws, {
317
+ type: "query-payload",
318
+ modelName,
319
+ queryKey,
320
+ error,
321
+ window: target.window
322
+ });
323
+ }
324
+ }
325
+ }));
404
326
  };
405
- const subscriptionDependsOnModel = (changedModelName, ownerModelName, subscription) => {
406
- if (ownerModelName === changedModelName) return true;
407
- return subscription.dependencyModelNames.includes(changedModelName);
327
+ var runAndSendCount = async ({ tenantId, targetSocketIds, ability, modelName, queryKey, query }) => {
328
+ const payload = {
329
+ type: "count-payload",
330
+ modelName,
331
+ queryKey,
332
+ count: await runRtsCount({
333
+ tenantId,
334
+ ability,
335
+ modelName,
336
+ query,
337
+ allowInternalModels
338
+ })
339
+ };
340
+ for (const socketId of targetSocketIds) {
341
+ const ws = sockets.get(socketId);
342
+ if (!ws) continue;
343
+ sendWs(ws, payload);
344
+ }
408
345
  };
409
- const hasAnySubscriptionsDependingOnModel = (tenantId, modelName) => {
410
- const tenantSubs = subscriptions.get(tenantId);
411
- if (!tenantSubs) return false;
412
- for (const userSubs of tenantSubs.values()) {
413
- for (const [ownerModelName, modelSubs] of userSubs.entries()) {
414
- for (const subscription of modelSubs.values()) {
415
- if (subscriptionDependsOnModel(modelName, ownerModelName, subscription)) {
416
- return true;
417
- }
418
- }
419
- }
420
- }
421
- return false;
346
+ var subscriptionDependsOnModel = (changedModelName, ownerModelName, subscription) => {
347
+ if (ownerModelName === changedModelName) return true;
348
+ return subscription.dependencyModelNames.includes(changedModelName);
422
349
  };
423
- const hasAnyCountSubscriptionsForModel = (tenantId, modelName) => {
424
- const tenantSubs = countSubscriptions.get(tenantId);
425
- if (!tenantSubs) return false;
426
- for (const userSubs of tenantSubs.values()) {
427
- const modelSubs = userSubs.get(modelName);
428
- if (modelSubs?.size) {
429
- return true;
430
- }
431
- }
432
- return false;
350
+ var hasAnySubscriptionsDependingOnModel = (tenantId, modelName) => {
351
+ const tenantSubs = subscriptions.get(tenantId);
352
+ if (!tenantSubs) return false;
353
+ for (const userSubs of tenantSubs.values()) for (const [ownerModelName, modelSubs] of userSubs.entries()) for (const subscription of modelSubs.values()) if (subscriptionDependsOnModel(modelName, ownerModelName, subscription)) return true;
354
+ return false;
433
355
  };
434
- const dispatchSubscriptionsForModel = async (tenantId, modelName) => {
435
- const tenantSubs = subscriptions.get(tenantId);
436
- if (!tenantSubs || !tenantSubs.size) return;
437
- for (const userSubs of tenantSubs.values()) {
438
- for (const [ownerModelName, modelSubs] of userSubs.entries()) {
439
- for (const [queryKey, sub] of modelSubs.entries()) {
440
- if (!subscriptionDependsOnModel(modelName, ownerModelName, sub)) continue;
441
- const targetSocketIds = Array.from(sub.socketIds);
442
- if (!targetSocketIds.length) continue;
443
- const socketId = targetSocketIds[0];
444
- const meta = socketMeta.get(socketId);
445
- const ability = meta?.ability;
446
- if (!ability) continue;
447
- try {
448
- await runAndSendSubscriptionQuery({
449
- tenantId,
450
- targetSocketIds,
451
- ability,
452
- modelName: ownerModelName,
453
- queryKey,
454
- subscription: sub
455
- });
456
- } catch (err) {
457
- const error = redactErrorMessage(err);
458
- const payload = {
459
- type: "query-payload",
460
- modelName: ownerModelName,
461
- queryKey,
462
- error
463
- };
464
- for (const socketId2 of targetSocketIds) {
465
- const ws = sockets.get(socketId2);
466
- if (!ws) continue;
467
- sendWs(ws, payload);
468
- }
469
- }
470
- }
471
- }
472
- }
356
+ var hasAnyCountSubscriptionsForModel = (tenantId, modelName) => {
357
+ const tenantSubs = countSubscriptions.get(tenantId);
358
+ if (!tenantSubs) return false;
359
+ for (const userSubs of tenantSubs.values()) if (userSubs.get(modelName)?.size) return true;
360
+ return false;
473
361
  };
474
- const dispatchCountSubscriptionsForModel = async (tenantId, modelName) => {
475
- const tenantSubs = countSubscriptions.get(tenantId);
476
- if (!tenantSubs || !tenantSubs.size) return;
477
- for (const userSubs of tenantSubs.values()) {
478
- const modelSubs = userSubs.get(modelName);
479
- if (!modelSubs) continue;
480
- for (const [queryKey, sub] of modelSubs.entries()) {
481
- const targetSocketIds = Array.from(sub.socketIds);
482
- if (!targetSocketIds.length) continue;
483
- const socketId = targetSocketIds[0];
484
- const meta = socketMeta.get(socketId);
485
- const ability = meta?.ability;
486
- if (!ability) continue;
487
- try {
488
- await runAndSendCount({
489
- tenantId,
490
- targetSocketIds,
491
- ability,
492
- modelName,
493
- queryKey,
494
- query: sub.query
495
- });
496
- } catch (err) {
497
- const error = redactErrorMessage(err);
498
- const payload = {
499
- type: "count-payload",
500
- modelName,
501
- queryKey,
502
- error
503
- };
504
- for (const targetSocketId of targetSocketIds) {
505
- const ws = sockets.get(targetSocketId);
506
- if (!ws) continue;
507
- sendWs(ws, payload);
508
- }
509
- }
510
- }
511
- }
362
+ var dispatchSubscriptionsForModel = async (tenantId, modelName) => {
363
+ const tenantSubs = subscriptions.get(tenantId);
364
+ if (!tenantSubs || !tenantSubs.size) return;
365
+ for (const userSubs of tenantSubs.values()) for (const [ownerModelName, modelSubs] of userSubs.entries()) for (const [queryKey, sub] of modelSubs.entries()) {
366
+ if (!subscriptionDependsOnModel(modelName, ownerModelName, sub)) continue;
367
+ const targetSocketIds = Array.from(sub.socketIds);
368
+ if (!targetSocketIds.length) continue;
369
+ const socketId = targetSocketIds[0];
370
+ const ability = socketMeta.get(socketId)?.ability;
371
+ if (!ability) continue;
372
+ try {
373
+ await runAndSendSubscriptionQuery({
374
+ tenantId,
375
+ targetSocketIds,
376
+ ability,
377
+ modelName: ownerModelName,
378
+ queryKey,
379
+ subscription: sub
380
+ });
381
+ } catch (err) {
382
+ const payload = {
383
+ type: "query-payload",
384
+ modelName: ownerModelName,
385
+ queryKey,
386
+ error: redactErrorMessage(err)
387
+ };
388
+ for (const socketId of targetSocketIds) {
389
+ const ws = sockets.get(socketId);
390
+ if (!ws) continue;
391
+ sendWs(ws, payload);
392
+ }
393
+ }
394
+ }
512
395
  };
513
- const ensureChangeStream = async (tenantId, modelName) => {
514
- const tenantStreams = changeStreams.get(tenantId) ?? /* @__PURE__ */ new Map();
515
- changeStreams.set(tenantId, tenantStreams);
516
- if (tenantStreams.has(modelName)) return;
517
- const model = await getTenantModel(tenantId, modelName);
518
- const stream = model.watch([], {
519
- fullDocument: "updateLookup"
520
- });
521
- stream.on("change", () => {
522
- scheduleDispatchSubscriptionsForModel(tenantId, modelName);
523
- });
524
- stream.on("close", () => {
525
- clearDispatchTimer(tenantId, modelName);
526
- const map = changeStreams.get(tenantId);
527
- map?.delete(modelName);
528
- if (map && map.size === 0) changeStreams.delete(tenantId);
529
- });
530
- stream.on("error", () => {
531
- try {
532
- clearDispatchTimer(tenantId, modelName);
533
- stream.close();
534
- } catch {
535
- }
536
- });
537
- tenantStreams.set(modelName, stream);
396
+ var dispatchCountSubscriptionsForModel = async (tenantId, modelName) => {
397
+ const tenantSubs = countSubscriptions.get(tenantId);
398
+ if (!tenantSubs || !tenantSubs.size) return;
399
+ for (const userSubs of tenantSubs.values()) {
400
+ const modelSubs = userSubs.get(modelName);
401
+ if (!modelSubs) continue;
402
+ for (const [queryKey, sub] of modelSubs.entries()) {
403
+ const targetSocketIds = Array.from(sub.socketIds);
404
+ if (!targetSocketIds.length) continue;
405
+ const socketId = targetSocketIds[0];
406
+ const ability = socketMeta.get(socketId)?.ability;
407
+ if (!ability) continue;
408
+ try {
409
+ await runAndSendCount({
410
+ tenantId,
411
+ targetSocketIds,
412
+ ability,
413
+ modelName,
414
+ queryKey,
415
+ query: sub.query
416
+ });
417
+ } catch (err) {
418
+ const payload = {
419
+ type: "count-payload",
420
+ modelName,
421
+ queryKey,
422
+ error: redactErrorMessage(err)
423
+ };
424
+ for (const targetSocketId of targetSocketIds) {
425
+ const ws = sockets.get(targetSocketId);
426
+ if (!ws) continue;
427
+ sendWs(ws, payload);
428
+ }
429
+ }
430
+ }
431
+ }
538
432
  };
539
- const closeUnusedChangeStream = (tenantId, modelName) => {
540
- if (hasAnySubscriptionsDependingOnModel(tenantId, modelName) || hasAnyCountSubscriptionsForModel(tenantId, modelName)) {
541
- return;
542
- }
543
- const tenantStreams = changeStreams.get(tenantId);
544
- const stream = tenantStreams?.get(modelName);
545
- if (!stream) return;
546
- try {
547
- stream.close();
548
- } catch {
549
- }
550
- clearDispatchTimer(tenantId, modelName);
551
- tenantStreams?.delete(modelName);
552
- if (tenantStreams && tenantStreams.size === 0) changeStreams.delete(tenantId);
433
+ var ensureChangeStream = async (tenantId, modelName) => {
434
+ const tenantStreams = changeStreams.get(tenantId) ?? /* @__PURE__ */ new Map();
435
+ changeStreams.set(tenantId, tenantStreams);
436
+ if (tenantStreams.has(modelName)) return;
437
+ const stream = (await getTenantModel(tenantId, modelName)).watch([], { fullDocument: "updateLookup" });
438
+ stream.on("change", () => {
439
+ scheduleDispatchSubscriptionsForModel(tenantId, modelName);
440
+ });
441
+ stream.on("close", () => {
442
+ clearDispatchTimer(tenantId, modelName);
443
+ const map = changeStreams.get(tenantId);
444
+ map?.delete(modelName);
445
+ if (map && map.size === 0) changeStreams.delete(tenantId);
446
+ });
447
+ stream.on("error", () => {
448
+ try {
449
+ clearDispatchTimer(tenantId, modelName);
450
+ stream.close();
451
+ } catch {}
452
+ });
453
+ tenantStreams.set(modelName, stream);
553
454
  };
554
- const addSocketSubscription = ({
555
- socketId,
556
- tenantId,
557
- userId,
558
- modelName,
559
- queryKey,
560
- query,
561
- options,
562
- dependencyModelNames
563
- }) => {
564
- const tenantSubs = subscriptions.get(tenantId) ?? /* @__PURE__ */ new Map();
565
- subscriptions.set(tenantId, tenantSubs);
566
- const userSubs = tenantSubs.get(userId) ?? /* @__PURE__ */ new Map();
567
- tenantSubs.set(userId, userSubs);
568
- const modelSubs = userSubs.get(modelName) ?? /* @__PURE__ */ new Map();
569
- userSubs.set(modelName, modelSubs);
570
- const existing = modelSubs.get(queryKey);
571
- if (existing) {
572
- existing.socketIds.add(socketId);
573
- existing.dependencyModelNames = Array.from(/* @__PURE__ */ new Set([...existing.dependencyModelNames, ...dependencyModelNames]));
574
- if (options.pagination) {
575
- existing.windows.set(socketId, {
576
- requestedCount: options.pagination.limit,
577
- version: 0
578
- });
579
- } else {
580
- existing.windows.delete(socketId);
581
- }
582
- } else {
583
- modelSubs.set(queryKey, {
584
- query,
585
- options,
586
- dependencyModelNames: Array.from(new Set(dependencyModelNames)),
587
- socketIds: /* @__PURE__ */ new Set([socketId]),
588
- windows: options.pagination ? /* @__PURE__ */ new Map([[socketId, {
589
- requestedCount: options.pagination.limit,
590
- version: 0
591
- }]]) : /* @__PURE__ */ new Map()
592
- });
593
- }
594
- const byModel = socketSubscriptions.get(socketId) ?? /* @__PURE__ */ new Map();
595
- socketSubscriptions.set(socketId, byModel);
596
- const querySet = byModel.get(modelName) ?? /* @__PURE__ */ new Set();
597
- byModel.set(modelName, querySet);
598
- querySet.add(queryKey);
455
+ var closeUnusedChangeStream = (tenantId, modelName) => {
456
+ if (hasAnySubscriptionsDependingOnModel(tenantId, modelName) || hasAnyCountSubscriptionsForModel(tenantId, modelName)) return;
457
+ const tenantStreams = changeStreams.get(tenantId);
458
+ const stream = tenantStreams?.get(modelName);
459
+ if (!stream) return;
460
+ try {
461
+ stream.close();
462
+ } catch {}
463
+ clearDispatchTimer(tenantId, modelName);
464
+ tenantStreams?.delete(modelName);
465
+ if (tenantStreams && tenantStreams.size === 0) changeStreams.delete(tenantId);
599
466
  };
600
- const addSocketCountSubscription = ({
601
- socketId,
602
- tenantId,
603
- userId,
604
- modelName,
605
- queryKey,
606
- query
607
- }) => {
608
- const tenantSubs = countSubscriptions.get(tenantId) ?? /* @__PURE__ */ new Map();
609
- countSubscriptions.set(tenantId, tenantSubs);
610
- const userSubs = tenantSubs.get(userId) ?? /* @__PURE__ */ new Map();
611
- tenantSubs.set(userId, userSubs);
612
- const modelSubs = userSubs.get(modelName) ?? /* @__PURE__ */ new Map();
613
- userSubs.set(modelName, modelSubs);
614
- const existing = modelSubs.get(queryKey);
615
- if (existing) {
616
- existing.socketIds.add(socketId);
617
- } else {
618
- modelSubs.set(queryKey, {
619
- query,
620
- socketIds: /* @__PURE__ */ new Set([socketId])
621
- });
622
- }
623
- const byModel = socketCountSubscriptions.get(socketId) ?? /* @__PURE__ */ new Map();
624
- socketCountSubscriptions.set(socketId, byModel);
625
- const querySet = byModel.get(modelName) ?? /* @__PURE__ */ new Set();
626
- byModel.set(modelName, querySet);
627
- querySet.add(queryKey);
467
+ var addSocketSubscription = ({ socketId, tenantId, userId, modelName, queryKey, query, options, dependencyModelNames }) => {
468
+ const tenantSubs = subscriptions.get(tenantId) ?? /* @__PURE__ */ new Map();
469
+ subscriptions.set(tenantId, tenantSubs);
470
+ const userSubs = tenantSubs.get(userId) ?? /* @__PURE__ */ new Map();
471
+ tenantSubs.set(userId, userSubs);
472
+ const modelSubs = userSubs.get(modelName) ?? /* @__PURE__ */ new Map();
473
+ userSubs.set(modelName, modelSubs);
474
+ const existing = modelSubs.get(queryKey);
475
+ if (existing) {
476
+ existing.socketIds.add(socketId);
477
+ existing.dependencyModelNames = Array.from(/* @__PURE__ */ new Set([...existing.dependencyModelNames, ...dependencyModelNames]));
478
+ if (options.pagination) existing.windows.set(socketId, {
479
+ requestedCount: options.pagination.limit,
480
+ version: 0
481
+ });
482
+ else existing.windows.delete(socketId);
483
+ } else modelSubs.set(queryKey, {
484
+ query,
485
+ options,
486
+ dependencyModelNames: Array.from(new Set(dependencyModelNames)),
487
+ socketIds: /* @__PURE__ */ new Set([socketId]),
488
+ windows: options.pagination ? /* @__PURE__ */ new Map([[socketId, {
489
+ requestedCount: options.pagination.limit,
490
+ version: 0
491
+ }]]) : /* @__PURE__ */ new Map()
492
+ });
493
+ const byModel = socketSubscriptions.get(socketId) ?? /* @__PURE__ */ new Map();
494
+ socketSubscriptions.set(socketId, byModel);
495
+ const querySet = byModel.get(modelName) ?? /* @__PURE__ */ new Set();
496
+ byModel.set(modelName, querySet);
497
+ querySet.add(queryKey);
628
498
  };
629
- const removeSocketSubscription = ({
630
- socketId,
631
- tenantId,
632
- userId,
633
- modelName,
634
- queryKey
635
- }) => {
636
- const tenantSubs = subscriptions.get(tenantId);
637
- const userSubs = tenantSubs?.get(userId);
638
- const modelSubs = userSubs?.get(modelName);
639
- const sub = modelSubs?.get(queryKey);
640
- const affectedModelNames = /* @__PURE__ */ new Set([modelName, ...sub?.dependencyModelNames ?? []]);
641
- if (sub) {
642
- sub.socketIds.delete(socketId);
643
- sub.windows.delete(socketId);
644
- if (!sub.socketIds.size) {
645
- modelSubs?.delete(queryKey);
646
- }
647
- }
648
- const byModel = socketSubscriptions.get(socketId);
649
- const set = byModel?.get(modelName);
650
- if (set) {
651
- set.delete(queryKey);
652
- if (!set.size) {
653
- byModel?.delete(modelName);
654
- }
655
- }
656
- if (modelSubs && modelSubs.size === 0) {
657
- userSubs?.delete(modelName);
658
- }
659
- if (userSubs && userSubs.size === 0) {
660
- tenantSubs?.delete(userId);
661
- }
662
- for (const affectedModelName of affectedModelNames) {
663
- if (hasAnySubscriptionsDependingOnModel(tenantId, affectedModelName) || hasAnyCountSubscriptionsForModel(tenantId, affectedModelName)) {
664
- continue;
665
- }
666
- const tenantStreams = changeStreams.get(tenantId);
667
- const stream = tenantStreams?.get(affectedModelName);
668
- if (stream) {
669
- try {
670
- stream.close();
671
- } catch {
672
- }
673
- clearDispatchTimer(tenantId, affectedModelName);
674
- tenantStreams?.delete(affectedModelName);
675
- if (tenantStreams && tenantStreams.size === 0) changeStreams.delete(tenantId);
676
- }
677
- }
678
- if (tenantSubs && tenantSubs.size === 0) subscriptions.delete(tenantId);
679
- if (byModel && byModel.size === 0) socketSubscriptions.delete(socketId);
499
+ var addSocketCountSubscription = ({ socketId, tenantId, userId, modelName, queryKey, query }) => {
500
+ const tenantSubs = countSubscriptions.get(tenantId) ?? /* @__PURE__ */ new Map();
501
+ countSubscriptions.set(tenantId, tenantSubs);
502
+ const userSubs = tenantSubs.get(userId) ?? /* @__PURE__ */ new Map();
503
+ tenantSubs.set(userId, userSubs);
504
+ const modelSubs = userSubs.get(modelName) ?? /* @__PURE__ */ new Map();
505
+ userSubs.set(modelName, modelSubs);
506
+ const existing = modelSubs.get(queryKey);
507
+ if (existing) existing.socketIds.add(socketId);
508
+ else modelSubs.set(queryKey, {
509
+ query,
510
+ socketIds: /* @__PURE__ */ new Set([socketId])
511
+ });
512
+ const byModel = socketCountSubscriptions.get(socketId) ?? /* @__PURE__ */ new Map();
513
+ socketCountSubscriptions.set(socketId, byModel);
514
+ const querySet = byModel.get(modelName) ?? /* @__PURE__ */ new Set();
515
+ byModel.set(modelName, querySet);
516
+ querySet.add(queryKey);
680
517
  };
681
- const removeSocketCountSubscription = ({
682
- socketId,
683
- tenantId,
684
- userId,
685
- modelName,
686
- queryKey
687
- }) => {
688
- const tenantSubs = countSubscriptions.get(tenantId);
689
- const userSubs = tenantSubs?.get(userId);
690
- const modelSubs = userSubs?.get(modelName);
691
- const sub = modelSubs?.get(queryKey);
692
- if (sub) {
693
- sub.socketIds.delete(socketId);
694
- if (!sub.socketIds.size) {
695
- modelSubs?.delete(queryKey);
696
- }
697
- }
698
- const byModel = socketCountSubscriptions.get(socketId);
699
- const set = byModel?.get(modelName);
700
- if (set) {
701
- set.delete(queryKey);
702
- if (!set.size) {
703
- byModel?.delete(modelName);
704
- }
705
- }
706
- if (modelSubs && modelSubs.size === 0) {
707
- userSubs?.delete(modelName);
708
- }
709
- if (userSubs && userSubs.size === 0) {
710
- tenantSubs?.delete(userId);
711
- }
712
- if (!hasAnySubscriptionsDependingOnModel(tenantId, modelName) && !hasAnyCountSubscriptionsForModel(tenantId, modelName)) {
713
- const tenantStreams = changeStreams.get(tenantId);
714
- const stream = tenantStreams?.get(modelName);
715
- if (stream) {
716
- try {
717
- stream.close();
718
- } catch {
719
- }
720
- clearDispatchTimer(tenantId, modelName);
721
- tenantStreams?.delete(modelName);
722
- if (tenantStreams && tenantStreams.size === 0) changeStreams.delete(tenantId);
723
- }
724
- }
725
- if (tenantSubs && tenantSubs.size === 0) countSubscriptions.delete(tenantId);
726
- if (byModel && byModel.size === 0) socketCountSubscriptions.delete(socketId);
518
+ var removeSocketSubscription = ({ socketId, tenantId, userId, modelName, queryKey }) => {
519
+ const tenantSubs = subscriptions.get(tenantId);
520
+ const userSubs = tenantSubs?.get(userId);
521
+ const modelSubs = userSubs?.get(modelName);
522
+ const sub = modelSubs?.get(queryKey);
523
+ const affectedModelNames = /* @__PURE__ */ new Set([modelName, ...sub?.dependencyModelNames ?? []]);
524
+ if (sub) {
525
+ sub.socketIds.delete(socketId);
526
+ sub.windows.delete(socketId);
527
+ if (!sub.socketIds.size) modelSubs?.delete(queryKey);
528
+ }
529
+ const byModel = socketSubscriptions.get(socketId);
530
+ const set = byModel?.get(modelName);
531
+ if (set) {
532
+ set.delete(queryKey);
533
+ if (!set.size) byModel?.delete(modelName);
534
+ }
535
+ if (modelSubs && modelSubs.size === 0) userSubs?.delete(modelName);
536
+ if (userSubs && userSubs.size === 0) tenantSubs?.delete(userId);
537
+ for (const affectedModelName of affectedModelNames) {
538
+ if (hasAnySubscriptionsDependingOnModel(tenantId, affectedModelName) || hasAnyCountSubscriptionsForModel(tenantId, affectedModelName)) continue;
539
+ const tenantStreams = changeStreams.get(tenantId);
540
+ const stream = tenantStreams?.get(affectedModelName);
541
+ if (stream) {
542
+ try {
543
+ stream.close();
544
+ } catch {}
545
+ clearDispatchTimer(tenantId, affectedModelName);
546
+ tenantStreams?.delete(affectedModelName);
547
+ if (tenantStreams && tenantStreams.size === 0) changeStreams.delete(tenantId);
548
+ }
549
+ }
550
+ if (tenantSubs && tenantSubs.size === 0) subscriptions.delete(tenantId);
551
+ if (byModel && byModel.size === 0) socketSubscriptions.delete(socketId);
727
552
  };
728
- const getSocketSubscriptionCount = (socketId) => {
729
- let count = 0;
730
- const querySubscriptionsByModel = socketSubscriptions.get(socketId);
731
- if (querySubscriptionsByModel) {
732
- for (const set of querySubscriptionsByModel.values()) count += set.size;
733
- }
734
- const countSubscriptionsByModel = socketCountSubscriptions.get(socketId);
735
- if (countSubscriptionsByModel) {
736
- for (const set of countSubscriptionsByModel.values()) count += set.size;
737
- }
738
- return count;
553
+ var removeSocketCountSubscription = ({ socketId, tenantId, userId, modelName, queryKey }) => {
554
+ const tenantSubs = countSubscriptions.get(tenantId);
555
+ const userSubs = tenantSubs?.get(userId);
556
+ const modelSubs = userSubs?.get(modelName);
557
+ const sub = modelSubs?.get(queryKey);
558
+ if (sub) {
559
+ sub.socketIds.delete(socketId);
560
+ if (!sub.socketIds.size) modelSubs?.delete(queryKey);
561
+ }
562
+ const byModel = socketCountSubscriptions.get(socketId);
563
+ const set = byModel?.get(modelName);
564
+ if (set) {
565
+ set.delete(queryKey);
566
+ if (!set.size) byModel?.delete(modelName);
567
+ }
568
+ if (modelSubs && modelSubs.size === 0) userSubs?.delete(modelName);
569
+ if (userSubs && userSubs.size === 0) tenantSubs?.delete(userId);
570
+ if (!hasAnySubscriptionsDependingOnModel(tenantId, modelName) && !hasAnyCountSubscriptionsForModel(tenantId, modelName)) {
571
+ const tenantStreams = changeStreams.get(tenantId);
572
+ const stream = tenantStreams?.get(modelName);
573
+ if (stream) {
574
+ try {
575
+ stream.close();
576
+ } catch {}
577
+ clearDispatchTimer(tenantId, modelName);
578
+ tenantStreams?.delete(modelName);
579
+ if (tenantStreams && tenantStreams.size === 0) changeStreams.delete(tenantId);
580
+ }
581
+ }
582
+ if (tenantSubs && tenantSubs.size === 0) countSubscriptions.delete(tenantId);
583
+ if (byModel && byModel.size === 0) socketCountSubscriptions.delete(socketId);
739
584
  };
740
- const cleanupSocket = (socketId) => {
741
- const meta = socketMeta.get(socketId);
742
- if (meta) {
743
- const byModel = socketSubscriptions.get(socketId);
744
- if (byModel) {
745
- for (const [modelName, keys] of byModel.entries()) {
746
- for (const queryKey of keys.values()) {
747
- removeSocketSubscription({
748
- socketId,
749
- tenantId: meta.tenantId,
750
- userId: meta.userId,
751
- modelName,
752
- queryKey
753
- });
754
- }
755
- }
756
- }
757
- const countByModel = socketCountSubscriptions.get(socketId);
758
- if (countByModel) {
759
- for (const [modelName, keys] of countByModel.entries()) {
760
- for (const queryKey of keys.values()) {
761
- removeSocketCountSubscription({
762
- socketId,
763
- tenantId: meta.tenantId,
764
- userId: meta.userId,
765
- modelName,
766
- queryKey
767
- });
768
- }
769
- }
770
- }
771
- }
772
- socketSubscriptions.delete(socketId);
773
- socketCountSubscriptions.delete(socketId);
774
- const cleanupFns = socketCleanup.get(socketId) ?? [];
775
- socketCleanup.delete(socketId);
776
- for (const fn of cleanupFns) {
777
- try {
778
- fn();
779
- } catch {
780
- }
781
- }
782
- sockets.delete(socketId);
783
- socketMeta.delete(socketId);
784
- socketWrappers.delete(socketId);
585
+ var getSocketSubscriptionCount = (socketId) => {
586
+ let count = 0;
587
+ const querySubscriptionsByModel = socketSubscriptions.get(socketId);
588
+ if (querySubscriptionsByModel) for (const set of querySubscriptionsByModel.values()) count += set.size;
589
+ const countSubscriptionsByModel = socketCountSubscriptions.get(socketId);
590
+ if (countSubscriptionsByModel) for (const set of countSubscriptionsByModel.values()) count += set.size;
591
+ return count;
785
592
  };
786
- const handleClientMessage = async ({
787
- socketId,
788
- meta,
789
- message
790
- }) => {
791
- const ws = sockets.get(socketId);
792
- if (!ws) return;
793
- if (message.type === "event") {
794
- const wrapper = socketWrappers.get(socketId);
795
- wrapper?.dispatch(message.event, message.payload);
796
- return;
797
- }
798
- if (message.type === "rts-message") {
799
- const wrapper = socketWrappers.get(socketId);
800
- wrapper?.dispatchRtsMessage(message.channel, message.payload);
801
- return;
802
- }
803
- const isCountMessage = message.type === "run-count" || message.type === "register-count" || message.type === "remove-count";
804
- const sendAccessError = (error) => {
805
- if (isCountMessage) {
806
- sendWs(ws, {
807
- type: "count-payload",
808
- modelName: message.modelName ?? "",
809
- queryKey: message.queryKey ?? "",
810
- error
811
- });
812
- return;
813
- }
814
- sendWs(ws, {
815
- type: "query-payload",
816
- modelName: message.modelName ?? "",
817
- queryKey: message.queryKey ?? "",
818
- error
819
- });
820
- };
821
- if (!message.modelName || typeof message.modelName !== "string") return;
822
- if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(message.modelName)) {
823
- sendAccessError("Model not allowed");
824
- return;
825
- }
826
- if (!message.queryKey || typeof message.queryKey !== "string") return;
827
- if (message.queryKey.length > QUERY_KEY_MAX_LEN) return;
828
- if (message.type === "remove-query") {
829
- removeSocketSubscription({
830
- socketId,
831
- tenantId: meta.tenantId,
832
- userId: meta.userId,
833
- modelName: message.modelName,
834
- queryKey: message.queryKey
835
- });
836
- return;
837
- }
838
- if (message.type === "remove-count") {
839
- removeSocketCountSubscription({
840
- socketId,
841
- tenantId: meta.tenantId,
842
- userId: meta.userId,
843
- modelName: message.modelName,
844
- queryKey: message.queryKey
845
- });
846
- return;
847
- }
848
- const ability = meta.ability;
849
- if (!ability.can("read", message.modelName)) {
850
- sendAccessError("forbidden");
851
- return;
852
- }
853
- if (message.type === "set-query-window") {
854
- if (!isValidQueryWindowCount(message.requestedCount)) {
855
- sendWs(ws, {
856
- type: "query-payload",
857
- modelName: message.modelName,
858
- queryKey: message.queryKey,
859
- error: "Invalid requestedCount"
860
- });
861
- return;
862
- }
863
- const subscription = subscriptions.get(meta.tenantId)?.get(meta.userId)?.get(message.modelName)?.get(message.queryKey);
864
- const window = subscription?.windows.get(socketId);
865
- if (!subscription || !subscription.options.pagination || !window) {
866
- sendWs(ws, {
867
- type: "query-payload",
868
- modelName: message.modelName,
869
- queryKey: message.queryKey,
870
- error: "Paginated query subscription not found"
871
- });
872
- return;
873
- }
874
- window.requestedCount = message.requestedCount;
875
- await runAndSendSubscriptionQuery({
876
- tenantId: meta.tenantId,
877
- targetSocketIds: [socketId],
878
- ability,
879
- modelName: message.modelName,
880
- queryKey: message.queryKey,
881
- subscription
882
- });
883
- return;
884
- }
885
- if (!message.query || typeof message.query !== "object") return;
886
- if (message.type === "register-query" || message.type === "run-query") {
887
- const options = normalizeRtsQueryOptions(message.options);
888
- if (message.type === "register-query") {
889
- if (options.pagination && !isValidQueryWindowCount(options.pagination.limit)) {
890
- sendWs(ws, {
891
- type: "query-payload",
892
- modelName: message.modelName,
893
- queryKey: message.queryKey,
894
- error: "Invalid requestedCount"
895
- });
896
- return;
897
- }
898
- const existing = socketSubscriptions.get(socketId)?.get(message.modelName)?.has(message.queryKey) ?? false;
899
- if (!existing && getSocketSubscriptionCount(socketId) >= maxSubscriptionsPerSocket) {
900
- sendWs(ws, {
901
- type: "query-payload",
902
- modelName: message.modelName,
903
- queryKey: message.queryKey,
904
- error: "Too many subscriptions"
905
- });
906
- return;
907
- }
908
- let dependencyModelNames = [];
909
- if (options.populate !== void 0) {
910
- try {
911
- dependencyModelNames = await resolveRtsQueryDependencyModelNames({
912
- tenantId: meta.tenantId,
913
- ability,
914
- modelName: message.modelName,
915
- options,
916
- allowInternalModels
917
- });
918
- } catch (err) {
919
- const error = redactErrorMessage(err);
920
- sendWs(ws, {
921
- type: "query-payload",
922
- modelName: message.modelName,
923
- queryKey: message.queryKey,
924
- error
925
- });
926
- return;
927
- }
928
- }
929
- if (!sockets.has(socketId)) return;
930
- addSocketSubscription({
931
- socketId,
932
- tenantId: meta.tenantId,
933
- userId: meta.userId,
934
- modelName: message.modelName,
935
- queryKey: message.queryKey,
936
- query: message.query,
937
- options,
938
- dependencyModelNames
939
- });
940
- try {
941
- const modelNamesToWatch = /* @__PURE__ */ new Set([message.modelName, ...dependencyModelNames]);
942
- for (const modelName of modelNamesToWatch) {
943
- await ensureChangeStream(meta.tenantId, modelName);
944
- }
945
- } catch (err) {
946
- const error = redactErrorMessage(err);
947
- sendWs(ws, {
948
- type: "query-payload",
949
- modelName: message.modelName,
950
- queryKey: message.queryKey,
951
- error
952
- });
953
- return;
954
- }
955
- if (!sockets.has(socketId)) {
956
- for (const modelName of /* @__PURE__ */ new Set([message.modelName, ...dependencyModelNames])) {
957
- closeUnusedChangeStream(meta.tenantId, modelName);
958
- }
959
- return;
960
- }
961
- if (message.runInitialQuery === false) {
962
- return;
963
- }
964
- const subscription = subscriptions.get(meta.tenantId)?.get(meta.userId)?.get(message.modelName)?.get(message.queryKey);
965
- if (!subscription) return;
966
- try {
967
- await runAndSendSubscriptionQuery({
968
- tenantId: meta.tenantId,
969
- targetSocketIds: [socketId],
970
- ability,
971
- modelName: message.modelName,
972
- queryKey: message.queryKey,
973
- subscription
974
- });
975
- } catch (err) {
976
- const error = redactErrorMessage(err);
977
- sendWs(ws, {
978
- type: "query-payload",
979
- modelName: message.modelName,
980
- queryKey: message.queryKey,
981
- error
982
- });
983
- }
984
- return;
985
- }
986
- try {
987
- await runAndSendQuery({
988
- tenantId: meta.tenantId,
989
- targetSocketIds: [socketId],
990
- ability,
991
- modelName: message.modelName,
992
- queryKey: message.queryKey,
993
- query: message.query,
994
- options
995
- });
996
- } catch (err) {
997
- const error = redactErrorMessage(err);
998
- sendWs(ws, {
999
- type: "query-payload",
1000
- modelName: message.modelName,
1001
- queryKey: message.queryKey,
1002
- error
1003
- });
1004
- }
1005
- return;
1006
- }
1007
- if (message.type === "register-count" || message.type === "run-count") {
1008
- if (message.type === "register-count") {
1009
- const existing = socketCountSubscriptions.get(socketId)?.get(message.modelName)?.has(message.queryKey) ?? false;
1010
- if (!existing && getSocketSubscriptionCount(socketId) >= maxSubscriptionsPerSocket) {
1011
- sendWs(ws, {
1012
- type: "count-payload",
1013
- modelName: message.modelName,
1014
- queryKey: message.queryKey,
1015
- error: "Too many subscriptions"
1016
- });
1017
- return;
1018
- }
1019
- addSocketCountSubscription({
1020
- socketId,
1021
- tenantId: meta.tenantId,
1022
- userId: meta.userId,
1023
- modelName: message.modelName,
1024
- queryKey: message.queryKey,
1025
- query: message.query
1026
- });
1027
- try {
1028
- await ensureChangeStream(meta.tenantId, message.modelName);
1029
- } catch (err) {
1030
- const error = redactErrorMessage(err);
1031
- sendWs(ws, {
1032
- type: "count-payload",
1033
- modelName: message.modelName,
1034
- queryKey: message.queryKey,
1035
- error
1036
- });
1037
- return;
1038
- }
1039
- if (!sockets.has(socketId)) {
1040
- closeUnusedChangeStream(meta.tenantId, message.modelName);
1041
- return;
1042
- }
1043
- if (message.runInitialQuery === false) {
1044
- return;
1045
- }
1046
- }
1047
- try {
1048
- await runAndSendCount({
1049
- tenantId: meta.tenantId,
1050
- targetSocketIds: [socketId],
1051
- ability,
1052
- modelName: message.modelName,
1053
- queryKey: message.queryKey,
1054
- query: message.query
1055
- });
1056
- } catch (err) {
1057
- const error = redactErrorMessage(err);
1058
- sendWs(ws, {
1059
- type: "count-payload",
1060
- modelName: message.modelName,
1061
- queryKey: message.queryKey,
1062
- error
1063
- });
1064
- }
1065
- }
593
+ var cleanupSocket = (socketId) => {
594
+ const meta = socketMeta.get(socketId);
595
+ if (meta) {
596
+ const byModel = socketSubscriptions.get(socketId);
597
+ if (byModel) for (const [modelName, keys] of byModel.entries()) for (const queryKey of keys.values()) removeSocketSubscription({
598
+ socketId,
599
+ tenantId: meta.tenantId,
600
+ userId: meta.userId,
601
+ modelName,
602
+ queryKey
603
+ });
604
+ const countByModel = socketCountSubscriptions.get(socketId);
605
+ if (countByModel) for (const [modelName, keys] of countByModel.entries()) for (const queryKey of keys.values()) removeSocketCountSubscription({
606
+ socketId,
607
+ tenantId: meta.tenantId,
608
+ userId: meta.userId,
609
+ modelName,
610
+ queryKey
611
+ });
612
+ }
613
+ socketSubscriptions.delete(socketId);
614
+ socketCountSubscriptions.delete(socketId);
615
+ const cleanupFns = socketCleanup.get(socketId) ?? [];
616
+ socketCleanup.delete(socketId);
617
+ for (const fn of cleanupFns) try {
618
+ fn();
619
+ } catch {}
620
+ sockets.delete(socketId);
621
+ socketMeta.delete(socketId);
622
+ socketWrappers.delete(socketId);
1066
623
  };
1067
- const initRts = ({
1068
- server,
1069
- path = "/rts",
1070
- sessionMiddleware,
1071
- maxPayloadBytes: maxPayloadBytesArg,
1072
- maxSubscriptionsPerSocket: maxSubscriptionsPerSocketArg,
1073
- dispatchDebounceMs: dispatchDebounceMsArg,
1074
- allowInternalModels: allowInternalModelsArg
1075
- }) => {
1076
- if (initializedServers.has(server)) return;
1077
- initializedServers.add(server);
1078
- if (typeof maxPayloadBytesArg === "number" && Number.isFinite(maxPayloadBytesArg) && maxPayloadBytesArg > 0) {
1079
- maxPayloadBytes = Math.floor(maxPayloadBytesArg);
1080
- }
1081
- if (typeof maxSubscriptionsPerSocketArg === "number" && Number.isFinite(maxSubscriptionsPerSocketArg) && maxSubscriptionsPerSocketArg > 0) {
1082
- maxSubscriptionsPerSocket = Math.floor(maxSubscriptionsPerSocketArg);
1083
- }
1084
- if (typeof dispatchDebounceMsArg === "number" && Number.isFinite(dispatchDebounceMsArg) && dispatchDebounceMsArg >= 0) {
1085
- dispatchDebounceMs = Math.floor(dispatchDebounceMsArg);
1086
- }
1087
- allowInternalModels = Boolean(allowInternalModelsArg);
1088
- const wss = new WebSocketServer({
1089
- noServer: true,
1090
- maxPayload: maxPayloadBytes
1091
- });
1092
- server.on("upgrade", (req, socket, head) => {
1093
- ensureSocketErrorHandler(socket, () => ({
1094
- upgradeHost: req.headers.host ?? "",
1095
- upgradeUrl: req.url ?? "",
1096
- userAgent: typeof req.headers["user-agent"] === "string" ? req.headers["user-agent"] : ""
1097
- }));
1098
- upgradeMeta.delete(req);
1099
- let url;
1100
- try {
1101
- url = new URL(req.url ?? "", `http://${req.headers.host ?? "localhost"}`);
1102
- } catch {
1103
- badRequest(socket, "Invalid URL");
1104
- return;
1105
- }
1106
- if (url.pathname !== path) return;
1107
- void (async () => {
1108
- try {
1109
- const meta = await parseUpgradeMeta({
1110
- req,
1111
- url,
1112
- sessionMiddleware
1113
- });
1114
- upgradeMeta.set(req, meta);
1115
- wss.handleUpgrade(req, socket, head, (ws) => {
1116
- wss.emit("connection", ws, req);
1117
- });
1118
- } catch (err) {
1119
- const message = err instanceof Error ? err.message : "RTS upgrade failed";
1120
- const upgradeReq = req;
1121
- const sessionUser = upgradeReq.session?.user;
1122
- const sessionUserId = typeof sessionUser?.id === "string" ? sessionUser.id.trim() : "";
1123
- const sessionTenantId = typeof sessionUser?.currentTenantId === "string" ? sessionUser.currentTenantId.trim() : "";
1124
- const authenticatedUserIdHeader = req.headers["rb-authenticated-user-id"];
1125
- const authenticatedTenantIdHeader = req.headers["rb-authenticated-tenant-id"];
1126
- console.warn("[rb/rts] upgrade rejected", {
1127
- reason: message,
1128
- host: req.headers.host ?? "",
1129
- url: req.url ?? "",
1130
- hasSessionUserId: Boolean(sessionUserId),
1131
- hasCurrentTenantId: Boolean(sessionTenantId),
1132
- hasAuthenticatedUserIdHeader: Boolean(Array.isArray(authenticatedUserIdHeader) ? authenticatedUserIdHeader[0] : authenticatedUserIdHeader),
1133
- hasAuthenticatedTenantIdHeader: Boolean(Array.isArray(authenticatedTenantIdHeader) ? authenticatedTenantIdHeader[0] : authenticatedTenantIdHeader),
1134
- userAgent: typeof req.headers["user-agent"] === "string" ? req.headers["user-agent"] : ""
1135
- });
1136
- if (message === "Missing current tenant in session") {
1137
- badRequest(socket, message);
1138
- return;
1139
- }
1140
- unauthorized(socket, message);
1141
- return;
1142
- }
1143
- })().catch(() => {
1144
- badRequest(socket, "RTS upgrade failed");
1145
- });
1146
- });
1147
- wss.on("connection", (ws, req) => {
1148
- const meta = upgradeMeta.get(req);
1149
- upgradeMeta.delete(req);
1150
- if (!meta) {
1151
- try {
1152
- ws.close();
1153
- } catch {
1154
- }
1155
- return;
1156
- }
1157
- const socketId = randomUUID();
1158
- sockets.set(socketId, ws);
1159
- socketMeta.set(socketId, meta);
1160
- const wrapper = new RtsSocket({
1161
- id: socketId,
1162
- ws,
1163
- meta
1164
- });
1165
- socketWrappers.set(socketId, wrapper);
1166
- const cleanupFns = [];
1167
- for (const handler of customHandlers) {
1168
- try {
1169
- const cleanup = handler(wrapper);
1170
- if (typeof cleanup === "function") cleanupFns.push(cleanup);
1171
- } catch {
1172
- }
1173
- }
1174
- if (cleanupFns.length) socketCleanup.set(socketId, cleanupFns);
1175
- let messageQueue = Promise.resolve();
1176
- ws.on("message", (raw) => {
1177
- let parsed;
1178
- try {
1179
- parsed = safeJsonParse(raw);
1180
- } catch {
1181
- return;
1182
- }
1183
- if (!parsed || typeof parsed !== "object") return;
1184
- const message = parsed;
1185
- if (message.type !== "event" && message.type !== "rts-message" && message.type !== "run-query" && message.type !== "register-query" && message.type !== "set-query-window" && message.type !== "remove-query" && message.type !== "run-count" && message.type !== "register-count" && message.type !== "remove-count") return;
1186
- messageQueue = messageQueue.then(() => handleClientMessage({
1187
- socketId,
1188
- meta,
1189
- message
1190
- })).catch(() => void 0);
1191
- });
1192
- ws.on("close", () => {
1193
- cleanupSocket(socketId);
1194
- });
1195
- ws.on("error", () => {
1196
- cleanupSocket(socketId);
1197
- });
1198
- });
624
+ var handleClientMessage = async ({ socketId, meta, message }) => {
625
+ const ws = sockets.get(socketId);
626
+ if (!ws) return;
627
+ if (message.type === "event") {
628
+ socketWrappers.get(socketId)?.dispatch(message.event, message.payload);
629
+ return;
630
+ }
631
+ if (message.type === "rts-message") {
632
+ socketWrappers.get(socketId)?.dispatchRtsMessage(message.channel, message.payload);
633
+ return;
634
+ }
635
+ const isCountMessage = message.type === "run-count" || message.type === "register-count" || message.type === "remove-count";
636
+ const sendAccessError = (error) => {
637
+ if (isCountMessage) {
638
+ sendWs(ws, {
639
+ type: "count-payload",
640
+ modelName: message.modelName ?? "",
641
+ queryKey: message.queryKey ?? "",
642
+ error
643
+ });
644
+ return;
645
+ }
646
+ sendWs(ws, {
647
+ type: "query-payload",
648
+ modelName: message.modelName ?? "",
649
+ queryKey: message.queryKey ?? "",
650
+ error
651
+ });
652
+ };
653
+ if (!message.modelName || typeof message.modelName !== "string") return;
654
+ if (!allowInternalModels && INTERNAL_MODEL_NAMES.has(message.modelName)) {
655
+ sendAccessError("Model not allowed");
656
+ return;
657
+ }
658
+ if (!message.queryKey || typeof message.queryKey !== "string") return;
659
+ if (message.queryKey.length > QUERY_KEY_MAX_LEN) return;
660
+ if (message.type === "remove-query") {
661
+ removeSocketSubscription({
662
+ socketId,
663
+ tenantId: meta.tenantId,
664
+ userId: meta.userId,
665
+ modelName: message.modelName,
666
+ queryKey: message.queryKey
667
+ });
668
+ return;
669
+ }
670
+ if (message.type === "remove-count") {
671
+ removeSocketCountSubscription({
672
+ socketId,
673
+ tenantId: meta.tenantId,
674
+ userId: meta.userId,
675
+ modelName: message.modelName,
676
+ queryKey: message.queryKey
677
+ });
678
+ return;
679
+ }
680
+ const ability = meta.ability;
681
+ if (!ability.can("read", message.modelName)) {
682
+ sendAccessError("forbidden");
683
+ return;
684
+ }
685
+ if (message.type === "set-query-window") {
686
+ if (!isValidQueryWindowCount(message.requestedCount)) {
687
+ sendWs(ws, {
688
+ type: "query-payload",
689
+ modelName: message.modelName,
690
+ queryKey: message.queryKey,
691
+ error: "Invalid requestedCount"
692
+ });
693
+ return;
694
+ }
695
+ const subscription = subscriptions.get(meta.tenantId)?.get(meta.userId)?.get(message.modelName)?.get(message.queryKey);
696
+ const window = subscription?.windows.get(socketId);
697
+ if (!subscription || !subscription.options.pagination || !window) {
698
+ sendWs(ws, {
699
+ type: "query-payload",
700
+ modelName: message.modelName,
701
+ queryKey: message.queryKey,
702
+ error: "Paginated query subscription not found"
703
+ });
704
+ return;
705
+ }
706
+ window.requestedCount = message.requestedCount;
707
+ await runAndSendSubscriptionQuery({
708
+ tenantId: meta.tenantId,
709
+ targetSocketIds: [socketId],
710
+ ability,
711
+ modelName: message.modelName,
712
+ queryKey: message.queryKey,
713
+ subscription
714
+ });
715
+ return;
716
+ }
717
+ if (!message.query || typeof message.query !== "object") return;
718
+ if (message.type === "register-query" || message.type === "run-query") {
719
+ const options = normalizeRtsQueryOptions(message.options);
720
+ if (message.type === "register-query") {
721
+ if (options.pagination && !isValidQueryWindowCount(options.pagination.limit)) {
722
+ sendWs(ws, {
723
+ type: "query-payload",
724
+ modelName: message.modelName,
725
+ queryKey: message.queryKey,
726
+ error: "Invalid requestedCount"
727
+ });
728
+ return;
729
+ }
730
+ if (!(socketSubscriptions.get(socketId)?.get(message.modelName)?.has(message.queryKey) ?? false) && getSocketSubscriptionCount(socketId) >= maxSubscriptionsPerSocket) {
731
+ sendWs(ws, {
732
+ type: "query-payload",
733
+ modelName: message.modelName,
734
+ queryKey: message.queryKey,
735
+ error: "Too many subscriptions"
736
+ });
737
+ return;
738
+ }
739
+ let dependencyModelNames = [];
740
+ if (options.populate !== void 0) try {
741
+ dependencyModelNames = await resolveRtsQueryDependencyModelNames({
742
+ tenantId: meta.tenantId,
743
+ ability,
744
+ modelName: message.modelName,
745
+ options,
746
+ allowInternalModels
747
+ });
748
+ } catch (err) {
749
+ const error = redactErrorMessage(err);
750
+ sendWs(ws, {
751
+ type: "query-payload",
752
+ modelName: message.modelName,
753
+ queryKey: message.queryKey,
754
+ error
755
+ });
756
+ return;
757
+ }
758
+ if (!sockets.has(socketId)) return;
759
+ addSocketSubscription({
760
+ socketId,
761
+ tenantId: meta.tenantId,
762
+ userId: meta.userId,
763
+ modelName: message.modelName,
764
+ queryKey: message.queryKey,
765
+ query: message.query,
766
+ options,
767
+ dependencyModelNames
768
+ });
769
+ try {
770
+ const modelNamesToWatch = /* @__PURE__ */ new Set([message.modelName, ...dependencyModelNames]);
771
+ for (const modelName of modelNamesToWatch) await ensureChangeStream(meta.tenantId, modelName);
772
+ } catch (err) {
773
+ const error = redactErrorMessage(err);
774
+ sendWs(ws, {
775
+ type: "query-payload",
776
+ modelName: message.modelName,
777
+ queryKey: message.queryKey,
778
+ error
779
+ });
780
+ return;
781
+ }
782
+ if (!sockets.has(socketId)) {
783
+ for (const modelName of /* @__PURE__ */ new Set([message.modelName, ...dependencyModelNames])) closeUnusedChangeStream(meta.tenantId, modelName);
784
+ return;
785
+ }
786
+ if (message.runInitialQuery === false) return;
787
+ const subscription = subscriptions.get(meta.tenantId)?.get(meta.userId)?.get(message.modelName)?.get(message.queryKey);
788
+ if (!subscription) return;
789
+ try {
790
+ await runAndSendSubscriptionQuery({
791
+ tenantId: meta.tenantId,
792
+ targetSocketIds: [socketId],
793
+ ability,
794
+ modelName: message.modelName,
795
+ queryKey: message.queryKey,
796
+ subscription
797
+ });
798
+ } catch (err) {
799
+ const error = redactErrorMessage(err);
800
+ sendWs(ws, {
801
+ type: "query-payload",
802
+ modelName: message.modelName,
803
+ queryKey: message.queryKey,
804
+ error
805
+ });
806
+ }
807
+ return;
808
+ }
809
+ try {
810
+ await runAndSendQuery({
811
+ tenantId: meta.tenantId,
812
+ targetSocketIds: [socketId],
813
+ ability,
814
+ modelName: message.modelName,
815
+ queryKey: message.queryKey,
816
+ query: message.query,
817
+ options
818
+ });
819
+ } catch (err) {
820
+ const error = redactErrorMessage(err);
821
+ sendWs(ws, {
822
+ type: "query-payload",
823
+ modelName: message.modelName,
824
+ queryKey: message.queryKey,
825
+ error
826
+ });
827
+ }
828
+ return;
829
+ }
830
+ if (message.type === "register-count" || message.type === "run-count") {
831
+ if (message.type === "register-count") {
832
+ if (!(socketCountSubscriptions.get(socketId)?.get(message.modelName)?.has(message.queryKey) ?? false) && getSocketSubscriptionCount(socketId) >= maxSubscriptionsPerSocket) {
833
+ sendWs(ws, {
834
+ type: "count-payload",
835
+ modelName: message.modelName,
836
+ queryKey: message.queryKey,
837
+ error: "Too many subscriptions"
838
+ });
839
+ return;
840
+ }
841
+ addSocketCountSubscription({
842
+ socketId,
843
+ tenantId: meta.tenantId,
844
+ userId: meta.userId,
845
+ modelName: message.modelName,
846
+ queryKey: message.queryKey,
847
+ query: message.query
848
+ });
849
+ try {
850
+ await ensureChangeStream(meta.tenantId, message.modelName);
851
+ } catch (err) {
852
+ const error = redactErrorMessage(err);
853
+ sendWs(ws, {
854
+ type: "count-payload",
855
+ modelName: message.modelName,
856
+ queryKey: message.queryKey,
857
+ error
858
+ });
859
+ return;
860
+ }
861
+ if (!sockets.has(socketId)) {
862
+ closeUnusedChangeStream(meta.tenantId, message.modelName);
863
+ return;
864
+ }
865
+ if (message.runInitialQuery === false) return;
866
+ }
867
+ try {
868
+ await runAndSendCount({
869
+ tenantId: meta.tenantId,
870
+ targetSocketIds: [socketId],
871
+ ability,
872
+ modelName: message.modelName,
873
+ queryKey: message.queryKey,
874
+ query: message.query
875
+ });
876
+ } catch (err) {
877
+ const error = redactErrorMessage(err);
878
+ sendWs(ws, {
879
+ type: "count-payload",
880
+ modelName: message.modelName,
881
+ queryKey: message.queryKey,
882
+ error
883
+ });
884
+ }
885
+ }
1199
886
  };
1200
- const registerRtsHandler = (handler) => {
1201
- customHandlers.push(handler);
887
+ var initRts = ({ server, path = "/rts", sessionMiddleware, postSyncSessionMiddleware, maxPayloadBytes: maxPayloadBytesArg, maxSubscriptionsPerSocket: maxSubscriptionsPerSocketArg, dispatchDebounceMs: dispatchDebounceMsArg, allowInternalModels: allowInternalModelsArg }) => {
888
+ if (initializedServers.has(server)) return;
889
+ initializedServers.add(server);
890
+ if (typeof maxPayloadBytesArg === "number" && Number.isFinite(maxPayloadBytesArg) && maxPayloadBytesArg > 0) maxPayloadBytes = Math.floor(maxPayloadBytesArg);
891
+ if (typeof maxSubscriptionsPerSocketArg === "number" && Number.isFinite(maxSubscriptionsPerSocketArg) && maxSubscriptionsPerSocketArg > 0) maxSubscriptionsPerSocket = Math.floor(maxSubscriptionsPerSocketArg);
892
+ if (typeof dispatchDebounceMsArg === "number" && Number.isFinite(dispatchDebounceMsArg) && dispatchDebounceMsArg >= 0) dispatchDebounceMs = Math.floor(dispatchDebounceMsArg);
893
+ allowInternalModels = Boolean(allowInternalModelsArg);
894
+ const wss = new WebSocketServer({
895
+ noServer: true,
896
+ maxPayload: maxPayloadBytes
897
+ });
898
+ server.on("upgrade", (req, socket, head) => {
899
+ ensureSocketErrorHandler(socket, () => ({
900
+ upgradeHost: req.headers.host ?? "",
901
+ upgradeUrl: req.url ?? "",
902
+ userAgent: typeof req.headers["user-agent"] === "string" ? req.headers["user-agent"] : ""
903
+ }));
904
+ upgradeMeta.delete(req);
905
+ let url;
906
+ try {
907
+ url = new URL(req.url ?? "", `http://${req.headers.host ?? "localhost"}`);
908
+ } catch {
909
+ badRequest(socket, "Invalid URL");
910
+ return;
911
+ }
912
+ if (url.pathname !== path) return;
913
+ (async () => {
914
+ try {
915
+ const meta = await parseUpgradeMeta({
916
+ req,
917
+ url,
918
+ sessionMiddleware,
919
+ postSyncSessionMiddleware
920
+ });
921
+ upgradeMeta.set(req, meta);
922
+ wss.handleUpgrade(req, socket, head, (ws) => {
923
+ wss.emit("connection", ws, req);
924
+ });
925
+ } catch (err) {
926
+ const message = err instanceof Error ? err.message : "RTS upgrade failed";
927
+ const sessionUser = req.session?.user;
928
+ const sessionUserId = typeof sessionUser?.id === "string" ? sessionUser.id.trim() : "";
929
+ const sessionTenantId = typeof sessionUser?.currentTenantId === "string" ? sessionUser.currentTenantId.trim() : "";
930
+ const authenticatedUserIdHeader = req.headers["rb-authenticated-user-id"];
931
+ const authenticatedTenantIdHeader = req.headers["rb-authenticated-tenant-id"];
932
+ console.warn("[rb/rts] upgrade rejected", {
933
+ reason: message,
934
+ host: req.headers.host ?? "",
935
+ url: req.url ?? "",
936
+ hasSessionUserId: Boolean(sessionUserId),
937
+ hasCurrentTenantId: Boolean(sessionTenantId),
938
+ hasAuthenticatedUserIdHeader: Boolean(Array.isArray(authenticatedUserIdHeader) ? authenticatedUserIdHeader[0] : authenticatedUserIdHeader),
939
+ hasAuthenticatedTenantIdHeader: Boolean(Array.isArray(authenticatedTenantIdHeader) ? authenticatedTenantIdHeader[0] : authenticatedTenantIdHeader),
940
+ userAgent: typeof req.headers["user-agent"] === "string" ? req.headers["user-agent"] : ""
941
+ });
942
+ if (message === "Missing current tenant in session") {
943
+ badRequest(socket, message);
944
+ return;
945
+ }
946
+ unauthorized(socket, message);
947
+ return;
948
+ }
949
+ })().catch(() => {
950
+ badRequest(socket, "RTS upgrade failed");
951
+ });
952
+ });
953
+ wss.on("connection", (ws, req) => {
954
+ const meta = upgradeMeta.get(req);
955
+ upgradeMeta.delete(req);
956
+ if (!meta) {
957
+ try {
958
+ ws.close();
959
+ } catch {}
960
+ return;
961
+ }
962
+ const socketId = randomUUID();
963
+ sockets.set(socketId, ws);
964
+ socketMeta.set(socketId, meta);
965
+ const wrapper = new RtsSocket({
966
+ id: socketId,
967
+ ws,
968
+ meta
969
+ });
970
+ socketWrappers.set(socketId, wrapper);
971
+ const cleanupFns = [];
972
+ for (const handler of customHandlers) try {
973
+ const cleanup = handler(wrapper);
974
+ if (typeof cleanup === "function") cleanupFns.push(cleanup);
975
+ } catch {}
976
+ if (cleanupFns.length) socketCleanup.set(socketId, cleanupFns);
977
+ let messageQueue = Promise.resolve();
978
+ ws.on("message", (raw) => {
979
+ let parsed;
980
+ try {
981
+ parsed = safeJsonParse(raw);
982
+ } catch {
983
+ return;
984
+ }
985
+ if (!parsed || typeof parsed !== "object") return;
986
+ const message = parsed;
987
+ if (message.type !== "event" && message.type !== "rts-message" && message.type !== "run-query" && message.type !== "register-query" && message.type !== "set-query-window" && message.type !== "remove-query" && message.type !== "run-count" && message.type !== "register-count" && message.type !== "remove-count") return;
988
+ messageQueue = messageQueue.then(() => handleClientMessage({
989
+ socketId,
990
+ meta,
991
+ message
992
+ })).catch(() => void 0);
993
+ });
994
+ ws.on("close", () => {
995
+ cleanupSocket(socketId);
996
+ });
997
+ ws.on("error", () => {
998
+ cleanupSocket(socketId);
999
+ });
1000
+ });
1202
1001
  };
1203
- const publishRtsMessage = ({
1204
- tenantId,
1205
- userId,
1206
- channel,
1207
- payload
1208
- }) => {
1209
- const normalizedTenantId = tenantId.trim();
1210
- const normalizedUserId = userId?.trim();
1211
- const normalizedChannel = channel.trim();
1212
- if (!normalizedTenantId || !normalizedChannel) return;
1213
- for (const [socketId, meta] of socketMeta.entries()) {
1214
- if (meta.tenantId !== normalizedTenantId) continue;
1215
- if (normalizedUserId && meta.userId !== normalizedUserId) continue;
1216
- const ws = sockets.get(socketId);
1217
- if (!ws) continue;
1218
- sendWs(ws, {
1219
- type: "rts-message",
1220
- channel: normalizedChannel,
1221
- payload
1222
- });
1223
- }
1002
+ var registerRtsHandler = (handler) => {
1003
+ customHandlers.push(handler);
1224
1004
  };
1225
- const notifyRtsModelChanged = (tenantId, modelName) => {
1226
- scheduleDispatchSubscriptionsForModel(tenantId, modelName);
1005
+ var publishRtsMessage = ({ tenantId, userId, channel, payload }) => {
1006
+ const normalizedTenantId = tenantId.trim();
1007
+ const normalizedUserId = userId?.trim();
1008
+ const normalizedChannel = channel.trim();
1009
+ if (!normalizedTenantId || !normalizedChannel) return;
1010
+ for (const [socketId, meta] of socketMeta.entries()) {
1011
+ if (meta.tenantId !== normalizedTenantId) continue;
1012
+ if (normalizedUserId && meta.userId !== normalizedUserId) continue;
1013
+ const ws = sockets.get(socketId);
1014
+ if (!ws) continue;
1015
+ sendWs(ws, {
1016
+ type: "rts-message",
1017
+ channel: normalizedChannel,
1018
+ payload
1019
+ });
1020
+ }
1227
1021
  };
1228
- export {
1229
- initRts,
1230
- notifyRtsModelChanged,
1231
- publishRtsMessage,
1232
- registerRtsHandler,
1233
- routes
1022
+ var notifyRtsModelChanged = (tenantId, modelName) => {
1023
+ scheduleDispatchSubscriptionsForModel(tenantId, modelName);
1234
1024
  };
1235
- //# sourceMappingURL=index.js.map
1025
+ //#endregion
1026
+ export { initRts, notifyRtsModelChanged, publishRtsMessage, registerRtsHandler, routes };
1027
+
1028
+ //# sourceMappingURL=index.js.map