@guren/server 0.2.0-alpha.7 → 1.0.0-rc.9

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 (54) hide show
  1. package/dist/Application-DtWDHXr1.d.ts +2110 -0
  2. package/dist/BroadcastManager-AkIWUGJo.d.ts +466 -0
  3. package/dist/CacheManager-BkvHEOZX.d.ts +244 -0
  4. package/dist/ConsoleKernel-CqCVrdZs.d.ts +207 -0
  5. package/dist/EventManager-CmIoLt7r.d.ts +207 -0
  6. package/dist/Gate-CNkBYf8m.d.ts +268 -0
  7. package/dist/HealthManager-DUyMIzsZ.d.ts +141 -0
  8. package/dist/I18nManager-Dtgzsf5n.d.ts +270 -0
  9. package/dist/LogManager-7mxnkaPM.d.ts +256 -0
  10. package/dist/MailManager-DpMvYiP9.d.ts +292 -0
  11. package/dist/Scheduler-BstvSca7.d.ts +469 -0
  12. package/dist/StorageManager-oZTHqaza.d.ts +337 -0
  13. package/dist/api-token-JOif2CtG.d.ts +1792 -0
  14. package/dist/app-key-CsBfRC_Q.d.ts +214 -0
  15. package/dist/auth/index.d.ts +418 -0
  16. package/dist/auth/index.js +6742 -0
  17. package/dist/authorization/index.d.ts +129 -0
  18. package/dist/authorization/index.js +621 -0
  19. package/dist/broadcasting/index.d.ts +233 -0
  20. package/dist/broadcasting/index.js +907 -0
  21. package/dist/cache/index.d.ts +233 -0
  22. package/dist/cache/index.js +817 -0
  23. package/dist/encryption/index.d.ts +222 -0
  24. package/dist/encryption/index.js +602 -0
  25. package/dist/events/index.d.ts +155 -0
  26. package/dist/events/index.js +330 -0
  27. package/dist/health/index.d.ts +185 -0
  28. package/dist/health/index.js +379 -0
  29. package/dist/i18n/index.d.ts +101 -0
  30. package/dist/i18n/index.js +597 -0
  31. package/dist/index-9_Jzj5jo.d.ts +7 -0
  32. package/dist/index.d.ts +2628 -619
  33. package/dist/index.js +22229 -3116
  34. package/dist/lambda/index.d.ts +156 -0
  35. package/dist/lambda/index.js +91 -0
  36. package/dist/logging/index.d.ts +50 -0
  37. package/dist/logging/index.js +557 -0
  38. package/dist/mail/index.d.ts +288 -0
  39. package/dist/mail/index.js +695 -0
  40. package/dist/mcp/index.d.ts +139 -0
  41. package/dist/mcp/index.js +382 -0
  42. package/dist/notifications/index.d.ts +271 -0
  43. package/dist/notifications/index.js +741 -0
  44. package/dist/queue/index.d.ts +423 -0
  45. package/dist/queue/index.js +958 -0
  46. package/dist/runtime/index.d.ts +93 -0
  47. package/dist/runtime/index.js +834 -0
  48. package/dist/scheduling/index.d.ts +41 -0
  49. package/dist/scheduling/index.js +836 -0
  50. package/dist/storage/index.d.ts +196 -0
  51. package/dist/storage/index.js +832 -0
  52. package/dist/vite/index.js +203 -3
  53. package/package.json +93 -6
  54. package/dist/chunk-FK2XQSBF.js +0 -160
