adonisjs-pulse 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.
Files changed (71) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +119 -0
  3. package/build/configure.d.ts +2 -0
  4. package/build/configure.js +47 -0
  5. package/build/index.d.ts +5 -0
  6. package/build/index.js +5 -0
  7. package/build/providers/pulse_provider.d.ts +16 -0
  8. package/build/providers/pulse_provider.js +64 -0
  9. package/build/resources/views/components/card-header.edge +30 -0
  10. package/build/resources/views/components/card.edge +8 -0
  11. package/build/resources/views/components/no-results.edge +3 -0
  12. package/build/resources/views/components/pulse.edge +79 -0
  13. package/build/resources/views/components/scroll.edge +3 -0
  14. package/build/resources/views/components/table.edge +3 -0
  15. package/build/resources/views/components/td.edge +4 -0
  16. package/build/resources/views/components/th.edge +4 -0
  17. package/build/resources/views/components/thead.edge +3 -0
  18. package/build/resources/views/components/theme-switcher.edge +48 -0
  19. package/build/resources/views/dashboard.edge +7 -0
  20. package/build/resources/views/livewire/cache.edge +111 -0
  21. package/build/resources/views/livewire/exceptions.edge +48 -0
  22. package/build/resources/views/livewire/period-selector.edge +11 -0
  23. package/build/resources/views/livewire/servers.edge +86 -0
  24. package/build/resources/views/livewire/slow-queries.edge +50 -0
  25. package/build/resources/views/livewire/slow-requests.edge +62 -0
  26. package/build/resources/views/livewire/usage.edge +63 -0
  27. package/build/services/pulse.d.ts +3 -0
  28. package/build/services/pulse.js +6 -0
  29. package/build/src/entry.d.ts +24 -0
  30. package/build/src/entry.js +67 -0
  31. package/build/src/livewire/cache.d.ts +4 -0
  32. package/build/src/livewire/cache.js +33 -0
  33. package/build/src/livewire/card.d.ts +22 -0
  34. package/build/src/livewire/card.js +96 -0
  35. package/build/src/livewire/exceptions.d.ts +4 -0
  36. package/build/src/livewire/exceptions.js +29 -0
  37. package/build/src/livewire/period_selector.d.ts +5 -0
  38. package/build/src/livewire/period_selector.js +16 -0
  39. package/build/src/livewire/servers.d.ts +4 -0
  40. package/build/src/livewire/servers.js +36 -0
  41. package/build/src/livewire/slow_queries.d.ts +5 -0
  42. package/build/src/livewire/slow_queries.js +37 -0
  43. package/build/src/livewire/slow_requests.d.ts +5 -0
  44. package/build/src/livewire/slow_requests.js +37 -0
  45. package/build/src/pulse.d.ts +44 -0
  46. package/build/src/pulse.js +224 -0
  47. package/build/src/recorders/cache_interactions.d.ts +34 -0
  48. package/build/src/recorders/cache_interactions.js +80 -0
  49. package/build/src/recorders/exceptions.d.ts +8 -0
  50. package/build/src/recorders/exceptions.js +81 -0
  51. package/build/src/recorders/index.d.ts +7 -0
  52. package/build/src/recorders/index.js +6 -0
  53. package/build/src/recorders/recorder.d.ts +24 -0
  54. package/build/src/recorders/recorder.js +45 -0
  55. package/build/src/recorders/servers.d.ts +24 -0
  56. package/build/src/recorders/servers.js +133 -0
  57. package/build/src/recorders/slow_queries.d.ts +8 -0
  58. package/build/src/recorders/slow_queries.js +27 -0
  59. package/build/src/recorders/slow_requests.d.ts +11 -0
  60. package/build/src/recorders/slow_requests.js +80 -0
  61. package/build/src/storage/database_storage.d.ts +20 -0
  62. package/build/src/storage/database_storage.js +357 -0
  63. package/build/src/types.d.ts +91 -0
  64. package/build/src/types.js +3 -0
  65. package/build/src/value.d.ts +9 -0
  66. package/build/src/value.js +20 -0
  67. package/build/stubs/config.stub +58 -0
  68. package/build/stubs/main.d.ts +5 -0
  69. package/build/stubs/main.js +7 -0
  70. package/build/stubs/migration.stub +58 -0
  71. package/package.json +100 -0
