@djangocfg/centrifugo 2.1.101 → 2.1.103

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,857 @@
1
+ import { Filter, X, Search, Activity, Play, Pause, Trash2, Download, Radio, RefreshCw, Wifi, WifiOff, Clock, Maximize2, Circle, Info, CheckCircle2, AlertCircle } from 'lucide-react';
2
+ import moment from 'moment';
3
+ import { createContext, useState, useRef, useCallback, useEffect, useMemo, useContext } from 'react';
4
+ import { Badge, Button, Input, Card, CardHeader, CardTitle, CardContent, ScrollArea, Tabs, TabsList, TabsTrigger, TabsContent, Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from '@djangocfg/ui-nextjs';
5
+ import { createConsola } from 'consola';
6
+ import '@djangocfg/api/auth';
7
+ import { useEventListener, events } from '@djangocfg/ui-core/hooks';
8
+ import { jsxs, jsx } from 'react/jsx-runtime';
9
+ import 'centrifuge';
10
+
11
+ var __defProp = Object.defineProperty;
12
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
13
+ var consolaLogger = createConsola({
14
+ level: 4 ,
15
+ formatOptions: {
16
+ colors: true,
17
+ date: false,
18
+ compact: false
19
+ }
20
+ }).withTag("[Centrifugo]");
21
+ function getConsolaLogger(tag) {
22
+ const consola = consolaLogger.withTag(`[${tag}]`);
23
+ return {
24
+ debug: /* @__PURE__ */ __name((message, data) => consola.debug(message, data || ""), "debug"),
25
+ info: /* @__PURE__ */ __name((message, data) => consola.info(message, data || ""), "info"),
26
+ success: /* @__PURE__ */ __name((message, data) => consola.success(message, data || ""), "success"),
27
+ warning: /* @__PURE__ */ __name((message, data) => consola.warn(message, data || ""), "warning"),
28
+ error: /* @__PURE__ */ __name((message, error) => consola.error(message, error || ""), "error")
29
+ };
30
+ }
31
+ __name(getConsolaLogger, "getConsolaLogger");
32
+ var CENTRIFUGO_MONITOR_EVENTS = {
33
+ OPEN_MONITOR_DIALOG: "CENTRIFUGO_OPEN_MONITOR_DIALOG",
34
+ CLOSE_MONITOR_DIALOG: "CENTRIFUGO_CLOSE_MONITOR_DIALOG"
35
+ };
36
+ var emitOpenMonitorDialog = /* @__PURE__ */ __name((payload) => {
37
+ events.publish({
38
+ type: CENTRIFUGO_MONITOR_EVENTS.OPEN_MONITOR_DIALOG,
39
+ payload: payload || {}
40
+ });
41
+ }, "emitOpenMonitorDialog");
42
+ function MessageFilters({
43
+ filters,
44
+ onFiltersChange,
45
+ autoScroll,
46
+ onAutoScrollChange
47
+ }) {
48
+ const hasActiveFilters = filters.channels && filters.channels.length > 0 || filters.types && filters.types.length > 0 || filters.levels && filters.levels.length > 0 || filters.searchQuery;
49
+ const handleClearFilters = /* @__PURE__ */ __name(() => {
50
+ onFiltersChange({});
51
+ }, "handleClearFilters");
52
+ const handleSearchChange = /* @__PURE__ */ __name((e) => {
53
+ onFiltersChange({ ...filters, searchQuery: e.target.value });
54
+ }, "handleSearchChange");
55
+ const handleToggleLevel = /* @__PURE__ */ __name((level) => {
56
+ const currentLevels = filters.levels || [];
57
+ const newLevels = currentLevels.includes(level) ? currentLevels.filter((l) => l !== level) : [...currentLevels, level];
58
+ onFiltersChange({
59
+ ...filters,
60
+ levels: newLevels.length > 0 ? newLevels : void 0
61
+ });
62
+ }, "handleToggleLevel");
63
+ const handleToggleType = /* @__PURE__ */ __name((type) => {
64
+ const currentTypes = filters.types || [];
65
+ const newTypes = currentTypes.includes(type) ? currentTypes.filter((t) => t !== type) : [...currentTypes, type];
66
+ onFiltersChange({
67
+ ...filters,
68
+ types: newTypes.length > 0 ? newTypes : void 0
69
+ });
70
+ }, "handleToggleType");
71
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3 p-3 border rounded-lg bg-muted/30", children: [
72
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
73
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
74
+ /* @__PURE__ */ jsx(Filter, { className: "h-4 w-4 text-muted-foreground" }),
75
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Filters" }),
76
+ hasActiveFilters && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: "Active" })
77
+ ] }),
78
+ hasActiveFilters && /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "ghost", onClick: handleClearFilters, children: [
79
+ /* @__PURE__ */ jsx(X, { className: "h-3 w-3 mr-1" }),
80
+ "Clear"
81
+ ] })
82
+ ] }),
83
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
84
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 text-muted-foreground" }),
85
+ /* @__PURE__ */ jsx(
86
+ Input,
87
+ {
88
+ type: "text",
89
+ placeholder: "Search messages...",
90
+ value: filters.searchQuery || "",
91
+ onChange: handleSearchChange,
92
+ className: "flex-1"
93
+ }
94
+ )
95
+ ] }),
96
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
97
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Level:" }),
98
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ["info", "success", "warning", "error"].map((level) => {
99
+ const isActive = filters.levels?.includes(level);
100
+ return /* @__PURE__ */ jsx(
101
+ Badge,
102
+ {
103
+ variant: isActive ? "default" : "outline",
104
+ className: "cursor-pointer",
105
+ onClick: () => handleToggleLevel(level),
106
+ children: level
107
+ },
108
+ level
109
+ );
110
+ }) })
111
+ ] }),
112
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
113
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Type:" }),
114
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ["connection", "subscription", "publication", "unsubscription", "error", "system"].map((type) => {
115
+ const isActive = filters.types?.includes(type);
116
+ return /* @__PURE__ */ jsx(
117
+ Badge,
118
+ {
119
+ variant: isActive ? "default" : "outline",
120
+ className: "cursor-pointer",
121
+ onClick: () => handleToggleType(type),
122
+ children: type
123
+ },
124
+ type
125
+ );
126
+ }) })
127
+ ] }),
128
+ onAutoScrollChange && /* @__PURE__ */ jsx("div", { className: "pt-2 border-t", children: /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm cursor-pointer", children: [
129
+ /* @__PURE__ */ jsx(
130
+ "input",
131
+ {
132
+ type: "checkbox",
133
+ checked: autoScroll,
134
+ onChange: (e) => onAutoScrollChange(e.target.checked),
135
+ className: "h-4 w-4 rounded border-gray-300"
136
+ }
137
+ ),
138
+ /* @__PURE__ */ jsx("span", { children: "Auto-scroll to latest" })
139
+ ] }) })
140
+ ] });
141
+ }
142
+ __name(MessageFilters, "MessageFilters");
143
+ function MessagesFeed({
144
+ maxMessages = 100,
145
+ showFilters = true,
146
+ showControls = true,
147
+ channels = [],
148
+ autoScroll: initialAutoScroll = true,
149
+ onMessageClick,
150
+ className = ""
151
+ }) {
152
+ const { isConnected, client } = useCentrifugo();
153
+ const [messages, setMessages] = useState([]);
154
+ const [isPaused, setIsPaused] = useState(false);
155
+ const [autoScroll, setAutoScroll] = useState(initialAutoScroll);
156
+ const [filters, setFilters] = useState({
157
+ channels: channels.length > 0 ? channels : void 0
158
+ });
159
+ const scrollRef = useRef(null);
160
+ const addMessage = useCallback(
161
+ (message) => {
162
+ if (isPaused) return;
163
+ setMessages((prev) => {
164
+ const newMessages = [message, ...prev];
165
+ return newMessages.slice(0, maxMessages);
166
+ });
167
+ },
168
+ [isPaused, maxMessages]
169
+ );
170
+ useEffect(() => {
171
+ if (!client) return;
172
+ const centrifuge = client.getCentrifuge();
173
+ const handleConnected = /* @__PURE__ */ __name(() => {
174
+ const now = moment.utc().valueOf();
175
+ addMessage({
176
+ id: `conn-${now}`,
177
+ timestamp: now,
178
+ type: "connection",
179
+ level: "success",
180
+ message: "Connected to Centrifugo"
181
+ });
182
+ }, "handleConnected");
183
+ const handleDisconnected = /* @__PURE__ */ __name(() => {
184
+ const now = moment.utc().valueOf();
185
+ addMessage({
186
+ id: `disconn-${now}`,
187
+ timestamp: now,
188
+ type: "connection",
189
+ level: "error",
190
+ message: "Disconnected from Centrifugo"
191
+ });
192
+ }, "handleDisconnected");
193
+ const handleError = /* @__PURE__ */ __name((ctx) => {
194
+ const now = moment.utc().valueOf();
195
+ addMessage({
196
+ id: `error-${now}`,
197
+ timestamp: now,
198
+ type: "error",
199
+ level: "error",
200
+ message: ctx.error?.message || "Connection error",
201
+ data: ctx
202
+ });
203
+ }, "handleError");
204
+ centrifuge.on("connected", handleConnected);
205
+ centrifuge.on("disconnected", handleDisconnected);
206
+ centrifuge.on("error", handleError);
207
+ return () => {
208
+ centrifuge.off("connected", handleConnected);
209
+ centrifuge.off("disconnected", handleDisconnected);
210
+ centrifuge.off("error", handleError);
211
+ };
212
+ }, [client, addMessage]);
213
+ useEffect(() => {
214
+ if (!client) return;
215
+ const centrifuge = client.getCentrifuge();
216
+ const handleSubscribed = /* @__PURE__ */ __name((ctx) => {
217
+ const now = moment.utc().valueOf();
218
+ addMessage({
219
+ id: `sub-${now}`,
220
+ timestamp: now,
221
+ type: "subscription",
222
+ level: "info",
223
+ channel: ctx.channel,
224
+ message: `Subscribed to ${ctx.channel}`,
225
+ data: ctx
226
+ });
227
+ }, "handleSubscribed");
228
+ const handleUnsubscribed = /* @__PURE__ */ __name((ctx) => {
229
+ const now = moment.utc().valueOf();
230
+ addMessage({
231
+ id: `unsub-${now}`,
232
+ timestamp: now,
233
+ type: "unsubscription",
234
+ level: "info",
235
+ channel: ctx.channel,
236
+ message: `Unsubscribed from ${ctx.channel}`,
237
+ data: ctx
238
+ });
239
+ }, "handleUnsubscribed");
240
+ const handlePublication = /* @__PURE__ */ __name((ctx) => {
241
+ const now = moment.utc().valueOf();
242
+ addMessage({
243
+ id: `pub-${now}-${Math.random()}`,
244
+ timestamp: now,
245
+ type: "publication",
246
+ level: "success",
247
+ channel: ctx.channel,
248
+ message: `Message from ${ctx.channel}`,
249
+ data: ctx.data
250
+ });
251
+ }, "handlePublication");
252
+ centrifuge.on("subscribed", handleSubscribed);
253
+ centrifuge.on("unsubscribed", handleUnsubscribed);
254
+ centrifuge.on("publication", handlePublication);
255
+ return () => {
256
+ centrifuge.off("subscribed", handleSubscribed);
257
+ centrifuge.off("unsubscribed", handleUnsubscribed);
258
+ centrifuge.off("publication", handlePublication);
259
+ };
260
+ }, [client, addMessage]);
261
+ useEffect(() => {
262
+ if (autoScroll && scrollRef.current) {
263
+ scrollRef.current.scrollTop = 0;
264
+ }
265
+ }, [messages, autoScroll]);
266
+ const filteredMessages = useMemo(() => {
267
+ return messages.filter((msg) => {
268
+ if (filters.channels && filters.channels.length > 0) {
269
+ if (!msg.channel || !filters.channels.includes(msg.channel)) {
270
+ return false;
271
+ }
272
+ }
273
+ if (filters.types && filters.types.length > 0) {
274
+ if (!filters.types.includes(msg.type)) {
275
+ return false;
276
+ }
277
+ }
278
+ if (filters.levels && filters.levels.length > 0) {
279
+ if (!filters.levels.includes(msg.level)) {
280
+ return false;
281
+ }
282
+ }
283
+ if (filters.searchQuery) {
284
+ const query = filters.searchQuery.toLowerCase();
285
+ const searchableText = [
286
+ msg.message,
287
+ msg.channel,
288
+ JSON.stringify(msg.data)
289
+ ].filter(Boolean).join(" ").toLowerCase();
290
+ if (!searchableText.includes(query)) {
291
+ return false;
292
+ }
293
+ }
294
+ return true;
295
+ }).map((msg) => ({
296
+ ...msg,
297
+ formattedTime: moment.utc(msg.timestamp).format("HH:mm:ss"),
298
+ formattedData: msg.data ? JSON.stringify(msg.data, null, 2) : null
299
+ }));
300
+ }, [messages, filters]);
301
+ const handleClear = /* @__PURE__ */ __name(() => {
302
+ setMessages([]);
303
+ }, "handleClear");
304
+ const handleDownload = /* @__PURE__ */ __name(() => {
305
+ const json = JSON.stringify(filteredMessages, null, 2);
306
+ const blob = new Blob([json], { type: "application/json" });
307
+ const url = URL.createObjectURL(blob);
308
+ const a = document.createElement("a");
309
+ a.href = url;
310
+ a.download = `centrifugo-messages-${moment.utc().format("YYYY-MM-DD-HHmmss")}.json`;
311
+ document.body.appendChild(a);
312
+ a.click();
313
+ document.body.removeChild(a);
314
+ URL.revokeObjectURL(url);
315
+ }, "handleDownload");
316
+ const getLevelIcon = /* @__PURE__ */ __name((level) => {
317
+ switch (level) {
318
+ case "error":
319
+ return /* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4 text-red-500" });
320
+ case "warning":
321
+ return /* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4 text-yellow-500" });
322
+ case "success":
323
+ return /* @__PURE__ */ jsx(CheckCircle2, { className: "h-4 w-4 text-green-500" });
324
+ case "info":
325
+ return /* @__PURE__ */ jsx(Info, { className: "h-4 w-4 text-blue-500" });
326
+ default:
327
+ return /* @__PURE__ */ jsx(Circle, { className: "h-4 w-4 text-gray-500" });
328
+ }
329
+ }, "getLevelIcon");
330
+ const getLevelVariant = /* @__PURE__ */ __name((level) => {
331
+ switch (level) {
332
+ case "error":
333
+ return "destructive";
334
+ case "warning":
335
+ return "secondary";
336
+ case "success":
337
+ return "outline";
338
+ default:
339
+ return "default";
340
+ }
341
+ }, "getLevelVariant");
342
+ return /* @__PURE__ */ jsxs(Card, { className, children: [
343
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
344
+ /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2", children: [
345
+ /* @__PURE__ */ jsx(Activity, { className: "h-5 w-5" }),
346
+ "Messages Feed",
347
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", children: filteredMessages.length })
348
+ ] }),
349
+ showControls && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
350
+ /* @__PURE__ */ jsx(
351
+ Button,
352
+ {
353
+ size: "sm",
354
+ variant: "outline",
355
+ onClick: () => setIsPaused(!isPaused),
356
+ children: isPaused ? /* @__PURE__ */ jsx(Play, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Pause, { className: "h-4 w-4" })
357
+ }
358
+ ),
359
+ /* @__PURE__ */ jsx(
360
+ Button,
361
+ {
362
+ size: "sm",
363
+ variant: "outline",
364
+ onClick: handleClear,
365
+ disabled: messages.length === 0,
366
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
367
+ }
368
+ ),
369
+ /* @__PURE__ */ jsx(
370
+ Button,
371
+ {
372
+ size: "sm",
373
+ variant: "outline",
374
+ onClick: handleDownload,
375
+ disabled: filteredMessages.length === 0,
376
+ children: /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" })
377
+ }
378
+ )
379
+ ] })
380
+ ] }) }),
381
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
382
+ showFilters && /* @__PURE__ */ jsx(
383
+ MessageFilters,
384
+ {
385
+ filters,
386
+ onFiltersChange: setFilters,
387
+ autoScroll,
388
+ onAutoScrollChange: setAutoScroll
389
+ }
390
+ ),
391
+ /* @__PURE__ */ jsx(ScrollArea, { className: "h-[400px]", viewportRef: scrollRef, children: filteredMessages.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [
392
+ /* @__PURE__ */ jsx(Activity, { className: "h-12 w-12 text-muted-foreground mb-4" }),
393
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: isPaused ? "Paused - Click play to resume" : "No messages yet" })
394
+ ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: filteredMessages.map((msg) => /* @__PURE__ */ jsx(
395
+ "div",
396
+ {
397
+ className: "p-3 rounded border hover:bg-muted/50 transition-colors cursor-pointer",
398
+ onClick: () => onMessageClick?.(msg),
399
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
400
+ /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 mt-0.5", children: getLevelIcon(msg.level) }),
401
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 space-y-1", children: [
402
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
403
+ /* @__PURE__ */ jsx(Badge, { variant: getLevelVariant(msg.level), className: "text-xs", children: msg.type }),
404
+ msg.channel && /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-xs", children: msg.channel }),
405
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: msg.formattedTime })
406
+ ] }),
407
+ msg.message && /* @__PURE__ */ jsx("p", { className: "text-sm break-words", children: msg.message }),
408
+ msg.formattedData && /* @__PURE__ */ jsxs("details", { className: "text-xs", children: [
409
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-muted-foreground hover:text-foreground", children: "View data" }),
410
+ /* @__PURE__ */ jsx("pre", { className: "mt-2 p-2 bg-muted rounded overflow-x-auto", children: msg.formattedData })
411
+ ] })
412
+ ] })
413
+ ] })
414
+ },
415
+ msg.id
416
+ )) }) })
417
+ ] })
418
+ ] });
419
+ }
420
+ __name(MessagesFeed, "MessagesFeed");
421
+ var logger = getConsolaLogger("SubscriptionsList");
422
+ function SubscriptionsList({
423
+ showControls = true,
424
+ onSubscriptionClick,
425
+ className = ""
426
+ }) {
427
+ const { isConnected, client } = useCentrifugo();
428
+ const [subscriptions, setSubscriptions] = useState([]);
429
+ const updateSubscriptions = /* @__PURE__ */ __name(() => {
430
+ if (!client || !isConnected) {
431
+ setSubscriptions([]);
432
+ return;
433
+ }
434
+ try {
435
+ const centrifuge = client.getCentrifuge();
436
+ const subs = centrifuge.subscriptions();
437
+ const subscriptionsList = [];
438
+ for (const [channel, sub] of Object.entries(subs)) {
439
+ subscriptionsList.push({
440
+ channel,
441
+ state: sub.state
442
+ });
443
+ }
444
+ setSubscriptions(subscriptionsList);
445
+ } catch (error) {
446
+ logger.error("Failed to get subscriptions", error);
447
+ setSubscriptions([]);
448
+ }
449
+ }, "updateSubscriptions");
450
+ useEffect(() => {
451
+ updateSubscriptions();
452
+ if (!client) return;
453
+ const centrifuge = client.getCentrifuge();
454
+ const handleSubscribed = /* @__PURE__ */ __name(() => updateSubscriptions(), "handleSubscribed");
455
+ const handleUnsubscribed = /* @__PURE__ */ __name(() => updateSubscriptions(), "handleUnsubscribed");
456
+ centrifuge.on("subscribed", handleSubscribed);
457
+ centrifuge.on("unsubscribed", handleUnsubscribed);
458
+ const interval = setInterval(updateSubscriptions, 3e3);
459
+ return () => {
460
+ centrifuge.off("subscribed", handleSubscribed);
461
+ centrifuge.off("unsubscribed", handleUnsubscribed);
462
+ clearInterval(interval);
463
+ };
464
+ }, [client, isConnected]);
465
+ const handleUnsubscribe = /* @__PURE__ */ __name(async (channel) => {
466
+ if (!client) return;
467
+ try {
468
+ await client.unsubscribe(channel);
469
+ updateSubscriptions();
470
+ } catch (error) {
471
+ logger.error("Failed to unsubscribe", error);
472
+ }
473
+ }, "handleUnsubscribe");
474
+ return /* @__PURE__ */ jsxs(Card, { className, children: [
475
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
476
+ /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2", children: [
477
+ /* @__PURE__ */ jsx(Radio, { className: "h-5 w-5" }),
478
+ "Active Subscriptions",
479
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", children: subscriptions.length })
480
+ ] }),
481
+ showControls && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: updateSubscriptions, children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" }) })
482
+ ] }) }),
483
+ /* @__PURE__ */ jsx(CardContent, { children: !isConnected ? /* @__PURE__ */ jsx("div", { className: "text-center py-8 text-sm text-muted-foreground", children: "Not connected to Centrifugo" }) : subscriptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-8 text-sm text-muted-foreground", children: "No active subscriptions" }) : /* @__PURE__ */ jsx(ScrollArea, { className: "h-[300px]", children: /* @__PURE__ */ jsx("div", { className: "space-y-2", children: subscriptions.map((sub) => /* @__PURE__ */ jsxs(
484
+ "div",
485
+ {
486
+ className: "flex items-center justify-between p-3 rounded border hover:bg-muted/50 transition-colors",
487
+ children: [
488
+ /* @__PURE__ */ jsx(
489
+ "div",
490
+ {
491
+ className: "flex-1 min-w-0 cursor-pointer",
492
+ onClick: () => onSubscriptionClick?.(sub.channel),
493
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
494
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-xs", children: sub.state }),
495
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-mono truncate", children: sub.channel })
496
+ ] })
497
+ }
498
+ ),
499
+ showControls && /* @__PURE__ */ jsx(
500
+ Button,
501
+ {
502
+ size: "sm",
503
+ variant: "ghost",
504
+ onClick: () => handleUnsubscribe(sub.channel),
505
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4 text-destructive" })
506
+ }
507
+ )
508
+ ]
509
+ },
510
+ sub.channel
511
+ )) }) }) })
512
+ ] });
513
+ }
514
+ __name(SubscriptionsList, "SubscriptionsList");
515
+ function CentrifugoMonitor({
516
+ variant = "full",
517
+ showConnectionStatus = true,
518
+ showMessagesFeed = true,
519
+ showSubscriptions = true,
520
+ showFilters = true,
521
+ showControls = true,
522
+ maxMessages = 100,
523
+ channels = [],
524
+ autoScroll = true,
525
+ onMessageClick,
526
+ onSubscriptionClick,
527
+ className = ""
528
+ }) {
529
+ if (variant === "minimal") {
530
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(ConnectionStatus, { variant: "detailed", showUptime: true, showSubscriptions: true }) });
531
+ }
532
+ if (variant === "compact") {
533
+ return /* @__PURE__ */ jsxs("div", { className: `space-y-4 ${className}`, children: [
534
+ showConnectionStatus && /* @__PURE__ */ jsx(ConnectionStatus, { variant: "detailed", showUptime: true, showSubscriptions: true }),
535
+ showMessagesFeed && /* @__PURE__ */ jsx(
536
+ MessagesFeed,
537
+ {
538
+ maxMessages,
539
+ showFilters: false,
540
+ showControls,
541
+ channels,
542
+ autoScroll,
543
+ onMessageClick
544
+ }
545
+ )
546
+ ] });
547
+ }
548
+ const tabsToShow = [
549
+ showConnectionStatus && { value: "connection", label: "Connection" },
550
+ showMessagesFeed && { value: "messages", label: "Messages" },
551
+ showSubscriptions && { value: "subscriptions", label: "Subscriptions" }
552
+ ].filter(Boolean);
553
+ if (tabsToShow.length === 0) {
554
+ return null;
555
+ }
556
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs(Tabs, { defaultValue: tabsToShow[0].value, children: [
557
+ /* @__PURE__ */ jsx(TabsList, { className: `grid w-full grid-cols-${tabsToShow.length}`, children: tabsToShow.map((tab) => /* @__PURE__ */ jsx(TabsTrigger, { value: tab.value, children: tab.label }, tab.value)) }),
558
+ showConnectionStatus && /* @__PURE__ */ jsx(TabsContent, { value: "connection", className: "mt-4", children: /* @__PURE__ */ jsx(ConnectionStatus, { variant: "detailed", showUptime: true, showSubscriptions: true }) }),
559
+ showMessagesFeed && /* @__PURE__ */ jsx(TabsContent, { value: "messages", className: "mt-4", children: /* @__PURE__ */ jsx(
560
+ MessagesFeed,
561
+ {
562
+ maxMessages,
563
+ showFilters,
564
+ showControls,
565
+ channels,
566
+ autoScroll,
567
+ onMessageClick
568
+ }
569
+ ) }),
570
+ showSubscriptions && /* @__PURE__ */ jsx(TabsContent, { value: "subscriptions", className: "mt-4", children: /* @__PURE__ */ jsx(
571
+ SubscriptionsList,
572
+ {
573
+ showControls,
574
+ onSubscriptionClick
575
+ }
576
+ ) })
577
+ ] }) });
578
+ }
579
+ __name(CentrifugoMonitor, "CentrifugoMonitor");
580
+ function CentrifugoMonitorDialog() {
581
+ const [open, setOpen] = useState(false);
582
+ const [variant, setVariant] = useState("full");
583
+ useEventListener(
584
+ CENTRIFUGO_MONITOR_EVENTS.OPEN_MONITOR_DIALOG,
585
+ (payload) => {
586
+ if (payload?.variant) {
587
+ setVariant(payload.variant);
588
+ }
589
+ setOpen(true);
590
+ }
591
+ );
592
+ useEventListener(
593
+ CENTRIFUGO_MONITOR_EVENTS.CLOSE_MONITOR_DIALOG,
594
+ () => {
595
+ setOpen(false);
596
+ }
597
+ );
598
+ const handleClose = /* @__PURE__ */ __name(() => {
599
+ setOpen(false);
600
+ }, "handleClose");
601
+ return /* @__PURE__ */ jsx(Sheet, { open, onOpenChange: (isOpen) => !isOpen && handleClose(), children: /* @__PURE__ */ jsxs(
602
+ SheetContent,
603
+ {
604
+ side: "right",
605
+ className: "max-w-2xl w-[90vw] sm:w-[672px]",
606
+ children: [
607
+ /* @__PURE__ */ jsxs(SheetHeader, { children: [
608
+ /* @__PURE__ */ jsxs(SheetTitle, { className: "flex items-center gap-2", children: [
609
+ /* @__PURE__ */ jsx(Activity, { className: "h-5 w-5" }),
610
+ "Centrifugo Monitor"
611
+ ] }),
612
+ /* @__PURE__ */ jsx(SheetDescription, { children: "Real-time WebSocket monitoring and debugging" })
613
+ ] }),
614
+ /* @__PURE__ */ jsx("div", { className: "mt-6", children: /* @__PURE__ */ jsx(CentrifugoMonitor, { variant }) })
615
+ ]
616
+ }
617
+ ) });
618
+ }
619
+ __name(CentrifugoMonitorDialog, "CentrifugoMonitorDialog");
620
+
621
+ // src/config.ts
622
+ process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
623
+ createContext(void 0);
624
+ var CentrifugoContext = createContext(void 0);
625
+ function useCentrifugo() {
626
+ const context = useContext(CentrifugoContext);
627
+ if (context === void 0) {
628
+ throw new Error("useCentrifugo must be used within a CentrifugoProvider");
629
+ }
630
+ return context;
631
+ }
632
+ __name(useCentrifugo, "useCentrifugo");
633
+ var logger2 = getConsolaLogger("ConnectionStatus");
634
+ function ConnectionStatus({
635
+ variant = "badge",
636
+ showUptime = false,
637
+ showSubscriptions = false,
638
+ className = ""
639
+ }) {
640
+ const { isConnected, client } = useCentrifugo();
641
+ const [connectionTime, setConnectionTime] = useState(null);
642
+ const [uptime, setUptime] = useState("");
643
+ const [activeSubscriptions, setActiveSubscriptions] = useState(0);
644
+ useEffect(() => {
645
+ if (isConnected && !connectionTime) {
646
+ setConnectionTime(moment.utc());
647
+ } else if (!isConnected) {
648
+ setConnectionTime(null);
649
+ }
650
+ }, [isConnected, connectionTime]);
651
+ useEffect(() => {
652
+ if (!isConnected || !connectionTime) {
653
+ setUptime("");
654
+ return;
655
+ }
656
+ const updateUptime = /* @__PURE__ */ __name(() => {
657
+ const now = moment.utc();
658
+ const duration = moment.duration(now.diff(connectionTime));
659
+ const hours = Math.floor(duration.asHours());
660
+ const minutes = duration.minutes();
661
+ const seconds = duration.seconds();
662
+ if (hours > 0) {
663
+ setUptime(`${hours}h ${minutes}m`);
664
+ } else if (minutes > 0) {
665
+ setUptime(`${minutes}m ${seconds}s`);
666
+ } else {
667
+ setUptime(`${seconds}s`);
668
+ }
669
+ }, "updateUptime");
670
+ updateUptime();
671
+ const interval = setInterval(updateUptime, 1e3);
672
+ return () => clearInterval(interval);
673
+ }, [isConnected, connectionTime]);
674
+ useEffect(() => {
675
+ if (!client || !isConnected) {
676
+ setActiveSubscriptions(0);
677
+ return;
678
+ }
679
+ const updateCount = /* @__PURE__ */ __name(() => {
680
+ try {
681
+ const centrifuge = client.getCentrifuge();
682
+ const subs = centrifuge.subscriptions();
683
+ setActiveSubscriptions(Object.keys(subs).length);
684
+ } catch (error) {
685
+ logger2.error("Failed to get active subscriptions", error);
686
+ }
687
+ }, "updateCount");
688
+ updateCount();
689
+ const interval = setInterval(updateCount, 2e3);
690
+ return () => clearInterval(interval);
691
+ }, [client, isConnected]);
692
+ if (variant === "badge") {
693
+ return /* @__PURE__ */ jsxs(
694
+ Badge,
695
+ {
696
+ variant: isConnected ? "default" : "destructive",
697
+ className: `flex items-center gap-1 ${isConnected ? "animate-pulse" : ""} ${className}`,
698
+ children: [
699
+ /* @__PURE__ */ jsx("span", { className: `h-2 w-2 rounded-full ${isConnected ? "bg-green-500" : "bg-red-500"}` }),
700
+ isConnected ? "Connected" : "Disconnected"
701
+ ]
702
+ }
703
+ );
704
+ }
705
+ if (variant === "inline") {
706
+ return /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-2 ${className}`, children: [
707
+ isConnected ? /* @__PURE__ */ jsx(Wifi, { className: "h-4 w-4 text-green-600" }) : /* @__PURE__ */ jsx(WifiOff, { className: "h-4 w-4 text-red-600" }),
708
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: isConnected ? "Connected" : "Disconnected" }),
709
+ showUptime && uptime && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
710
+ "(",
711
+ uptime,
712
+ ")"
713
+ ] }),
714
+ showSubscriptions && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [
715
+ /* @__PURE__ */ jsx(Radio, { className: "h-3 w-3" }),
716
+ activeSubscriptions
717
+ ] })
718
+ ] });
719
+ }
720
+ return /* @__PURE__ */ jsxs("div", { className: `space-y-3 ${className}`, children: [
721
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxs(
722
+ Badge,
723
+ {
724
+ variant: isConnected ? "default" : "destructive",
725
+ className: `flex items-center gap-1 ${isConnected ? "animate-pulse" : ""}`,
726
+ children: [
727
+ /* @__PURE__ */ jsx("span", { className: `h-2 w-2 rounded-full ${isConnected ? "bg-green-500" : "bg-red-500"}` }),
728
+ isConnected ? "Connected" : "Disconnected"
729
+ ]
730
+ }
731
+ ) }),
732
+ isConnected ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
733
+ showUptime && uptime && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
734
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground flex items-center gap-1", children: [
735
+ /* @__PURE__ */ jsx(Clock, { className: "h-3 w-3" }),
736
+ "Uptime:"
737
+ ] }),
738
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: uptime })
739
+ ] }),
740
+ showSubscriptions && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
741
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground flex items-center gap-1", children: [
742
+ /* @__PURE__ */ jsx(Radio, { className: "h-3 w-3" }),
743
+ "Subscriptions:"
744
+ ] }),
745
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: activeSubscriptions })
746
+ ] })
747
+ ] }) : /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground p-2 rounded bg-red-50 dark:bg-red-950/20", children: "Real-time features unavailable" })
748
+ ] });
749
+ }
750
+ __name(ConnectionStatus, "ConnectionStatus");
751
+ function ConnectionStatusCard({
752
+ showUptime = true,
753
+ showSubscriptions = true,
754
+ className = ""
755
+ }) {
756
+ const { isConnected } = useCentrifugo();
757
+ const statusColor = isConnected ? "border-green-500" : "border-red-500";
758
+ const handleClick = /* @__PURE__ */ __name(() => {
759
+ emitOpenMonitorDialog({ variant: "full" });
760
+ }, "handleClick");
761
+ return /* @__PURE__ */ jsxs(
762
+ Card,
763
+ {
764
+ className: `${statusColor} ${className} cursor-pointer hover:shadow-lg transition-shadow`,
765
+ style: { borderLeftWidth: "4px" },
766
+ onClick: handleClick,
767
+ children: [
768
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
769
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: "WebSocket" }),
770
+ /* @__PURE__ */ jsx("div", { className: isConnected ? "text-green-600" : "text-red-600", children: isConnected ? /* @__PURE__ */ jsx(Wifi, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(WifiOff, { className: "h-4 w-4" }) })
771
+ ] }),
772
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(
773
+ ConnectionStatus,
774
+ {
775
+ variant: "detailed",
776
+ showUptime,
777
+ showSubscriptions
778
+ }
779
+ ) })
780
+ ]
781
+ }
782
+ );
783
+ }
784
+ __name(ConnectionStatusCard, "ConnectionStatusCard");
785
+ function CentrifugoMonitorFAB({
786
+ position = "bottom-left",
787
+ size = "md",
788
+ variant = "full"
789
+ }) {
790
+ const positionStyles = {
791
+ "bottom-left": { bottom: "1rem", left: "1rem" },
792
+ "bottom-right": { bottom: "1rem", right: "1rem" },
793
+ "top-left": { top: "1rem", left: "1rem" },
794
+ "top-right": { top: "1rem", right: "1rem" }
795
+ };
796
+ const sizeStyles = {
797
+ sm: { width: "48px", height: "48px" },
798
+ md: { width: "56px", height: "56px" },
799
+ lg: { width: "64px", height: "64px" }
800
+ };
801
+ const iconSizes = {
802
+ sm: "h-5 w-5",
803
+ md: "h-6 w-6",
804
+ lg: "h-7 w-7"
805
+ };
806
+ const handleClick = /* @__PURE__ */ __name(() => {
807
+ emitOpenMonitorDialog({ variant });
808
+ }, "handleClick");
809
+ return /* @__PURE__ */ jsx(
810
+ "button",
811
+ {
812
+ onClick: handleClick,
813
+ className: "rounded-full bg-primary text-primary-foreground shadow-lg hover:bg-primary/90 transition-all duration-200 flex items-center justify-center hover:scale-110",
814
+ style: {
815
+ position: "fixed",
816
+ ...positionStyles[position],
817
+ ...sizeStyles[size],
818
+ zIndex: 9999
819
+ },
820
+ "aria-label": "Open Centrifugo Monitor",
821
+ children: /* @__PURE__ */ jsx(Activity, { className: iconSizes[size] })
822
+ }
823
+ );
824
+ }
825
+ __name(CentrifugoMonitorFAB, "CentrifugoMonitorFAB");
826
+ function CentrifugoMonitorWidget({
827
+ title = "WebSocket Monitor",
828
+ showExpandButton = true,
829
+ className = ""
830
+ }) {
831
+ const handleExpand = /* @__PURE__ */ __name(() => {
832
+ emitOpenMonitorDialog({ variant: "full" });
833
+ }, "handleExpand");
834
+ return /* @__PURE__ */ jsxs(Card, { className, children: [
835
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
836
+ /* @__PURE__ */ jsxs(CardTitle, { className: "text-sm font-medium flex items-center gap-2", children: [
837
+ /* @__PURE__ */ jsx(Activity, { className: "h-4 w-4" }),
838
+ title
839
+ ] }),
840
+ showExpandButton && /* @__PURE__ */ jsx(
841
+ Button,
842
+ {
843
+ size: "sm",
844
+ variant: "ghost",
845
+ onClick: handleExpand,
846
+ children: /* @__PURE__ */ jsx(Maximize2, { className: "h-4 w-4" })
847
+ }
848
+ )
849
+ ] }),
850
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(ConnectionStatus, { variant: "detailed", showUptime: true, showSubscriptions: true }) })
851
+ ] });
852
+ }
853
+ __name(CentrifugoMonitorWidget, "CentrifugoMonitorWidget");
854
+
855
+ export { CentrifugoMonitor, CentrifugoMonitorDialog, CentrifugoMonitorFAB, CentrifugoMonitorWidget, ConnectionStatus, ConnectionStatusCard, MessageFilters, MessagesFeed, SubscriptionsList };
856
+ //# sourceMappingURL=components.mjs.map
857
+ //# sourceMappingURL=components.mjs.map