@rpcbase/server 0.464.0 → 0.466.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.
@@ -0,0 +1,6 @@
1
+ type ServerEnv = {
2
+ [key: string]: string | undefined;
3
+ };
4
+ export declare const checkInitReplicaSet: (serverEnv: ServerEnv) => Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=checkInitReplicaSet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkInitReplicaSet.d.ts","sourceRoot":"","sources":["../src/checkInitReplicaSet.ts"],"names":[],"mappings":"AAGA,KAAK,SAAS,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAMtD,eAAO,MAAM,mBAAmB,GAAU,WAAW,SAAS,KAAG,OAAO,CAAC,IAAI,CAiH5E,CAAA"}
@@ -0,0 +1,490 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { loadModel } from "@rpcbase/db";
3
+ import { WebSocketServer } from "ws";
4
+ const TENANT_ID_QUERY_PARAM = "rb-tenant-id";
5
+ const USER_ID_HEADER = "rb-user-id";
6
+ const QUERY_KEY_MAX_LEN = 4096;
7
+ const QUERY_MAX_LIMIT = 4096;
8
+ const initializedServers = /* @__PURE__ */ new WeakSet();
9
+ const customHandlers = [];
10
+ const sockets = /* @__PURE__ */ new Map();
11
+ const socketMeta = /* @__PURE__ */ new Map();
12
+ const socketWrappers = /* @__PURE__ */ new Map();
13
+ const socketCleanup = /* @__PURE__ */ new Map();
14
+ const socketSubscriptions = /* @__PURE__ */ new Map();
15
+ const subscriptions = /* @__PURE__ */ new Map();
16
+ const changeStreams = /* @__PURE__ */ new Map();
17
+ class RtsSocket {
18
+ id;
19
+ tenantId;
20
+ userId;
21
+ ws;
22
+ handlers = /* @__PURE__ */ new Map();
23
+ constructor({
24
+ id,
25
+ ws,
26
+ meta
27
+ }) {
28
+ this.id = id;
29
+ this.ws = ws;
30
+ this.tenantId = meta.tenantId;
31
+ this.userId = meta.userId;
32
+ }
33
+ on(event, handler) {
34
+ const set = this.handlers.get(event) ?? /* @__PURE__ */ new Set();
35
+ set.add(handler);
36
+ this.handlers.set(event, set);
37
+ return () => this.off(event, handler);
38
+ }
39
+ off(event, handler) {
40
+ const set = this.handlers.get(event);
41
+ if (!set) return;
42
+ set.delete(handler);
43
+ if (!set.size) this.handlers.delete(event);
44
+ }
45
+ emit(event, payload) {
46
+ sendWs(this.ws, { type: "event", event, payload });
47
+ }
48
+ close() {
49
+ try {
50
+ this.ws.close();
51
+ } catch {
52
+ }
53
+ }
54
+ dispatch(event, payload) {
55
+ const set = this.handlers.get(event);
56
+ if (!set) return;
57
+ for (const handler of set) {
58
+ handler(payload);
59
+ }
60
+ }
61
+ }
62
+ const rawToText = (raw) => {
63
+ if (typeof raw === "string") return raw;
64
+ if (raw instanceof ArrayBuffer) return Buffer.from(raw).toString();
65
+ if (Array.isArray(raw)) return Buffer.concat(raw).toString();
66
+ return raw.toString();
67
+ };
68
+ const safeJsonParse = (raw) => JSON.parse(rawToText(raw));
69
+ const sendWs = (ws, message) => {
70
+ if (ws.readyState !== 1) return;
71
+ ws.send(JSON.stringify(message));
72
+ };
73
+ const unauthorized = (socket, message = "Unauthorized") => {
74
+ try {
75
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
76
+ socket.write(`Error: ${message}\r
77
+ `);
78
+ socket.end();
79
+ } catch {
80
+ socket.destroy();
81
+ }
82
+ };
83
+ const badRequest = (socket, message = "Bad Request") => {
84
+ try {
85
+ socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
86
+ socket.write(`Error: ${message}\r
87
+ `);
88
+ socket.end();
89
+ } catch {
90
+ socket.destroy();
91
+ }
92
+ };
93
+ const runSessionMiddleware = async (sessionMiddleware, req) => {
94
+ await new Promise((resolve, reject) => {
95
+ sessionMiddleware(req, {}, (err) => {
96
+ if (err) reject(err);
97
+ else resolve();
98
+ });
99
+ });
100
+ };
101
+ const parseUpgradeMeta = async ({
102
+ req,
103
+ url,
104
+ sessionMiddleware
105
+ }) => {
106
+ const tenantId = url.searchParams.get(TENANT_ID_QUERY_PARAM);
107
+ if (!tenantId) {
108
+ throw new Error("Missing rb-tenant-id query parameter");
109
+ }
110
+ const raw = req.headers[USER_ID_HEADER];
111
+ const headerUserId = Array.isArray(raw) ? raw[0] : raw;
112
+ if (headerUserId) return { tenantId, userId: headerUserId };
113
+ if (!sessionMiddleware) {
114
+ throw new Error("Missing rb-user-id header (reverse-proxy) and no session middleware configured");
115
+ }
116
+ const upgradeReq = req;
117
+ try {
118
+ await runSessionMiddleware(sessionMiddleware, upgradeReq);
119
+ } catch {
120
+ throw new Error("Failed to load session for RTS");
121
+ }
122
+ const sessionUser = upgradeReq.session?.user;
123
+ const sessionUserId = sessionUser?.id;
124
+ if (!sessionUserId) {
125
+ throw new Error("Not signed in (missing session.user.id)");
126
+ }
127
+ const signedInTenants = sessionUser?.signed_in_tenants;
128
+ const currentTenantId = sessionUser?.current_tenant_id;
129
+ if (Array.isArray(signedInTenants) && signedInTenants.length > 0) {
130
+ if (!signedInTenants.includes(tenantId)) {
131
+ throw new Error("Tenant not authorized for this session");
132
+ }
133
+ } else if (currentTenantId) {
134
+ if (currentTenantId !== tenantId) {
135
+ throw new Error("Tenant not authorized for this session");
136
+ }
137
+ } else {
138
+ throw new Error("Tenant not authorized for this session");
139
+ }
140
+ return { tenantId, userId: sessionUserId };
141
+ };
142
+ const getTenantModel = async (tenantId, modelName) => {
143
+ const ctx = {
144
+ req: {
145
+ session: {
146
+ user: {
147
+ current_tenant_id: tenantId
148
+ }
149
+ }
150
+ }
151
+ };
152
+ return loadModel(modelName, ctx);
153
+ };
154
+ const normalizeLimit = (limit) => {
155
+ if (typeof limit !== "number") return QUERY_MAX_LIMIT;
156
+ if (!Number.isFinite(limit)) return QUERY_MAX_LIMIT;
157
+ return Math.min(QUERY_MAX_LIMIT, Math.abs(limit));
158
+ };
159
+ const normalizeOptions = (options) => {
160
+ if (!options || typeof options !== "object") return {};
161
+ const normalized = {};
162
+ if (options.projection && typeof options.projection === "object") {
163
+ normalized.projection = options.projection;
164
+ }
165
+ if (options.sort && typeof options.sort === "object") {
166
+ normalized.sort = options.sort;
167
+ }
168
+ normalized.limit = normalizeLimit(options.limit);
169
+ return normalized;
170
+ };
171
+ const runAndSendQuery = async ({
172
+ tenantId,
173
+ targetSocketIds,
174
+ modelName,
175
+ queryKey,
176
+ query,
177
+ options
178
+ }) => {
179
+ const model = await getTenantModel(tenantId, modelName);
180
+ const projection = options.projection ?? void 0;
181
+ const sort = options.sort;
182
+ const limit = normalizeLimit(options.limit);
183
+ const queryPromise = model.find(query, projection);
184
+ if (sort && Object.keys(sort).length) {
185
+ queryPromise.sort(sort);
186
+ }
187
+ queryPromise.limit(limit);
188
+ const data = await queryPromise;
189
+ const payload = { type: "query_payload", modelName, queryKey, data };
190
+ for (const socketId of targetSocketIds) {
191
+ const ws = sockets.get(socketId);
192
+ if (!ws) continue;
193
+ sendWs(ws, payload);
194
+ }
195
+ };
196
+ const dispatchSubscriptionsForModel = async (tenantId, modelName) => {
197
+ const tenantSubs = subscriptions.get(tenantId);
198
+ const modelSubs = tenantSubs?.get(modelName);
199
+ if (!modelSubs || !modelSubs.size) return;
200
+ for (const [queryKey, sub] of modelSubs.entries()) {
201
+ const targetSocketIds = Array.from(sub.socketIds);
202
+ if (!targetSocketIds.length) continue;
203
+ try {
204
+ await runAndSendQuery({
205
+ tenantId,
206
+ targetSocketIds,
207
+ modelName,
208
+ queryKey,
209
+ query: sub.query,
210
+ options: sub.options
211
+ });
212
+ } catch (err) {
213
+ const error = err instanceof Error ? err.message : "Unknown error";
214
+ const payload = { type: "query_payload", modelName, queryKey, error };
215
+ for (const socketId of targetSocketIds) {
216
+ const ws = sockets.get(socketId);
217
+ if (!ws) continue;
218
+ sendWs(ws, payload);
219
+ }
220
+ }
221
+ }
222
+ };
223
+ const ensureChangeStream = async (tenantId, modelName) => {
224
+ const tenantStreams = changeStreams.get(tenantId) ?? /* @__PURE__ */ new Map();
225
+ changeStreams.set(tenantId, tenantStreams);
226
+ if (tenantStreams.has(modelName)) return;
227
+ const model = await getTenantModel(tenantId, modelName);
228
+ const stream = model.watch([], {
229
+ fullDocument: "updateLookup"
230
+ });
231
+ stream.on("change", () => {
232
+ void dispatchSubscriptionsForModel(tenantId, modelName);
233
+ });
234
+ stream.on("close", () => {
235
+ const map = changeStreams.get(tenantId);
236
+ map?.delete(modelName);
237
+ if (map && map.size === 0) changeStreams.delete(tenantId);
238
+ });
239
+ stream.on("error", () => {
240
+ try {
241
+ stream.close();
242
+ } catch {
243
+ }
244
+ });
245
+ tenantStreams.set(modelName, stream);
246
+ };
247
+ const addSocketSubscription = ({
248
+ socketId,
249
+ tenantId,
250
+ modelName,
251
+ queryKey,
252
+ query,
253
+ options
254
+ }) => {
255
+ const tenantSubs = subscriptions.get(tenantId) ?? /* @__PURE__ */ new Map();
256
+ subscriptions.set(tenantId, tenantSubs);
257
+ const modelSubs = tenantSubs.get(modelName) ?? /* @__PURE__ */ new Map();
258
+ tenantSubs.set(modelName, modelSubs);
259
+ const existing = modelSubs.get(queryKey);
260
+ if (existing) {
261
+ existing.socketIds.add(socketId);
262
+ } else {
263
+ modelSubs.set(queryKey, {
264
+ query,
265
+ options,
266
+ socketIds: /* @__PURE__ */ new Set([socketId])
267
+ });
268
+ }
269
+ const byModel = socketSubscriptions.get(socketId) ?? /* @__PURE__ */ new Map();
270
+ socketSubscriptions.set(socketId, byModel);
271
+ const querySet = byModel.get(modelName) ?? /* @__PURE__ */ new Set();
272
+ byModel.set(modelName, querySet);
273
+ querySet.add(queryKey);
274
+ };
275
+ const removeSocketSubscription = ({
276
+ socketId,
277
+ tenantId,
278
+ modelName,
279
+ queryKey
280
+ }) => {
281
+ const tenantSubs = subscriptions.get(tenantId);
282
+ const modelSubs = tenantSubs?.get(modelName);
283
+ const sub = modelSubs?.get(queryKey);
284
+ if (sub) {
285
+ sub.socketIds.delete(socketId);
286
+ if (!sub.socketIds.size) {
287
+ modelSubs?.delete(queryKey);
288
+ }
289
+ }
290
+ const byModel = socketSubscriptions.get(socketId);
291
+ const set = byModel?.get(modelName);
292
+ if (set) {
293
+ set.delete(queryKey);
294
+ if (!set.size) {
295
+ byModel?.delete(modelName);
296
+ }
297
+ }
298
+ if (modelSubs && modelSubs.size === 0) {
299
+ tenantSubs?.delete(modelName);
300
+ const tenantStreams = changeStreams.get(tenantId);
301
+ const stream = tenantStreams?.get(modelName);
302
+ if (stream) {
303
+ try {
304
+ stream.close();
305
+ } catch {
306
+ }
307
+ tenantStreams?.delete(modelName);
308
+ if (tenantStreams && tenantStreams.size === 0) changeStreams.delete(tenantId);
309
+ }
310
+ }
311
+ if (tenantSubs && tenantSubs.size === 0) subscriptions.delete(tenantId);
312
+ if (byModel && byModel.size === 0) socketSubscriptions.delete(socketId);
313
+ };
314
+ const cleanupSocket = (socketId) => {
315
+ const meta = socketMeta.get(socketId);
316
+ if (meta) {
317
+ const byModel = socketSubscriptions.get(socketId);
318
+ if (byModel) {
319
+ for (const [modelName, keys] of byModel.entries()) {
320
+ for (const queryKey of keys.values()) {
321
+ removeSocketSubscription({
322
+ socketId,
323
+ tenantId: meta.tenantId,
324
+ modelName,
325
+ queryKey
326
+ });
327
+ }
328
+ }
329
+ }
330
+ }
331
+ socketSubscriptions.delete(socketId);
332
+ const cleanupFns = socketCleanup.get(socketId) ?? [];
333
+ socketCleanup.delete(socketId);
334
+ for (const fn of cleanupFns) {
335
+ try {
336
+ fn();
337
+ } catch {
338
+ }
339
+ }
340
+ sockets.delete(socketId);
341
+ socketMeta.delete(socketId);
342
+ socketWrappers.delete(socketId);
343
+ };
344
+ const handleClientMessage = async ({
345
+ socketId,
346
+ meta,
347
+ message
348
+ }) => {
349
+ const ws = sockets.get(socketId);
350
+ if (!ws) return;
351
+ if (message.type === "event") {
352
+ const wrapper = socketWrappers.get(socketId);
353
+ wrapper?.dispatch(message.event, message.payload);
354
+ return;
355
+ }
356
+ if (!message.modelName || typeof message.modelName !== "string") return;
357
+ if (!message.queryKey || typeof message.queryKey !== "string") return;
358
+ if (message.queryKey.length > QUERY_KEY_MAX_LEN) return;
359
+ if (message.type === "remove_query") {
360
+ removeSocketSubscription({
361
+ socketId,
362
+ tenantId: meta.tenantId,
363
+ modelName: message.modelName,
364
+ queryKey: message.queryKey
365
+ });
366
+ return;
367
+ }
368
+ if (!message.query || typeof message.query !== "object") return;
369
+ const options = normalizeOptions(message.options);
370
+ if (message.type === "registerQuery") {
371
+ addSocketSubscription({
372
+ socketId,
373
+ tenantId: meta.tenantId,
374
+ modelName: message.modelName,
375
+ queryKey: message.queryKey,
376
+ query: message.query,
377
+ options
378
+ });
379
+ try {
380
+ await ensureChangeStream(meta.tenantId, message.modelName);
381
+ } catch (err) {
382
+ const error = err instanceof Error ? err.message : "Unable to initialize change stream";
383
+ sendWs(ws, { type: "query_payload", modelName: message.modelName, queryKey: message.queryKey, error });
384
+ return;
385
+ }
386
+ }
387
+ try {
388
+ await runAndSendQuery({
389
+ tenantId: meta.tenantId,
390
+ targetSocketIds: [socketId],
391
+ modelName: message.modelName,
392
+ queryKey: message.queryKey,
393
+ query: message.query,
394
+ options
395
+ });
396
+ } catch (err) {
397
+ const error = err instanceof Error ? err.message : "Unknown error";
398
+ sendWs(ws, { type: "query_payload", modelName: message.modelName, queryKey: message.queryKey, error });
399
+ }
400
+ };
401
+ const initRts = ({
402
+ server,
403
+ path = "/rts",
404
+ sessionMiddleware
405
+ }) => {
406
+ if (initializedServers.has(server)) return;
407
+ initializedServers.add(server);
408
+ const wss = new WebSocketServer({ noServer: true });
409
+ server.on("upgrade", (req, socket, head) => {
410
+ let url;
411
+ try {
412
+ url = new URL(req.url ?? "", `http://${req.headers.host ?? "localhost"}`);
413
+ } catch {
414
+ badRequest(socket, "Invalid URL");
415
+ return;
416
+ }
417
+ if (url.pathname !== path) return;
418
+ void (async () => {
419
+ try {
420
+ const meta = await parseUpgradeMeta({ req, url, sessionMiddleware });
421
+ req.__rb_rts_meta = meta;
422
+ wss.handleUpgrade(req, socket, head, (ws) => {
423
+ wss.emit("connection", ws, req);
424
+ });
425
+ } catch (err) {
426
+ const message = err instanceof Error ? err.message : "RTS upgrade failed";
427
+ if (message.startsWith("Missing rb-tenant-id")) {
428
+ badRequest(socket, message);
429
+ return;
430
+ }
431
+ unauthorized(socket, message);
432
+ return;
433
+ }
434
+ })().catch(() => {
435
+ badRequest(socket, "RTS upgrade failed");
436
+ });
437
+ });
438
+ wss.on("connection", (ws, req) => {
439
+ const meta = req.__rb_rts_meta;
440
+ if (!meta) {
441
+ try {
442
+ ws.close();
443
+ } catch {
444
+ }
445
+ return;
446
+ }
447
+ const socketId = randomUUID();
448
+ sockets.set(socketId, ws);
449
+ socketMeta.set(socketId, meta);
450
+ const wrapper = new RtsSocket({ id: socketId, ws, meta });
451
+ socketWrappers.set(socketId, wrapper);
452
+ const cleanupFns = [];
453
+ for (const handler of customHandlers) {
454
+ try {
455
+ const cleanup = handler(wrapper);
456
+ if (typeof cleanup === "function") cleanupFns.push(cleanup);
457
+ } catch {
458
+ }
459
+ }
460
+ if (cleanupFns.length) socketCleanup.set(socketId, cleanupFns);
461
+ ws.on("message", (raw) => {
462
+ let parsed;
463
+ try {
464
+ parsed = safeJsonParse(raw);
465
+ } catch {
466
+ return;
467
+ }
468
+ if (!parsed || typeof parsed !== "object") return;
469
+ const message = parsed;
470
+ void handleClientMessage({ socketId, meta, message });
471
+ });
472
+ ws.on("close", () => {
473
+ cleanupSocket(socketId);
474
+ });
475
+ ws.on("error", () => {
476
+ cleanupSocket(socketId);
477
+ });
478
+ });
479
+ };
480
+ const registerRtsHandler = (handler) => {
481
+ customHandlers.push(handler);
482
+ };
483
+ const notifyRtsModelChanged = (tenantId, modelName) => {
484
+ void dispatchSubscriptionsForModel(tenantId, modelName);
485
+ };
486
+ export {
487
+ initRts as i,
488
+ notifyRtsModelChanged as n,
489
+ registerRtsHandler as r
490
+ };
package/dist/index.d.ts CHANGED
@@ -4,4 +4,5 @@ export * from './hashPassword';
4
4
  export * from './passwordHashStorage';