@@ -0,0 +1,11 @@
1
+ <div class="flex items-center gap-2">
2
+ <select
3
+ wire:model.live="period"
4
+ class="block w-full rounded-md border-0 bg-white dark:bg-gray-800 py-1.5 pl-3 pr-10 text-gray-900 dark:text-gray-100 ring-1 ring-inset ring-gray-300 dark:ring-gray-700 focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6"
5
+ >
6
+ <option value="1_hour">Last hour</option>
7
+ <option value="6_hours">Last 6 hours</option>
8
+ <option value="24_hours">Last 24 hours</option>
9
+ <option value="7_days">Last 7 days</option>
10
+ </select>
11
+ </div>
@@ -0,0 +1,86 @@
1
+ <x-pulse::card :cols="cols ?? 'full'" :rows="rows ?? 1">
2
+ <x-pulse::scroll>
3
+ @if(servers && servers.length > 0)
4
+ <div class="grid grid-cols-[max-content,minmax(max-content,1fr),max-content,minmax(min-content,2fr),max-content,minmax(min-content,2fr),minmax(max-content,1fr)] gap-x-1">
5
+ <div></div>
6
+ <div></div>
7
+ <div class="text-xs uppercase text-left text-gray-500 dark:text-gray-400 font-bold py-2">CPU</div>
8
+ <div></div>
9
+ <div class="text-xs uppercase text-left text-gray-500 dark:text-gray-400 font-bold py-2">Memory</div>
10
+ <div></div>
11
+ <div class="text-xs uppercase text-left text-gray-500 dark:text-gray-400 font-bold py-2">Storage</div>
12
+ @each(server in servers)
13
+ <div class="flex items-center {{ servers.length > 1 ? 'py-2' : '' }}" title="{{ server.recently_reported ? 'Online' : 'Offline' }}">
14
+ @if(server.recently_reported)
15
+ <div class="w-5 flex justify-center mr-1">
16
+ <div class="h-1 w-1 bg-green-500 rounded-full animate-pulse"></div>
17
+ </div>
18
+ @else
19
+ <svg xmlns="http://
20
+ www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" class="w-5 h-5 stroke-red-500 mr-1">
21
+ <path stroke-linecap="round" stroke-linejoin="round" d="M3 3l8.735 8.735m0 0a.374.374 0 11.53.53m-.53-.53l.53.53m0 0L21 21M14.652 9.348a3.75 3.75 0 010 5.304m2.121-7.425a6.75 6.75 0 010 9.546m2.121-11.667c3.808 3.807 3.808 9.98 0 13.788m-9.546-4.242a3.733 3.733 0 01-1.06-2.122m-1.061 4.243a6.75 6.75 0 01-1.625-6.929m-.496 9.05c-3.068-3.067-3.664-7.67-1.79-11.334M12 12h.008v.008H12V12z" />
22
+ </svg>
23
+ @end
24
+ </div>
25
+ <div class="flex items-center pr-8 xl:pr-12 {{ servers.length > 1 ? 'py-2' : '' }} {{ !server.recently_reported ? 'opacity-25 animate-pulse' : '' }}">
26
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" class="w-6 h-6 mr-2 stroke-gray-500 dark:stroke-gray-400">
27
+ <path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 0 0-.12-1.03l-2.268-9.64a3.375 3.375 0 0 0-3.285-2.602H7.923a3.375 3.375 0 0 0-3.285 2.602l-2.268 9.64a4.5 4.5 0 0 0-.12 1.03v.228m19.5 0a3 3 0 0 1-3 3H5.25a3 3 0 0 1-3-3m19.5 0a3 3 0 0 0-3-3H5.25a3 3 0 0 0-3 3m16.5 0h.008v.008h-.008v-.008Zm-3 0h.008v.008h-.008v-.008Z" />
28
+ </svg>
29
+ <span class="text-base font-bold text-gray-600 dark:text-gray-300">{{ server.name }}</span>
30
+ </div>
31
+ <div class="flex items-center {{ servers.length > 1 ? 'py-2' : '' }} {{ !server.recently_reported ? 'opacity-25 animate-pulse' : '' }}">
32
+ <div class="text-xl font-bold text-gray-700 dark:text-gray-200 w-14 whitespace-nowrap tabular-nums">
33
+ {{ server.cpu }}%
34
+ </div>
35
+ </div>
36
+ <div class="flex items-center pr-8 xl:pr-12 {{ servers.length > 1 ? 'py-2' : '' }} {{ !server.recently_reported ? 'opacity-25 animate-pulse' : '' }}">
37
+ <div class="w-full min-w-[5rem] max-w-xs h-9 relative">
38
+ <div class="w-full h-full ring-1 ring-gray-900/5 bg-gray-100 dark:bg-gray-800 rounded-md shadow-sm overflow-hidden">
39
+ <div class="h-full bg-purple-500 transition-all duration-300" style="width: {{ server.cpu }}%"></div>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ <div class="flex items-center {{ servers.length > 1 ? 'py-2' : '' }} {{ !server.recently_reported ? 'opacity-25 animate-pulse' : '' }}">
44
+ <div class="w-36 flex-shrink-0 whitespace-nowrap tabular-nums">
45
+ <span class="text-xl font-bold text-gray-700 dark:text-gray-200">
46
+ {{ formatBytes(server.memory_used) }}
47
+ </span>
48
+ <span class="text-sm font-medium text-gray-500 dark:text-gray-400">
49
+ / {{ formatBytes(server.memory_total) }}
50
+ </span>
51
+ </div>
52
+ </div>
53
+ <div class="flex items-center pr-8 xl:pr-12 {{ servers.length > 1 ? 'py-2' : '' }} {{ !server.recently_reported ? 'opacity-25 animate-pulse' : '' }}">
54
+ @let(memoryPercent = server.memory_total > 0 ? Math.round((server.memory_used / server.memory_total) * 100) : 0)
55
+ <div class="w-full min-w-[5rem] max-w-xs h-9 relative">
56
+ <div class="w-full h-full ring-1 ring-gray-900/5 bg-gray-100 dark:bg-gray-800 rounded-md shadow-sm overflow-hidden">
57
+ <div class="h-full bg-purple-500 transition-all duration-300" style="width: {{ memoryPercent }}%"></div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ <div class="flex items-center gap-8 {{ servers.length > 1 ? 'py-2' : '' }} {{ !server.recently_reported ? 'opacity-25 animate-pulse' : '' }}">
62
+ @if(server.storage && server.storage.length > 0)
63
+ @each(disk in server.storage)
64
+ <div class="flex items-center gap-4" title="Directory: {{ disk.directory }}">
65
+ <div class="whitespace-nowrap tabular-nums">
66
+ <span class="text-xl font-bold text-gray-700 dark:text-gray-200">{{ formatBytes(disk.used) }}</span>
67
+ <span class="text-sm font-medium text-gray-500 dark:text-gray-400">/ {{ formatBytes(disk.total) }}</span>
68
+ </div>
69
+ @let(diskPercent = disk.total > 0 ? Math.round((disk.used / disk.total) * 100) : 0)
70
+ <div class="relative h-8 w-8">
71
+ <svg class="h-8 w-8 -rotate-90" viewBox="0 0 36 36">
72
+ <circle cx="18" cy="18" r="14" fill="none" stroke-width="4" class="stroke-purple-500/20"></circle>
73
+ <circle cx="18" cy="18" r="14" fill="none" stroke-width="4" stroke-dasharray="{{ diskPercent * 0.88 }}, 100" stroke-linecap="round" class="stroke-purple-500"></circle>
74
+ </svg>
75
+ </div>
76
+ </div>
77
+ @end
78
+ @end
79
+ </div>
80
+ @end
81
+ </div>
82
+ @else
83
+ <x-pulse::no-results message="No servers reporting" />
84
+ @end
85
+ </x-pulse::scroll>
86
+ </x-pulse::card>
@@ -0,0 +1,50 @@
1
+ <x-pulse::card :cols="cols ?? 8" :rows="rows ?? 1">
2
+ <x-pulse::card-header title="Slow Queries" :details="'past ' + period">
3
+ <x-slot name="icon">
4
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
5
+ <path stroke-linecap="round" stroke-linejoin="round" d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />
6
+ </svg>
7
+ </x-slot>
8
+ </x-pulse::card-header>
9
+
10
+ <x-pulse::scroll>
11
+ @if(queries && queries.length > 0)
12
+ <x-pulse::table>
13
+ <colgroup>
14
+ <col width="100%" />
15
+ <col width="0%" />
16
+ <col width="0%" />
17
+ </colgroup>
18
+ <x-pulse::thead>
19
+ <tr>
20
+ <x-pulse::th>Query</x-pulse::th>
21
+ <x-pulse::th class="text-right">Count</x-pulse::th>
22
+ <x-pulse::th class="text-right">Slowest</x-pulse::th>
23
+ </tr>
24
+ </x-pulse::thead>
25
+ <tbody>
26
+ @each(query in queries)
27
+ <tr class="h-2 first:h-0"></tr>
28
+ <tr>
29
+ <x-pulse::td class="max-w-[1px]">
30
+ <code class="block truncate text-xs text-gray-900 dark:text-gray-100" title="{{ query.sql }}">
31
+ {{ query.sql }}
32
+ </code>
33
+ </x-pulse::td>
34
+ <x-pulse::td class="text-right tabular-nums text-gray-700 dark:text-gray-300 font-bold whitespace-nowrap">
35
+ {{ formatNumber(query.count) }}
36
+ </x-pulse::td>
37
+ <x-pulse::td class="text-right tabular-nums whitespace-nowrap">
38
+ <span class="{{ query.slowest >= 1000 ? 'text-red-600 dark:text-red-400' : query.slowest >= 500 ? 'text-yellow-600 dark:text-yellow-400' : 'text-gray-700 dark:text-gray-300' }} font-bold">
39
+ {{ formatDuration(query.slowest) }}
40
+ </span>
41
+ </x-pulse::td>
42
+ </tr>
43
+ @end
44
+ </tbody>
45
+ </x-pulse::table>
46
+ @else
47
+ <x-pulse::no-results message="No slow queries" />
48
+ @end
49
+ </x-pulse::scroll>
50
+ </x-pulse::card>
@@ -0,0 +1,62 @@
1
+ <x-pulse::card :cols="cols ?? 6" :rows="rows ?? 1">
2
+ <x-pulse::card-header title="Slow Requests" :details="'past ' + period">
3
+ <x-slot name="icon">
4
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
5
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
6
+ </svg>
7
+ </x-slot>
8
+ </x-pulse::card-header>
9
+
10
+ <x-pulse::scroll>
11
+ @if(requests && requests.length > 0)
12
+ <x-pulse::table>
13
+ <colgroup>
14
+ <col width="0%" />
15
+ <col width="100%" />
16
+ <col width="0%" />
17
+ <col width="0%" />
18
+ </colgroup>
19
+ <x-pulse::thead>
20
+ <tr>
21
+ <x-pulse::th>Method</x-pulse::th>
22
+ <x-pulse::th>Route</x-pulse::th>
23
+ <x-pulse::th class="text-right">Count</x-pulse::th>
24
+ <x-pulse::th class="text-right">Slowest</x-pulse::th>
25
+ </tr>
26
+ </x-pulse::thead>
27
+ <tbody>
28
+ @each(request in requests)
29
+ <tr class="h-2 first:h-0"></tr>
30
+ <tr>
31
+ <x-pulse::td class="whitespace-nowrap">
32
+ <span class="inline-flex items-center rounded px-1.5 py-0.5 text-xs font-medium {{ request.method === 'GET' ? 'bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300' : request.method === 'POST' ? 'bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300' : request.method === 'PUT' || request.method === 'PATCH' ? 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300' : request.method === 'DELETE' ? 'bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300' : 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300' }}">
33
+ {{ request.method }}
34
+ </span>
35
+ </x-pulse::td>
36
+ <x-pulse::td class="max-w-[1px]">
37
+ <div class="truncate text-gray-900 dark:text-gray-100" title="{{ request.uri }}">
38
+ {{ request.uri }}
39
+ </div>
40
+ @if(request.action)
41
+ <div class="mt-0.5 truncate text-xs text-gray-500 dark:text-gray-400" title="{{ request.action }}">
42
+ {{ request.action }}
43
+ </div>
44
+ @end
45
+ </x-pulse::td>
46
+ <x-pulse::td class="text-right tabular-nums text-gray-700 dark:text-gray-300 font-bold whitespace-nowrap">
47
+ {{ formatNumber(request.count) }}
48
+ </x-pulse::td>
49
+ <x-pulse::td class="text-right tabular-nums whitespace-nowrap">
50
+ <span class="{{ request.slowest >= 1000 ? 'text-red-600 dark:text-red-400' : request.slowest >= 500 ? 'text-yellow-600 dark:text-yellow-400' : 'text-gray-700 dark:text-gray-300' }} font-bold">
51
+ {{ formatDuration(request.slowest) }}
52
+ </span>
53
+ </x-pulse::td>
54
+ </tr>
55
+ @end
56
+ </tbody>
57
+ </x-pulse::table>
58
+ @else
59
+ <x-pulse::no-results message="No slow requests" />
60
+ @end
61
+ </x-pulse::scroll>
62
+ </x-pulse::card>
@@ -0,0 +1,63 @@
1
+ <x-pulse::card :cols="cols ?? 4" :rows="rows ?? 2">
2
+ <x-pulse::card-header title="Application Usage">
3
+ <x-slot name="actions">
4
+ <select
5
+ wire:model.live="type"
6
+ class="block rounded border-0 bg-transparent py-0.5 pl-2 pr-7 text-xs text-gray-500 dark:text-gray-400 ring-1 ring-inset ring-gray-300 dark:ring-gray-700 focus:ring-2 focus:ring-indigo-600"
7
+ >
8
+ <option value="requests">Requests</option>
9
+ <option value="slow_requests">Slow Requests</option>
10
+ <option value="jobs">Jobs</option>
11
+ </select>
12
+ </x-slot>
13
+ </x-pulse::card-header>
14
+
15
+ <x-pulse::scroll>
16
+ @if(users && users.length > 0)
17
+ <div class="divide-y divide-gray-200 dark:divide-gray-700">
18
+ @each(user in users)
19
+ <div class="flex items-center gap-3 px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/50">
20
+ <div class="flex-shrink-0">
21
+ @if(user.avatar)
22
+ <img
23
+ src="{{ user.avatar }}"
24
+ alt="{{ user.name }}"
25
+ class="h-8 w-8 rounded-full object-cover"
26
+ />
27
+ @else
28
+ <div class="flex h-8 w-8 items-center justify-center rounded-full bg-gray-200 dark:bg-gray-700 text-sm font-medium text-gray-600 dark:text-gray-300">
29
+ {{ user.name ? user.name.charAt(0).toUpperCase() : '?' }}
30
+ </div>
31
+ @end
32
+ </div>
33
+ <div class="min-w-0 flex-1">
34
+ <div class="truncate text-sm font-medium text-gray-900 dark:text-gray-100">
35
+ {{ user.name || 'Unknown' }}
36
+ </div>
37
+ @if(user.email)
38
+ <div class="truncate text-xs text-gray-500 dark:text-gray-400">
39
+ {{ user.email }}
40
+ </div>
41
+ @end
42
+ </div>
43
+ <div class="flex-shrink-0 text-right">
44
+ <div class="text-sm font-medium tabular-nums text-gray-900 dark:text-gray-100">
45
+ {{ formatNumber(user.count) }}
46
+ </div>
47
+ @if(user.percentage)
48
+ <div class="mt-0.5 h-1 w-16 overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700">
49
+ <div
50
+ class="h-full rounded-full bg-indigo-500"
51
+ style="width: {{ user.percentage }}%"
52
+ ></div>
53
+ </div>
54
+ @end
55
+ </div>
56
+ </div>
57
+ @end
58
+ </div>
59
+ @else
60
+ <x-pulse::no-results message="No usage data" />
61
+ @end
62
+ </x-pulse::scroll>
63
+ </x-pulse::card>
@@ -0,0 +1,3 @@
1
+ import type { Pulse } from '../src/pulse.js';
2
+ declare let pulse: Pulse;
3
+ export { pulse as default };
@@ -0,0 +1,6 @@
1
+ import app from '@adonisjs/core/services/app';
2
+ let pulse;
3
+ await app.booted(async () => {
4
+ pulse = await app.container.make('pulse');
5
+ });
6
+ export { pulse as default };
@@ -0,0 +1,24 @@
1
+ import type { AggregationType, EntryAttributes } from './types.js';
2
+ export declare class Entry {
3
+ timestamp: number;
4
+ type: string;
5
+ key: string;
6
+ value: number | null;
7
+ protected aggregations: AggregationType[];
8
+ protected onlyBucketsFlag: boolean;
9
+ constructor(timestamp: number, type: string, key: string, value?: number | null);
10
+ count(): this;
11
+ min(): this;
12
+ max(): this;
13
+ sum(): this;
14
+ avg(): this;
15
+ onlyBuckets(): this;
16
+ getAggregations(): AggregationType[];
17
+ isCount(): boolean;
18
+ isMin(): boolean;
19
+ isMax(): boolean;
20
+ isSum(): boolean;
21
+ isAvg(): boolean;
22
+ isOnlyBuckets(): boolean;
23
+ attributes(): EntryAttributes;
24
+ }
@@ -0,0 +1,67 @@
1
+ export class Entry {
2
+ timestamp;
3
+ type;
4
+ key;
5
+ value;
6
+ aggregations = [];
7
+ onlyBucketsFlag = false;
8
+ constructor(timestamp, type, key, value = null) {
9
+ this.timestamp = timestamp;
10
+ this.type = type;
11
+ this.key = key;
12
+ this.value = value;
13
+ }
14
+ count() {
15
+ this.aggregations.push('count');
16
+ return this;
17
+ }
18
+ min() {
19
+ this.aggregations.push('min');
20
+ return this;
21
+ }
22
+ max() {
23
+ this.aggregations.push('max');
24
+ return this;
25
+ }
26
+ sum() {
27
+ this.aggregations.push('sum');
28
+ return this;
29
+ }
30
+ avg() {
31
+ this.aggregations.push('avg');
32
+ return this;
33
+ }
34
+ onlyBuckets() {
35
+ this.onlyBucketsFlag = true;
36
+ return this;
37
+ }
38
+ getAggregations() {
39
+ return this.aggregations;
40
+ }
41
+ isCount() {
42
+ return this.aggregations.includes('count');
43
+ }
44
+ isMin() {
45
+ return this.aggregations.includes('min');
46
+ }
47
+ isMax() {
48
+ return this.aggregations.includes('max');
49
+ }
50
+ isSum() {
51
+ return this.aggregations.includes('sum');
52
+ }
53
+ isAvg() {
54
+ return this.aggregations.includes('avg');
55
+ }
56
+ isOnlyBuckets() {
57
+ return this.onlyBucketsFlag;
58
+ }
59
+ attributes() {
60
+ return {
61
+ timestamp: this.timestamp,
62
+ type: this.type,
63
+ key: this.key,
64
+ value: this.value,
65
+ };
66
+ }
67
+ }
@@ -0,0 +1,4 @@
1
+ import { Card } from './card.js';
2
+ export declare class Cache extends Card {
3
+ render(): Promise<string>;
4
+ }
@@ -0,0 +1,33 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { lazy } from 'adonisjs-livewire';
8
+ import { Card } from './card.js';
9
+ let Cache = class Cache extends Card {
10
+ async render() {
11
+ const totals = await this.aggregateTotal(['cache_hit', 'cache_miss'], 'count');
12
+ const allCacheInteractions = {
13
+ hits: totals['cache_hit'] ?? 0,
14
+ misses: totals['cache_miss'] ?? 0,
15
+ };
16
+ const cacheKeyInteractions = await this.aggregateTypes(['cache_hit', 'cache_miss'], 'count', 'cache_hit', 'desc', 101);
17
+ const keys = cacheKeyInteractions.map((row) => ({
18
+ key: row.key,
19
+ hits: row['cache_hit'] ?? 0,
20
+ misses: row['cache_miss'] ?? 0,
21
+ }));
22
+ return await this.view.render('pulse::livewire/cache', {
23
+ period: this.periodForHumans(),
24
+ config: { sampleRate: 1, groups: {} },
25
+ allCacheInteractions,
26
+ cacheKeyInteractions: keys,
27
+ });
28
+ }
29
+ };
30
+ Cache = __decorate([
31
+ lazy()
32
+ ], Cache);
33
+ export { Cache };
@@ -0,0 +1,22 @@
1
+ import { Component } from 'adonisjs-livewire';
2
+ import type { AggregationType, AggregateResult, AggregateTypesResult, StoredValue } from '../types.js';
3
+ export declare abstract class Card extends Component {
4
+ cols: number | 'full' | null;
5
+ rows: number | 'full' | null;
6
+ expand: boolean;
7
+ class: string;
8
+ period: string;
9
+ placeholder(): string;
10
+ placeholderData(): Record<string, any>;
11
+ protected periodAsInterval(): number;
12
+ protected periodForHumans(): string;
13
+ protected values(type: string, keys?: string[]): Promise<StoredValue[]>;
14
+ protected graph(types: string[], aggregate: AggregationType): Promise<Map<number, Map<string, number>>>;
15
+ protected aggregate(type: string, aggregates: AggregationType | AggregationType[], orderBy?: AggregationType, direction?: 'asc' | 'desc', limit?: number): Promise<Map<string, AggregateResult>>;
16
+ protected aggregateTypes(types: string[], aggregate: AggregationType, orderBy?: string, direction?: 'asc' | 'desc', limit?: number): Promise<AggregateTypesResult[]>;
17
+ protected aggregateTotal(types: string[], aggregate: AggregationType): Promise<Record<string, number>>;
18
+ protected formatBytes(bytes: number): string;
19
+ protected formatNumber(num: number): string;
20
+ protected formatDuration(ms: number): string;
21
+ protected timeAgo(timestamp: number): string;
22
+ }
@@ -0,0 +1,96 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component, url } from 'adonisjs-livewire';
8
+ import pulse from 'adonisjs-pulse/services/pulse';
9
+ export class Card extends Component {
10
+ cols = null;
11
+ rows = null;
12
+ expand = false;
13
+ class = '';
14
+ period = '1_hour';
15
+ placeholder() {
16
+ return '<div>Loading...</div>';
17
+ }
18
+ placeholderData() {
19
+ return {
20
+ cols: this.cols,
21
+ rows: this.rows,
22
+ class: this.class,
23
+ };
24
+ }
25
+ periodAsInterval() {
26
+ switch (this.period) {
27
+ case '1_hour':
28
+ return 3600;
29
+ case '6_hours':
30
+ return 21600;
31
+ case '24_hours':
32
+ return 86400;
33
+ case '7_days':
34
+ return 604800;
35
+ default:
36
+ return 3600;
37
+ }
38
+ }
39
+ periodForHumans() {
40
+ switch (this.period) {
41
+ case '1_hour':
42
+ return '1 hour';
43
+ case '6_hours':
44
+ return '6 hours';
45
+ case '24_hours':
46
+ return '24 hours';
47
+ case '7_days':
48
+ return '7 days';
49
+ default:
50
+ return '1 hour';
51
+ }
52
+ }
53
+ async values(type, keys) {
54
+ return pulse.getValues(type, keys);
55
+ }
56
+ async graph(types, aggregate) {
57
+ return pulse.graph(types, aggregate, this.periodAsInterval());
58
+ }
59
+ async aggregate(type, aggregates, orderBy, direction = 'desc', limit = 101) {
60
+ const aggregateArray = Array.isArray(aggregates) ? aggregates : [aggregates];
61
+ return pulse.aggregate(type, aggregateArray, this.periodAsInterval(), orderBy, direction, limit);
62
+ }
63
+ async aggregateTypes(types, aggregate, orderBy, direction = 'desc', limit = 101) {
64
+ return pulse.aggregateTypes(types, aggregate, this.periodAsInterval(), orderBy, direction, limit);
65
+ }
66
+ async aggregateTotal(types, aggregate) {
67
+ return pulse.aggregateTotal(types, aggregate, this.periodAsInterval());
68
+ }
69
+ formatBytes(bytes) {
70
+ if (bytes === 0)
71
+ return '0 B';
72
+ const k = 1024;
73
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
74
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
75
+ return `${Number.parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
76
+ }
77
+ formatNumber(num) {
78
+ return num.toLocaleString();
79
+ }
80
+ formatDuration(ms) {
81
+ return ms >= 1000 ? `${(ms / 1000).toFixed(2)}s` : `${ms}ms`;
82
+ }
83
+ timeAgo(timestamp) {
84
+ const seconds = Math.floor((Date.now() - timestamp) / 1000);
85
+ if (seconds < 60)
86
+ return `${seconds}s ago`;
87
+ if (seconds < 3600)
88
+ return `${Math.floor(seconds / 60)}m ago`;
89
+ if (seconds < 86400)
90
+ return `${Math.floor(seconds / 3600)}h ago`;
91
+ return `${Math.floor(seconds / 86400)}d ago`;
92
+ }
93
+ }
94
+ __decorate([
95
+ url()
96
+ ], Card.prototype, "period", void 0);
@@ -0,0 +1,4 @@
1
+ import { Card } from './card.js';
2
+ export declare class Exceptions extends Card {
3
+ render(): Promise<string>;
4
+ }
@@ -0,0 +1,29 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { lazy } from 'adonisjs-livewire';
8
+ import { Card } from './card.js';
9
+ let Exceptions = class Exceptions extends Card {
10
+ async render() {
11
+ const aggregateData = await this.aggregate('exception', ['count'], 'count');
12
+ const exceptions = Array.from(aggregateData.entries()).map(([key, row]) => {
13
+ const [className, location] = JSON.parse(key);
14
+ return {
15
+ class: className,
16
+ location,
17
+ count: row.count ?? 0,
18
+ };
19
+ });
20
+ return await this.view.render('pulse::livewire/exceptions', {
21
+ period: this.periodForHumans(),
22
+ exceptions,
23
+ });
24
+ }
25
+ };
26
+ Exceptions = __decorate([
27
+ lazy()
28
+ ], Exceptions);
29
+ export { Exceptions };
@@ -0,0 +1,5 @@
1
+ import { Component } from 'adonisjs-livewire';
2
+ export declare class PeriodSelector extends Component {
3
+ period: string;
4
+ render(): Promise<string>;
5
+ }
@@ -0,0 +1,16 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component, url } from 'adonisjs-livewire';
8
+ export class PeriodSelector extends Component {
9
+ period = '1_hour';
10
+ async render() {
11
+ return await this.view.render('pulse::livewire/period-selector');
12
+ }
13
+ }
14
+ __decorate([
15
+ url()
16
+ ], PeriodSelector.prototype, "period", void 0);
@@ -0,0 +1,4 @@
1
+ import { Card } from './card.js';
2
+ export declare class Servers extends Card {
3
+ render(): Promise<string>;
4
+ }
@@ -0,0 +1,36 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { lazy } from 'adonisjs-livewire';
8
+ import { Card } from './card.js';
9
+ let Servers = class Servers extends Card {
10
+ async render() {
11
+ const values = await this.values('system');
12
+ const servers = values
13
+ .map((s) => {
14
+ try {
15
+ const data = JSON.parse(s.value);
16
+ return {
17
+ ...data,
18
+ recently_reported: Date.now() / 1000 - s.timestamp < 60,
19
+ updated_at: s.timestamp * 1000,
20
+ };
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ })
26
+ .filter(Boolean);
27
+ return await this.view.render('pulse::livewire/servers', {
28
+ period: this.periodForHumans(),
29
+ servers,
30
+ });
31
+ }
32
+ };
33
+ Servers = __decorate([
34
+ lazy()
35
+ ], Servers);
36
+ export { Servers };
@@ -0,0 +1,5 @@
1
+ import { Card } from './card.js';
2
+ export declare class SlowQueries extends Card {
3
+ orderBy: string;
4
+ render(): Promise<string>;
5
+ }