dialogue-ts 0.0.1

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,1424 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ createDefaultLogger: () => createDefaultLogger,
24
+ createDialogue: () => createDialogue,
25
+ createHistoryManager: () => createHistoryManager,
26
+ createRateLimiter: () => createRateLimiter,
27
+ createSilentLogger: () => createSilentLogger,
28
+ defineEvent: () => defineEvent,
29
+ getEventByName: () => getEventByName,
30
+ isEventAllowed: () => isEventAllowed,
31
+ validateEventData: () => validateEventData
32
+ });
33
+ module.exports = __toCommonJS(src_exports);
34
+
35
+ // src/create-dialogue.ts
36
+ var import_hono = require("hono");
37
+ var import_slang_ts3 = require("slang-ts");
38
+
39
+ // src/history.ts
40
+ function createHistoryManager(config = {}) {
41
+ const store = /* @__PURE__ */ new Map();
42
+ function ensureRoomEvent(roomId, eventName) {
43
+ let roomHistory = store.get(roomId);
44
+ if (!roomHistory) {
45
+ roomHistory = /* @__PURE__ */ new Map();
46
+ store.set(roomId, roomHistory);
47
+ }
48
+ let eventHistory = roomHistory.get(eventName);
49
+ if (!eventHistory) {
50
+ eventHistory = [];
51
+ roomHistory.set(eventName, eventHistory);
52
+ }
53
+ return eventHistory;
54
+ }
55
+ return {
56
+ push(roomId, eventName, event, limit) {
57
+ const events = ensureRoomEvent(roomId, eventName);
58
+ events.push(event);
59
+ if (events.length > limit) {
60
+ const evictCount = events.length - limit;
61
+ const evicted = events.splice(0, evictCount);
62
+ if (config.onCleanup && evicted.length > 0) {
63
+ config.onCleanup(roomId, eventName, evicted);
64
+ }
65
+ }
66
+ },
67
+ get(roomId, eventName, start, end) {
68
+ const roomHistory = store.get(roomId);
69
+ if (!roomHistory) {
70
+ return [];
71
+ }
72
+ const events = roomHistory.get(eventName);
73
+ if (!events || events.length === 0) {
74
+ return [];
75
+ }
76
+ const len = events.length;
77
+ const actualStart = Math.max(0, len - end);
78
+ const actualEnd = len - start;
79
+ if (actualStart >= actualEnd) {
80
+ return [];
81
+ }
82
+ return events.slice(actualStart, actualEnd).reverse();
83
+ },
84
+ getAll(roomId, limit) {
85
+ const roomHistory = store.get(roomId);
86
+ if (!roomHistory) {
87
+ return [];
88
+ }
89
+ const allEvents = [];
90
+ for (const events of roomHistory.values()) {
91
+ allEvents.push(...events);
92
+ }
93
+ allEvents.sort((a, b) => b.timestamp - a.timestamp);
94
+ if (limit !== void 0 && limit > 0) {
95
+ return allEvents.slice(0, limit);
96
+ }
97
+ return allEvents;
98
+ },
99
+ count(roomId, eventName) {
100
+ const roomHistory = store.get(roomId);
101
+ if (!roomHistory) {
102
+ return 0;
103
+ }
104
+ const events = roomHistory.get(eventName);
105
+ return events?.length ?? 0;
106
+ },
107
+ clearRoom(roomId) {
108
+ const roomHistory = store.get(roomId);
109
+ if (!roomHistory) {
110
+ return;
111
+ }
112
+ if (config.onCleanup) {
113
+ for (const [eventName, events] of roomHistory.entries()) {
114
+ if (events.length > 0) {
115
+ config.onCleanup(roomId, eventName, events);
116
+ }
117
+ }
118
+ }
119
+ store.delete(roomId);
120
+ },
121
+ getEventNames(roomId) {
122
+ const roomHistory = store.get(roomId);
123
+ if (!roomHistory) {
124
+ return [];
125
+ }
126
+ return Array.from(roomHistory.keys());
127
+ }
128
+ };
129
+ }
130
+
131
+ // src/logger.ts
132
+ function createDefaultLogger() {
133
+ return {
134
+ debug(entry) {
135
+ if (process.env.NODE_ENV === "development" || process.env.DEBUG) {
136
+ console.debug("[Dialogue] [DEBUG]", entry);
137
+ }
138
+ },
139
+ info(entry) {
140
+ console.info("[Dialogue] [INFO]", entry);
141
+ },
142
+ warn(entry) {
143
+ console.warn("[Dialogue] [WARN]", entry);
144
+ },
145
+ error(entry) {
146
+ console.error("[Dialogue] [ERROR]", entry);
147
+ }
148
+ };
149
+ }
150
+ function createSilentLogger() {
151
+ const noop = () => {
152
+ };
153
+ return {
154
+ debug: noop,
155
+ info: noop,
156
+ warn: noop,
157
+ error: noop
158
+ };
159
+ }
160
+
161
+ // src/server.ts
162
+ var import_bun_engine = require("@socket.io/bun-engine");
163
+ var import_cors = require("hono/cors");
164
+ var import_socket = require("socket.io");
165
+
166
+ // src/client-handler.ts
167
+ var import_nanoid = require("nanoid");
168
+ var WILDCARD = "*";
169
+ function createConnectedClient(socket, userId, meta, roomManager, logger) {
170
+ const id = (0, import_nanoid.nanoid)();
171
+ const joinedRooms = /* @__PURE__ */ new Set();
172
+ const subscriptions = /* @__PURE__ */ new Map();
173
+ const client = {
174
+ id,
175
+ userId,
176
+ socket,
177
+ meta,
178
+ join(roomId) {
179
+ const room = roomManager.get(roomId);
180
+ if (!room) {
181
+ logger.warn({
182
+ message: `Room '${roomId}' does not exist`,
183
+ atFunction: "client.join",
184
+ data: { roomId, clientId: id }
185
+ });
186
+ return;
187
+ }
188
+ if (joinedRooms.has(roomId)) {
189
+ socket.emit("dialogue:joined", { roomId, roomName: room.name });
190
+ return;
191
+ }
192
+ const added = roomManager.addParticipant(roomId, client);
193
+ if (!added) {
194
+ logger.warn({
195
+ message: `Room '${roomId}' is full`,
196
+ atFunction: "client.join",
197
+ data: { roomId, clientId: id }
198
+ });
199
+ socket.emit("dialogue:error", {
200
+ code: "ROOM_FULL",
201
+ message: `Room '${roomId}' is at capacity`
202
+ });
203
+ return;
204
+ }
205
+ joinedRooms.add(roomId);
206
+ subscriptions.set(roomId, /* @__PURE__ */ new Set());
207
+ for (const eventName of room.defaultSubscriptions) {
208
+ this.subscribe(roomId, eventName);
209
+ }
210
+ socket.emit("dialogue:joined", { roomId, roomName: room.name });
211
+ },
212
+ leave(roomId) {
213
+ if (!joinedRooms.has(roomId)) {
214
+ return;
215
+ }
216
+ roomManager.removeParticipant(roomId, id);
217
+ joinedRooms.delete(roomId);
218
+ subscriptions.delete(roomId);
219
+ socket.emit("dialogue:left", { roomId });
220
+ },
221
+ subscribe(roomId, eventName) {
222
+ if (!joinedRooms.has(roomId)) {
223
+ logger.warn({
224
+ message: `Cannot subscribe to '${eventName}' - not in room '${roomId}'`,
225
+ atFunction: "client.subscribe",
226
+ data: { roomId, eventName, clientId: id }
227
+ });
228
+ return;
229
+ }
230
+ const roomSubs = subscriptions.get(roomId);
231
+ if (roomSubs) {
232
+ roomSubs.add(eventName);
233
+ }
234
+ },
235
+ subscribeAll(roomId) {
236
+ this.subscribe(roomId, WILDCARD);
237
+ },
238
+ unsubscribe(roomId, eventName) {
239
+ const roomSubs = subscriptions.get(roomId);
240
+ if (roomSubs) {
241
+ roomSubs.delete(eventName);
242
+ }
243
+ },
244
+ rooms() {
245
+ return Array.from(joinedRooms);
246
+ },
247
+ subscriptions(roomId) {
248
+ const roomSubs = subscriptions.get(roomId);
249
+ return roomSubs ? Array.from(roomSubs) : [];
250
+ },
251
+ send(event, data) {
252
+ socket.emit(event, data);
253
+ },
254
+ disconnect() {
255
+ for (const roomId of joinedRooms) {
256
+ roomManager.removeParticipant(roomId, id);
257
+ }
258
+ joinedRooms.clear();
259
+ subscriptions.clear();
260
+ socket.disconnect(true);
261
+ }
262
+ };
263
+ return client;
264
+ }
265
+ function extractUserFromSocket(socket) {
266
+ const auth = socket.handshake.auth;
267
+ let userId = socket.id;
268
+ if (typeof auth.userId === "string") {
269
+ userId = auth.userId;
270
+ } else if (typeof auth.token === "string") {
271
+ userId = auth.token;
272
+ }
273
+ const meta = {};
274
+ if (typeof auth.role === "string") {
275
+ meta.role = auth.role;
276
+ }
277
+ for (const [key, value] of Object.entries(auth)) {
278
+ if (key !== "userId" && key !== "token") {
279
+ meta[key] = value;
280
+ }
281
+ }
282
+ return { userId, meta };
283
+ }
284
+
285
+ // src/rate-limiter.ts
286
+ function createRateLimiter(config) {
287
+ const entries = /* @__PURE__ */ new Map();
288
+ const cleanupInterval = setInterval(() => {
289
+ const now = Date.now();
290
+ for (const [key, entry] of entries) {
291
+ if (now >= entry.resetAt) {
292
+ entries.delete(key);
293
+ }
294
+ }
295
+ }, config.windowMs);
296
+ cleanupInterval.unref?.();
297
+ return {
298
+ isAllowed(key) {
299
+ const now = Date.now();
300
+ const entry = entries.get(key);
301
+ if (!entry || now >= entry.resetAt) {
302
+ entries.set(key, {
303
+ count: 1,
304
+ resetAt: now + config.windowMs
305
+ });
306
+ return true;
307
+ }
308
+ if (entry.count >= config.maxRequests) {
309
+ return false;
310
+ }
311
+ entry.count++;
312
+ return true;
313
+ },
314
+ remaining(key) {
315
+ const now = Date.now();
316
+ const entry = entries.get(key);
317
+ if (!entry || now >= entry.resetAt) {
318
+ return config.maxRequests;
319
+ }
320
+ return Math.max(0, config.maxRequests - entry.count);
321
+ },
322
+ clear() {
323
+ entries.clear();
324
+ }
325
+ };
326
+ }
327
+
328
+ // src/room.ts
329
+ var import_slang_ts2 = require("slang-ts");
330
+
331
+ // src/define-event.ts
332
+ var import_slang_ts = require("slang-ts");
333
+ function defineEvent(name, options) {
334
+ const definition = {
335
+ name,
336
+ description: options?.description,
337
+ schema: options?.schema,
338
+ history: options?.history
339
+ };
340
+ return Object.freeze(definition);
341
+ }
342
+ function validateEventData(event, data) {
343
+ if (!event.schema) {
344
+ return (0, import_slang_ts.Ok)(data);
345
+ }
346
+ const result = event.schema.safeParse(data);
347
+ if (result.success) {
348
+ return (0, import_slang_ts.Ok)(result.data);
349
+ }
350
+ const errorMessages = result.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
351
+ return (0, import_slang_ts.Err)(`Event '${event.name}' validation failed: ${errorMessages}`);
352
+ }
353
+ function isEventAllowed(eventName, allowedEvents) {
354
+ if (allowedEvents.length === 0) {
355
+ return false;
356
+ }
357
+ return allowedEvents.some((e) => e.name === "*" || e.name === eventName);
358
+ }
359
+ function getEventByName(eventName, events) {
360
+ return events.find((e) => e.name === eventName);
361
+ }
362
+
363
+ // src/room.ts
364
+ var WILDCARD2 = "*";
365
+ function isParticipantSubscribed(participant, roomId, eventName) {
366
+ const subs = participant.subscriptions(roomId);
367
+ return subs.includes(eventName) || subs.includes(WILDCARD2);
368
+ }
369
+ function createRoom(id, config, _io, logger, historyManager, hooks, externalParticipants, getContext) {
370
+ const participants = externalParticipants ?? /* @__PURE__ */ new Map();
371
+ const eventHandlers = /* @__PURE__ */ new Map();
372
+ function handlePostBroadcast(eventName, message) {
373
+ const eventDef = getEventByName(eventName, config.events);
374
+ if (eventDef?.history?.enabled && historyManager) {
375
+ historyManager.push(id, eventName, message, eventDef.history.limit);
376
+ }
377
+ if (hooks?.events?.onTriggered) {
378
+ Promise.resolve(hooks.events.onTriggered(id, message)).catch((err) => {
379
+ logger.error({
380
+ message: `Error in onTriggered hook for '${eventName}'`,
381
+ atFunction: "room.trigger.onTriggered",
382
+ data: err
383
+ });
384
+ });
385
+ }
386
+ const handlers = eventHandlers.get(eventName);
387
+ if (handlers) {
388
+ for (const handler of handlers) {
389
+ Promise.resolve(handler(message)).catch((err) => {
390
+ logger.error({
391
+ message: `Error in event handler for '${eventName}'`,
392
+ atFunction: "room.trigger.handler",
393
+ data: err
394
+ });
395
+ });
396
+ }
397
+ }
398
+ }
399
+ const room = {
400
+ id,
401
+ name: config.name,
402
+ description: config.description,
403
+ maxSize: config.maxSize,
404
+ events: config.events,
405
+ defaultSubscriptions: config.defaultSubscriptions ?? [],
406
+ createdById: config.createdById,
407
+ trigger(event, data, from, meta) {
408
+ if (!isEventAllowed(event.name, config.events)) {
409
+ const errorMsg = `Event '${event.name}' is not allowed in room '${id}'`;
410
+ logger.warn({
411
+ message: errorMsg,
412
+ atFunction: "room.trigger",
413
+ data: { eventName: event.name, roomId: id }
414
+ });
415
+ return (0, import_slang_ts2.Err)(errorMsg);
416
+ }
417
+ const eventDef = getEventByName(event.name, config.events) ?? event;
418
+ const validation = validateEventData(eventDef, data);
419
+ if (validation.isErr) {
420
+ logger.warn({
421
+ message: validation.error,
422
+ atFunction: "room.trigger",
423
+ data: {
424
+ eventName: event.name,
425
+ roomId: id,
426
+ validationError: validation.error
427
+ }
428
+ });
429
+ return (0, import_slang_ts2.Err)(validation.error);
430
+ }
431
+ const message = {
432
+ event: event.name,
433
+ roomId: id,
434
+ data: validation.value,
435
+ from: from ?? "system",
436
+ timestamp: Date.now(),
437
+ ...meta && { meta }
438
+ };
439
+ let finalMessage = message;
440
+ if (hooks?.events?.beforeEach && getContext) {
441
+ const context = getContext();
442
+ const hookResult = hooks.events.beforeEach({
443
+ context,
444
+ roomId: id,
445
+ message,
446
+ from: message.from
447
+ });
448
+ if (hookResult.isErr) {
449
+ logger.debug({
450
+ message: `Event '${event.name}' blocked by beforeEach: ${hookResult.error}`,
451
+ atFunction: "room.trigger.beforeEach",
452
+ data: {
453
+ eventName: event.name,
454
+ roomId: id,
455
+ reason: hookResult.error
456
+ }
457
+ });
458
+ return (0, import_slang_ts2.Err)(hookResult.error);
459
+ }
460
+ finalMessage = hookResult.value;
461
+ }
462
+ let recipientCount = 0;
463
+ for (const [, participant] of participants) {
464
+ if (isParticipantSubscribed(participant, id, event.name)) {
465
+ participant.socket.emit("dialogue:event", finalMessage);
466
+ recipientCount++;
467
+ }
468
+ }
469
+ handlePostBroadcast(event.name, finalMessage);
470
+ if (hooks?.events?.afterEach && getContext) {
471
+ const context = getContext();
472
+ hooks.events.afterEach({
473
+ context,
474
+ roomId: id,
475
+ message: finalMessage,
476
+ recipientCount
477
+ });
478
+ }
479
+ return (0, import_slang_ts2.Ok)(void 0);
480
+ },
481
+ on(event, handler) {
482
+ let handlers = eventHandlers.get(event.name);
483
+ if (!handlers) {
484
+ handlers = /* @__PURE__ */ new Set();
485
+ eventHandlers.set(event.name, handlers);
486
+ }
487
+ handlers.add(handler);
488
+ return () => {
489
+ handlers?.delete(handler);
490
+ if (handlers?.size === 0) {
491
+ eventHandlers.delete(event.name);
492
+ }
493
+ };
494
+ },
495
+ size() {
496
+ return participants.size;
497
+ },
498
+ isFull() {
499
+ if (config.maxSize === void 0) {
500
+ return false;
501
+ }
502
+ return participants.size >= config.maxSize;
503
+ },
504
+ participants() {
505
+ return Array.from(participants.values());
506
+ },
507
+ async history(eventName, start, end) {
508
+ if (!historyManager) {
509
+ return [];
510
+ }
511
+ const inMemoryEvents = historyManager.get(id, eventName, start, end);
512
+ const inMemoryCount = historyManager.count(id, eventName);
513
+ if (inMemoryEvents.length >= end - start || !hooks?.events?.onLoad) {
514
+ return inMemoryEvents;
515
+ }
516
+ const missingStart = Math.max(start, inMemoryCount);
517
+ const missingEnd = end;
518
+ if (missingStart >= missingEnd) {
519
+ return inMemoryEvents;
520
+ }
521
+ try {
522
+ const externalEvents = await hooks.events.onLoad(
523
+ id,
524
+ eventName,
525
+ missingStart - inMemoryCount,
526
+ missingEnd - inMemoryCount
527
+ );
528
+ return [...inMemoryEvents, ...externalEvents];
529
+ } catch (err) {
530
+ logger.error({
531
+ message: `Error loading history for '${eventName}'`,
532
+ atFunction: "room.history.onLoad",
533
+ data: err
534
+ });
535
+ return inMemoryEvents;
536
+ }
537
+ }
538
+ };
539
+ return room;
540
+ }
541
+ function addParticipantToRoom(room, client, participants) {
542
+ if (room.maxSize !== void 0 && participants.size >= room.maxSize) {
543
+ return false;
544
+ }
545
+ participants.set(client.id, client);
546
+ client.socket.join(room.id);
547
+ return true;
548
+ }
549
+ function removeParticipantFromRoom(room, clientId, participants) {
550
+ const client = participants.get(clientId);
551
+ if (client) {
552
+ client.socket.leave(room.id);
553
+ participants.delete(clientId);
554
+ }
555
+ }
556
+ function createRoomManager(io, logger, historyManager, hooks, getContext) {
557
+ const rooms = /* @__PURE__ */ new Map();
558
+ const roomParticipants = /* @__PURE__ */ new Map();
559
+ return {
560
+ /**
561
+ * Registers a room from config
562
+ */
563
+ register(id, config) {
564
+ const participantsMap = /* @__PURE__ */ new Map();
565
+ roomParticipants.set(id, participantsMap);
566
+ const room = createRoom(
567
+ id,
568
+ config,
569
+ io,
570
+ logger,
571
+ historyManager,
572
+ hooks,
573
+ participantsMap,
574
+ getContext
575
+ );
576
+ rooms.set(id, room);
577
+ if (hooks?.rooms?.onCreated) {
578
+ Promise.resolve(hooks.rooms.onCreated(room)).catch((err) => {
579
+ logger.error({
580
+ message: `Error in onCreated hook for room '${id}'`,
581
+ atFunction: "roomManager.register.onCreated",
582
+ data: err
583
+ });
584
+ });
585
+ }
586
+ return room;
587
+ },
588
+ /**
589
+ * Gets a room by ID
590
+ */
591
+ get(id) {
592
+ return rooms.get(id) ?? null;
593
+ },
594
+ /**
595
+ * Gets all rooms
596
+ */
597
+ all() {
598
+ return Array.from(rooms.values());
599
+ },
600
+ /**
601
+ * Adds a participant to a room
602
+ */
603
+ addParticipant(roomId, client) {
604
+ const room = rooms.get(roomId);
605
+ const participants = roomParticipants.get(roomId);
606
+ if (!(room && participants)) {
607
+ return false;
608
+ }
609
+ return addParticipantToRoom(room, client, participants);
610
+ },
611
+ /**
612
+ * Removes a participant from a room
613
+ */
614
+ removeParticipant(roomId, clientId) {
615
+ const room = rooms.get(roomId);
616
+ const participants = roomParticipants.get(roomId);
617
+ if (room && participants) {
618
+ removeParticipantFromRoom(room, clientId, participants);
619
+ }
620
+ },
621
+ /**
622
+ * Removes a client from all rooms
623
+ */
624
+ removeFromAllRooms(clientId) {
625
+ for (const [roomId] of rooms) {
626
+ this.removeParticipant(roomId, clientId);
627
+ }
628
+ },
629
+ /**
630
+ * Gets participants for a room
631
+ */
632
+ getParticipants(roomId) {
633
+ const participants = roomParticipants.get(roomId);
634
+ return participants ? Array.from(participants.values()) : [];
635
+ },
636
+ /**
637
+ * Gets room size
638
+ */
639
+ getRoomSize(roomId) {
640
+ return roomParticipants.get(roomId)?.size ?? 0;
641
+ },
642
+ /**
643
+ * Unregisters (deletes) a room by ID.
644
+ * Removes all participants and cleans up resources.
645
+ * @returns true if room was deleted, false if it didn't exist
646
+ */
647
+ unregister(id) {
648
+ const room = rooms.get(id);
649
+ if (!room) {
650
+ return false;
651
+ }
652
+ const participants = roomParticipants.get(id);
653
+ if (participants) {
654
+ for (const client of participants.values()) {
655
+ client.socket.leave(id);
656
+ }
657
+ participants.clear();
658
+ }
659
+ if (historyManager) {
660
+ historyManager.clearRoom(id);
661
+ }
662
+ io.to(id).emit("dialogue:roomDeleted", { roomId: id });
663
+ rooms.delete(id);
664
+ roomParticipants.delete(id);
665
+ logger.info({
666
+ message: `Room '${id}' deleted`,
667
+ atFunction: "roomManager.unregister",
668
+ data: { roomId: id }
669
+ });
670
+ if (hooks?.rooms?.onDeleted) {
671
+ Promise.resolve(hooks.rooms.onDeleted(id)).catch((err) => {
672
+ logger.error({
673
+ message: `Error in onDeleted hook for room '${id}'`,
674
+ atFunction: "roomManager.unregister.onDeleted",
675
+ data: err
676
+ });
677
+ });
678
+ }
679
+ return true;
680
+ }
681
+ };
682
+ }
683
+
684
+ // src/server.ts
685
+ function buildCorsOptions(cors) {
686
+ if (cors === void 0 || cors === true) {
687
+ return {
688
+ origin: true,
689
+ methods: ["GET", "POST"],
690
+ credentials: true
691
+ };
692
+ }
693
+ if (cors === false) {
694
+ return { origin: false };
695
+ }
696
+ return {
697
+ origin: cors.origin,
698
+ methods: cors.methods ?? ["GET", "POST"],
699
+ credentials: cors.credentials ?? true
700
+ };
701
+ }
702
+ function buildHonoCorsOptions(cors) {
703
+ if (cors === void 0 || cors === true) {
704
+ return {
705
+ origin: "*",
706
+ allowMethods: ["GET", "POST", "OPTIONS"],
707
+ credentials: true
708
+ };
709
+ }
710
+ if (cors === false) {
711
+ return {
712
+ origin: () => void 0,
713
+ allowMethods: ["GET", "POST"],
714
+ credentials: false
715
+ };
716
+ }
717
+ const originValue = cors.origin;
718
+ let resolvedOrigin;
719
+ if (originValue === true) {
720
+ resolvedOrigin = "*";
721
+ } else if (originValue === false) {
722
+ resolvedOrigin = () => void 0;
723
+ } else {
724
+ resolvedOrigin = originValue;
725
+ }
726
+ return {
727
+ origin: resolvedOrigin,
728
+ allowMethods: cors.methods ?? ["GET", "POST", "OPTIONS"],
729
+ credentials: cors.credentials ?? true
730
+ };
731
+ }
732
+ function addCorsHeaders(response, request, corsConfig) {
733
+ const origin = request.headers.get("Origin");
734
+ if (!origin) {
735
+ return response;
736
+ }
737
+ const headers = new Headers(response.headers);
738
+ if (corsConfig === void 0 || corsConfig === true) {
739
+ headers.set("Access-Control-Allow-Origin", origin);
740
+ headers.set("Access-Control-Allow-Credentials", "true");
741
+ } else if (corsConfig === false) {
742
+ return response;
743
+ } else {
744
+ const allowedOrigin = corsConfig.origin;
745
+ if (allowedOrigin === true) {
746
+ headers.set("Access-Control-Allow-Origin", origin);
747
+ } else if (typeof allowedOrigin === "string" && allowedOrigin === origin) {
748
+ headers.set("Access-Control-Allow-Origin", origin);
749
+ } else if (Array.isArray(allowedOrigin) && allowedOrigin.includes(origin)) {
750
+ headers.set("Access-Control-Allow-Origin", origin);
751
+ }
752
+ if (corsConfig.credentials !== false) {
753
+ headers.set("Access-Control-Allow-Credentials", "true");
754
+ }
755
+ }
756
+ headers.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
757
+ headers.set("Access-Control-Allow-Headers", "Content-Type");
758
+ return new Response(response.body, {
759
+ status: response.status,
760
+ statusText: response.statusText,
761
+ headers
762
+ });
763
+ }
764
+ function sendHistoryOnJoin(socket, roomId, roomConfig, historyManager) {
765
+ if (!(roomConfig?.syncHistoryOnJoin && historyManager)) {
766
+ return;
767
+ }
768
+ const limit = typeof roomConfig.syncHistoryOnJoin === "number" ? roomConfig.syncHistoryOnJoin : void 0;
769
+ const historyEvents = historyManager.getAll(roomId, limit);
770
+ if (historyEvents.length > 0) {
771
+ socket.emit("dialogue:history", {
772
+ roomId,
773
+ events: historyEvents
774
+ });
775
+ }
776
+ }
777
+ function createDialogueContext(io, connectedClients, roomManager) {
778
+ const clientsRecord = {};
779
+ for (const [id, client] of connectedClients.entries()) {
780
+ clientsRecord[id] = client;
781
+ }
782
+ const roomsRecord = {};
783
+ for (const room of roomManager.all()) {
784
+ roomsRecord[room.id] = room;
785
+ }
786
+ return {
787
+ io,
788
+ clients: clientsRecord,
789
+ rooms: roomsRecord
790
+ };
791
+ }
792
+ function setupServer(app, config, historyManager) {
793
+ const logger = config.logger ?? createDefaultLogger();
794
+ const hooks = config.hooks;
795
+ const connectedClients = /* @__PURE__ */ new Map();
796
+ const userIdToSocketIds = /* @__PURE__ */ new Map();
797
+ const corsOptions = buildCorsOptions(config.cors);
798
+ const honoCorsOptions = buildHonoCorsOptions(config.cors);
799
+ app.use("*", (0, import_cors.cors)(honoCorsOptions));
800
+ const io = new import_socket.Server({ cors: corsOptions });
801
+ const engine = new import_bun_engine.Server();
802
+ io.bind(engine);
803
+ const getContextForHooks = () => {
804
+ return createDialogueContext(io, connectedClients, roomManager);
805
+ };
806
+ const roomManager = createRoomManager(
807
+ io,
808
+ logger,
809
+ historyManager,
810
+ hooks,
811
+ getContextForHooks
812
+ );
813
+ const historyRateLimiter = createRateLimiter({
814
+ maxRequests: 20,
815
+ windowMs: 6e4
816
+ });
817
+ for (const [roomId, roomConfig] of Object.entries(config.rooms)) {
818
+ roomManager.register(roomId, roomConfig);
819
+ }
820
+ if (hooks?.socket?.authenticate) {
821
+ const authenticateHook = hooks.socket.authenticate;
822
+ io.use(async (socket, next) => {
823
+ const authData = socket.handshake.auth;
824
+ const context = createDialogueContext(io, connectedClients, roomManager);
825
+ try {
826
+ const result = await Promise.resolve(
827
+ authenticateHook({
828
+ context,
829
+ clientSocket: socket,
830
+ authData
831
+ })
832
+ );
833
+ if (result.isErr) {
834
+ logger.warn({
835
+ message: `Authentication rejected: ${result.error}`,
836
+ atFunction: "setupServer.authenticate",
837
+ data: { socketId: socket.id }
838
+ });
839
+ return next(new Error(result.error));
840
+ }
841
+ socket.data = {
842
+ ...socket.data,
843
+ authenticatedAuthData: result.value
844
+ };
845
+ return next();
846
+ } catch (err) {
847
+ logger.error({
848
+ message: "Error in authenticate hook",
849
+ atFunction: "setupServer.authenticate",
850
+ data: err
851
+ });
852
+ return next(new Error("Authentication failed"));
853
+ }
854
+ });
855
+ }
856
+ io.on("connection", (socket) => {
857
+ let userId;
858
+ let meta;
859
+ let authData;
860
+ if (socket.data?.authenticatedAuthData) {
861
+ authData = socket.data.authenticatedAuthData;
862
+ userId = authData.jwt.sub;
863
+ meta = {};
864
+ } else {
865
+ const extracted = extractUserFromSocket(socket);
866
+ userId = extracted.userId;
867
+ meta = extracted.meta;
868
+ }
869
+ const client = createConnectedClient(
870
+ socket,
871
+ userId,
872
+ meta,
873
+ roomManager,
874
+ logger
875
+ );
876
+ if (authData) {
877
+ client.auth = authData;
878
+ }
879
+ connectedClients.set(socket.id, client);
880
+ const userSockets = userIdToSocketIds.get(client.userId) ?? /* @__PURE__ */ new Set();
881
+ userSockets.add(socket.id);
882
+ userIdToSocketIds.set(client.userId, userSockets);
883
+ socket.emit("dialogue:connected", {
884
+ clientId: client.id,
885
+ userId: client.userId
886
+ });
887
+ if (hooks?.clients?.onConnected) {
888
+ Promise.resolve(hooks.clients.onConnected(client)).catch((err) => {
889
+ logger.error({
890
+ message: "Error in onConnected hook",
891
+ atFunction: "setupServer.onConnected",
892
+ data: err
893
+ });
894
+ });
895
+ }
896
+ socket.on("dialogue:join", (data) => {
897
+ if (typeof data?.roomId !== "string") {
898
+ return;
899
+ }
900
+ const room = roomManager.get(data.roomId);
901
+ if (!room) {
902
+ socket.emit("dialogue:error", {
903
+ code: "ROOM_NOT_FOUND",
904
+ message: `Room '${data.roomId}' does not exist`
905
+ });
906
+ return;
907
+ }
908
+ if (hooks?.clients?.beforeJoin) {
909
+ const context = createDialogueContext(
910
+ io,
911
+ connectedClients,
912
+ roomManager
913
+ );
914
+ const joinResult = hooks.clients.beforeJoin({
915
+ context,
916
+ client,
917
+ roomId: data.roomId,
918
+ room
919
+ });
920
+ if (joinResult.isErr) {
921
+ logger.warn({
922
+ message: `Join denied for room '${data.roomId}': ${joinResult.error}`,
923
+ atFunction: "setupServer.beforeJoin",
924
+ data: {
925
+ roomId: data.roomId,
926
+ clientId: client.id,
927
+ reason: joinResult.error
928
+ }
929
+ });
930
+ socket.emit("dialogue:error", {
931
+ code: "JOIN_DENIED",
932
+ message: joinResult.error
933
+ });
934
+ return;
935
+ }
936
+ }
937
+ client.join(data.roomId);
938
+ if (hooks?.clients?.onJoined) {
939
+ Promise.resolve(hooks.clients.onJoined(client, data.roomId)).catch(
940
+ (err) => {
941
+ logger.error({
942
+ message: "Error in onJoined hook",
943
+ atFunction: "setupServer.onJoined",
944
+ data: err
945
+ });
946
+ }
947
+ );
948
+ }
949
+ sendHistoryOnJoin(
950
+ socket,
951
+ data.roomId,
952
+ config.rooms[data.roomId],
953
+ historyManager
954
+ );
955
+ });
956
+ socket.on("dialogue:leave", (data) => {
957
+ if (typeof data?.roomId === "string") {
958
+ client.leave(data.roomId);
959
+ if (hooks?.clients?.onLeft) {
960
+ Promise.resolve(hooks.clients.onLeft(client, data.roomId)).catch(
961
+ (err) => {
962
+ logger.error({
963
+ message: "Error in onLeft hook",
964
+ atFunction: "setupServer.onLeft",
965
+ data: err
966
+ });
967
+ }
968
+ );
969
+ }
970
+ }
971
+ });
972
+ socket.on(
973
+ "dialogue:subscribe",
974
+ (data) => {
975
+ if (typeof data?.roomId === "string" && typeof data?.eventName === "string") {
976
+ client.subscribe(data.roomId, data.eventName);
977
+ }
978
+ }
979
+ );
980
+ socket.on("dialogue:subscribeAll", (data) => {
981
+ if (typeof data?.roomId === "string") {
982
+ client.subscribeAll(data.roomId);
983
+ }
984
+ });
985
+ socket.on(
986
+ "dialogue:unsubscribe",
987
+ (data) => {
988
+ if (typeof data?.roomId === "string" && typeof data?.eventName === "string") {
989
+ client.unsubscribe(data.roomId, data.eventName);
990
+ }
991
+ }
992
+ );
993
+ socket.on(
994
+ "dialogue:getHistory",
995
+ async (data) => {
996
+ if (!historyRateLimiter.isAllowed(socket.id)) {
997
+ socket.emit("dialogue:error", {
998
+ code: "RATE_LIMITED",
999
+ message: "Too many history requests. Please wait before trying again."
1000
+ });
1001
+ return;
1002
+ }
1003
+ if (typeof data?.roomId !== "string") {
1004
+ socket.emit("dialogue:error", {
1005
+ code: "INVALID_REQUEST",
1006
+ message: "roomId is required"
1007
+ });
1008
+ return;
1009
+ }
1010
+ const room = roomManager.get(data.roomId);
1011
+ if (!room) {
1012
+ socket.emit("dialogue:error", {
1013
+ code: "ROOM_NOT_FOUND",
1014
+ message: `Room '${data.roomId}' does not exist`
1015
+ });
1016
+ return;
1017
+ }
1018
+ const start = data.start ?? 0;
1019
+ const end = data.end ?? 50;
1020
+ if (!data.eventName) {
1021
+ socket.emit("dialogue:historyResponse", {
1022
+ roomId: data.roomId,
1023
+ eventName: null,
1024
+ events: [],
1025
+ start,
1026
+ end
1027
+ });
1028
+ return;
1029
+ }
1030
+ const events = await room.history(data.eventName, start, end);
1031
+ socket.emit("dialogue:historyResponse", {
1032
+ roomId: data.roomId,
1033
+ eventName: data.eventName,
1034
+ events,
1035
+ start,
1036
+ end
1037
+ });
1038
+ }
1039
+ );
1040
+ socket.on(
1041
+ "dialogue:trigger",
1042
+ (data) => {
1043
+ if (typeof data?.roomId !== "string" || typeof data?.event !== "string") {
1044
+ return;
1045
+ }
1046
+ const room = roomManager.get(data.roomId);
1047
+ if (!room) {
1048
+ socket.emit("dialogue:error", {
1049
+ code: "ROOM_NOT_FOUND",
1050
+ message: `Room '${data.roomId}' does not exist`
1051
+ });
1052
+ return;
1053
+ }
1054
+ const eventDef = room.events.find((e) => e.name === data.event);
1055
+ const hasWildcard = room.events.some((e) => e.name === "*");
1056
+ if (!(eventDef || hasWildcard)) {
1057
+ socket.emit("dialogue:error", {
1058
+ code: "EVENT_NOT_ALLOWED",
1059
+ message: `Event '${data.event}' is not allowed in room '${data.roomId}'`
1060
+ });
1061
+ return;
1062
+ }
1063
+ const triggerEvent = eventDef ?? { name: data.event };
1064
+ const result = room.trigger(
1065
+ triggerEvent,
1066
+ data.data,
1067
+ client.userId,
1068
+ data.meta
1069
+ );
1070
+ if (result.isErr) {
1071
+ socket.emit("dialogue:error", {
1072
+ code: "VALIDATION_FAILED",
1073
+ message: result.error
1074
+ });
1075
+ }
1076
+ }
1077
+ );
1078
+ socket.on("dialogue:listRooms", () => {
1079
+ const rooms = roomManager.all().map((room) => ({
1080
+ id: room.id,
1081
+ name: room.name,
1082
+ description: room.description,
1083
+ size: roomManager.getRoomSize(room.id),
1084
+ maxSize: room.maxSize
1085
+ }));
1086
+ socket.emit("dialogue:rooms", rooms);
1087
+ });
1088
+ socket.on(
1089
+ "dialogue:createRoom",
1090
+ (data) => {
1091
+ if (typeof data?.id !== "string" || typeof data?.name !== "string") {
1092
+ socket.emit("dialogue:error", {
1093
+ code: "INVALID_REQUEST",
1094
+ message: "Room id and name are required"
1095
+ });
1096
+ return;
1097
+ }
1098
+ if (roomManager.get(data.id)) {
1099
+ socket.emit("dialogue:error", {
1100
+ code: "ROOM_EXISTS",
1101
+ message: `Room '${data.id}' already exists`
1102
+ });
1103
+ return;
1104
+ }
1105
+ const room = roomManager.register(data.id, {
1106
+ name: data.name,
1107
+ description: data.description,
1108
+ maxSize: data.maxSize,
1109
+ events: [],
1110
+ createdById: client.userId
1111
+ });
1112
+ socket.emit("dialogue:roomCreated", {
1113
+ id: room.id,
1114
+ name: room.name,
1115
+ description: room.description,
1116
+ maxSize: room.maxSize,
1117
+ createdById: room.createdById
1118
+ });
1119
+ socket.broadcast.emit("dialogue:roomCreated", {
1120
+ id: room.id,
1121
+ name: room.name,
1122
+ description: room.description,
1123
+ maxSize: room.maxSize,
1124
+ createdById: room.createdById
1125
+ });
1126
+ logger.info({
1127
+ message: `Room '${data.id}' created by ${client.userId}`,
1128
+ atFunction: "setupServer.createRoom",
1129
+ data: { roomId: data.id, createdBy: client.userId }
1130
+ });
1131
+ }
1132
+ );
1133
+ socket.on("dialogue:deleteRoom", (data) => {
1134
+ if (typeof data?.roomId !== "string") {
1135
+ return;
1136
+ }
1137
+ const room = roomManager.get(data.roomId);
1138
+ if (!room) {
1139
+ socket.emit("dialogue:error", {
1140
+ code: "ROOM_NOT_FOUND",
1141
+ message: `Room '${data.roomId}' does not exist`
1142
+ });
1143
+ return;
1144
+ }
1145
+ if (room.createdById && room.createdById !== client.userId) {
1146
+ socket.emit("dialogue:error", {
1147
+ code: "PERMISSION_DENIED",
1148
+ message: "Only the room creator can delete this room"
1149
+ });
1150
+ return;
1151
+ }
1152
+ const deleted = roomManager.unregister(data.roomId);
1153
+ if (deleted) {
1154
+ io.emit("dialogue:roomDeleted", { roomId: data.roomId });
1155
+ logger.info({
1156
+ message: `Room '${data.roomId}' deleted by ${client.userId}`,
1157
+ atFunction: "setupServer.deleteRoom",
1158
+ data: { roomId: data.roomId, deletedBy: client.userId }
1159
+ });
1160
+ }
1161
+ });
1162
+ socket.on("disconnect", () => {
1163
+ if (hooks?.clients?.onDisconnected) {
1164
+ Promise.resolve(hooks.clients.onDisconnected(client)).catch((err) => {
1165
+ logger.error({
1166
+ message: "Error in onDisconnected hook",
1167
+ atFunction: "setupServer.onDisconnected",
1168
+ data: err
1169
+ });
1170
+ });
1171
+ }
1172
+ roomManager.removeFromAllRooms(client.id);
1173
+ connectedClients.delete(socket.id);
1174
+ const userSockets2 = userIdToSocketIds.get(client.userId);
1175
+ if (userSockets2) {
1176
+ userSockets2.delete(socket.id);
1177
+ if (userSockets2.size === 0) {
1178
+ userIdToSocketIds.delete(client.userId);
1179
+ }
1180
+ }
1181
+ });
1182
+ });
1183
+ const { websocket } = engine.handler();
1184
+ const port = config.port ?? 3e3;
1185
+ let bunServer = null;
1186
+ return {
1187
+ io,
1188
+ engine,
1189
+ roomManager,
1190
+ start() {
1191
+ bunServer = Bun.serve({
1192
+ port,
1193
+ idleTimeout: 30,
1194
+ async fetch(req, server) {
1195
+ const url = new URL(req.url);
1196
+ if (url.pathname.startsWith("/socket.io")) {
1197
+ if (req.method === "OPTIONS") {
1198
+ return addCorsHeaders(
1199
+ new Response(null, { status: 204 }),
1200
+ req,
1201
+ config.cors
1202
+ );
1203
+ }
1204
+ const response = await engine.handleRequest(req, server);
1205
+ return addCorsHeaders(response, req, config.cors);
1206
+ }
1207
+ return app.fetch(req);
1208
+ },
1209
+ websocket
1210
+ });
1211
+ logger.info({
1212
+ message: `Server running on http://localhost:${port}`,
1213
+ atFunction: "setupServer.start",
1214
+ data: { port }
1215
+ });
1216
+ return Promise.resolve();
1217
+ },
1218
+ stop() {
1219
+ if (bunServer) {
1220
+ bunServer.stop();
1221
+ bunServer = null;
1222
+ }
1223
+ for (const client of connectedClients.values()) {
1224
+ client.disconnect();
1225
+ }
1226
+ connectedClients.clear();
1227
+ userIdToSocketIds.clear();
1228
+ io.close();
1229
+ logger.info({
1230
+ message: "Server stopped",
1231
+ atFunction: "setupServer.stop",
1232
+ data: null
1233
+ });
1234
+ return Promise.resolve();
1235
+ },
1236
+ /**
1237
+ * Gets a connected client by socket ID
1238
+ */
1239
+ getConnectedClient(socketId) {
1240
+ return connectedClients.get(socketId);
1241
+ },
1242
+ /**
1243
+ * Gets all connected clients
1244
+ */
1245
+ getAllConnectedClients() {
1246
+ return Array.from(connectedClients.values());
1247
+ },
1248
+ /**
1249
+ * Gets all connected clients for a specific user ID.
1250
+ * Returns array since a user may have multiple connections.
1251
+ */
1252
+ getClientsByUserId(userId) {
1253
+ const socketIds = userIdToSocketIds.get(userId);
1254
+ if (!socketIds) {
1255
+ return [];
1256
+ }
1257
+ const clients = [];
1258
+ for (const socketId of socketIds) {
1259
+ const client = connectedClients.get(socketId);
1260
+ if (client) {
1261
+ clients.push(client);
1262
+ }
1263
+ }
1264
+ return clients;
1265
+ },
1266
+ /**
1267
+ * Gets all room IDs that a user is currently in.
1268
+ * Aggregates rooms across all connections for this user.
1269
+ */
1270
+ getClientRooms(userId) {
1271
+ const clients = this.getClientsByUserId(userId);
1272
+ const roomSet = /* @__PURE__ */ new Set();
1273
+ for (const client of clients) {
1274
+ for (const roomId of client.rooms()) {
1275
+ roomSet.add(roomId);
1276
+ }
1277
+ }
1278
+ return Array.from(roomSet);
1279
+ },
1280
+ /**
1281
+ * Checks if a user is in a specific room (any of their connections)
1282
+ */
1283
+ isUserInRoom(userId, roomId) {
1284
+ const clients = this.getClientsByUserId(userId);
1285
+ return clients.some((client) => client.rooms().includes(roomId));
1286
+ }
1287
+ };
1288
+ }
1289
+
1290
+ // src/create-dialogue.ts
1291
+ function createDialogue(config) {
1292
+ const app = config.app ?? new import_hono.Hono();
1293
+ const logger = config.logger ?? createDefaultLogger();
1294
+ const historyManager = createHistoryManager({
1295
+ onCleanup: config.hooks?.events?.onCleanup,
1296
+ onLoad: config.hooks?.events?.onLoad
1297
+ });
1298
+ const {
1299
+ io,
1300
+ roomManager,
1301
+ start,
1302
+ stop,
1303
+ getConnectedClient: _getConnectedClient,
1304
+ getAllConnectedClients,
1305
+ getClientsByUserId,
1306
+ getClientRooms: getClientRoomIds,
1307
+ isUserInRoom
1308
+ } = setupServer(app, config, historyManager);
1309
+ const dialogue = {
1310
+ app,
1311
+ io,
1312
+ trigger(roomId, event, data, from, meta) {
1313
+ const room = roomManager.get(roomId);
1314
+ if (!room) {
1315
+ const errorMsg = `Room '${roomId}' does not exist`;
1316
+ logger.warn({
1317
+ message: errorMsg,
1318
+ atFunction: "dialogue.trigger",
1319
+ data: { roomId, eventName: event.name }
1320
+ });
1321
+ return (0, import_slang_ts3.Err)(errorMsg);
1322
+ }
1323
+ return room.trigger(event, data, from, meta);
1324
+ },
1325
+ on(roomId, event, handler) {
1326
+ const room = roomManager.get(roomId);
1327
+ if (!room) {
1328
+ logger.warn({
1329
+ message: `Room '${roomId}' does not exist`,
1330
+ atFunction: "dialogue.on",
1331
+ data: { roomId, eventName: event.name }
1332
+ });
1333
+ return () => {
1334
+ };
1335
+ }
1336
+ return room.on(event, handler);
1337
+ },
1338
+ room(id) {
1339
+ return roomManager.get(id);
1340
+ },
1341
+ rooms() {
1342
+ return roomManager.all();
1343
+ },
1344
+ createRoom(id, config2) {
1345
+ if (roomManager.get(id)) {
1346
+ logger.warn({
1347
+ message: `Room '${id}' already exists`,
1348
+ atFunction: "dialogue.createRoom",
1349
+ data: { roomId: id }
1350
+ });
1351
+ return null;
1352
+ }
1353
+ const room = roomManager.register(id, config2);
1354
+ io.emit("dialogue:roomCreated", {
1355
+ id: room.id,
1356
+ name: room.name,
1357
+ description: room.description,
1358
+ maxSize: room.maxSize
1359
+ });
1360
+ logger.info({
1361
+ message: `Room '${id}' created`,
1362
+ atFunction: "dialogue.createRoom",
1363
+ data: { roomId: id, roomName: config2.name }
1364
+ });
1365
+ return room;
1366
+ },
1367
+ deleteRoom(id) {
1368
+ const deleted = roomManager.unregister(id);
1369
+ if (deleted) {
1370
+ io.emit("dialogue:roomDeleted", { roomId: id });
1371
+ }
1372
+ return deleted;
1373
+ },
1374
+ getClients(userId) {
1375
+ return getClientsByUserId(userId);
1376
+ },
1377
+ getAllClients() {
1378
+ return getAllConnectedClients();
1379
+ },
1380
+ getClientRooms(userId) {
1381
+ const roomIds = getClientRoomIds(userId);
1382
+ const clients = getClientsByUserId(userId);
1383
+ return {
1384
+ ids: roomIds,
1385
+ forAll(callback) {
1386
+ for (const roomId of roomIds) {
1387
+ callback(roomId);
1388
+ }
1389
+ },
1390
+ leaveAll(callback) {
1391
+ if (callback) {
1392
+ for (const roomId of roomIds) {
1393
+ callback(roomId);
1394
+ }
1395
+ }
1396
+ for (const client of clients) {
1397
+ for (const roomId of client.rooms()) {
1398
+ client.leave(roomId);
1399
+ }
1400
+ }
1401
+ }
1402
+ };
1403
+ },
1404
+ isInRoom(userId, roomId) {
1405
+ return isUserInRoom(userId, roomId);
1406
+ },
1407
+ start,
1408
+ stop
1409
+ };
1410
+ return dialogue;
1411
+ }
1412
+ // Annotate the CommonJS export names for ESM import in node:
1413
+ 0 && (module.exports = {
1414
+ createDefaultLogger,
1415
+ createDialogue,
1416
+ createHistoryManager,
1417
+ createRateLimiter,
1418
+ createSilentLogger,
1419
+ defineEvent,
1420
+ getEventByName,
1421
+ isEventAllowed,
1422
+ validateEventData
1423
+ });
1424
+ //# sourceMappingURL=index.cjs.map