5
5
  export * from './ssrMiddleware';
6
6
  export * from './email';
7
+ export * from './rts/index';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,uBAAuB,CAAA;AACrC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,uBAAuB,CAAA;AACrC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA"}
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import session from "express-session";
2
2
  import { RedisStore } from "connect-redis";
3
3
  import MongoStore from "connect-mongo";
4
4
  import { createClient } from "redis";
5
+ import { MongoClient } from "mongodb";
5
6
  import env from "@rpcbase/env";
6
7
  import { initApiClient, SsrErrorFallback, SSR_ERROR_STATE_GLOBAL_KEY, serializeSsrErrorState } from "@rpcbase/client";
7
8
  import { dirname, posix, sep } from "path";
@@ -18,6 +19,7 @@ import { renderToPipeableStream, renderToStaticMarkup } from "react-dom/server";
18
19
  import { jsx } from "react/jsx-runtime";
19
20
  import { createPath, matchRoutes, parsePath, createStaticRouter, StaticRouterProvider } from "@rpcbase/router";
20
21
  import { Resend } from "resend";
22
+ import { i, n, r } from "./index-BXODFGBH.js";
21
23
  function getDefaultExportFromCjs(x) {
22
24
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
23
25
  }
@@ -93,9 +95,9 @@ function requireLib() {
93
95
  }