@@ -0,0 +1,907 @@
1
+ // src/broadcasting/channels/Channel.ts
2
+ var Channel = class {
3
+ constructor(name, driver) {
4
+ this.name = name;
5
+ this.driver = driver;
6
+ }
7
+ /**
8
+ * Broadcast an event to this channel.
9
+ */
10
+ async broadcast(event, data) {
11
+ await this.driver.publish(this.name, event, data);
12
+ }
13
+ /**
14
+ * Subscribe to events on this channel.
15
+ * Returns an unsubscribe function.
16
+ */
17
+ subscribe(callback) {
18
+ return this.driver.subscribe(this.name, (e) => {
19
+ callback(e.event, e.data);
20
+ });
21
+ }
22
+ /**
23
+ * Get the full channel name for broadcasting.
24
+ */
25
+ getChannelName() {
26
+ return this.name;
27
+ }
28
+ };
29
+
30
+ // src/broadcasting/channels/PrivateChannel.ts
31
+ var PrivateChannel = class _PrivateChannel extends Channel {
32
+ /**
33
+ * Channel name prefix.
34
+ */
35
+ static PREFIX = "private-";
36
+ constructor(name, driver) {
37
+ const channelName = name.startsWith(_PrivateChannel.PREFIX) ? name : `${_PrivateChannel.PREFIX}${name}`;
38
+ super(channelName, driver);
39
+ }
40
+ /**
41
+ * Get the channel name without the private prefix.
42
+ */
43
+ getBaseName() {
44
+ return this.name.startsWith(_PrivateChannel.PREFIX) ? this.name.slice(_PrivateChannel.PREFIX.length) : this.name;
45
+ }
46
+ /**
47
+ * Check if a channel name is a private channel.
48
+ */
49
+ static isPrivateChannel(name) {
50
+ return name.startsWith(_PrivateChannel.PREFIX);
51
+ }
52
+ /**
53
+ * Normalize a channel name to include the private prefix.
54
+ */
55
+ static normalize(name) {
56
+ return name.startsWith(_PrivateChannel.PREFIX) ? name : `${_PrivateChannel.PREFIX}${name}`;
57
+ }
58
+ };
59
+
60
+ // src/broadcasting/channels/PresenceChannel.ts
61
+ var PresenceChannel = class _PresenceChannel extends Channel {
62
+ /**
63
+ * Channel name prefix.
64
+ */
65
+ static PREFIX = "presence-";
66
+ presenceDriver;
67
+ constructor(name, driver) {
68
+ const channelName = name.startsWith(_PresenceChannel.PREFIX) ? name : `${_PresenceChannel.PREFIX}${name}`;
69
+ super(channelName, driver);
70
+ if (this.isPresenceDriver(driver)) {
71
+ this.presenceDriver = driver;
72
+ } else {
73
+ throw new Error(
74
+ `Driver does not support presence channels. Ensure the driver implements PresenceBroadcastDriver interface.`
75
+ );
76
+ }
77
+ }
78
+ /**
79
+ * Check if a driver supports presence.
80
+ */
81
+ isPresenceDriver(driver) {
82
+ return "getMembers" in driver && "addMember" in driver && "removeMember" in driver;
83
+ }
84
+ /**
85
+ * Get members in this channel.
86
+ */
87
+ members() {
88
+ return this.presenceDriver.getMembers(this.name);
89
+ }
90
+ /**
91
+ * Join the channel.
92
+ */
93
+ async join(member) {
94
+ this.presenceDriver.addMember(this.name, member);
95
+ await this.broadcast("presence:joining", {
96
+ member
97
+ });
98
+ }
99
+ /**
100
+ * Leave the channel.
101
+ */
102
+ async leave(memberId) {
103
+ const members = this.members();
104
+ const member = members.find((m) => m.id === memberId);
105
+ this.presenceDriver.removeMember(this.name, memberId);
106
+ if (member) {
107
+ await this.broadcast("presence:leaving", {
108
+ member
109
+ });
110
+ }
111
+ }
112
+ /**
113
+ * Check if a member is in the channel.
114
+ */
115
+ hasMember(memberId) {
116
+ return this.members().some((m) => m.id === memberId);
117
+ }
118
+ /**
119
+ * Get member by ID.
120
+ */
121
+ getMember(memberId) {
122
+ return this.members().find((m) => m.id === memberId);
123
+ }
124
+ /**
125
+ * Get member count.
126
+ */
127
+ count() {
128
+ return this.members().length;
129
+ }
130
+ /**
131
+ * Get the channel name without the presence prefix.
132
+ */
133
+ getBaseName() {
134
+ return this.name.startsWith(_PresenceChannel.PREFIX) ? this.name.slice(_PresenceChannel.PREFIX.length) : this.name;
135
+ }
136
+ /**
137
+ * Check if a channel name is a presence channel.
138
+ */
139
+ static isPresenceChannel(name) {
140
+ return name.startsWith(_PresenceChannel.PREFIX);
141
+ }
142
+ /**
143
+ * Normalize a channel name to include the presence prefix.
144
+ */
145
+ static normalize(name) {
146
+ return name.startsWith(_PresenceChannel.PREFIX) ? name : `${_PresenceChannel.PREFIX}${name}`;
147
+ }
148
+ };
149
+
150
+ // src/broadcasting/drivers/MemoryDriver.ts
151
+ var MemoryDriver = class {
152
+ /**
153
+ * Channel subscribers.
154
+ */
155
+ subscribers = /* @__PURE__ */ new Map();
156
+ /**
157
+ * Presence channel members.
158
+ */
159
+ presenceMembers = /* @__PURE__ */ new Map();
160
+ /**
161
+ * Published events (for testing).
162
+ */
163
+ publishedEvents = [];
164
+ /**
165
+ * Publish an event to a channel.
166
+ */
167
+ async publish(channel, event, data) {
168
+ const broadcastEvent = {
169
+ channel,
170
+ event,
171
+ data,
172
+ timestamp: /* @__PURE__ */ new Date()
173
+ };
174
+ this.publishedEvents.push(broadcastEvent);
175
+ const callbacks = this.subscribers.get(channel);
176
+ if (callbacks) {
177
+ for (const callback of callbacks) {
178
+ try {
179
+ callback(broadcastEvent);
180
+ } catch (error) {
181
+ console.error(`Error in broadcast subscriber:`, error);
182
+ }
183
+ }
184
+ }
185
+ }
186
+ /**
187
+ * Subscribe to a channel.
188
+ */
189
+ subscribe(channel, callback) {
190
+ let callbacks = this.subscribers.get(channel);
191
+ if (!callbacks) {
192
+ callbacks = /* @__PURE__ */ new Set();
193
+ this.subscribers.set(channel, callbacks);
194
+ }
195
+ callbacks.add(callback);
196
+ return () => {
197
+ this.unsubscribe(channel, callback);
198
+ };
199
+ }
200
+ /**
201
+ * Unsubscribe from a channel.
202
+ */
203
+ unsubscribe(channel, callback) {
204
+ const callbacks = this.subscribers.get(channel);
205
+ if (callbacks) {
206
+ callbacks.delete(callback);
207
+ if (callbacks.size === 0) {
208
+ this.subscribers.delete(channel);
209
+ }
210
+ }
211
+ }
212
+ /**
213
+ * Get members of a presence channel.
214
+ */
215
+ getMembers(channel) {
216
+ const members = this.presenceMembers.get(channel);
217
+ return members ? Array.from(members.values()) : [];
218
+ }
219
+ /**
220
+ * Add a member to a presence channel.
221
+ */
222
+ addMember(channel, member) {
223
+ let members = this.presenceMembers.get(channel);
224
+ if (!members) {
225
+ members = /* @__PURE__ */ new Map();
226
+ this.presenceMembers.set(channel, members);
227
+ }
228
+ members.set(member.id, member);
229
+ }
230
+ /**
231
+ * Remove a member from a presence channel.
232
+ */
233
+ removeMember(channel, memberId) {
234
+ const members = this.presenceMembers.get(channel);
235
+ if (members) {
236
+ members.delete(memberId);
237
+ if (members.size === 0) {
238
+ this.presenceMembers.delete(channel);
239
+ }
240
+ }
241
+ }
242
+ /**
243
+ * Check if a channel has subscribers.
244
+ */
245
+ hasSubscribers(channel) {
246
+ const callbacks = this.subscribers.get(channel);
247
+ return callbacks !== void 0 && callbacks.size > 0;
248
+ }
249
+ /**
250
+ * Get subscriber count for a channel.
251
+ */
252
+ getSubscriberCount(channel) {
253
+ const callbacks = this.subscribers.get(channel);
254
+ return callbacks ? callbacks.size : 0;
255
+ }
256
+ /**
257
+ * Get all channels with subscribers.
258
+ */
259
+ getChannels() {
260
+ return Array.from(this.subscribers.keys());
261
+ }
262
+ /**
263
+ * Get all presence channels.
264
+ */
265
+ getPresenceChannels() {
266
+ return Array.from(this.presenceMembers.keys());
267
+ }
268
+ /**
269
+ * Get all published events (for testing).
270
+ */
271
+ getPublishedEvents() {
272
+ return [...this.publishedEvents];
273
+ }
274
+ /**
275
+ * Get published events for a specific channel.
276
+ */
277
+ getPublishedEventsFor(channel) {
278
+ return this.publishedEvents.filter((e) => e.channel === channel);
279
+ }
280
+ /**
281
+ * Clear all data (for testing).
282
+ */
283
+ clear() {
284
+ this.subscribers.clear();
285
+ this.presenceMembers.clear();
286
+ this.publishedEvents = [];
287
+ }
288
+ /**
289
+ * Clear published events only.
290
+ */
291
+ clearPublishedEvents() {
292
+ this.publishedEvents = [];
293
+ }
294
+ };
295
+
296
+ // src/broadcasting/drivers/RedisDriver.ts
297
+ var RedisDriver = class {
298
+ /**
299
+ * Publisher Redis client.
300
+ */
301
+ publisher;
302
+ /**
303
+ * Subscriber Redis client.
304
+ */
305
+ subscriber;
306
+ /**
307
+ * Local subscribers.
308
+ */
309
+ subscribers = /* @__PURE__ */ new Map();
310
+ /**
311
+ * Local presence members cache.
312
+ */
313
+ localPresence = /* @__PURE__ */ new Map();
314
+ /**
315
+ * Presence key prefix.
316
+ */
317
+ presencePrefix = "broadcasting:presence:";
318
+ /**
319
+ * Whether the driver is initialized.
320
+ */
321
+ initialized = false;
322
+ constructor(redis, options = {}) {
323
+ this.publisher = redis;
324
+ this.subscriber = redis.duplicate();
325
+ if (options.presencePrefix) {
326
+ this.presencePrefix = options.presencePrefix;
327
+ }
328
+ this.setupSubscriber();
329
+ }
330
+ /**
331
+ * Setup the subscriber client.
332
+ */
333
+ setupSubscriber() {
334
+ this.subscriber.on("message", (channel, message) => {
335
+ try {
336
+ const event = JSON.parse(message);
337
+ event.timestamp = new Date(event.timestamp);
338
+ const callbacks = this.subscribers.get(channel);
339
+ if (callbacks) {
340
+ for (const callback of callbacks) {
341
+ try {
342
+ callback(event);
343
+ } catch (error) {
344
+ console.error(`Error in broadcast subscriber:`, error);
345
+ }
346
+ }
347
+ }
348
+ } catch (error) {
349
+ console.error(`Error parsing broadcast message:`, error);
350
+ }
351
+ });
352
+ this.initialized = true;
353
+ }
354
+ /**
355
+ * Publish an event to a channel.
356
+ */
357
+ async publish(channel, event, data) {
358
+ const broadcastEvent = {
359
+ channel,
360
+ event,
361
+ data,
362
+ timestamp: /* @__PURE__ */ new Date()
363
+ };
364
+ await this.publisher.publish(channel, JSON.stringify(broadcastEvent));
365
+ }
366
+ /**
367
+ * Subscribe to a channel.
368
+ */
369
+ subscribe(channel, callback) {
370
+ let callbacks = this.subscribers.get(channel);
371
+ const isNew = !callbacks;
372
+ if (!callbacks) {
373
+ callbacks = /* @__PURE__ */ new Set();
374
+ this.subscribers.set(channel, callbacks);
375
+ }
376
+ callbacks.add(callback);
377
+ if (isNew) {
378
+ this.subscriber.subscribe(channel).catch((error) => {
379
+ console.error(`Error subscribing to Redis channel:`, error);
380
+ });
381
+ }
382
+ return () => {
383
+ this.unsubscribe(channel, callback);
384
+ };
385
+ }
386
+ /**
387
+ * Unsubscribe from a channel.
388
+ */
389
+ unsubscribe(channel, callback) {
390
+ const callbacks = this.subscribers.get(channel);
391
+ if (callbacks) {
392
+ callbacks.delete(callback);
393
+ if (callbacks.size === 0) {
394
+ this.subscribers.delete(channel);
395
+ this.subscriber.unsubscribe(channel).catch((error) => {
396
+ console.error(`Error unsubscribing from Redis channel:`, error);
397
+ });
398
+ }
399
+ }
400
+ }
401
+ /**
402
+ * Get members of a presence channel.
403
+ */
404
+ getMembers(channel) {
405
+ const members = this.localPresence.get(channel);
406
+ return members ? Array.from(members.values()) : [];
407
+ }
408
+ /**
409
+ * Get members asynchronously from Redis.
410
+ */
411
+ async getMembersAsync(channel) {
412
+ const key = `${this.presencePrefix}${channel}`;
413
+ const data = await this.publisher.hgetall(key);
414
+ const members = [];
415
+ for (const [_, value] of Object.entries(data)) {
416
+ try {
417
+ members.push(JSON.parse(value));
418
+ } catch {
419
+ }
420
+ }
421
+ const localMembers = /* @__PURE__ */ new Map();
422
+ for (const member of members) {
423
+ localMembers.set(member.id, member);
424
+ }
425
+ this.localPresence.set(channel, localMembers);
426
+ return members;
427
+ }
428
+ /**
429
+ * Add a member to a presence channel.
430
+ */
431
+ addMember(channel, member) {
432
+ let members = this.localPresence.get(channel);
433
+ if (!members) {
434
+ members = /* @__PURE__ */ new Map();
435
+ this.localPresence.set(channel, members);
436
+ }
437
+ members.set(member.id, member);
438
+ const key = `${this.presencePrefix}${channel}`;
439
+ this.publisher.hset(key, String(member.id), JSON.stringify(member)).catch((error) => {
440
+ console.error(`Error adding presence member to Redis:`, error);
441
+ });
442
+ }
443
+ /**
444
+ * Remove a member from a presence channel.
445
+ */
446
+ removeMember(channel, memberId) {
447
+ const members = this.localPresence.get(channel);
448
+ if (members) {
449
+ members.delete(memberId);
450
+ if (members.size === 0) {
451
+ this.localPresence.delete(channel);
452
+ }
453
+ }
454
+ const key = `${this.presencePrefix}${channel}`;
455
+ this.publisher.hdel(key, String(memberId)).catch((error) => {
456
+ console.error(`Error removing presence member from Redis:`, error);
457
+ });
458
+ }
459
+ /**
460
+ * Check if a channel has subscribers.
461
+ */
462
+ hasSubscribers(channel) {
463
+ const callbacks = this.subscribers.get(channel);
464
+ return callbacks !== void 0 && callbacks.size > 0;
465
+ }
466
+ /**
467
+ * Get subscriber count for a channel.
468
+ */
469
+ getSubscriberCount(channel) {
470
+ const callbacks = this.subscribers.get(channel);
471
+ return callbacks ? callbacks.size : 0;
472
+ }
473
+ };
474
+
475
+ // src/http/request.ts
476
+ function isPlainObject(value) {
477
+ return typeof value === "object" && value !== null && !Array.isArray(value);
478
+ }
479
+ async function parseRequestPayload(ctx) {
480
+ const contentType = ctx.req.header("content-type") ?? "";
481
+ if (contentType.includes("application/json")) {
482
+ const body = await ctx.req.json().catch(() => ({}));
483
+ return isPlainObject(body) ? body : {};
484
+ }
485
+ if (typeof ctx.req.parseBody === "function") {
486
+ const form = await ctx.req.parseBody();
487
+ return Object.fromEntries(
488
+ Object.entries(form).map(([key, value]) => [key, Array.isArray(value) ? value[0] : value])
489
+ );
490
+ }
491
+ return {};
492
+ }
493
+
494
+ // src/broadcasting/BroadcastManager.ts
495
+ var BroadcastManager = class {
496
+ /**
497
+ * Default driver name.
498
+ */
499
+ defaultDriver = "memory";
500
+ /**
501
+ * Driver factories.
502
+ */
503
+ driverFactories = /* @__PURE__ */ new Map();
504
+ /**
505
+ * Resolved drivers.
506
+ */
507
+ resolvedDrivers = /* @__PURE__ */ new Map();
508
+ /**
509
+ * Channel registrations.
510
+ */
511
+ channelRegistrations = [];
512
+ /**
513
+ * SSE clients.
514
+ */
515
+ sseClients = /* @__PURE__ */ new Map();
516
+ /**
517
+ * WebSocket clients.
518
+ */
519
+ wsClients = /* @__PURE__ */ new Map();
520
+ constructor(options = {}) {
521
+ if (options.default) {
522
+ this.defaultDriver = options.default;
523
+ }
524
+ if (options.drivers) {
525
+ for (const [name, factory] of Object.entries(options.drivers)) {
526
+ this.registerDriver(name, factory);
527
+ }
528
+ }
529
+ if (!this.driverFactories.has("memory")) {
530
+ this.registerDriver("memory", () => new MemoryDriver());
531
+ }
532
+ }
533
+ /**
534
+ * Register a driver factory.
535
+ */
536
+ registerDriver(name, factory) {
537
+ this.driverFactories.set(name, factory);
538
+ return this;
539
+ }
540
+ /**
541
+ * Get a driver by name.
542
+ */
543
+ driver(name) {
544
+ const driverName = name ?? this.defaultDriver;
545
+ const resolved = this.resolvedDrivers.get(driverName);
546
+ if (resolved) {
547
+ return resolved;
548
+ }
549
+ const factory = this.driverFactories.get(driverName);
550
+ if (!factory) {
551
+ throw new Error(`Broadcast driver "${driverName}" not found`);
552
+ }
553
+ const driver = factory();
554
+ this.resolvedDrivers.set(driverName, driver);
555
+ return driver;
556
+ }
557
+ /**
558
+ * Register a public channel authorizer.
559
+ */
560
+ channel(pattern, authorizer) {
561
+ this.channelRegistrations.push({
562
+ pattern,
563
+ type: "public",
564
+ authorizer
565
+ });
566
+ return this;
567
+ }
568
+ /**
569
+ * Register a private channel authorizer.
570
+ */
571
+ privateChannel(pattern, authorizer) {
572
+ this.channelRegistrations.push({
573
+ pattern: PrivateChannel.normalize(pattern),
574
+ type: "private",
575
+ authorizer
576
+ });
577
+ return this;
578
+ }
579
+ /**
580
+ * Register a presence channel authorizer.
581
+ */
582
+ presenceChannel(pattern, authorizer) {
583
+ this.channelRegistrations.push({
584
+ pattern: PresenceChannel.normalize(pattern),
585
+ type: "presence",
586
+ authorizer
587
+ });
588
+ return this;
589
+ }
590
+ /**
591
+ * Broadcast an event to a channel.
592
+ */
593
+ async broadcast(channelName, event, data) {
594
+ await this.driver().publish(channelName, event, data);
595
+ }
596
+ /**
597
+ * Get a public channel.
598
+ */
599
+ toChannel(name) {
600
+ return new Channel(name, this.driver());
601
+ }
602
+ /**
603
+ * Get a private channel.
604
+ */
605
+ toPrivate(name) {
606
+ return new PrivateChannel(name, this.driver());
607
+ }
608
+ /**
609
+ * Get a presence channel.
610
+ */
611
+ toPresence(name) {
612
+ return new PresenceChannel(name, this.driver());
613
+ }
614
+ /**
615
+ * Authorize a channel for a user.
616
+ */
617
+ async authorize(channelName, user) {
618
+ const registration = this.findChannelRegistration(channelName);
619
+ if (!registration) {
620
+ return true;
621
+ }
622
+ if (registration.type === "presence") {
623
+ const presenceAuth = registration.authorizer;
624
+ return await presenceAuth(channelName, user);
625
+ }
626
+ return await registration.authorizer(channelName, user);
627
+ }
628
+ /**
629
+ * Find channel registration for a channel name.
630
+ */
631
+ findChannelRegistration(channelName) {
632
+ return this.channelRegistrations.find(
633
+ (reg) => this.matchPattern(reg.pattern, channelName)
634
+ );
635
+ }
636
+ /**
637
+ * Match a channel name against a pattern.
638
+ */
639
+ matchPattern(pattern, channelName) {
640
+ const regexPattern = pattern.replace(/\{[^}]+\}/g, "[^.]+").replace(/\*\*/g, ".+").replace(/\*/g, "[^.]+");
641
+ const regex = new RegExp(`^${regexPattern}$`);
642
+ return regex.test(channelName);
643
+ }
644
+ /**
645
+ * Create SSE middleware.
646
+ */
647
+ sseMiddleware(options = {}) {
648
+ const pingInterval = options.pingInterval ?? 3e4;
649
+ const retry = options.retry ?? 3e3;
650
+ return async (ctx) => {
651
+ const clientId = this.generateClientId();
652
+ const encoder = new TextEncoder();
653
+ const manager = this;
654
+ let controller = null;
655
+ let client = null;
656
+ let pingTimer = null;
657
+ const sendRaw = (message) => {
658
+ if (!controller) return;
659
+ controller.enqueue(encoder.encode(message));
660
+ };
661
+ const cleanup = () => {
662
+ if (pingTimer) {
663
+ clearInterval(pingTimer);
664
+ pingTimer = null;
665
+ }
666
+ if (client) {
667
+ manager.sseClients.delete(client.id);
668
+ client = null;
669
+ }
670
+ if (controller) {
671
+ try {
672
+ controller.close();
673
+ } catch {
674
+ }
675
+ controller = null;
676
+ }
677
+ };
678
+ const stream = new ReadableStream({
679
+ start(streamController) {
680
+ controller = streamController;
681
+ client = {
682
+ id: clientId,
683
+ userId: void 0,
684
+ channels: /* @__PURE__ */ new Set(),
685
+ send: (event, data) => {
686
+ const message = `event: ${event}
687
+ data: ${JSON.stringify(data)}
688
+
689
+ `;
690
+ sendRaw(message);
691
+ },
692
+ close: cleanup
693
+ };
694
+ manager.sseClients.set(clientId, client);
695
+ sendRaw(`retry: ${retry}
696
+
697
+ `);
698
+ pingTimer = setInterval(() => {
699
+ try {
700
+ client?.send("ping", { time: Date.now() });
701
+ } catch {
702
+ cleanup();
703
+ }
704
+ }, pingInterval);
705
+ },
706
+ cancel() {
707
+ cleanup();
708
+ }
709
+ });
710
+ ctx.req.raw.signal.addEventListener("abort", cleanup, { once: true });
711
+ return new Response(stream, {
712
+ headers: {
713
+ "Content-Type": "text/event-stream",
714
+ "Cache-Control": "no-cache",
715
+ Connection: "keep-alive"
716
+ }
717
+ });
718
+ };
719
+ }
720
+ /**
721
+ * Create auth middleware for channel authorization.
722
+ */
723
+ authMiddleware(options = {}) {
724
+ return async (ctx) => {
725
+ const getUser = options.getUser ?? ((c) => c.auth?.user);
726
+ const user = await getUser(ctx);
727
+ const payload = await parseRequestPayload(ctx);
728
+ const channel = typeof payload.channel === "string" ? payload.channel : void 0;
729
+ const channels = Array.isArray(payload.channels) ? payload.channels.filter((value) => typeof value === "string") : typeof payload.channels === "string" ? [payload.channels] : channel ? [channel] : [];
730
+ if (channels.length === 0) {
731
+ return ctx.json({ error: "No channel specified" }, 400);
732
+ }
733
+ const results = {};
734
+ for (const ch of channels) {
735
+ const authResult = await this.authorize(ch, user);
736
+ if (authResult === false || authResult === null) {
737
+ results[ch] = { authorized: false };
738
+ } else if (authResult === true) {
739
+ results[ch] = { authorized: true };
740
+ } else {
741
+ results[ch] = {
742
+ authorized: true,
743
+ member: authResult
744
+ };
745
+ }
746
+ }
747
+ return ctx.json(results);
748
+ };
749
+ }
750
+ /**
751
+ * Subscribe a client to a channel.
752
+ */
753
+ subscribeClient(clientId, channel) {
754
+ const client = this.sseClients.get(clientId);
755
+ if (!client) return false;
756
+ client.channels.add(channel);
757
+ this.driver().subscribe(channel, (event) => {
758
+ if (client.channels.has(channel)) {
759
+ client.send(event.event, event.data);
760
+ }
761
+ });
762
+ return true;
763
+ }
764
+ /**
765
+ * Register a WebSocket client and return its generated client ID.
766
+ */
767
+ registerWebSocketClient(client) {
768
+ const clientId = this.generateClientId("ws");
769
+ this.wsClients.set(clientId, {
770
+ ...client,
771
+ id: clientId,
772
+ channels: /* @__PURE__ */ new Set()
773
+ });
774
+ return clientId;
775
+ }
776
+ /**
777
+ * Remove a WebSocket client and close the underlying connection.
778
+ */
779
+ removeWebSocketClient(clientId) {
780
+ const client = this.wsClients.get(clientId);
781
+ if (!client) return false;
782
+ this.wsClients.delete(clientId);
783
+ client.close();
784
+ return true;
785
+ }
786
+ /**
787
+ * Subscribe a WebSocket client to a channel.
788
+ */
789
+ subscribeWebSocketClient(clientId, channel) {
790
+ const client = this.wsClients.get(clientId);
791
+ if (!client) return false;
792
+ client.channels.add(channel);
793
+ this.driver().subscribe(channel, async (event) => {
794
+ if (client.channels.has(channel)) {
795
+ await client.send(event.event, event.data);
796
+ }
797
+ });
798
+ return true;
799
+ }
800
+ /**
801
+ * Unsubscribe a WebSocket client from a channel.
802
+ */
803
+ unsubscribeWebSocketClient(clientId, channel) {
804
+ const client = this.wsClients.get(clientId);
805
+ if (!client) return false;
806
+ client.channels.delete(channel);
807
+ return true;
808
+ }
809
+ /**
810
+ * Get a WebSocket client by ID.
811
+ */
812
+ getWebSocketClient(clientId) {
813
+ return this.wsClients.get(clientId);
814
+ }
815
+ /**
816
+ * Get all WebSocket clients.
817
+ */
818
+ getWebSocketClients() {
819
+ return Array.from(this.wsClients.values());
820
+ }
821
+ /**
822
+ * Unsubscribe a client from a channel.
823
+ */
824
+ unsubscribeClient(clientId, channel) {
825
+ const client = this.sseClients.get(clientId);
826
+ if (!client) return false;
827
+ client.channels.delete(channel);
828
+ return true;
829
+ }
830
+ /**
831
+ * Get SSE client by ID.
832
+ */
833
+ getClient(clientId) {
834
+ return this.sseClients.get(clientId);
835
+ }
836
+ /**
837
+ * Get all SSE clients.
838
+ */
839
+ getClients() {
840
+ return Array.from(this.sseClients.values());
841
+ }
842
+ /**
843
+ * Get clients subscribed to a channel.
844
+ */
845
+ getChannelClients(channel) {
846
+ return this.getClients().filter((client) => client.channels.has(channel));
847
+ }
848
+ /**
849
+ * Generate a unique client ID.
850
+ */
851
+ generateClientId(prefix = "sse") {
852
+ const timestamp = Date.now().toString(36);
853
+ const random = Math.random().toString(36).substring(2, 10);
854
+ return `${prefix}_${timestamp}${random}`;
855
+ }
856
+ };
857
+ var globalBroadcastManager = null;
858
+ function setBroadcastManager(manager) {
859
+ globalBroadcastManager = manager;
860
+ }
861
+ function getBroadcastManager() {
862
+ if (!globalBroadcastManager) {
863
+ throw new Error("BroadcastManager not initialized. Call setBroadcastManager() first.");
864
+ }
865
+ return globalBroadcastManager;
866
+ }
867
+ function createBroadcastManager(options) {
868
+ return new BroadcastManager(options);
869
+ }
870
+
871
+ // src/broadcasting/typed.ts
872
+ function createTypedBroadcaster(manager) {
873
+ return {
874
+ broadcast(channel, event, payload) {
875
+ return manager.broadcast(channel, event, payload);
876
+ },
877
+ toChannel(channel) {
878
+ return wrapChannel(channel, manager.toChannel(channel));
879
+ },
880
+ toPrivate(channel) {
881
+ return wrapChannel(channel, manager.toPrivate(channel));
882
+ },
883
+ toPresence(channel) {
884
+ return wrapChannel(channel, manager.toPresence(channel));
885
+ }
886
+ };
887
+ }
888
+ function wrapChannel(name, channel) {
889
+ return {
890
+ name,
891
+ broadcast(event, payload) {
892
+ return channel.broadcast(event, payload);
893
+ }
894
+ };
895
+ }
896
+ export {
897
+ BroadcastManager,
898
+ Channel,
899
+ MemoryDriver,
900
+ PresenceChannel,
901
+ PrivateChannel,
902
+ RedisDriver,
903
+ createBroadcastManager,
904
+ createTypedBroadcaster,
905
+ getBroadcastManager,
906
+ setBroadcastManager
907
+ };