94
96
  return ip;
95
97
  });
96
- for (var i = 0; i < forwardedIps.length; i++) {
97
- if (is.ip(forwardedIps[i])) {
98
- return forwardedIps[i];
98
+ for (var i2 = 0; i2 < forwardedIps.length; i2++) {
99
+ if (is.ip(forwardedIps[i2])) {
100
+ return forwardedIps[i2];
99
101
  }
100
102
  }
101
103
  return null;
@@ -206,8 +208,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? dirname(proces
206
208
  if (".js" === ext || ".mjs" === ext || ".cjs" === ext) file = file.slice(0, -1 * ext.length);
207
209
  const decodedFile = decodeURIComponent(file);
208
210
  if (!dir) dir = ".";
209
- const n = dir.lastIndexOf("/node_modules");
210
- if (n > -1) return `${dir.slice(n + 14).replace(/\//g, ".")}:${decodedFile}`;
211
+ const n2 = dir.lastIndexOf("/node_modules");
212
+ if (n2 > -1) return `${dir.slice(n2 + 14).replace(/\//g, ".")}:${decodedFile}`;
211
213
  if (dir.startsWith(normalizedBase)) {
212
214
  const moduleName = dir.slice(normalizedBase.length + 1).replace(/\//g, ".");
213
215
  return moduleName ? `${moduleName}:${decodedFile}` : decodedFile;
@@ -334,12 +336,12 @@ class UUID {
334
336
  }
335
337
  if (hex) {
336
338
  const inner = new Uint8Array(16);
337
- for (let i = 0; i < 16; i += 4) {
338
- const n = parseInt(hex.substring(2 * i, 2 * i + 8), 16);
339
- inner[i + 0] = n >>> 24;
340
- inner[i + 1] = n >>> 16;
341
- inner[i + 2] = n >>> 8;
342
- inner[i + 3] = n;
339
+ for (let i2 = 0; i2 < 16; i2 += 4) {
340
+ const n2 = parseInt(hex.substring(2 * i2, 2 * i2 + 8), 16);
341
+ inner[i2 + 0] = n2 >>> 24;
342
+ inner[i2 + 1] = n2 >>> 16;
343
+ inner[i2 + 2] = n2 >>> 8;
344
+ inner[i2 + 3] = n2;
343
345
  }
344
346
  return new UUID(inner);
345
347
  }
@@ -347,18 +349,18 @@ class UUID {
347
349
  }
348
350
  toString() {
349
351
  let text = "";
350
- for (let i = 0; i < this.bytes.length; i++) {
351
- text += DIGITS.charAt(this.bytes[i] >>> 4);
352
- text += DIGITS.charAt(15 & this.bytes[i]);
353
- if (3 === i || 5 === i || 7 === i || 9 === i) text += "-";
352
+ for (let i2 = 0; i2 < this.bytes.length; i2++) {
353
+ text += DIGITS.charAt(this.bytes[i2] >>> 4);
354
+ text += DIGITS.charAt(15 & this.bytes[i2]);
355
+ if (3 === i2 || 5 === i2 || 7 === i2 || 9 === i2) text += "-";
354
356
  }
355
357
  return text;
356
358
  }
357
359
  toHex() {
358
360
  let text = "";
359
- for (let i = 0; i < this.bytes.length; i++) {
360
- text += DIGITS.charAt(this.bytes[i] >>> 4);
361
- text += DIGITS.charAt(15 & this.bytes[i]);
361
+ for (let i2 = 0; i2 < this.bytes.length; i2++) {
362
+ text += DIGITS.charAt(this.bytes[i2] >>> 4);
363
+ text += DIGITS.charAt(15 & this.bytes[i2]);
362
364
  }
363
365
  return text;
364
366
  }
@@ -366,12 +368,12 @@ class UUID {
366
368
  return this.toString();
367
369
  }
368
370
  getVariant() {
369
- const n = this.bytes[8] >>> 4;
370
- if (n < 0) throw new Error("unreachable");
371
- if (n <= 7) return this.bytes.every((e) => 0 === e) ? "NIL" : "VAR_0";
372
- if (n <= 11) return "VAR_10";
373
- if (n <= 13) return "VAR_110";
374
- if (n <= 15) return this.bytes.every((e) => 255 === e) ? "MAX" : "VAR_RESERVED";
371
+ const n2 = this.bytes[8] >>> 4;
372
+ if (n2 < 0) throw new Error("unreachable");
373
+ if (n2 <= 7) return this.bytes.every((e) => 0 === e) ? "NIL" : "VAR_0";
374
+ if (n2 <= 11) return "VAR_10";
375
+ if (n2 <= 13) return "VAR_110";
376
+ if (n2 <= 15) return this.bytes.every((e) => 255 === e) ? "MAX" : "VAR_RESERVED";
375
377
  else throw new Error("unreachable");
376
378
  }
377
379
  getVersion() {
@@ -384,8 +386,8 @@ class UUID {
384
386
  return 0 === this.compareTo(other);
385
387
  }
386
388
  compareTo(other) {
387
- for (let i = 0; i < 16; i++) {
388
- const diff = this.bytes[i] - other.bytes[i];
389
+ for (let i2 = 0; i2 < 16; i2++) {
390
+ const diff = this.bytes[i2] - other.bytes[i2];
389
391
  if (0 !== diff) return Math.sign(diff);
390
392
  }
391
393
  return 0;
@@ -720,8 +722,8 @@ function removeTrailingSlash(url) {
720
722
  }
721
723
  async function retriable(fn, props) {
722
724
  let lastError = null;
723
- for (let i = 0; i < props.retryCount + 1; i++) {
724
- if (i > 0) await new Promise((r) => setTimeout(r, props.retryDelay));
725
+ for (let i2 = 0; i2 < props.retryCount + 1; i2++) {
726
+ if (i2 > 0) await new Promise((r2) => setTimeout(r2, props.retryDelay));
725
727
  try {
726
728
  const res = await fn();
727
729
  return res;
@@ -1423,8 +1425,8 @@ function getFilenameToChunkIdMap(stackParser) {
1423
1425
  if (result) acc[result[0]] = result[1];
1424
1426
  else {
1425
1427
  const parsedStack = stackParser(stackKey);
1426
- for (let i = parsedStack.length - 1; i >= 0; i--) {
1427
- const stackFrame = parsedStack[i];
1428
+ for (let i2 = parsedStack.length - 1; i2 >= 0; i2--) {
1429
+ const stackFrame = parsedStack[i2];
1428
1430
  const filename = stackFrame?.filename;
1429
1431
  const chunkId = chunkIdMap[stackKey];
1430
1432
  if (filename && chunkId) {
@@ -1632,8 +1634,8 @@ function createStackParser(platform, ...parsers) {
1632
1634
  return (stack, skipFirstLines = 0) => {
1633
1635
  const frames = [];
1634
1636
  const lines = stack.split("\n");
1635
- for (let i = skipFirstLines; i < lines.length; i++) {
1636
- const line = lines[i];
1637
+ for (let i2 = skipFirstLines; i2 < lines.length; i2++) {
1638
+ const line = lines[i2];
1637
1639
  if (line.length > 1024) continue;
1638
1640
  const cleanedLine = WEBPACK_ERROR_REGEXP.test(line) ? line.replace(WEBPACK_ERROR_REGEXP, "$1") : line;
1639
1641
  if (!cleanedLine.match(/\S*Error: /)) {
@@ -1715,10 +1717,10 @@ function extractExceptionKeysForMessage(err, maxLength = 40) {
1715
1717
  const keys = Object.keys(err);
1716
1718
  keys.sort();
1717
1719
  if (!keys.length) return "[object has no keys]";
1718
- for (let i = keys.length; i > 0; i--) {
1719
- const serialized = keys.slice(0, i).join(", ");
1720
+ for (let i2 = keys.length; i2 > 0; i2--) {
1721
+ const serialized = keys.slice(0, i2).join(", ");
1720
1722
  if (!(serialized.length > maxLength)) {
1721
- if (i === keys.length) return serialized;
1723
+ if (i2 === keys.length) return serialized;
1722
1724
  return serialized.length <= maxLength ? serialized : `${serialized.slice(0, maxLength)}...`;
1723
1725
  }
1724
1726
  }
@@ -1827,8 +1829,8 @@ const MAX_CONTEXTLINES_COLNO = 1e3;
1827
1829
  const MAX_CONTEXTLINES_LINENO = 1e4;
1828
1830
  async function addSourceContext(frames) {
1829
1831
  const filesToLines = {};
1830
- for (let i = frames.length - 1; i >= 0; i--) {
1831
- const frame = frames[i];
1832
+ for (let i2 = frames.length - 1; i2 >= 0; i2--) {
1833
+ const frame = frames[i2];
1832
1834
  const filename = frame?.filename;
1833
1835
  if (!frame || "string" != typeof filename || "number" != typeof frame.lineno || shouldSkipContextLinesForFile(filename) || shouldSkipContextLinesForFrame(frame)) continue;
1834
1836
  const filesToLinesOutput = filesToLines[filename];
@@ -1844,7 +1846,7 @@ async function addSourceContext(frames) {
1844
1846
  if (!filesToLineRanges) continue;
1845
1847
  filesToLineRanges.sort((a, b) => a - b);
1846
1848
  const ranges = makeLineReaderRanges(filesToLineRanges);
1847
- if (ranges.every((r) => rangeExistsInContentCache(file, r))) continue;
1849
+ if (ranges.every((r2) => rangeExistsInContentCache(file, r2))) continue;
1848
1850
  const cache = emplace(LRU_FILE_CONTENTS_CACHE, file, {});
1849
1851
  readlinePromises.push(getContextLinesFromFile(file, ranges, cache));
1850
1852
  }
@@ -1912,8 +1914,8 @@ function addSourceContextToFrames(frames, cache) {
1912
1914
  function addContextToFrame(lineno, frame, contents) {
1913
1915
  if (void 0 === frame.lineno || void 0 === contents) return;
1914
1916
  frame.pre_context = [];
1915
- for (let i = makeRangeStart(lineno); i < lineno; i++) {
1916
- const line = contents[i];
1917
+ for (let i2 = makeRangeStart(lineno); i2 < lineno; i2++) {
1918
+ const line = contents[i2];
1917
1919
  if (void 0 === line) return void clearLineContext(frame);
1918
1920
  frame.pre_context.push(line);
1919
1921
  }
@@ -1921,8 +1923,8 @@ function addContextToFrame(lineno, frame, contents) {
1921
1923
  frame.context_line = contents[lineno];
1922
1924
  const end = makeRangeEnd(lineno);
1923
1925
  frame.post_context = [];
1924
- for (let i = lineno + 1; i <= end; i++) {
1925
- const line = contents[i];
1926
+ for (let i2 = lineno + 1; i2 <= end; i2++) {
1927
+ const line = contents[i2];
1926
1928
  if (void 0 === line) break;
1927
1929
  frame.post_context.push(line);
1928
1930
  }
@@ -1943,29 +1945,29 @@ function shouldSkipContextLinesForFrame(frame) {
1943
1945
  function rangeExistsInContentCache(file, range) {
1944
1946
  const contents = LRU_FILE_CONTENTS_CACHE.get(file);
1945
1947
  if (void 0 === contents) return false;
1946
- for (let i = range[0]; i <= range[1]; i++) if (void 0 === contents[i]) return false;
1948
+ for (let i2 = range[0]; i2 <= range[1]; i2++) if (void 0 === contents[i2]) return false;
1947
1949
  return true;
1948
1950
  }
1949
1951
  function makeLineReaderRanges(lines) {
1950
1952
  if (!lines.length) return [];
1951
- let i = 0;
1953
+ let i2 = 0;
1952
1954
  const line = lines[0];
1953
1955
  if ("number" != typeof line) return [];
1954
1956
  let current = makeContextRange(line);
1955
1957
  const out = [];
1956
1958
  while (true) {
1957
- if (i === lines.length - 1) {
1959
+ if (i2 === lines.length - 1) {
1958
1960
  out.push(current);
1959
1961
  break;
1960
1962
  }
1961
- const next = lines[i + 1];
1963
+ const next = lines[i2 + 1];
1962
1964
  if ("number" != typeof next) break;
1963
1965
  if (next <= current[1]) current[1] = next + DEFAULT_LINES_OF_CONTEXT;
1964
1966
  else {
1965
1967
  out.push(current);
1966
1968
  current = makeContextRange(next);
1967
1969
  }
1968
- i++;
1970
+ i2++;
1969
1971
  }
1970
1972
  return out;
1971
1973
  }
@@ -3277,6 +3279,101 @@ const metricsIngestProxyMiddleware = (app) => {
3277
3279
  })
3278
3280
  );
3279
3281
  };
3282
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3283
+ let hasWarnedNoReplicationEnabled = false;
3284
+ const checkInitReplicaSet = async (serverEnv) => {
3285
+ const port = serverEnv.DB_PORT?.trim();
3286
+ if (!port) return;
3287
+ const host = (serverEnv.DB_HOST ?? "localhost").trim();
3288
+ const replSetName = "rs0";
3289
+ const memberHost = `${host}:${port}`;
3290
+ const maxAttempts = 10;
3291
+ const serverSelectionTimeoutMs = 2e3;
3292
+ const uri = `mongodb://${host}:${port}/?directConnection=true`;
3293
+ const waitForReplicaSetReady = async (admin) => {
3294
+ const maxWaitAttempts = 10;
3295
+ for (let attempt = 1; attempt <= maxWaitAttempts; attempt++) {
3296
+ try {
3297
+ const status = await admin.command({ replSetGetStatus: 1 });
3298
+ const state = Number(status?.myState ?? 0);
3299
+ if (status?.ok && (state === 1 || state === 2)) return;
3300
+ } catch {
3301
+ }
3302
+ await sleep(250 * attempt);
3303
+ }
3304
+ };
3305
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
3306
+ let client2 = null;
3307
+ try {
3308
+ client2 = new MongoClient(uri, {
3309
+ family: 4,
3310
+ serverSelectionTimeoutMS: serverSelectionTimeoutMs,
3311
+ connectTimeoutMS: serverSelectionTimeoutMs
3312
+ });
3313
+ await client2.connect();
3314
+ const admin = client2.db("admin").admin();
3315
+ await admin.command({ ping: 1 });
3316
+ try {
3317
+ await admin.command({ replSetGetStatus: 1 });
3318
+ return;
3319
+ } catch (error) {
3320
+ const codeName = error?.codeName;
3321
+ if (codeName === "NotYetInitialized") {
3322
+ try {
3323
+ const res = await admin.command({
3324
+ replSetInitiate: {
3325
+ _id: replSetName,
3326
+ members: [{ _id: 0, host: memberHost }]
3327
+ }
3328
+ });
3329
+ if (res?.ok) {
3330
+ console.warn(`[rb/server] MongoDB replica set '${replSetName}' initiated (${memberHost}).`);
3331
+ } else {
3332
+ console.warn(`[rb/server] MongoDB replica set initiation returned ok=${String(res?.ok)}.`);
3333
+ }
3334
+ } catch (initError) {
3335
+ const initCodeName = initError?.codeName;
3336
+ if (initCodeName !== "AlreadyInitialized") {
3337
+ const message2 = initError instanceof Error ? initError.message : String(initError);
3338
+ console.warn(`[rb/server] MongoDB replica set initiation failed: ${message2}`);
3339
+ if (initCodeName === "InvalidReplicaSetConfig") {
3340
+ console.warn(
3341
+ `[rb/server] Hint: the replica set member host must match the mongod address/port. If MongoDB runs in Docker with port mapping, ensure the container listens on ${port} (e.g. mongod --port ${port}) instead of the default 27017.`
3342
+ );
3343
+ }
3344
+ }
3345
+ }
3346
+ await waitForReplicaSetReady(admin);
3347
+ return;
3348
+ }
3349
+ if (codeName === "NoReplicationEnabled") {
3350
+ if (!hasWarnedNoReplicationEnabled) {
3351
+ hasWarnedNoReplicationEnabled = true;
3352
+ console.warn(
3353
+ `[rb/server] MongoDB is not started with --replSet ${replSetName} (replication disabled). Change streams require a replica set; start mongod with --replSet.`
3354
+ );
3355
+ }
3356
+ return;
3357
+ }
3358
+ const message = error instanceof Error ? error.message : String(error);
3359
+ console.warn(`[rb/server] MongoDB replica set check failed: ${message}`);
3360
+ return;
3361
+ }
3362
+ } catch (error) {
3363
+ if (attempt === maxAttempts) {
3364
+ const message = error instanceof Error ? error.message : String(error);
3365
+ console.warn(`[rb/server] MongoDB replica set auto-init skipped (unable to connect to ${host}:${port}): ${message}`);
3366
+ return;
3367
+ }
3368
+ await sleep(250 * attempt);
3369
+ } finally {
3370
+ try {
3371
+ await client2?.close();
3372
+ } catch {
3373
+ }
3374
+ }
3375
+ }
3376
+ };
3280
3377
  process.env = {
3281
3378
  ...env,
3282
3379
  ...__rb_env__,
@@ -3296,17 +3393,24 @@ const getMongoUrl = (serverEnv) => {
3296
3393
  }
3297
3394
  return void 0;
3298
3395
  };
3299
- const createMongoSessionStore = (serverEnv) => {
3396
+ const createMongoSessionStore = async (serverEnv) => {
3300
3397
  const mongoUrl = getMongoUrl(serverEnv);
3301
3398
  if (!mongoUrl) {
3302
3399
  throw new Error("Missing REDIS_URL and Mongo connection details (MONGODB_URL/MONGO_URL/MONGODB_URI/DB_PORT)");
3303
3400
  }
3304
3401
  console.log("Using MongoDB session store");
3305
- return MongoStore.create({
3306
- mongoUrl,
3402
+ const client2 = await MongoClient.connect(mongoUrl, {
3403
+ family: 4,
3404
+ serverSelectionTimeoutMS: 2e3,
3405
+ connectTimeoutMS: 2e3
3406
+ });
3407
+ const store = MongoStore.create({
3408
+ client: client2,
3307
3409
  collectionName: "sessions",
3308
3410
  ttl: SESSION_MAX_AGE_S
3309
3411
  });
3412
+ await store.collectionP;
3413
+ return store;
3310
3414
  };
3311
3415
  const createRedisSessionStore = async (redisUrl) => {
3312
3416
  const reconnectStrategy = (retries) => {
@@ -3341,6 +3445,10 @@ const createRedisSessionStore = async (redisUrl) => {
3341
3445
  };
3342
3446
  const initServer = async (app, serverEnv) => {
3343
3447
  await initApiClient({ app });
3448
+ const replicaSetInitPromise = checkInitReplicaSet(serverEnv).catch((error) => {
3449
+ const message = error instanceof Error ? error.message : String(error);
3450
+ console.warn(`[rb/server] MongoDB replica set auto-init error: ${message}`);
3451
+ });
3344
3452
  app.disable("x-powered-by");
3345
3453
  app.set("trust proxy", true);
3346
3454
  app.use(requestIp.mw());
@@ -3363,7 +3471,13 @@ const initServer = async (app, serverEnv) => {
3363
3471
  }
3364
3472
  const sessionSecret = getDerivedKey(serverEnv.MASTER_KEY, "express_session_key");
3365
3473
  const redisUrl = serverEnv.REDIS_URL?.trim();
3366
- const store = redisUrl ? await createRedisSessionStore(redisUrl) : createMongoSessionStore(serverEnv);
3474
+ let store;
3475
+ if (redisUrl) {
3476
+ store = await createRedisSessionStore(redisUrl);
3477
+ } else {
3478
+ await replicaSetInitPromise;
3479
+ store = await createMongoSessionStore(serverEnv);
3480
+ }
3367
3481
  const sessionConfig = {
3368
3482
  name: "session",
3369
3483
  store,
@@ -3378,7 +3492,9 @@ const initServer = async (app, serverEnv) => {
3378
3492
  if (isProduction$1) {
3379
3493
  sessionConfig.cookie.secure = true;
3380
3494
  }
3381
- app.use(session(sessionConfig));
3495
+ const sessionMiddleware = session(sessionConfig);
3496
+ app.locals.rbSessionMiddleware = sessionMiddleware;
3497
+ app.use(sessionMiddleware);
3382
3498
  };
3383
3499
  async function hashPassword(password, salt) {
3384
3500
  const keyLength = 64;
@@ -3422,15 +3538,15 @@ const parseEnvInt = (value) => {
3422
3538
  return parsed;
3423
3539
  };
3424
3540
  const isPowerOfTwo = (value) => (value & value - 1) === 0;
3425
- const estimateScryptMemoryBytes = ({ N, r, p }) => {
3426
- return 128 * r * (N + p);
3541
+ const estimateScryptMemoryBytes = ({ N, r: r2, p }) => {
3542
+ return 128 * r2 * (N + p);
3427
3543
  };
3428
3544
  const validateScryptParams = (params) => {
3429
- const { N, r, p, keylen, saltBytes, maxmemBytes } = params;
3545
+ const { N, r: r2, p, keylen, saltBytes, maxmemBytes } = params;
3430
3546
  if (!Number.isSafeInteger(N) || N < 2 || N > MAX_SCRYPT_N || !isPowerOfTwo(N)) {
3431
3547
  return { ok: false, error: "invalid_scrypt_N" };
3432
3548
  }
3433
- if (!Number.isSafeInteger(r) || r < 1 || r > MAX_SCRYPT_R) {
3549
+ if (!Number.isSafeInteger(r2) || r2 < 1 || r2 > MAX_SCRYPT_R) {
3434
3550
  return { ok: false, error: "invalid_scrypt_r" };
3435
3551
  }
3436
3552
  if (!Number.isSafeInteger(p) || p < 1 || p > MAX_SCRYPT_P) {
@@ -3445,7 +3561,7 @@ const validateScryptParams = (params) => {
3445
3561
  if (!Number.isSafeInteger(maxmemBytes) || maxmemBytes < 16 * 1024 * 1024 || maxmemBytes > MAX_SCRYPT_MAXMEM_BYTES) {
3446
3562
  return { ok: false, error: "invalid_scrypt_maxmem" };
3447
3563
  }
3448
- const estimatedMem = estimateScryptMemoryBytes({ N, r, p });
3564
+ const estimatedMem = estimateScryptMemoryBytes({ N, r: r2, p });
3449
3565
  if (estimatedMem > maxmemBytes) {
3450
3566
  return { ok: false, error: "scrypt_params_exceed_maxmem" };
3451
3567
  }
@@ -3480,9 +3596,9 @@ const getCurrentScryptParams = (opts) => {
3480
3596
  return params;
3481
3597
  };
3482
3598
  const scryptAsync = async (password, salt, params) => {
3483
- const { N, r, p, keylen, maxmemBytes } = params;
3599
+ const { N, r: r2, p, keylen, maxmemBytes } = params;
3484
3600
  return await new Promise((resolve, reject) => {
3485
- crypto.scrypt(password, salt, keylen, { N, r, p, maxmem: maxmemBytes }, (err, derivedKey) => {
3601
+ crypto.scrypt(password, salt, keylen, { N, r: r2, p, maxmem: maxmemBytes }, (err, derivedKey) => {
3486
3602
  if (err) {
3487
3603
  reject(err);
3488
3604
  return;
@@ -3529,10 +3645,10 @@ const parseStoredScryptHash = (stored) => {
3529
3645
  params.set(key, value);
3530
3646
  }
3531
3647
  const N = params.get("N");
3532
- const r = params.get("r");
3648
+ const r2 = params.get("r");
3533
3649
  const p = params.get("p");
3534
3650
  const keylen = params.get("keylen");
3535
- if (N === void 0 || r === void 0 || p === void 0 || keylen === void 0) return null;
3651
+ if (N === void 0 || r2 === void 0 || p === void 0 || keylen === void 0) return null;
3536
3652
  if (params.size !== 4) return null;
3537
3653
  const salt = parseB64(saltB64);
3538
3654
  const dk = parseB64(dkB64);
@@ -3543,31 +3659,31 @@ const parseStoredScryptHash = (stored) => {
3543
3659
  const currentMaxmemBytes = getCurrentMaxmemBytes();
3544
3660
  const validated = validateScryptParams({
3545
3661
  N,
3546
- r,
3662
+ r: r2,
3547
3663
  p,
3548
3664
  keylen,
3549
3665
  saltBytes: salt.length,
3550
3666
  maxmemBytes: currentMaxmemBytes
3551
3667
  });
3552
3668
  if (!validated.ok) return null;
3553
- return { N, r, p, keylen, salt, dk };
3669
+ return { N, r: r2, p, keylen, salt, dk };
3554
3670
  };
3555
3671
  async function hashPasswordForStorage(password, opts) {
3556
- const { N, r, p, keylen, saltBytes, maxmemBytes } = getCurrentScryptParams(opts);
3672
+ const { N, r: r2, p, keylen, saltBytes, maxmemBytes } = getCurrentScryptParams(opts);
3557
3673
  const salt = crypto.randomBytes(saltBytes);
3558
- const dk = await scryptAsync(password, salt, { N, r, p, keylen, maxmemBytes });
3674
+ const dk = await scryptAsync(password, salt, { N, r: r2, p, keylen, maxmemBytes });
3559
3675
  const saltB64 = salt.toString("base64");
3560
3676
  const dkB64 = dk.toString("base64");
3561
- return `$scrypt$N=${N},r=${r},p=${p},keylen=${keylen}$${saltB64}$${dkB64}`;
3677
+ return `$scrypt$N=${N},r=${r2},p=${p},keylen=${keylen}$${saltB64}$${dkB64}`;
3562
3678
  }
3563
3679
  async function verifyPasswordFromStorage(password, stored) {
3564
3680
  const parsed = parseStoredScryptHash(stored);
3565
3681
  if (!parsed) return false;
3566
- const { N, r, p, keylen, salt, dk } = parsed;
3682
+ const { N, r: r2, p, keylen, salt, dk } = parsed;
3567
3683
  const maxmemBytes = getCurrentMaxmemBytes();
3568
3684
  let derivedKey;
3569
3685
  try {
3570
- derivedKey = await scryptAsync(password, salt, { N, r, p, keylen, maxmemBytes });
3686
+ derivedKey = await scryptAsync(password, salt, { N, r: r2, p, keylen, maxmemBytes });
3571
3687
  } catch {
3572
3688
  return false;
3573
3689
  }
@@ -3590,7 +3706,7 @@ function createLocation(current, to, state = null, key) {
3590
3706
  return location;
3591
3707
  }
3592
3708
  function getShortCircuitMatches(routes) {
3593
- const route = routes.length === 1 ? routes[0] : routes.find((r) => r.index || !r.path || r.path === "/") || {
3709
+ const route = routes.length === 1 ? routes[0] : routes.find((r2) => r2.index || !r2.path || r2.path === "/") || {
3594
3710
  id: "__shim-error-route__"
3595
3711
  };
3596
3712
  return {
@@ -4095,7 +4211,10 @@ export {
4095
4211
  getDerivedKey,
4096
4212
  hashPassword,
4097
4213
  hashPasswordForStorage,
4214
+ i as initRts,
4098
4215
  initServer,
4216
+ n as notifyRtsModelChanged,
4217
+ r as registerRtsHandler,
4099
4218
  sendEmail,
4100
4219
  ssrMiddleware,
4101
4220
  verifyPasswordFromStorage
@@ -1 +1 @@
1
- {"version":3,"file":"initServer.d.ts","sourceRoot":"","sources":["../src/initServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAuBrC,KAAK,SAAS,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AA8EtD,eAAO,MAAM,UAAU,GAAU,KAAK,WAAW,EAAE,WAAW,SAAS,kBA2DtE,CAAA"}
1
+ {"version":3,"file":"initServer.d.ts","sourceRoot":"","sources":["../src/initServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAyBrC,KAAK,SAAS,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAuFtD,eAAO,MAAM,UAAU,GAAU,KAAK,WAAW,EAAE,WAAW,SAAS,kBAsEtE,CAAA"}
@@ -0,0 +1,34 @@
1
+ import { Server as HttpServer } from 'node:http';
2
+ import { RequestHandler } from 'express';
3
+ import { WebSocket } from 'ws';
4
+ type SocketMeta = {
5
+ tenantId: string;
6
+ userId: string;
7
+ };
8
+ type HandlerFn = (socket: RtsSocket) => void | (() => void);
9
+ declare class RtsSocket {
10
+ readonly id: string;
11
+ readonly tenantId: string;
12
+ readonly userId: string;
13
+ private readonly ws;
14
+ private readonly handlers;
15
+ constructor({ id, ws, meta, }: {
16
+ id: string;
17
+ ws: WebSocket;
18
+ meta: SocketMeta;
19
+ });
20
+ on(event: string, handler: (payload: unknown) => void): () => void;
21
+ off(event: string, handler: (payload: unknown) => void): void;
22
+ emit(event: string, payload?: unknown): void;
23
+ close(): void;
24
+ dispatch(event: string, payload: unknown): void;
25
+ }
26
+ export declare const initRts: ({ server, path, sessionMiddleware, }: {
27
+ server: HttpServer;
28
+ path?: string;
29
+ sessionMiddleware?: RequestHandler;
30
+ }) => void;
31
+ export declare const registerRtsHandler: (handler: HandlerFn) => void;
32
+ export declare const notifyRtsModelChanged: (tenantId: string, modelName: string) => void;
33
+ export {};
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rts/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAA;AAGtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAG7C,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,IAAI,CAAA;AAqBlE,KAAK,UAAU,GAAG;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAoBD,KAAK,SAAS,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,CAAA;AAoB3D,cAAM,SAAS;IACb,SAAgB,EAAE,EAAE,MAAM,CAAA;IAC1B,SAAgB,QAAQ,EAAE,MAAM,CAAA;IAChC,SAAgB,MAAM,EAAE,MAAM,CAAA;IAE9B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqD;gBAE3D,EACjB,EAAE,EACF,EAAE,EACF,IAAI,GACL,EAAE;QACD,EAAE,EAAE,MAAM,CAAA;QACV,EAAE,EAAE,SAAS,CAAA;QACb,IAAI,EAAE,UAAU,CAAA;KACjB;IAOM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI;IAOlE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAO7D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI;IAI5C,KAAK,IAAI,IAAI;IAQb,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;CAOvD;AA4aD,eAAO,MAAM,OAAO,GAAI,sCAIrB;IACD,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iBAAiB,CAAC,EAAE,cAAc,CAAA;CACnC,KAAG,IAyFH,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,SAAS,SAAS,KAAG,IAEvD,CAAA;AAED,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,IAE3E,CAAA"}
package/dist/rts.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './rts/index';
2
+ //# sourceMappingURL=rts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rts.d.ts","sourceRoot":"","sources":["../src/rts.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA"}
package/dist/rts.js ADDED
@@ -0,0 +1,6 @@
1
+ import { i, n, r } from "./index-BXODFGBH.js";
2
+ export {
3
+ i as initRts,
4
+ n as notifyRtsModelChanged,
5
+ r as registerRtsHandler
6
+ };
package/package.json CHANGED
@@ -1,12 +1,24 @@
1
1
  {
2
2
  "name": "@rpcbase/server",
3
- "version": "0.464.0",
3
+ "version": "0.466.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
7
7
  ],
8
8
  "main": "./dist/index.js",
9
9
  "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./rts": {
17
+ "types": "./dist/rts.d.ts",
18
+ "import": "./dist/rts.js",
19
+ "default": "./dist/rts.js"
20
+ }
21
+ },
10
22
  "scripts": {
11
23
  "build": "wireit",
12
24
  "test": "wireit",
@@ -63,10 +75,13 @@
63
75
  "connect-redis": "9.0.0",
64
76
  "express-session": "1.18.2",
65
77
  "http-proxy-middleware": "3.0.5",
78
+ "mongodb": "7.0.0",
66
79
  "redis": "5.10.0",
67
- "resend": "6.5.2"
80
+ "resend": "6.5.2",
81
+ "ws": "8.18.0"
68
82
  },
69
83
  "devDependencies": {
84
+ "@types/ws": "8.18.1",
70
85
  "request-ip": "3.3.0"
71
86
  }
72
87
  }