bunqueue-dashboard 0.0.44 → 0.0.45
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.
- package/README.md +6 -4
- package/dist/.vite/manifest.json +31 -31
- package/dist/assets/{Alerts-C961Al5D.js → Alerts-vzIYtFpW.js} +1 -1
- package/dist/assets/{Benchmark-DQNZV7dM.js → Benchmark-C16tT4pF.js} +1 -1
- package/dist/assets/{CopilotPanel-Dlm8XwBj.js → CopilotPanel-FlAW3RlL.js} +3 -3
- package/dist/assets/{Database-DGM2BIHy.js → Database-DV2pcDLL.js} +1 -1
- package/dist/assets/{Diagnostics-_Sh3UyaT.js → Diagnostics-B6apZrC5.js} +1 -1
- package/dist/assets/{Dlq-mtInIoTh.js → Dlq-MRmkQrNC.js} +1 -1
- package/dist/assets/{DlqControl-Lq9ISb9b.js → DlqControl-DNJPVOTF.js} +1 -1
- package/dist/assets/{DlqPro-0_75vrd1.js → DlqPro-CwtTDwGN.js} +1 -1
- package/dist/assets/{Flows-CE7KzaFN.js → Flows-1dC8F_wx.js} +1 -1
- package/dist/assets/{JobInspector-DyWZk1eW.js → JobInspector-Ct9Ga-UT.js} +1 -1
- package/dist/assets/{Jobs-ELSH1Wbd.js → Jobs-Chqv0RuL.js} +1 -1
- package/dist/assets/{JobsPro-BlWwDeFN.js → JobsPro-CLzOhKAL.js} +1 -1
- package/dist/assets/{Logs-DMukqmHy.js → Logs-C5PNP9Wq.js} +1 -1
- package/dist/assets/{LogsPro-BfYbmZHi.js → LogsPro-B3KwTSsn.js} +1 -1
- package/dist/assets/McpServer-fVy27Mof.js +24 -0
- package/dist/assets/{Metrics-Cm5ZvfTo.js → Metrics-Ba46pdzT.js} +1 -1
- package/dist/assets/{MetricsPro-C9g1sp-u.js → MetricsPro-B9rD_KQc.js} +1 -1
- package/dist/assets/{Overview-CKAA3h9f.js → Overview-DV2qo5v3.js} +1 -1
- package/dist/assets/{QueueControl-Bvm8GUQa.js → QueueControl-DqBvuDnF.js} +1 -1
- package/dist/assets/{QueueDetail-BRDATlp0.js → QueueDetail-COSH9bJl.js} +1 -1
- package/dist/assets/{QueueDetailPro-Dkz4-1wn.js → QueueDetailPro-ClKtyHIf.js} +1 -1
- package/dist/assets/{Queues-BV99vr68.js → Queues-D2-R8nYh.js} +1 -1
- package/dist/assets/{QueuesOverview-CT6JBaim.js → QueuesOverview-tokquA3g.js} +1 -1
- package/dist/assets/{S3BackupPro-CjR0y18n.js → S3BackupPro-CDX5WQUY.js} +1 -1
- package/dist/assets/{ServerControl-BH6Doax0.js → ServerControl-BmccMNW9.js} +1 -1
- package/dist/assets/{Settings-CL7odwoU.js → Settings-UcujwvKv.js} +1 -1
- package/dist/assets/{Usage-BWZkz7Se.js → Usage-1KxG1mJ9.js} +1 -1
- package/dist/assets/{UsagePro-CoBbI-Cl.js → UsagePro-BGLufsE7.js} +1 -1
- package/dist/assets/{Workers-CBEDvXm_.js → Workers-BX1qspaQ.js} +1 -1
- package/dist/assets/{WorkersPro-Dk4NfplT.js → WorkersPro-BsUQVgQZ.js} +1 -1
- package/dist/assets/{index-BFW2_47n.js → index-8Ii-_Rml.js} +3 -3
- package/dist/index.html +1 -1
- package/package.json +17 -9
- package/patches/ai@7.0.14.patch +25 -0
- package/scripts/bunVersion.ts +1 -1
- package/scripts/embedded.gen.ts +122 -122
- package/dist/assets/McpServer-DNrOEp9j.js +0 -22
package/README.md
CHANGED
|
@@ -13,9 +13,11 @@ process itself**, all from one place.
|
|
|
13
13
|
|
|
14
14
|
For Bun developers and AI-agent builders running bunqueue who want to _operate_ their queue, not just watch it.
|
|
15
15
|
|
|
16
|
-
### [▶ Try the live demo](https://egeominotti.github.io/bunqueue-dashboard/)
|
|
16
|
+
### [📚 Read the documentation](https://egeominotti.github.io/bunqueue-dashboard/docs/) · [▶ Try the live demo](https://egeominotti.github.io/bunqueue-dashboard/)
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
[Quick start](https://egeominotti.github.io/bunqueue-dashboard/docs/quickstart) · [User guide](https://egeominotti.github.io/bunqueue-dashboard/docs/user-guide) · [Deployment](https://egeominotti.github.io/bunqueue-dashboard/docs/deploy/) · [Verified test coverage](https://egeominotti.github.io/bunqueue-dashboard/docs/testing)
|
|
19
|
+
|
|
20
|
+
The live demo uses sample data. The docs explain how to connect and operate a real Bunqueue server.
|
|
19
21
|
|
|
20
22
|
[](https://www.npmjs.com/package/bunqueue-dashboard)
|
|
21
23
|
[](https://github.com/egeominotti/bunqueue-dashboard/actions/workflows/ci.yml)
|
|
@@ -69,7 +71,7 @@ The full dashboard running on sample data, no server needed.
|
|
|
69
71
|
- **[Deployment](https://egeominotti.github.io/bunqueue-dashboard/docs/deploy/)**, Docker (Caddy), Kubernetes, PM2, and hosting platforms (Vercel, Netlify, Cloudflare, Fly.io, Render, Cloud Run).
|
|
70
72
|
- **[Architecture](https://egeominotti.github.io/bunqueue-dashboard/docs/architecture)** and **[API mapping](https://egeominotti.github.io/bunqueue-dashboard/docs/api-mapping)**, how it fits together and every endpoint it drives.
|
|
71
73
|
- **[Testing matrix](docs/testing.md)**, real-server coverage, reproduction steps and verification limits.
|
|
72
|
-
- **[llms.txt](https://egeominotti.github.io/bunqueue-dashboard/docs/llms.txt)**, the
|
|
74
|
+
- **[llms.txt](https://egeominotti.github.io/bunqueue-dashboard/docs/llms.txt)**, the documentation index for LLMs; **[llms-full.txt](https://egeominotti.github.io/bunqueue-dashboard/docs/llms-full.txt)** contains the full text.
|
|
73
75
|
|
|
74
76
|
## Table of contents
|
|
75
77
|
|
|
@@ -132,7 +134,7 @@ contracts; it never patches Bunqueue internals.
|
|
|
132
134
|
|
|
133
135
|
## Quick start
|
|
134
136
|
|
|
135
|
-
**Prerequisites:** [Bun](https://bun.sh) 1.4.
|
|
137
|
+
**Prerequisites:** [Bun](https://bun.sh) 1.4.2 and a reachable bunqueue server (or let the control
|
|
136
138
|
agent start one for you from the **Server** page).
|
|
137
139
|
|
|
138
140
|
### Run from npm (no clone)
|
package/dist/.vite/manifest.json
CHANGED
|
@@ -229,7 +229,7 @@
|
|
|
229
229
|
]
|
|
230
230
|
},
|
|
231
231
|
"index.html": {
|
|
232
|
-
"file": "assets/index-
|
|
232
|
+
"file": "assets/index-8Ii-_Rml.js",
|
|
233
233
|
"name": "index",
|
|
234
234
|
"src": "index.html",
|
|
235
235
|
"isEntry": true,
|
|
@@ -352,7 +352,7 @@
|
|
|
352
352
|
"src": "node_modules/@fontsource-variable/jetbrains-mono/files/jetbrains-mono-latin-wght-normal.woff2"
|
|
353
353
|
},
|
|
354
354
|
"src/components/copilot/CopilotPanel.tsx": {
|
|
355
|
-
"file": "assets/CopilotPanel-
|
|
355
|
+
"file": "assets/CopilotPanel-FlAW3RlL.js",
|
|
356
356
|
"name": "CopilotPanel",
|
|
357
357
|
"src": "src/components/copilot/CopilotPanel.tsx",
|
|
358
358
|
"isDynamicEntry": true,
|
|
@@ -378,7 +378,7 @@
|
|
|
378
378
|
"isDynamicEntry": true
|
|
379
379
|
},
|
|
380
380
|
"src/pages/Alerts.tsx": {
|
|
381
|
-
"file": "assets/Alerts-
|
|
381
|
+
"file": "assets/Alerts-vzIYtFpW.js",
|
|
382
382
|
"name": "Alerts",
|
|
383
383
|
"src": "src/pages/Alerts.tsx",
|
|
384
384
|
"isDynamicEntry": true,
|
|
@@ -416,7 +416,7 @@
|
|
|
416
416
|
]
|
|
417
417
|
},
|
|
418
418
|
"src/pages/Dlq.tsx": {
|
|
419
|
-
"file": "assets/Dlq-
|
|
419
|
+
"file": "assets/Dlq-MRmkQrNC.js",
|
|
420
420
|
"name": "Dlq",
|
|
421
421
|
"src": "src/pages/Dlq.tsx",
|
|
422
422
|
"isDynamicEntry": true,
|
|
@@ -454,7 +454,7 @@
|
|
|
454
454
|
]
|
|
455
455
|
},
|
|
456
456
|
"src/pages/Jobs.tsx": {
|
|
457
|
-
"file": "assets/Jobs-
|
|
457
|
+
"file": "assets/Jobs-Chqv0RuL.js",
|
|
458
458
|
"name": "Jobs",
|
|
459
459
|
"src": "src/pages/Jobs.tsx",
|
|
460
460
|
"isDynamicEntry": true,
|
|
@@ -476,7 +476,7 @@
|
|
|
476
476
|
]
|
|
477
477
|
},
|
|
478
478
|
"src/pages/Logs.tsx": {
|
|
479
|
-
"file": "assets/Logs-
|
|
479
|
+
"file": "assets/Logs-C5PNP9Wq.js",
|
|
480
480
|
"name": "Logs",
|
|
481
481
|
"src": "src/pages/Logs.tsx",
|
|
482
482
|
"isDynamicEntry": true,
|
|
@@ -496,7 +496,7 @@
|
|
|
496
496
|
]
|
|
497
497
|
},
|
|
498
498
|
"src/pages/Metrics.tsx": {
|
|
499
|
-
"file": "assets/Metrics-
|
|
499
|
+
"file": "assets/Metrics-Ba46pdzT.js",
|
|
500
500
|
"name": "Metrics",
|
|
501
501
|
"src": "src/pages/Metrics.tsx",
|
|
502
502
|
"isDynamicEntry": true,
|
|
@@ -512,7 +512,7 @@
|
|
|
512
512
|
]
|
|
513
513
|
},
|
|
514
514
|
"src/pages/Overview.tsx": {
|
|
515
|
-
"file": "assets/Overview-
|
|
515
|
+
"file": "assets/Overview-DV2qo5v3.js",
|
|
516
516
|
"name": "Overview",
|
|
517
517
|
"src": "src/pages/Overview.tsx",
|
|
518
518
|
"isDynamicEntry": true,
|
|
@@ -529,7 +529,7 @@
|
|
|
529
529
|
]
|
|
530
530
|
},
|
|
531
531
|
"src/pages/QueueDetail.tsx": {
|
|
532
|
-
"file": "assets/QueueDetail-
|
|
532
|
+
"file": "assets/QueueDetail-COSH9bJl.js",
|
|
533
533
|
"name": "QueueDetail",
|
|
534
534
|
"src": "src/pages/QueueDetail.tsx",
|
|
535
535
|
"isDynamicEntry": true,
|
|
@@ -552,7 +552,7 @@
|
|
|
552
552
|
]
|
|
553
553
|
},
|
|
554
554
|
"src/pages/Queues.tsx": {
|
|
555
|
-
"file": "assets/Queues-
|
|
555
|
+
"file": "assets/Queues-D2-R8nYh.js",
|
|
556
556
|
"name": "Queues",
|
|
557
557
|
"src": "src/pages/Queues.tsx",
|
|
558
558
|
"isDynamicEntry": true,
|
|
@@ -584,7 +584,7 @@
|
|
|
584
584
|
]
|
|
585
585
|
},
|
|
586
586
|
"src/pages/Settings.tsx": {
|
|
587
|
-
"file": "assets/Settings-
|
|
587
|
+
"file": "assets/Settings-UcujwvKv.js",
|
|
588
588
|
"name": "Settings",
|
|
589
589
|
"src": "src/pages/Settings.tsx",
|
|
590
590
|
"isDynamicEntry": true,
|
|
@@ -601,7 +601,7 @@
|
|
|
601
601
|
]
|
|
602
602
|
},
|
|
603
603
|
"src/pages/Usage.tsx": {
|
|
604
|
-
"file": "assets/Usage-
|
|
604
|
+
"file": "assets/Usage-1KxG1mJ9.js",
|
|
605
605
|
"name": "Usage",
|
|
606
606
|
"src": "src/pages/Usage.tsx",
|
|
607
607
|
"isDynamicEntry": true,
|
|
@@ -617,7 +617,7 @@
|
|
|
617
617
|
]
|
|
618
618
|
},
|
|
619
619
|
"src/pages/Workers.tsx": {
|
|
620
|
-
"file": "assets/Workers-
|
|
620
|
+
"file": "assets/Workers-BX1qspaQ.js",
|
|
621
621
|
"name": "Workers",
|
|
622
622
|
"src": "src/pages/Workers.tsx",
|
|
623
623
|
"isDynamicEntry": true,
|
|
@@ -635,7 +635,7 @@
|
|
|
635
635
|
]
|
|
636
636
|
},
|
|
637
637
|
"src/pages/control/Benchmark.tsx": {
|
|
638
|
-
"file": "assets/Benchmark-
|
|
638
|
+
"file": "assets/Benchmark-C16tT4pF.js",
|
|
639
639
|
"name": "Benchmark",
|
|
640
640
|
"src": "src/pages/control/Benchmark.tsx",
|
|
641
641
|
"isDynamicEntry": true,
|
|
@@ -701,7 +701,7 @@
|
|
|
701
701
|
]
|
|
702
702
|
},
|
|
703
703
|
"src/pages/control/Database.tsx": {
|
|
704
|
-
"file": "assets/Database-
|
|
704
|
+
"file": "assets/Database-DV2pcDLL.js",
|
|
705
705
|
"name": "Database",
|
|
706
706
|
"src": "src/pages/control/Database.tsx",
|
|
707
707
|
"isDynamicEntry": true,
|
|
@@ -724,7 +724,7 @@
|
|
|
724
724
|
]
|
|
725
725
|
},
|
|
726
726
|
"src/pages/control/Diagnostics.tsx": {
|
|
727
|
-
"file": "assets/Diagnostics-
|
|
727
|
+
"file": "assets/Diagnostics-B6apZrC5.js",
|
|
728
728
|
"name": "Diagnostics",
|
|
729
729
|
"src": "src/pages/control/Diagnostics.tsx",
|
|
730
730
|
"isDynamicEntry": true,
|
|
@@ -746,7 +746,7 @@
|
|
|
746
746
|
]
|
|
747
747
|
},
|
|
748
748
|
"src/pages/control/DlqControl.tsx": {
|
|
749
|
-
"file": "assets/DlqControl-
|
|
749
|
+
"file": "assets/DlqControl-DNJPVOTF.js",
|
|
750
750
|
"name": "DlqControl",
|
|
751
751
|
"src": "src/pages/control/DlqControl.tsx",
|
|
752
752
|
"isDynamicEntry": true,
|
|
@@ -770,7 +770,7 @@
|
|
|
770
770
|
]
|
|
771
771
|
},
|
|
772
772
|
"src/pages/control/DlqPro.tsx": {
|
|
773
|
-
"file": "assets/DlqPro-
|
|
773
|
+
"file": "assets/DlqPro-CwtTDwGN.js",
|
|
774
774
|
"name": "DlqPro",
|
|
775
775
|
"src": "src/pages/control/DlqPro.tsx",
|
|
776
776
|
"isDynamicEntry": true,
|
|
@@ -794,7 +794,7 @@
|
|
|
794
794
|
]
|
|
795
795
|
},
|
|
796
796
|
"src/pages/control/Flows.tsx": {
|
|
797
|
-
"file": "assets/Flows-
|
|
797
|
+
"file": "assets/Flows-1dC8F_wx.js",
|
|
798
798
|
"name": "Flows",
|
|
799
799
|
"src": "src/pages/control/Flows.tsx",
|
|
800
800
|
"isDynamicEntry": true,
|
|
@@ -814,7 +814,7 @@
|
|
|
814
814
|
]
|
|
815
815
|
},
|
|
816
816
|
"src/pages/control/JobInspector.tsx": {
|
|
817
|
-
"file": "assets/JobInspector-
|
|
817
|
+
"file": "assets/JobInspector-Ct9Ga-UT.js",
|
|
818
818
|
"name": "JobInspector",
|
|
819
819
|
"src": "src/pages/control/JobInspector.tsx",
|
|
820
820
|
"isDynamicEntry": true,
|
|
@@ -839,7 +839,7 @@
|
|
|
839
839
|
]
|
|
840
840
|
},
|
|
841
841
|
"src/pages/control/JobsPro.tsx": {
|
|
842
|
-
"file": "assets/JobsPro-
|
|
842
|
+
"file": "assets/JobsPro-CLzOhKAL.js",
|
|
843
843
|
"name": "JobsPro",
|
|
844
844
|
"src": "src/pages/control/JobsPro.tsx",
|
|
845
845
|
"isDynamicEntry": true,
|
|
@@ -864,7 +864,7 @@
|
|
|
864
864
|
]
|
|
865
865
|
},
|
|
866
866
|
"src/pages/control/LogsPro.tsx": {
|
|
867
|
-
"file": "assets/LogsPro-
|
|
867
|
+
"file": "assets/LogsPro-B3KwTSsn.js",
|
|
868
868
|
"name": "LogsPro",
|
|
869
869
|
"src": "src/pages/control/LogsPro.tsx",
|
|
870
870
|
"isDynamicEntry": true,
|
|
@@ -886,7 +886,7 @@
|
|
|
886
886
|
]
|
|
887
887
|
},
|
|
888
888
|
"src/pages/control/McpServer.tsx": {
|
|
889
|
-
"file": "assets/McpServer-
|
|
889
|
+
"file": "assets/McpServer-fVy27Mof.js",
|
|
890
890
|
"name": "McpServer",
|
|
891
891
|
"src": "src/pages/control/McpServer.tsx",
|
|
892
892
|
"isDynamicEntry": true,
|
|
@@ -898,7 +898,7 @@
|
|
|
898
898
|
]
|
|
899
899
|
},
|
|
900
900
|
"src/pages/control/MetricsPro.tsx": {
|
|
901
|
-
"file": "assets/MetricsPro-
|
|
901
|
+
"file": "assets/MetricsPro-B9rD_KQc.js",
|
|
902
902
|
"name": "MetricsPro",
|
|
903
903
|
"src": "src/pages/control/MetricsPro.tsx",
|
|
904
904
|
"isDynamicEntry": true,
|
|
@@ -918,7 +918,7 @@
|
|
|
918
918
|
]
|
|
919
919
|
},
|
|
920
920
|
"src/pages/control/QueueControl.tsx": {
|
|
921
|
-
"file": "assets/QueueControl-
|
|
921
|
+
"file": "assets/QueueControl-DqBvuDnF.js",
|
|
922
922
|
"name": "QueueControl",
|
|
923
923
|
"src": "src/pages/control/QueueControl.tsx",
|
|
924
924
|
"isDynamicEntry": true,
|
|
@@ -942,7 +942,7 @@
|
|
|
942
942
|
]
|
|
943
943
|
},
|
|
944
944
|
"src/pages/control/QueueDetailPro.tsx": {
|
|
945
|
-
"file": "assets/QueueDetailPro-
|
|
945
|
+
"file": "assets/QueueDetailPro-ClKtyHIf.js",
|
|
946
946
|
"name": "QueueDetailPro",
|
|
947
947
|
"src": "src/pages/control/QueueDetailPro.tsx",
|
|
948
948
|
"isDynamicEntry": true,
|
|
@@ -969,7 +969,7 @@
|
|
|
969
969
|
]
|
|
970
970
|
},
|
|
971
971
|
"src/pages/control/QueuesOverview.tsx": {
|
|
972
|
-
"file": "assets/QueuesOverview-
|
|
972
|
+
"file": "assets/QueuesOverview-tokquA3g.js",
|
|
973
973
|
"name": "QueuesOverview",
|
|
974
974
|
"src": "src/pages/control/QueuesOverview.tsx",
|
|
975
975
|
"isDynamicEntry": true,
|
|
@@ -991,7 +991,7 @@
|
|
|
991
991
|
]
|
|
992
992
|
},
|
|
993
993
|
"src/pages/control/S3BackupPro.tsx": {
|
|
994
|
-
"file": "assets/S3BackupPro-
|
|
994
|
+
"file": "assets/S3BackupPro-CDX5WQUY.js",
|
|
995
995
|
"name": "S3BackupPro",
|
|
996
996
|
"src": "src/pages/control/S3BackupPro.tsx",
|
|
997
997
|
"isDynamicEntry": true,
|
|
@@ -1010,7 +1010,7 @@
|
|
|
1010
1010
|
]
|
|
1011
1011
|
},
|
|
1012
1012
|
"src/pages/control/ServerControl.tsx": {
|
|
1013
|
-
"file": "assets/ServerControl-
|
|
1013
|
+
"file": "assets/ServerControl-BmccMNW9.js",
|
|
1014
1014
|
"name": "ServerControl",
|
|
1015
1015
|
"src": "src/pages/control/ServerControl.tsx",
|
|
1016
1016
|
"isDynamicEntry": true,
|
|
@@ -1031,7 +1031,7 @@
|
|
|
1031
1031
|
]
|
|
1032
1032
|
},
|
|
1033
1033
|
"src/pages/control/UsagePro.tsx": {
|
|
1034
|
-
"file": "assets/UsagePro-
|
|
1034
|
+
"file": "assets/UsagePro-BGLufsE7.js",
|
|
1035
1035
|
"name": "UsagePro",
|
|
1036
1036
|
"src": "src/pages/control/UsagePro.tsx",
|
|
1037
1037
|
"isDynamicEntry": true,
|
|
@@ -1069,7 +1069,7 @@
|
|
|
1069
1069
|
]
|
|
1070
1070
|
},
|
|
1071
1071
|
"src/pages/control/WorkersPro.tsx": {
|
|
1072
|
-
"file": "assets/WorkersPro-
|
|
1072
|
+
"file": "assets/WorkersPro-BsUQVgQZ.js",
|
|
1073
1073
|
"name": "WorkersPro",
|
|
1074
1074
|
"src": "src/pages/control/WorkersPro.tsx",
|
|
1075
1075
|
"isDynamicEntry": true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i as e}from"./cn-zFEt5SO8.js";import{g as t,t as n}from"./react-vendor-CvwrP0tU.js";import{o as r}from"./connectionStore-BJLihGaC.js";import{O as i,r as a}from"./icons-BVzwsfbG.js";import{n as o,t as s}from"./Button-Ch1W94fJ.js";import{t as c}from"./Card-CWxrC8OY.js";import{i as l,t as u}from"./feedback-QDN1AsYt.js";import{t as d}from"./PageHeader-CvIyaFOO.js";import{l as f}from"./format-Cg7VCIZW.js";import{d as p,f as m,l as h,p as g,u as _}from"./index-BFW2_47n.js";import{a as v,n as y,o as b,t as x}from"./form-C4oby9pI.js";var S=e(t(),1),C=[`error_rate`,`p99_latency`,`waiting`,`failed`,`dlq`],w=[`>=`,`>`,`<=`,`<`],T=[`email`,`webhook`,`slack`],E=/^[a-zA-Z0-9_\-.:]+$/,D={error_rate:{unit:`% (0–100)`,placeholder:`e.g. 5`},p99_latency:{unit:`ms`,placeholder:`e.g. 250`},waiting:{unit:`jobs`,placeholder:`e.g. 100`},failed:{unit:`jobs`,placeholder:`e.g. 10`},dlq:{unit:`jobs`,placeholder:`e.g. 1`}};function O(e){let t=e.name.trim();if(!C.includes(e.metric))return{ok:!1,error:`Invalid metric`,field:`metric`};if(!w.includes(e.operator))return{ok:!1,error:`Invalid operator`,field:`operator`};let n=e.metric===`p99_latency`?``:e.queue.trim();if(!t)return{ok:!1,error:`Rule name is required`,field:`name`};if(t.length>200)return{ok:!1,error:`Rule name must be 200 characters or fewer`,field:`name`};if(!e.threshold.trim())return{ok:!1,error:`Threshold is required`,field:`threshold`};let r=Number(e.threshold);return!Number.isFinite(r)||r<0?{ok:!1,error:`Threshold must be a non-negative number`,field:`threshold`}:e.metric===`error_rate`&&r>100?{ok:!1,error:`Error-rate threshold must be between 0 and 100%`,field:`threshold`}:[`waiting`,`failed`,`dlq`].includes(e.metric)&&!Number.isSafeInteger(r)?{ok:!1,error:`Job-count thresholds must be whole numbers`,field:`threshold`}:n&&(n.length>256||!E.test(n))?{ok:!1,error:`Queue contains unsupported characters or is too long`,field:`queue`}:T.includes(e.channel)?{ok:!0,rule:{name:t,metric:e.metric,operator:e.operator,threshold:r,queue:n,channel:e.channel,enabled:!0}}:{ok:!1,error:`Invalid channel type`,field:`channel`}}var k=n();function A({onAdd:e}){let[t,n]=(0,S.useState)(``),[r,i]=(0,S.useState)(`error_rate`),[a,o]=(0,S.useState)(`>=`),[c,l]=(0,S.useState)(``),[u,d]=(0,S.useState)(``),[f,p]=(0,S.useState)(null),h=(0,S.useRef)(!1),g=(0,S.useId)(),_=e=>f?.field===e;return(0,k.jsxs)(`div`,{className:`grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-5`,children:[(0,k.jsx)(x,{label:`Name`,children:(0,k.jsx)(y,{name:`alert-rule-name`,autoComplete:`off`,maxLength:200,value:t,"aria-invalid":_(`name`),"aria-describedby":_(`name`)?g:void 0,onChange:e=>{n(e.target.value),p(null)},placeholder:`High error rate`})}),(0,k.jsx)(x,{label:`Metric`,children:(0,k.jsx)(v,{name:`alert-rule-metric`,value:r,"aria-invalid":_(`metric`),"aria-describedby":_(`metric`)?g:void 0,onChange:e=>{i(e.target.value),p(null)},children:C.map(e=>(0,k.jsx)(`option`,{value:e,children:m[e]},e))})}),(0,k.jsx)(x,{label:`Operator`,children:(0,k.jsx)(v,{name:`alert-rule-operator`,value:a,"aria-invalid":_(`operator`),"aria-describedby":_(`operator`)?g:void 0,onChange:e=>{o(e.target.value),p(null)},children:w.map(e=>(0,k.jsx)(`option`,{value:e,children:e},e))})}),(0,k.jsx)(x,{label:`Threshold`,hint:D[r].unit,children:(0,k.jsx)(y,{name:`alert-rule-threshold`,autoComplete:`off`,type:`number`,min:0,max:r===`error_rate`?100:void 0,step:[`waiting`,`failed`,`dlq`].includes(r)?1:`any`,value:c,"aria-invalid":_(`threshold`),"aria-describedby":_(`threshold`)?g:void 0,onChange:e=>{l(e.target.value),p(null)},placeholder:D[r].placeholder})}),(0,k.jsx)(x,{label:`Queue (optional)`,hint:r===`p99_latency`?`global only — latency is not per queue`:void 0,children:(0,k.jsx)(y,{name:`alert-rule-queue`,autoComplete:`off`,spellCheck:!1,maxLength:256,value:u,"aria-invalid":_(`queue`),"aria-describedby":_(`queue`)?g:void 0,onChange:e=>{d(e.target.value),p(null)},disabled:r===`p99_latency`,placeholder:r===`p99_latency`?`Global`:`All queues`,className:`disabled:cursor-not-allowed disabled:opacity-40`})}),(0,k.jsxs)(`div`,{className:`col-span-full`,children:[(0,k.jsx)(s,{variant:`accent`,disabled:!t.trim()||!c.trim(),onClick:()=>{if(h.current)return;let n=O({name:t,metric:r,operator:a,threshold:c,queue:u,channel:`email`});if(!n.ok)return p({field:n.field,message:n.error});h.current=!0;try{let t=e(n.rule);t.ok||(h.current=!1,p({field:`name`,message:t.error}))}catch(e){h.current=!1,p({field:`name`,message:e.message})}},children:`Save rule`}),f&&(0,k.jsx)(`span`,{id:g,role:`alert`,className:`ml-3 text-xs text-danger`,children:f.message})]})]})}function j(){return typeof Notification>`u`?`unsupported`:Notification.permission}function M(){let{rules:e,addRule:t,removeRule:n,toggleRule:v}=g(),y=_(e=>e.breaching),x=_(e=>e.status),C=_(e=>e.error),w=_(e=>e.connectionIdentity),T=r(e=>p(e.baseUrl,e.token)),E=e.some(e=>e.enabled),D=w===T,O=D?y:[],M=D?x:E?`checking`:`idle`,N=D?C:null,[P,F]=(0,S.useState)(!1),[I,L]=(0,S.useState)(j);return(0,k.jsxs)(`div`,{children:[(0,k.jsx)(d,{title:`Alerts`,description:`Threshold rules evaluated live in your browser.`,live:E&&M===`live`,actions:(0,k.jsxs)(k.Fragment,{children:[I===`granted`?(0,k.jsxs)(`span`,{className:`flex items-center gap-1.5 text-sm text-success`,children:[(0,k.jsx)(a,{className:`size-4`}),` Desktop alerts on`]}):I===`unsupported`?(0,k.jsx)(`span`,{className:`text-sm text-faint`,children:`Notifications unsupported`}):I===`denied`?(0,k.jsx)(`span`,{className:`text-sm text-faint`,children:`Desktop alerts blocked — allow notifications in browser settings`}):(0,k.jsx)(s,{size:`sm`,onClick:async()=>{L(await h())},children:`Enable desktop notifications`}),(0,k.jsx)(s,{variant:`accent`,size:`sm`,"aria-expanded":P,"aria-controls":`alert-rule-form`,onClick:()=>F(e=>!e),children:`+ Create Alert Rule`})]})}),E&&M===`checking`&&(0,k.jsx)(`p`,{role:`status`,className:`mb-4 text-sm text-muted`,children:`Checking alert metrics for the connected server…`}),E&&M===`degraded`&&(0,k.jsx)(l,{message:N??`Alert metrics are unavailable; current rule results may be stale.`}),(0,k.jsx)(`div`,{className:`mb-6 rounded-lg border border-amber-500/20 bg-amber-500/5 px-4 py-3 text-sm text-amber-400/90`,children:`Rules are evaluated in this browser while a tab is open (even backgrounded), raising an in-app toast and — if enabled — a desktop notification. bunqueue OSS has no alerting backend, so email/webhook/slack delivery still needs your own monitoring or hosted bunqueue Cloud.`}),(0,k.jsxs)(`section`,{className:`mb-8`,children:[(0,k.jsx)(`h2`,{className:`mb-3 text-lg font-semibold text-fg`,children:`Triggered Alerts`}),O.length===0?(0,k.jsx)(u,{icon:(0,k.jsx)(a,{}),title:`No triggered alerts`,hint:E?M===`live`?`All enabled rules are within their thresholds.`:M===`checking`?`Alert metrics are still being checked.`:`Alert metrics are unavailable; no all-clear result is available.`:`No enabled rules yet. Create one below.`}):(0,k.jsx)(`div`,{className:`overflow-x-auto rounded-xl border border-danger/30 bg-red-500/5`,children:(0,k.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,k.jsx)(`thead`,{children:(0,k.jsxs)(`tr`,{className:`border-b border-line text-left text-[11px] uppercase tracking-wider text-faint`,children:[(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Rule`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Condition`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Queue`}),(0,k.jsx)(`th`,{className:`px-5 py-3 text-right font-medium`,children:`Current`}),(0,k.jsx)(`th`,{className:`px-5 py-3 text-right font-medium`,children:`Since`})]})}),(0,k.jsx)(`tbody`,{children:O.map(e=>(0,k.jsxs)(`tr`,{className:`border-b border-line last:border-0`,children:[(0,k.jsx)(`td`,{className:`px-5 py-3 font-medium text-danger`,children:e.ruleName}),(0,k.jsxs)(`td`,{className:`px-5 py-3 text-muted`,children:[e.metricLabel,` `,e.operator,` `,(0,k.jsx)(`span`,{className:`font-semibold text-fg`,children:e.threshold})]}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-muted`,children:e.queue||`All queues`}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-right tnum font-semibold text-danger`,children:Math.round(e.value*100)/100}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-right text-faint`,children:f(e.at)})]},e.ruleId))})]})})]}),(0,k.jsxs)(`section`,{children:[(0,k.jsx)(`h2`,{className:`mb-3 text-lg font-semibold text-fg`,children:`Alert Rules`}),P&&(0,k.jsx)(`div`,{id:`alert-rule-form`,children:(0,k.jsx)(c,{className:`mb-4`,children:(0,k.jsx)(A,{onAdd:e=>{let n=t(e);return n.ok&&F(!1),n}})})}),e.length===0?(0,k.jsx)(u,{title:`No alert rules`,hint:`Create a rule to be notified when a threshold is crossed.`}):(0,k.jsx)(`div`,{className:`overflow-x-auto rounded-xl border border-line bg-surface`,children:(0,k.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,k.jsx)(`thead`,{children:(0,k.jsxs)(`tr`,{className:`border-b border-line text-left text-[11px] uppercase tracking-wider text-faint`,children:[(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Rule`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Condition`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Queue`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Delivery`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Status`}),(0,k.jsx)(`th`,{className:`w-12 px-5 py-3`})]})}),(0,k.jsx)(`tbody`,{children:e.map(e=>(0,k.jsxs)(`tr`,{className:`border-b border-line last:border-0`,children:[(0,k.jsx)(`td`,{className:`px-5 py-3 font-medium text-fg`,children:e.name}),(0,k.jsxs)(`td`,{className:`px-5 py-3 text-muted`,children:[m[e.metric],` `,e.operator,` `,(0,k.jsx)(`span`,{className:`font-semibold text-accent`,children:e.threshold})]}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-muted`,children:e.queue||`All queues`}),(0,k.jsx)(`td`,{className:`px-5 py-3`,children:(0,k.jsx)(`span`,{className:`rounded-md bg-surface-2 px-2 py-0.5 text-xs text-muted`,children:`Browser`})}),(0,k.jsx)(`td`,{className:`px-5 py-3`,children:(0,k.jsx)(b,{checked:e.enabled,onChange:()=>v(e.id),label:`Enabled`})}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-right`,children:(0,k.jsx)(o,{"aria-label":`Delete rule ${e.name}`,onClick:()=>window.confirm(`Delete alert rule "${e.name}"?`)&&n(e.id),children:(0,k.jsx)(i,{className:`size-3.5`})})})]},e.id))})]})})]})]})}export{M as Alerts};
|
|
1
|
+
import{i as e}from"./cn-zFEt5SO8.js";import{g as t,t as n}from"./react-vendor-CvwrP0tU.js";import{o as r}from"./connectionStore-BJLihGaC.js";import{O as i,r as a}from"./icons-BVzwsfbG.js";import{n as o,t as s}from"./Button-Ch1W94fJ.js";import{t as c}from"./Card-CWxrC8OY.js";import{i as l,t as u}from"./feedback-QDN1AsYt.js";import{t as d}from"./PageHeader-CvIyaFOO.js";import{l as f}from"./format-Cg7VCIZW.js";import{d as p,f as m,l as h,p as g,u as _}from"./index-8Ii-_Rml.js";import{a as v,n as y,o as b,t as x}from"./form-C4oby9pI.js";var S=e(t(),1),C=[`error_rate`,`p99_latency`,`waiting`,`failed`,`dlq`],w=[`>=`,`>`,`<=`,`<`],T=[`email`,`webhook`,`slack`],E=/^[a-zA-Z0-9_\-.:]+$/,D={error_rate:{unit:`% (0–100)`,placeholder:`e.g. 5`},p99_latency:{unit:`ms`,placeholder:`e.g. 250`},waiting:{unit:`jobs`,placeholder:`e.g. 100`},failed:{unit:`jobs`,placeholder:`e.g. 10`},dlq:{unit:`jobs`,placeholder:`e.g. 1`}};function O(e){let t=e.name.trim();if(!C.includes(e.metric))return{ok:!1,error:`Invalid metric`,field:`metric`};if(!w.includes(e.operator))return{ok:!1,error:`Invalid operator`,field:`operator`};let n=e.metric===`p99_latency`?``:e.queue.trim();if(!t)return{ok:!1,error:`Rule name is required`,field:`name`};if(t.length>200)return{ok:!1,error:`Rule name must be 200 characters or fewer`,field:`name`};if(!e.threshold.trim())return{ok:!1,error:`Threshold is required`,field:`threshold`};let r=Number(e.threshold);return!Number.isFinite(r)||r<0?{ok:!1,error:`Threshold must be a non-negative number`,field:`threshold`}:e.metric===`error_rate`&&r>100?{ok:!1,error:`Error-rate threshold must be between 0 and 100%`,field:`threshold`}:[`waiting`,`failed`,`dlq`].includes(e.metric)&&!Number.isSafeInteger(r)?{ok:!1,error:`Job-count thresholds must be whole numbers`,field:`threshold`}:n&&(n.length>256||!E.test(n))?{ok:!1,error:`Queue contains unsupported characters or is too long`,field:`queue`}:T.includes(e.channel)?{ok:!0,rule:{name:t,metric:e.metric,operator:e.operator,threshold:r,queue:n,channel:e.channel,enabled:!0}}:{ok:!1,error:`Invalid channel type`,field:`channel`}}var k=n();function A({onAdd:e}){let[t,n]=(0,S.useState)(``),[r,i]=(0,S.useState)(`error_rate`),[a,o]=(0,S.useState)(`>=`),[c,l]=(0,S.useState)(``),[u,d]=(0,S.useState)(``),[f,p]=(0,S.useState)(null),h=(0,S.useRef)(!1),g=(0,S.useId)(),_=e=>f?.field===e;return(0,k.jsxs)(`div`,{className:`grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-5`,children:[(0,k.jsx)(x,{label:`Name`,children:(0,k.jsx)(y,{name:`alert-rule-name`,autoComplete:`off`,maxLength:200,value:t,"aria-invalid":_(`name`),"aria-describedby":_(`name`)?g:void 0,onChange:e=>{n(e.target.value),p(null)},placeholder:`High error rate`})}),(0,k.jsx)(x,{label:`Metric`,children:(0,k.jsx)(v,{name:`alert-rule-metric`,value:r,"aria-invalid":_(`metric`),"aria-describedby":_(`metric`)?g:void 0,onChange:e=>{i(e.target.value),p(null)},children:C.map(e=>(0,k.jsx)(`option`,{value:e,children:m[e]},e))})}),(0,k.jsx)(x,{label:`Operator`,children:(0,k.jsx)(v,{name:`alert-rule-operator`,value:a,"aria-invalid":_(`operator`),"aria-describedby":_(`operator`)?g:void 0,onChange:e=>{o(e.target.value),p(null)},children:w.map(e=>(0,k.jsx)(`option`,{value:e,children:e},e))})}),(0,k.jsx)(x,{label:`Threshold`,hint:D[r].unit,children:(0,k.jsx)(y,{name:`alert-rule-threshold`,autoComplete:`off`,type:`number`,min:0,max:r===`error_rate`?100:void 0,step:[`waiting`,`failed`,`dlq`].includes(r)?1:`any`,value:c,"aria-invalid":_(`threshold`),"aria-describedby":_(`threshold`)?g:void 0,onChange:e=>{l(e.target.value),p(null)},placeholder:D[r].placeholder})}),(0,k.jsx)(x,{label:`Queue (optional)`,hint:r===`p99_latency`?`global only — latency is not per queue`:void 0,children:(0,k.jsx)(y,{name:`alert-rule-queue`,autoComplete:`off`,spellCheck:!1,maxLength:256,value:u,"aria-invalid":_(`queue`),"aria-describedby":_(`queue`)?g:void 0,onChange:e=>{d(e.target.value),p(null)},disabled:r===`p99_latency`,placeholder:r===`p99_latency`?`Global`:`All queues`,className:`disabled:cursor-not-allowed disabled:opacity-40`})}),(0,k.jsxs)(`div`,{className:`col-span-full`,children:[(0,k.jsx)(s,{variant:`accent`,disabled:!t.trim()||!c.trim(),onClick:()=>{if(h.current)return;let n=O({name:t,metric:r,operator:a,threshold:c,queue:u,channel:`email`});if(!n.ok)return p({field:n.field,message:n.error});h.current=!0;try{let t=e(n.rule);t.ok||(h.current=!1,p({field:`name`,message:t.error}))}catch(e){h.current=!1,p({field:`name`,message:e.message})}},children:`Save rule`}),f&&(0,k.jsx)(`span`,{id:g,role:`alert`,className:`ml-3 text-xs text-danger`,children:f.message})]})]})}function j(){return typeof Notification>`u`?`unsupported`:Notification.permission}function M(){let{rules:e,addRule:t,removeRule:n,toggleRule:v}=g(),y=_(e=>e.breaching),x=_(e=>e.status),C=_(e=>e.error),w=_(e=>e.connectionIdentity),T=r(e=>p(e.baseUrl,e.token)),E=e.some(e=>e.enabled),D=w===T,O=D?y:[],M=D?x:E?`checking`:`idle`,N=D?C:null,[P,F]=(0,S.useState)(!1),[I,L]=(0,S.useState)(j);return(0,k.jsxs)(`div`,{children:[(0,k.jsx)(d,{title:`Alerts`,description:`Threshold rules evaluated live in your browser.`,live:E&&M===`live`,actions:(0,k.jsxs)(k.Fragment,{children:[I===`granted`?(0,k.jsxs)(`span`,{className:`flex items-center gap-1.5 text-sm text-success`,children:[(0,k.jsx)(a,{className:`size-4`}),` Desktop alerts on`]}):I===`unsupported`?(0,k.jsx)(`span`,{className:`text-sm text-faint`,children:`Notifications unsupported`}):I===`denied`?(0,k.jsx)(`span`,{className:`text-sm text-faint`,children:`Desktop alerts blocked — allow notifications in browser settings`}):(0,k.jsx)(s,{size:`sm`,onClick:async()=>{L(await h())},children:`Enable desktop notifications`}),(0,k.jsx)(s,{variant:`accent`,size:`sm`,"aria-expanded":P,"aria-controls":`alert-rule-form`,onClick:()=>F(e=>!e),children:`+ Create Alert Rule`})]})}),E&&M===`checking`&&(0,k.jsx)(`p`,{role:`status`,className:`mb-4 text-sm text-muted`,children:`Checking alert metrics for the connected server…`}),E&&M===`degraded`&&(0,k.jsx)(l,{message:N??`Alert metrics are unavailable; current rule results may be stale.`}),(0,k.jsx)(`div`,{className:`mb-6 rounded-lg border border-amber-500/20 bg-amber-500/5 px-4 py-3 text-sm text-amber-400/90`,children:`Rules are evaluated in this browser while a tab is open (even backgrounded), raising an in-app toast and — if enabled — a desktop notification. bunqueue OSS has no alerting backend, so email/webhook/slack delivery still needs your own monitoring or hosted bunqueue Cloud.`}),(0,k.jsxs)(`section`,{className:`mb-8`,children:[(0,k.jsx)(`h2`,{className:`mb-3 text-lg font-semibold text-fg`,children:`Triggered Alerts`}),O.length===0?(0,k.jsx)(u,{icon:(0,k.jsx)(a,{}),title:`No triggered alerts`,hint:E?M===`live`?`All enabled rules are within their thresholds.`:M===`checking`?`Alert metrics are still being checked.`:`Alert metrics are unavailable; no all-clear result is available.`:`No enabled rules yet. Create one below.`}):(0,k.jsx)(`div`,{className:`overflow-x-auto rounded-xl border border-danger/30 bg-red-500/5`,children:(0,k.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,k.jsx)(`thead`,{children:(0,k.jsxs)(`tr`,{className:`border-b border-line text-left text-[11px] uppercase tracking-wider text-faint`,children:[(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Rule`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Condition`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Queue`}),(0,k.jsx)(`th`,{className:`px-5 py-3 text-right font-medium`,children:`Current`}),(0,k.jsx)(`th`,{className:`px-5 py-3 text-right font-medium`,children:`Since`})]})}),(0,k.jsx)(`tbody`,{children:O.map(e=>(0,k.jsxs)(`tr`,{className:`border-b border-line last:border-0`,children:[(0,k.jsx)(`td`,{className:`px-5 py-3 font-medium text-danger`,children:e.ruleName}),(0,k.jsxs)(`td`,{className:`px-5 py-3 text-muted`,children:[e.metricLabel,` `,e.operator,` `,(0,k.jsx)(`span`,{className:`font-semibold text-fg`,children:e.threshold})]}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-muted`,children:e.queue||`All queues`}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-right tnum font-semibold text-danger`,children:Math.round(e.value*100)/100}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-right text-faint`,children:f(e.at)})]},e.ruleId))})]})})]}),(0,k.jsxs)(`section`,{children:[(0,k.jsx)(`h2`,{className:`mb-3 text-lg font-semibold text-fg`,children:`Alert Rules`}),P&&(0,k.jsx)(`div`,{id:`alert-rule-form`,children:(0,k.jsx)(c,{className:`mb-4`,children:(0,k.jsx)(A,{onAdd:e=>{let n=t(e);return n.ok&&F(!1),n}})})}),e.length===0?(0,k.jsx)(u,{title:`No alert rules`,hint:`Create a rule to be notified when a threshold is crossed.`}):(0,k.jsx)(`div`,{className:`overflow-x-auto rounded-xl border border-line bg-surface`,children:(0,k.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,k.jsx)(`thead`,{children:(0,k.jsxs)(`tr`,{className:`border-b border-line text-left text-[11px] uppercase tracking-wider text-faint`,children:[(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Rule`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Condition`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Queue`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Delivery`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Status`}),(0,k.jsx)(`th`,{className:`w-12 px-5 py-3`})]})}),(0,k.jsx)(`tbody`,{children:e.map(e=>(0,k.jsxs)(`tr`,{className:`border-b border-line last:border-0`,children:[(0,k.jsx)(`td`,{className:`px-5 py-3 font-medium text-fg`,children:e.name}),(0,k.jsxs)(`td`,{className:`px-5 py-3 text-muted`,children:[m[e.metric],` `,e.operator,` `,(0,k.jsx)(`span`,{className:`font-semibold text-accent`,children:e.threshold})]}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-muted`,children:e.queue||`All queues`}),(0,k.jsx)(`td`,{className:`px-5 py-3`,children:(0,k.jsx)(`span`,{className:`rounded-md bg-surface-2 px-2 py-0.5 text-xs text-muted`,children:`Browser`})}),(0,k.jsx)(`td`,{className:`px-5 py-3`,children:(0,k.jsx)(b,{checked:e.enabled,onChange:()=>v(e.id),label:`Enabled`})}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-right`,children:(0,k.jsx)(o,{"aria-label":`Delete rule ${e.name}`,onClick:()=>window.confirm(`Delete alert rule "${e.name}"?`)&&n(e.id),children:(0,k.jsx)(i,{className:`size-3.5`})})})]},e.id))})]})})]})]})}export{M as Alerts};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i as e,t}from"./cn-zFEt5SO8.js";import{g as n,t as r}from"./react-vendor-CvwrP0tU.js";import{o as i}from"./connectionStore-BJLihGaC.js";import{t as a}from"./bq-F5BbTG1T.js";import{b as o,x as s}from"./icons-BVzwsfbG.js";import{n as c}from"./usePolledData-0CVelkl2.js";import{t as l}from"./Button-Ch1W94fJ.js";import{n as u,t as d}from"./Card-CWxrC8OY.js";import{t as f}from"./PageHeader-CvIyaFOO.js";import{n as p,o as m,s as h}from"./format-Cg7VCIZW.js";import{a as g,n as _}from"./index-BFW2_47n.js";import{i as v,n as y,o as b,t as x}from"./form-C4oby9pI.js";import{t as S}from"./AreaChart-DEAalJ6P.js";var C=e(n(),1),w={total:1e6,batch:5e3,producers:64,payload:65536,workers:64,workerBatch:1e3,processMs:6e4,durationS:600},T={queue:`benchmark`,mode:`count`,total:5e3,durationS:30,batch:100,producers:4,payload:128,workers:4,workerBatch:100,processMs:5,durable:!1,removeOnComplete:!0},E=`bq-dashboard-benchmark-`;function D(){if(typeof globalThis.crypto?.randomUUID!=`function`)throw Error(`A cryptographically secure browser is required to create a benchmark queue.`);return`${E}${globalThis.crypto.randomUUID()}`}function O(e){return RegExp(`^${E}[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`,`i`).test(e)}var k={Smoke:{mode:`count`,total:200,batch:50,producers:2,workers:1,workerBatch:50,processMs:0},Standard:{mode:`count`,total:5e3,batch:100,producers:4,workers:4,workerBatch:100,processMs:5},Stress:{mode:`count`,total:5e4,batch:500,producers:16,workers:16,workerBatch:200,processMs:0},Soak:{mode:`duration`,durationS:30,batch:100,producers:4,workers:8,workerBatch:100,processMs:10}},A=(e,t,n)=>Number.isFinite(e)?Math.max(t,Math.min(n,Math.floor(e))):t;function j(e){return e?e.length>256?`Queue name is too long (maximum 256 characters).`:/^[a-zA-Z0-9_\-.:]+$/.test(e)?null:`Queue name contains unsupported characters.`:`Queue name is required.`}var M=(e,t)=>e.length?e[Math.min(e.length-1,Math.max(0,Math.ceil(t/100*e.length)-1))]:0,N=(e,t,n,r,i,a,o)=>Array.from({length:t},(t,s)=>({data:{i:e+s,blob:n},durable:r,removeOnComplete:i,...o===void 0?{}:{stallTimeout:o},...a?{jobId:`${a}-${e+s}`,tags:[`bunqueue-dashboard-benchmark`,a]}:{}})),P=e=>new Promise(t=>setTimeout(t,e)),F=async(e,t)=>{let n=performance.now()+e;for(;t()&&performance.now()<n;)await P(Math.min(100,Math.max(0,n-performance.now())))},I=e=>e instanceof Error?e.message:String(e),L=e=>e>=1e6?`${(e/1e6).toFixed(2)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:`${Math.round(e)}`,R=[`count`,`duration`],z=[`waiting`,`completed`,`failed`],B=null;function ee(){return B??=D(),B}var te=[`total`,`durationS`,`batch`,`producers`,`payload`,`workers`,`workerBatch`,`processMs`],ne=e=>({...e,total:String(e.total),durationS:String(e.durationS),batch:String(e.batch),producers:String(e.producers),payload:String(e.payload),workers:String(e.workers),workerBatch:String(e.workerBatch),processMs:String(e.processMs)}),V=e=>({...e,total:Number(e.total),durationS:Number(e.durationS),batch:Number(e.batch),producers:Number(e.producers),payload:Number(e.payload),workers:Number(e.workers),workerBatch:Number(e.workerBatch),processMs:Number(e.processMs)}),H=r();function re({draft:e,active:n,cleaning:r,cleanResult:i,setField:a,numberInput:o,onClean:s}){return(0,H.jsxs)(d,{className:`lg:col-span-1`,children:[(0,H.jsx)(u,{title:`Configuration`}),(0,H.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,H.jsx)(x,{label:`Dedicated queue`,hint:`Cryptographically generated for this browser tab; production queue names cannot be entered here.`,children:(0,H.jsx)(y,{name:`benchmark-queue`,autoComplete:`off`,value:e.queue,readOnly:!0,"aria-readonly":`true`})}),(0,H.jsx)(x,{label:`Mode`,hint:e.mode===`count`?`Push a fixed number of jobs.`:`Run producers + workers for a fixed time.`,children:(0,H.jsx)(v,{options:R,value:e.mode,onChange:e=>a(`mode`,e),disabled:n})}),e.mode===`count`?(0,H.jsx)(x,{label:`Total jobs`,hint:`max ${h(w.total)}`,children:(0,H.jsx)(y,{name:`benchmark-total`,autoComplete:`off`,type:`number`,min:1,max:w.total,value:e.total,disabled:n,onChange:o(`total`)})}):(0,H.jsx)(x,{label:`Duration (s)`,hint:`max ${w.durationS}s`,children:(0,H.jsx)(y,{name:`benchmark-duration-seconds`,autoComplete:`off`,type:`number`,min:1,max:w.durationS,value:e.durationS,disabled:n,onChange:o(`durationS`)})}),(0,H.jsx)(`div`,{className:`mt-1 text-[11px] font-semibold uppercase tracking-wider text-faint`,children:`Producers`}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,H.jsx)(x,{label:`Producers`,hint:`parallel, max ${w.producers}`,children:(0,H.jsx)(y,{name:`benchmark-producers`,autoComplete:`off`,type:`number`,min:1,max:w.producers,value:e.producers,disabled:n,onChange:o(`producers`)})}),(0,H.jsx)(x,{label:`Push batch`,hint:`jobs/req, max ${w.batch}`,children:(0,H.jsx)(y,{name:`benchmark-push-batch`,autoComplete:`off`,type:`number`,min:1,max:w.batch,value:e.batch,disabled:n,onChange:o(`batch`)})}),(0,H.jsx)(x,{label:`Payload`,hint:`bytes/job, max ${h(w.payload)}`,children:(0,H.jsx)(y,{name:`benchmark-payload-bytes`,autoComplete:`off`,type:`number`,min:0,max:w.payload,value:e.payload,disabled:n,onChange:o(`payload`)})})]}),(0,H.jsx)(`div`,{className:`mt-1 text-[11px] font-semibold uppercase tracking-wider text-faint`,children:`Workers (simulated)`}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,H.jsx)(x,{label:`Workers`,hint:`0 = produce only, max ${w.workers}`,children:(0,H.jsx)(y,{name:`benchmark-workers`,autoComplete:`off`,type:`number`,min:0,max:w.workers,value:e.workers,disabled:n,onChange:o(`workers`)})}),(0,H.jsx)(x,{label:`Pull batch`,hint:`jobs/pull, max ${w.workerBatch}`,children:(0,H.jsx)(y,{name:`benchmark-pull-batch`,autoComplete:`off`,type:`number`,min:1,max:w.workerBatch,value:e.workerBatch,disabled:n,onChange:o(`workerBatch`)})}),(0,H.jsx)(x,{label:`Process (ms)`,hint:`simulated work per pull`,children:(0,H.jsx)(y,{name:`benchmark-process-ms`,autoComplete:`off`,type:`number`,min:0,max:w.processMs,value:e.processMs,disabled:n,onChange:o(`processMs`)})})]}),(0,H.jsx)(U,{label:`Durable (fsync each job)`,checked:e.durable,active:n,onChange:e=>a(`durable`,e)}),(0,H.jsx)(U,{label:`Remove on complete`,checked:e.removeOnComplete,active:n,onChange:e=>a(`removeOnComplete`,e)}),(0,H.jsxs)(`div`,{className:`flex items-center gap-3 pt-1`,children:[(0,H.jsx)(l,{variant:`ghost`,size:`sm`,disabled:n||r,onClick:s,children:r?`Cleaning…`:`Clean queue`}),i&&(`error`in i?(0,H.jsxs)(`span`,{className:`text-xs text-danger`,children:[`Clean unverified — `,i.error]}):(0,H.jsx)(`span`,{className:t(`text-xs`,i.remaining>0?`text-warning`:`text-success`),children:i.remaining>0?`Cleaned — ${h(i.remaining)} active job(s) remain (requeued after the stall timeout)`:`Cleaned — 0 jobs remain`}))]})]})]})}function U({label:e,checked:t,active:n,onChange:r}){return(0,H.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,H.jsx)(b,{checked:t,onChange:r,disabled:n,label:e}),(0,H.jsx)(`span`,{className:`text-sm text-muted`,children:e})]})}function ie({heading:e,pollBaseUrl:t,etaText:n,shown:r,shownWorkers:i,producePct:a,drainPct:o,durationPct:s,live:c,summary:l}){return(0,H.jsxs)(`div`,{className:`flex flex-col gap-6 lg:col-span-2`,children:[(0,H.jsxs)(d,{children:[(0,H.jsxs)(`div`,{className:`mb-3 flex items-center justify-between`,children:[(0,H.jsx)(`h3`,{className:`text-base font-semibold text-fg`,children:e}),(0,H.jsxs)(`span`,{className:`text-right text-xs text-faint`,children:[(0,H.jsxs)(`span`,{className:`block`,children:[`Benchmark target: `,t]}),n&&(0,H.jsx)(`span`,{className:`block`,children:n})]})]}),(0,H.jsx)(W,{label:r.mode===`duration`?`Elapsed`:`Produced`,pct:r.mode===`duration`?s:a,tone:`accent`}),i>0&&r.mode===`count`&&(0,H.jsx)(W,{label:`Completed`,pct:o,tone:`emerald`}),(0,H.jsxs)(`div`,{className:`mt-4 grid grid-cols-2 gap-4 md:grid-cols-4`,children:[(0,H.jsx)(_,{label:`Pushed`,value:h(c.pushed),tone:`accent`,compact:!0}),(0,H.jsx)(_,{label:`Completed`,value:h(c.completed),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Push/sec`,value:L(c.pushPerSec),compact:!0}),(0,H.jsx)(_,{label:`Done/sec`,value:L(c.donePerSec),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Elapsed`,value:m(c.elapsedMs),compact:!0}),(0,H.jsx)(_,{label:`Active workers`,value:`${c.activeWorkers}/${i}`,tone:`blue`,compact:!0}),(0,H.jsx)(_,{label:`Data`,value:p(l?.bytes??c.bytes),compact:!0}),(0,H.jsx)(_,{label:`Errors`,value:h((l?.pushFailed??c.pushFailed)+(l?.ackFailed??c.ackFailed)),tone:c.pushFailed+c.ackFailed?`red`:`default`,compact:!0})]})]}),(0,H.jsxs)(d,{children:[(0,H.jsx)(u,{title:`Throughput`}),(0,H.jsx)(`p`,{className:`-mt-3 mb-4 text-xs text-faint`,children:`Enqueue (client → server) vs completion (workers) per second`}),(0,H.jsx)(S,{series:[{label:`Push/sec`,color:`#38bdf8`,points:c.pushSeries,area:!0},{label:`Done/sec`,color:`#34d399`,points:c.doneSeries,area:!0}]}),(0,H.jsxs)(`div`,{className:`mt-2 flex items-center gap-4 font-mono text-[11px] text-faint`,children:[(0,H.jsx)(G,{color:`#38bdf8`,label:`Push/sec`,value:c.pushPerSec}),(0,H.jsx)(G,{color:`#34d399`,label:`Done/sec`,value:c.donePerSec})]})]})]})}function ae({summary:e}){return e?(0,H.jsxs)(d,{className:`mt-6`,children:[(0,H.jsx)(u,{title:`Summary`}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 md:grid-cols-4 xl:grid-cols-8`,children:[(0,H.jsx)(_,{label:`Pushed`,value:h(e.pushed),tone:`accent`,compact:!0}),(0,H.jsx)(_,{label:`Completed`,value:h(e.completed),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Duration`,value:m(e.durationMs),compact:!0}),(0,H.jsx)(_,{label:`Avg push/s`,value:L(e.pushPerSec),tone:`accent`,compact:!0}),(0,H.jsx)(_,{label:`Avg done/s`,value:L(e.donePerSec),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Data rate`,value:`${p(e.mbPerSec)}/s`,compact:!0}),(0,H.jsx)(_,{label:`Push p95`,value:m(e.p95),compact:!0}),(0,H.jsx)(_,{label:`Push p99`,value:m(e.p99),compact:!0})]}),(0,H.jsxs)(`p`,{className:`mt-3 text-xs text-faint`,children:[`Push-batch latency avg `,m(e.avg),` · p50 `,m(e.p50),` · p95`,` `,m(e.p95),` · p99 `,m(e.p99),` · max `,m(e.max),`.`,e.error?` First error: ${e.error}`:``]})]}):null}function oe({pollQueue:e,pollBaseUrl:t,pinned:n,counts:r,error:i}){return(0,H.jsxs)(d,{className:`mt-6`,children:[(0,H.jsx)(u,{title:`Server queue: ${e}`,action:i?(0,H.jsx)(`span`,{className:`text-xs font-medium text-warning`,children:`stale — server unreachable`}):void 0}),(0,H.jsxs)(`p`,{className:`-mt-3 mb-4 text-xs text-faint`,children:[`Live counts from `,t,` (poll 1s).`,n?` Pinned to the current or last benchmark run.`:``]}),r?(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 md:grid-cols-5`,children:[(0,H.jsx)(_,{label:`Waiting`,value:h(r.waiting??0),tone:`amber`,compact:!0}),(0,H.jsx)(_,{label:`Active`,value:h(r.active??0),tone:`blue`,compact:!0}),(0,H.jsx)(_,{label:`Completed`,value:h(r.completed??0),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Failed`,value:h(r.failed??0),tone:r.failed?`red`:`default`,compact:!0}),(0,H.jsx)(_,{label:`Delayed`,value:h(r.delayed??0),compact:!0})]}):(0,H.jsx)(`p`,{className:`text-sm text-faint`,children:`No counts yet — waiting for the server to answer for this queue.`})]})}function W({label:e,pct:n,tone:r}){return(0,H.jsxs)(`div`,{className:`mb-2`,children:[(0,H.jsxs)(`div`,{className:`mb-1 flex items-center justify-between text-[11px] text-faint`,children:[(0,H.jsx)(`span`,{children:e}),(0,H.jsxs)(`span`,{className:`tnum`,children:[n.toFixed(0),`%`]})]}),(0,H.jsx)(`div`,{className:`h-1.5 overflow-hidden rounded-full bg-surface-2`,role:`progressbar`,"aria-label":e,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":Math.round(n),children:(0,H.jsx)(`div`,{className:t(`h-full rounded-full transition-[width] motion-reduce:transition-none`,r===`accent`?`bg-accent`:`bg-emerald-500`),style:{width:`${n}%`}})})]})}function G({color:e,label:t,value:n}){return(0,H.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,H.jsx)(`span`,{className:`size-2 rounded-full`,style:{background:e}}),t,` `,(0,H.jsxs)(`span`,{className:`text-fg`,children:[L(n),`/s`]})]})}function se({history:e,onClear:t}){if(!e.length)return null;let n=JSON.stringify(e,null,2);return(0,H.jsxs)(d,{padded:!1,className:`mt-6 overflow-hidden`,children:[(0,H.jsxs)(`div`,{className:`flex items-center justify-between border-b border-line px-5 py-3`,children:[(0,H.jsx)(`h3`,{className:`text-base font-semibold text-fg`,children:`Run history`}),(0,H.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,H.jsx)(g,{value:n}),(0,H.jsx)(l,{size:`sm`,variant:`ghost`,onClick:()=>{let e=new Blob([n],{type:`application/json`}),t=URL.createObjectURL(e),r=document.createElement(`a`);r.href=t,r.download=`benchmark-history-${Date.now()}.json`,r.click(),URL.revokeObjectURL(t)},children:`Export JSON`}),(0,H.jsx)(l,{size:`sm`,variant:`ghost`,onClick:t,children:`Clear`})]})]}),(0,H.jsx)(`div`,{className:`overflow-x-auto`,children:(0,H.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,H.jsx)(`thead`,{children:(0,H.jsxs)(`tr`,{className:`border-b border-line text-left text-[11px] uppercase tracking-wider text-faint`,children:[(0,H.jsx)(`th`,{className:`px-5 py-2 font-medium`,children:`Mode`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Prod`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Wkr`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Pushed`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Done`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Push/s`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Done/s`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`p95`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Dur`})]})}),(0,H.jsx)(`tbody`,{children:e.map(e=>(0,H.jsxs)(`tr`,{className:`border-b border-line last:border-0 hover:bg-surface-2/40`,children:[(0,H.jsx)(`td`,{className:`px-5 py-2 capitalize text-muted`,children:e.mode}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:e.producers}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:e.workers}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-fg`,children:h(e.pushed)}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-success`,children:h(e.completed)}),(0,H.jsxs)(`td`,{className:`px-5 py-2 text-right tnum text-accent`,children:[L(e.pushPerSec),`/s`]}),(0,H.jsxs)(`td`,{className:`px-5 py-2 text-right tnum text-success`,children:[L(e.donePerSec),`/s`]}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:m(e.p95)}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:m(e.durationMs)})]},e.id))})]})})]})}function ce(e=8){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`Benchmark compensation concurrency must be a positive whole number`);let t=0,n=[],r=()=>{--t,n.shift()?.()};return Object.freeze({run(i){return new Promise((a,o)=>{let s=()=>{t+=1,Promise.resolve().then(i).then(a,o).finally(r)};t<e?s():n.push(s)})}})}var le=[`waiting`,`prioritized`,`delayed`,`active`,`paused`,`waiting-children`,`completed`,`failed`];function K(e){return le.reduce((t,n)=>{let r=e[n];if(!Number.isSafeInteger(r)||r<0)throw Error(`Malformed queue count for "${n}".`);return t+r},0)}function q(e,t){if(!e||typeof e!=`object`||e.ok!==!0)throw Error(`${t} returned a malformed success response.`)}var J={pushed:0,completed:0,pushFailed:0,ackFailed:0,bytes:0,elapsedMs:0,pushPerSec:0,donePerSec:0,activeWorkers:0,pushSeries:[],doneSeries:[],etaMs:null,error:null},Y=()=>({pushed:0,completed:0,pushFailed:0,ackFailed:0,bytes:0,assigned:0,startedAt:0,pushLat:[],activeWorkers:0,error:null,lastAt:0,lastPushed:0,lastDone:0,pushSeries:[],doneSeries:[]});function ue(e,t,n,r){let i=r-e.startedAt,a=(r-e.lastAt)/1e3,o=a>0?Math.max(0,(e.pushed-e.lastPushed)/a):0,s=a>0?Math.max(0,(e.completed-e.lastDone)/a):0;e.lastAt=r,e.lastPushed=e.pushed,e.lastDone=e.completed,e.pushSeries=[...e.pushSeries,o].slice(-60),e.doneSeries=[...e.doneSeries,s].slice(-60);let c=null;if(t?.mode===`count`){let r=A(t.total,1,w.total);if(n===`draining`){let t=e.doneSeries.slice(-10),n=t.length?t.reduce((e,t)=>e+t,0)/t.length:0;n<=0&&i>0&&(n=e.completed/(i/1e3)),n>0&&(c=Math.max(0,r-e.completed)/n*1e3)}else o>0&&(c=Math.max(0,r-e.pushed)/o*1e3)}else t?.mode===`duration`&&(c=Math.max(0,A(t.durationS,1,w.durationS)*1e3-i));return{pushed:e.pushed,completed:e.completed,pushFailed:e.pushFailed,ackFailed:e.ackFailed,bytes:e.bytes,elapsedMs:i,pushPerSec:o,donePerSec:s,activeWorkers:e.activeWorkers,pushSeries:e.pushSeries,doneSeries:e.doneSeries,etaMs:c,error:e.error}}function de(e,t){let n=t/1e3||1,r=[...e.pushLat].sort((e,t)=>e-t);return{pushed:e.pushed,completed:e.completed,pushFailed:e.pushFailed,ackFailed:e.ackFailed,bytes:e.bytes,durationMs:t,pushPerSec:e.pushed/n,donePerSec:e.completed/n,mbPerSec:e.bytes/n,avg:r.length?r.reduce((e,t)=>e+t,0)/r.length:0,p50:M(r,50),p95:M(r,95),p99:M(r,99),max:r.length?r[r.length-1]:0,error:e.error}}function fe(e){let t=e.queue.trim(),n=j(t);return n?{ok:!1,error:n}:[`count`,`duration`].includes(e.mode)?typeof e.durable!=`boolean`||typeof e.removeOnComplete!=`boolean`?{ok:!1,error:`Benchmark job options are invalid.`}:{ok:!0,config:Object.freeze({...e,queue:t,total:A(e.total,1,w.total),durationS:A(e.durationS,1,w.durationS),batch:A(e.batch,1,w.batch),producers:A(e.producers,1,w.producers),payload:A(e.payload,0,w.payload),workers:A(e.workers,0,w.workers),workerBatch:A(e.workerBatch,1,w.workerBatch),processMs:A(e.processMs,0,w.processMs)})}:{ok:!1,error:`Benchmark mode is invalid.`}}async function pe(e,t,n){try{q(await e.overview(),`Dashboard preflight`)}catch(e){return{ok:!1,live:{...J,error:`Server unreachable — start it on the Server page first. (${I(e)})`}}}if(!n())return{ok:!0};try{let r=await e.counts(t);if(!n())return{ok:!0};if(q(r,`Queue-count preflight`),!r.counts||typeof r.counts!=`object`||Array.isArray(r.counts))throw Error(`Malformed queue-count response.`);let i=K(r.counts);if(i>0)return{ok:!1,live:{...J,error:`Dedicated benchmark queue "${t}" is not empty (${i} job(s) across all states). Clean it before running so no external work can be consumed or deleted.`}}}catch(e){return{ok:!1,live:{...J,error:`Could not verify that dedicated queue "${t}" is empty. (${I(e)})`}}}return{ok:!0}}function me(e){return async()=>{let{stats:t}=e;for(;e.shouldContinue();){let n;if(e.runConfig.mode===`count`){if(t.assigned>=e.total)break;n=Math.min(e.batch,e.total-t.assigned)}else{if(performance.now()>=e.deadline)break;n=e.batch}let r=t.assigned;t.assigned+=n;let i=N(r,n,e.blob,e.runConfig.durable,e.runConfig.removeOnComplete,e.runId,Math.max(12e4,e.processMs+6e4)),a=performance.now(),o=e.client.addJobsBulk(e.queue,i).then(r=>{if(!e.isCurrent())return;if(q(r,`Bulk enqueue`),!Array.isArray(r.ids)||r.ids.length!==n||r.ids.some(e=>typeof e!=`string`||e.length===0)||new Set(r.ids).size!==r.ids.length)throw Error(`Bulk enqueue returned invalid job IDs.`);let i=r.ids;for(let t of i)e.ownJobIds.add(t);t.pushed+=i.length,t.bytes+=i.length*e.payload;let a=Math.max(0,n-i.length);a>0&&(t.pushFailed+=a,t.error??=`Server created ${i.length} of ${n} jobs in a benchmark batch.`)}).catch(r=>{e.isCurrent()&&(t.pushFailed+=n,t.error??=I(r))});e.pendingPushes.add(o);try{await o}finally{e.pendingPushes.delete(o)}e.isCurrent()&&t.pushLat.push(performance.now()-a)}}}function he(e){return async()=>{let{stats:t}=e;for(;e.shouldContinue()&&!(e.runConfig.mode===`duration`&&performance.now()>=e.deadline||e.runConfig.mode===`count`&&t.completed>=e.total);){t.activeWorkers++;let n=[];try{if(n=await ge(e),!e.shouldContinue()){await X(e,n);break}n.length>0&&await _e(e,n)}finally{e.isCurrent()&&t.activeWorkers--}if(!e.shouldContinue())break;if(n.length===0){if(e.runConfig.mode===`count`&&e.producersDone()){e.ownJobIds.size>0&&(t.error??=`${e.ownJobIds.size} benchmark job(s) could not be drained.`);break}await F(50,e.shouldContinue)}}}}async function ge(e){try{let t=await e.client.pullBatch(e.queue,e.workerBatch,`dashboard-benchmark:${e.runId}`);if(t?.ok!==!0||!Array.isArray(t.jobs)||t.jobs.length>e.workerBatch||t.jobs.some(e=>!e||typeof e!=`object`||typeof e.id!=`string`||e.id.length===0)||!Array.isArray(t.tokens)||t.tokens.length!==t.jobs.length||t.tokens.some(e=>typeof e!=`string`||e.length===0)||new Set(t.jobs.map(e=>e.id)).size!==t.jobs.length)throw Error(`Malformed pull-batch response.`);let n=t.jobs.map((e,n)=>({id:e.id,token:t.tokens?.[n]}));return e.shouldContinue()?n:(await X(e,n),[])}catch(t){return e.isCurrent()&&(e.stats.error??=I(t)),[]}}async function _e(e,t){if(t.some(t=>!e.ownJobIds.has(t.id))&&e.pendingPushes.size>0&&await Promise.allSettled([...e.pendingPushes]),!e.shouldContinue())return X(e,t);let n=t.filter(t=>e.ownJobIds.has(t.id)),r=t.filter(t=>!e.ownJobIds.has(t.id));if(r.length>0){await ve(e,t,r.length);return}if(n.length===0)return;if(!e.shouldContinue()||(e.processMs>0&&await F(e.processMs,e.shouldContinue),!e.shouldContinue()))return X(e,n);let i=!1;try{let t=n.map(e=>e.id),r=n.map(e=>e.token),a=await e.client.heartbeatBatch(t,r);q(a,`Heartbeat benchmark jobs`);let o=a.data;if(!o||typeof o!=`object`||o.ok!==!0||!Number.isSafeInteger(o.count)||o.count!==n.length)throw Error(`Heartbeat confirmed ${String(o?.count)} of ${n.length} benchmark jobs; refusing a lossy batch ACK.`);if(!e.shouldContinue())return X(e,n);if(i=!0,q(await e.client.ackBatch(t,r),`Acknowledge benchmark jobs`),!e.isCurrent())return;e.stats.completed+=n.length;for(let t of n)e.ownJobIds.delete(t.id)}catch(t){e.isCurrent()&&(e.stats.ackFailed+=n.length,e.stats.error??=I(t)),i||await X(e,n)}}async function ve(e,t,n){let r=await Z(e,t);e.isCurrent()&&(e.stats.error??=r>0?`Detected ${n} foreign job(s); ${r} pulled job(s) could not be returned to waiting. Benchmark stopped.`:`Detected ${n} foreign job(s); returned the complete pulled batch to waiting and stopped without counting it.`),e.stop()}async function X(e,t){if(t.length===0)return;let n=await Z(e,t);n>0&&e.isCurrent()&&(e.stats.error??=`${n} interrupted benchmark job(s) could not be returned to waiting.`)}async function Z(e,t){return(await Promise.allSettled(t.map(t=>e.compensationQueue.run(async()=>{q(await e.client.retryJob(t.id,t.token),`Restore job ${t.id}`),e.ownJobIds.delete(t.id)})))).filter(e=>e.status===`rejected`).length}var ye=0;async function be(e,t,n){if(e.runningRef.current||!e.mountedRef.current)return;e.runningRef.current=!0;let r=++e.runGenerationRef.current,i=n??a.createServerTargetClient(a.captureServerRequestTarget()),o=()=>e.mountedRef.current&&e.runGenerationRef.current===r,s=()=>o()&&!e.stopRef.current;e.stopRef.current=!1;try{e.setSummary(null);let n=fe(t);if(!n.ok){e.setLive({...J,error:n.error}),Q(e,`error`);return}let r=n.config,a=await pe(i,r.queue,s);if(!o())return;if(!a.ok){e.setLive(a.live),Q(e,`error`);return}if(!s())return;e.producersDoneRef.current=!1,e.configRef.current=r,e.setRunConfig(r);let c=Y();c.startedAt=performance.now(),c.lastAt=performance.now(),e.statsRef.current=c,e.setLive({...J}),Q(e,`running`);let l={batch:r.batch,blob:`x`.repeat(r.payload),client:i,compensationQueue:ce(),deadline:performance.now()+r.durationS*1e3,isCurrent:o,ownJobIds:new Set,payload:r.payload,pendingPushes:new Set,processMs:r.processMs,producersDone:()=>e.producersDoneRef.current,queue:r.queue,runConfig:r,runId:`bqbench-${globalThis.crypto.randomUUID()}`,shouldContinue:s,stats:c,stop:()=>{e.stopRef.current=!0},total:r.total,workerBatch:r.workerBatch},u=me(l),d=he(l),f=Array.from({length:r.producers},u),p=Array.from({length:r.workers},d),m=Promise.all(f).then(()=>{o()&&(e.producersDoneRef.current=!0,s()&&r.mode===`count`&&r.workers>0&&e.phaseRef.current===`running`&&Q(e,`draining`))});if(await Promise.all([m,...p]),!o())return;xe(e,r,c)}finally{e.runningRef.current=!1}}function xe(e,t,n){let r=performance.now()-n.startedAt,i=de(n,r);e.setSummary(i),e.setLive(e=>({...e,pushed:n.pushed,completed:n.completed,pushFailed:n.pushFailed,ackFailed:n.ackFailed,bytes:n.bytes,elapsedMs:r,pushPerSec:0,donePerSec:0,activeWorkers:0,etaMs:0,error:n.error})),e.setHistory(e=>[{...i,id:++ye,at:Date.now(),mode:t.mode,producers:t.producers,workers:t.workers},...e].slice(0,12)),Q(e,e.stopRef.current?`stopped`:n.error?`error`:`done`)}function Q(e,t){e.phaseRef.current=t,e.setPhase(t)}function Se(){let[e,t]=(0,C.useState)(`idle`),[n,r]=(0,C.useState)(J),[i,a]=(0,C.useState)(null),[o,s]=(0,C.useState)([]),[c,l]=(0,C.useState)(null),u=(0,C.useRef)(!1),d=(0,C.useRef)(!0),f=(0,C.useRef)(0),p=(0,C.useRef)(!1),m=(0,C.useRef)(!1),h=(0,C.useRef)(null),g=(0,C.useRef)(`idle`),_=(0,C.useRef)(Y());return g.current=e,(0,C.useEffect)(()=>{let e=d,t=u,n=f;return d.current=!0,()=>{e.current=!1,t.current=!0,n.current++}},[]),(0,C.useEffect)(()=>{if(e!==`running`&&e!==`draining`)return;let t=setInterval(()=>{r(ue(_.current,h.current,g.current,performance.now()))},200);return()=>clearInterval(t)},[e]),{phase:e,live:n,summary:i,history:o,runCfg:c,run:(e,n)=>be({configRef:h,mountedRef:d,phaseRef:g,producersDoneRef:m,runningRef:p,runGenerationRef:f,setHistory:s,setLive:r,setPhase:t,setRunConfig:l,setSummary:a,statsRef:_,stopRef:u},e,n),stop:()=>{u.current=!0,(g.current===`running`||g.current===`draining`)&&(g.current=`stopping`,t(`stopping`))},reset:()=>{u.current=!0,f.current++,g.current=`idle`,a(null),r(J),t(`idle`)},clearHistory:()=>s([])}}function Ce({benchmark:e,counts:t,dedicatedQueue:n,draft:r,pollQueue:i,runTarget:a,setRunTarget:o}){let s=(0,C.useRef)(null),c=(0,C.useRef)(!0),[l,u]=(0,C.useState)(!1),[d,f]=(0,C.useState)(null);(0,C.useEffect)(()=>(c.current=!0,()=>{c.current=!1,s.current=null}),[]);let p=async()=>{let e=r.queue.trim();if(s.current||!e)return;if(!O(e)||e!==n){f({error:`refusing to clean a queue not owned by this dashboard session`});return}s.current=`clean`;let o=a??$(),l=t&&i===e?` Currently ${h(t.waiting??0)} waiting / ${h(t.completed??0)} completed.`:``;if(!window.confirm(`Clean every cleanable job from the dedicated dashboard benchmark queue "${e}" on ${o.target.baseUrl}? This is queue-wide; the cryptographic queue name is owned by this browser session.${l}`)){s.current=null;return}u(!0),f(null);try{let t=null;for(let n of z){if(!c.current)return;try{let t=await o.client.clean(e,{state:n,limit:w.total});if(q(t,`Clean ${n} jobs`),!Number.isSafeInteger(t.count)||t.count<0)throw Error(`Clean ${n} jobs returned an invalid count.`)}catch(e){t=e.message}}if(!c.current)return;let n=await o.client.counts(e).catch(e=>(t=e.message,null));if(!c.current)return;if(!n)f({error:t??`server unreachable — could not verify`});else if(n.ok!==!0||!n.counts||typeof n.counts!=`object`||Array.isArray(n.counts))f({error:`server returned a malformed queue-count response`});else try{let e=K(n.counts);f(t||e>0?{error:t??`${h(e)} job(s) still present`}:{remaining:0})}catch{f({error:`server returned malformed queue counts`})}}finally{c.current&&u(!1),s.current===`clean`&&(s.current=null)}},m=()=>{if(s.current)return;s.current=`run`;let n=V(r),c=n.queue.trim(),l=$();if(j(c)){e.run({...n,queue:c},l.client).finally(g);return}let u=0;if(!a&&t&&i===c)try{u=K(t)}catch{}let d=u>0?` Warning: it already holds ${h(u)} job(s) across all states; the safety preflight will refuse the run until the dedicated queue is empty.`:``;if(!window.confirm(`Enqueue real jobs into "${c}" on ${l.target.baseUrl}?${d}`)){s.current=null;return}o(l),e.run(n,l.client).finally(g)},g=()=>{s.current===`run`&&(s.current=null)};return{cleaning:l,cleanResult:d,cleanup:p,start:m}}function $(){let e=a.captureServerRequestTarget();return{target:e,client:a.createServerTargetClient(e)}}function we(){let[e]=(0,C.useState)(ee),[t,n]=(0,C.useState)(()=>ne({...T,queue:e})),r=Se(),u=i(e=>e.baseUrl),[d,p]=(0,C.useState)(null),m=(e,t)=>n(n=>({...n,[e]:t})),h=e=>t=>m(e,t.target.value),g=e=>n(t=>{let n={...t};for(let[t,r]of Object.entries(e))n[t]=te.includes(t)?String(r):r;return n}),[_,v]=(0,C.useState)(e);(0,C.useEffect)(()=>{let e=setTimeout(()=>v(t.queue.trim()||T.queue),400);return()=>clearTimeout(e)},[t.queue]);let y=d?.target.baseUrl??u,{data:b,error:x}=c(()=>d?d.client.counts(_):a.counts(_),[_,d],{intervalMs:1e3}),S=b?.counts??null,E=Ce({benchmark:r,counts:S,dedicatedQueue:e,draft:t,pollQueue:_,runTarget:d,setRunTarget:p}),{phase:D,live:O,summary:j,history:M}=r,N=D===`running`||D===`draining`||D===`stopping`,P=r.runCfg??V(t),F=A(P.total,1,w.total),I=A(P.workers,0,w.workers),L=P.mode===`count`&&(N||j)?Math.min(100,O.pushed/F*100):0,R=P.mode===`count`&&(N||j)?Math.min(100,O.completed/F*100):0,z=P.mode===`duration`?Math.min(100,O.elapsedMs/(A(P.durationS,1,w.durationS)*1e3)*100):0,B=Te(D,P.mode,!!j),U=Ee(D,O.etaMs);return(0,H.jsxs)(`div`,{children:[(0,H.jsx)(f,{title:`Benchmark`,description:`Drive real load against the server — producers bulk-enqueue jobs while simulated workers pull, process and ack them. Throughput is measured client-side; the queue genuinely fills and drains.`,actions:D===`stopping`?(0,H.jsxs)(l,{variant:`warning`,size:`sm`,disabled:!0,children:[(0,H.jsx)(o,{className:`size-3.5`}),` Stopping…`]}):N?(0,H.jsxs)(l,{variant:`warning`,size:`sm`,onClick:r.stop,children:[(0,H.jsx)(o,{className:`size-3.5`}),` Stop`]}):(0,H.jsxs)(l,{variant:`success`,size:`sm`,disabled:E.cleaning,onClick:E.start,children:[(0,H.jsx)(s,{className:`size-3.5`}),` Run benchmark`]})}),O.error&&(0,H.jsx)(`div`,{className:`mb-4 rounded-lg border border-red-500/20 bg-red-500/5 px-4 py-2 text-sm text-danger`,children:O.error}),(0,H.jsxs)(`div`,{className:`mb-4 flex flex-wrap items-center gap-2`,children:[(0,H.jsx)(`span`,{className:`text-xs font-medium uppercase tracking-wider text-faint`,children:`Presets`}),Object.keys(k).map(e=>(0,H.jsx)(`button`,{type:`button`,disabled:N,onClick:()=>g(k[e]),className:`rounded-md border border-line bg-surface-2 px-2.5 py-1 text-xs text-muted transition-colors hover:border-line-strong hover:text-fg disabled:opacity-40`,children:e},e))]}),(0,H.jsxs)(`div`,{className:`grid grid-cols-1 gap-6 lg:grid-cols-3`,children:[(0,H.jsx)(re,{draft:t,active:N,cleaning:E.cleaning,cleanResult:E.cleanResult,setField:m,numberInput:h,onClean:()=>void E.cleanup()}),(0,H.jsx)(ie,{heading:B,pollBaseUrl:y,etaText:U,shown:P,shownWorkers:I,producePct:L,drainPct:R,durationPct:z,live:O,summary:j})]}),(0,H.jsx)(ae,{summary:j}),(0,H.jsx)(oe,{pollQueue:_,pollBaseUrl:y,pinned:!!d,counts:S,error:x}),(0,H.jsx)(se,{history:M,onClear:r.clearHistory})]})}function Te(e,t,n){return e===`running`?t===`duration`?`Running…`:`Producing…`:e===`draining`?`Draining…`:e===`stopping`?`Stopping…`:e===`error`?`Error`:n?`Result`:`Ready`}function Ee(e,t){return(e===`running`||e===`draining`)&&t!=null?t>0?`ETA ${m(t)}`:`finishing…`:e===`stopped`?`stopped early`:e===`done`?`complete`:``}export{we as Benchmark};
|
|
1
|
+
import{i as e,t}from"./cn-zFEt5SO8.js";import{g as n,t as r}from"./react-vendor-CvwrP0tU.js";import{o as i}from"./connectionStore-BJLihGaC.js";import{t as a}from"./bq-F5BbTG1T.js";import{b as o,x as s}from"./icons-BVzwsfbG.js";import{n as c}from"./usePolledData-0CVelkl2.js";import{t as l}from"./Button-Ch1W94fJ.js";import{n as u,t as d}from"./Card-CWxrC8OY.js";import{t as f}from"./PageHeader-CvIyaFOO.js";import{n as p,o as m,s as h}from"./format-Cg7VCIZW.js";import{a as g,n as _}from"./index-8Ii-_Rml.js";import{i as v,n as y,o as b,t as x}from"./form-C4oby9pI.js";import{t as S}from"./AreaChart-DEAalJ6P.js";var C=e(n(),1),w={total:1e6,batch:5e3,producers:64,payload:65536,workers:64,workerBatch:1e3,processMs:6e4,durationS:600},T={queue:`benchmark`,mode:`count`,total:5e3,durationS:30,batch:100,producers:4,payload:128,workers:4,workerBatch:100,processMs:5,durable:!1,removeOnComplete:!0},E=`bq-dashboard-benchmark-`;function D(){if(typeof globalThis.crypto?.randomUUID!=`function`)throw Error(`A cryptographically secure browser is required to create a benchmark queue.`);return`${E}${globalThis.crypto.randomUUID()}`}function O(e){return RegExp(`^${E}[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`,`i`).test(e)}var k={Smoke:{mode:`count`,total:200,batch:50,producers:2,workers:1,workerBatch:50,processMs:0},Standard:{mode:`count`,total:5e3,batch:100,producers:4,workers:4,workerBatch:100,processMs:5},Stress:{mode:`count`,total:5e4,batch:500,producers:16,workers:16,workerBatch:200,processMs:0},Soak:{mode:`duration`,durationS:30,batch:100,producers:4,workers:8,workerBatch:100,processMs:10}},A=(e,t,n)=>Number.isFinite(e)?Math.max(t,Math.min(n,Math.floor(e))):t;function j(e){return e?e.length>256?`Queue name is too long (maximum 256 characters).`:/^[a-zA-Z0-9_\-.:]+$/.test(e)?null:`Queue name contains unsupported characters.`:`Queue name is required.`}var M=(e,t)=>e.length?e[Math.min(e.length-1,Math.max(0,Math.ceil(t/100*e.length)-1))]:0,N=(e,t,n,r,i,a,o)=>Array.from({length:t},(t,s)=>({data:{i:e+s,blob:n},durable:r,removeOnComplete:i,...o===void 0?{}:{stallTimeout:o},...a?{jobId:`${a}-${e+s}`,tags:[`bunqueue-dashboard-benchmark`,a]}:{}})),P=e=>new Promise(t=>setTimeout(t,e)),F=async(e,t)=>{let n=performance.now()+e;for(;t()&&performance.now()<n;)await P(Math.min(100,Math.max(0,n-performance.now())))},I=e=>e instanceof Error?e.message:String(e),L=e=>e>=1e6?`${(e/1e6).toFixed(2)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:`${Math.round(e)}`,R=[`count`,`duration`],z=[`waiting`,`completed`,`failed`],B=null;function ee(){return B??=D(),B}var te=[`total`,`durationS`,`batch`,`producers`,`payload`,`workers`,`workerBatch`,`processMs`],ne=e=>({...e,total:String(e.total),durationS:String(e.durationS),batch:String(e.batch),producers:String(e.producers),payload:String(e.payload),workers:String(e.workers),workerBatch:String(e.workerBatch),processMs:String(e.processMs)}),V=e=>({...e,total:Number(e.total),durationS:Number(e.durationS),batch:Number(e.batch),producers:Number(e.producers),payload:Number(e.payload),workers:Number(e.workers),workerBatch:Number(e.workerBatch),processMs:Number(e.processMs)}),H=r();function re({draft:e,active:n,cleaning:r,cleanResult:i,setField:a,numberInput:o,onClean:s}){return(0,H.jsxs)(d,{className:`lg:col-span-1`,children:[(0,H.jsx)(u,{title:`Configuration`}),(0,H.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,H.jsx)(x,{label:`Dedicated queue`,hint:`Cryptographically generated for this browser tab; production queue names cannot be entered here.`,children:(0,H.jsx)(y,{name:`benchmark-queue`,autoComplete:`off`,value:e.queue,readOnly:!0,"aria-readonly":`true`})}),(0,H.jsx)(x,{label:`Mode`,hint:e.mode===`count`?`Push a fixed number of jobs.`:`Run producers + workers for a fixed time.`,children:(0,H.jsx)(v,{options:R,value:e.mode,onChange:e=>a(`mode`,e),disabled:n})}),e.mode===`count`?(0,H.jsx)(x,{label:`Total jobs`,hint:`max ${h(w.total)}`,children:(0,H.jsx)(y,{name:`benchmark-total`,autoComplete:`off`,type:`number`,min:1,max:w.total,value:e.total,disabled:n,onChange:o(`total`)})}):(0,H.jsx)(x,{label:`Duration (s)`,hint:`max ${w.durationS}s`,children:(0,H.jsx)(y,{name:`benchmark-duration-seconds`,autoComplete:`off`,type:`number`,min:1,max:w.durationS,value:e.durationS,disabled:n,onChange:o(`durationS`)})}),(0,H.jsx)(`div`,{className:`mt-1 text-[11px] font-semibold uppercase tracking-wider text-faint`,children:`Producers`}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,H.jsx)(x,{label:`Producers`,hint:`parallel, max ${w.producers}`,children:(0,H.jsx)(y,{name:`benchmark-producers`,autoComplete:`off`,type:`number`,min:1,max:w.producers,value:e.producers,disabled:n,onChange:o(`producers`)})}),(0,H.jsx)(x,{label:`Push batch`,hint:`jobs/req, max ${w.batch}`,children:(0,H.jsx)(y,{name:`benchmark-push-batch`,autoComplete:`off`,type:`number`,min:1,max:w.batch,value:e.batch,disabled:n,onChange:o(`batch`)})}),(0,H.jsx)(x,{label:`Payload`,hint:`bytes/job, max ${h(w.payload)}`,children:(0,H.jsx)(y,{name:`benchmark-payload-bytes`,autoComplete:`off`,type:`number`,min:0,max:w.payload,value:e.payload,disabled:n,onChange:o(`payload`)})})]}),(0,H.jsx)(`div`,{className:`mt-1 text-[11px] font-semibold uppercase tracking-wider text-faint`,children:`Workers (simulated)`}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,H.jsx)(x,{label:`Workers`,hint:`0 = produce only, max ${w.workers}`,children:(0,H.jsx)(y,{name:`benchmark-workers`,autoComplete:`off`,type:`number`,min:0,max:w.workers,value:e.workers,disabled:n,onChange:o(`workers`)})}),(0,H.jsx)(x,{label:`Pull batch`,hint:`jobs/pull, max ${w.workerBatch}`,children:(0,H.jsx)(y,{name:`benchmark-pull-batch`,autoComplete:`off`,type:`number`,min:1,max:w.workerBatch,value:e.workerBatch,disabled:n,onChange:o(`workerBatch`)})}),(0,H.jsx)(x,{label:`Process (ms)`,hint:`simulated work per pull`,children:(0,H.jsx)(y,{name:`benchmark-process-ms`,autoComplete:`off`,type:`number`,min:0,max:w.processMs,value:e.processMs,disabled:n,onChange:o(`processMs`)})})]}),(0,H.jsx)(U,{label:`Durable (fsync each job)`,checked:e.durable,active:n,onChange:e=>a(`durable`,e)}),(0,H.jsx)(U,{label:`Remove on complete`,checked:e.removeOnComplete,active:n,onChange:e=>a(`removeOnComplete`,e)}),(0,H.jsxs)(`div`,{className:`flex items-center gap-3 pt-1`,children:[(0,H.jsx)(l,{variant:`ghost`,size:`sm`,disabled:n||r,onClick:s,children:r?`Cleaning…`:`Clean queue`}),i&&(`error`in i?(0,H.jsxs)(`span`,{className:`text-xs text-danger`,children:[`Clean unverified — `,i.error]}):(0,H.jsx)(`span`,{className:t(`text-xs`,i.remaining>0?`text-warning`:`text-success`),children:i.remaining>0?`Cleaned — ${h(i.remaining)} active job(s) remain (requeued after the stall timeout)`:`Cleaned — 0 jobs remain`}))]})]})]})}function U({label:e,checked:t,active:n,onChange:r}){return(0,H.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,H.jsx)(b,{checked:t,onChange:r,disabled:n,label:e}),(0,H.jsx)(`span`,{className:`text-sm text-muted`,children:e})]})}function ie({heading:e,pollBaseUrl:t,etaText:n,shown:r,shownWorkers:i,producePct:a,drainPct:o,durationPct:s,live:c,summary:l}){return(0,H.jsxs)(`div`,{className:`flex flex-col gap-6 lg:col-span-2`,children:[(0,H.jsxs)(d,{children:[(0,H.jsxs)(`div`,{className:`mb-3 flex items-center justify-between`,children:[(0,H.jsx)(`h3`,{className:`text-base font-semibold text-fg`,children:e}),(0,H.jsxs)(`span`,{className:`text-right text-xs text-faint`,children:[(0,H.jsxs)(`span`,{className:`block`,children:[`Benchmark target: `,t]}),n&&(0,H.jsx)(`span`,{className:`block`,children:n})]})]}),(0,H.jsx)(W,{label:r.mode===`duration`?`Elapsed`:`Produced`,pct:r.mode===`duration`?s:a,tone:`accent`}),i>0&&r.mode===`count`&&(0,H.jsx)(W,{label:`Completed`,pct:o,tone:`emerald`}),(0,H.jsxs)(`div`,{className:`mt-4 grid grid-cols-2 gap-4 md:grid-cols-4`,children:[(0,H.jsx)(_,{label:`Pushed`,value:h(c.pushed),tone:`accent`,compact:!0}),(0,H.jsx)(_,{label:`Completed`,value:h(c.completed),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Push/sec`,value:L(c.pushPerSec),compact:!0}),(0,H.jsx)(_,{label:`Done/sec`,value:L(c.donePerSec),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Elapsed`,value:m(c.elapsedMs),compact:!0}),(0,H.jsx)(_,{label:`Active workers`,value:`${c.activeWorkers}/${i}`,tone:`blue`,compact:!0}),(0,H.jsx)(_,{label:`Data`,value:p(l?.bytes??c.bytes),compact:!0}),(0,H.jsx)(_,{label:`Errors`,value:h((l?.pushFailed??c.pushFailed)+(l?.ackFailed??c.ackFailed)),tone:c.pushFailed+c.ackFailed?`red`:`default`,compact:!0})]})]}),(0,H.jsxs)(d,{children:[(0,H.jsx)(u,{title:`Throughput`}),(0,H.jsx)(`p`,{className:`-mt-3 mb-4 text-xs text-faint`,children:`Enqueue (client → server) vs completion (workers) per second`}),(0,H.jsx)(S,{series:[{label:`Push/sec`,color:`#38bdf8`,points:c.pushSeries,area:!0},{label:`Done/sec`,color:`#34d399`,points:c.doneSeries,area:!0}]}),(0,H.jsxs)(`div`,{className:`mt-2 flex items-center gap-4 font-mono text-[11px] text-faint`,children:[(0,H.jsx)(G,{color:`#38bdf8`,label:`Push/sec`,value:c.pushPerSec}),(0,H.jsx)(G,{color:`#34d399`,label:`Done/sec`,value:c.donePerSec})]})]})]})}function ae({summary:e}){return e?(0,H.jsxs)(d,{className:`mt-6`,children:[(0,H.jsx)(u,{title:`Summary`}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 md:grid-cols-4 xl:grid-cols-8`,children:[(0,H.jsx)(_,{label:`Pushed`,value:h(e.pushed),tone:`accent`,compact:!0}),(0,H.jsx)(_,{label:`Completed`,value:h(e.completed),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Duration`,value:m(e.durationMs),compact:!0}),(0,H.jsx)(_,{label:`Avg push/s`,value:L(e.pushPerSec),tone:`accent`,compact:!0}),(0,H.jsx)(_,{label:`Avg done/s`,value:L(e.donePerSec),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Data rate`,value:`${p(e.mbPerSec)}/s`,compact:!0}),(0,H.jsx)(_,{label:`Push p95`,value:m(e.p95),compact:!0}),(0,H.jsx)(_,{label:`Push p99`,value:m(e.p99),compact:!0})]}),(0,H.jsxs)(`p`,{className:`mt-3 text-xs text-faint`,children:[`Push-batch latency avg `,m(e.avg),` · p50 `,m(e.p50),` · p95`,` `,m(e.p95),` · p99 `,m(e.p99),` · max `,m(e.max),`.`,e.error?` First error: ${e.error}`:``]})]}):null}function oe({pollQueue:e,pollBaseUrl:t,pinned:n,counts:r,error:i}){return(0,H.jsxs)(d,{className:`mt-6`,children:[(0,H.jsx)(u,{title:`Server queue: ${e}`,action:i?(0,H.jsx)(`span`,{className:`text-xs font-medium text-warning`,children:`stale — server unreachable`}):void 0}),(0,H.jsxs)(`p`,{className:`-mt-3 mb-4 text-xs text-faint`,children:[`Live counts from `,t,` (poll 1s).`,n?` Pinned to the current or last benchmark run.`:``]}),r?(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 md:grid-cols-5`,children:[(0,H.jsx)(_,{label:`Waiting`,value:h(r.waiting??0),tone:`amber`,compact:!0}),(0,H.jsx)(_,{label:`Active`,value:h(r.active??0),tone:`blue`,compact:!0}),(0,H.jsx)(_,{label:`Completed`,value:h(r.completed??0),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Failed`,value:h(r.failed??0),tone:r.failed?`red`:`default`,compact:!0}),(0,H.jsx)(_,{label:`Delayed`,value:h(r.delayed??0),compact:!0})]}):(0,H.jsx)(`p`,{className:`text-sm text-faint`,children:`No counts yet — waiting for the server to answer for this queue.`})]})}function W({label:e,pct:n,tone:r}){return(0,H.jsxs)(`div`,{className:`mb-2`,children:[(0,H.jsxs)(`div`,{className:`mb-1 flex items-center justify-between text-[11px] text-faint`,children:[(0,H.jsx)(`span`,{children:e}),(0,H.jsxs)(`span`,{className:`tnum`,children:[n.toFixed(0),`%`]})]}),(0,H.jsx)(`div`,{className:`h-1.5 overflow-hidden rounded-full bg-surface-2`,role:`progressbar`,"aria-label":e,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":Math.round(n),children:(0,H.jsx)(`div`,{className:t(`h-full rounded-full transition-[width] motion-reduce:transition-none`,r===`accent`?`bg-accent`:`bg-emerald-500`),style:{width:`${n}%`}})})]})}function G({color:e,label:t,value:n}){return(0,H.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,H.jsx)(`span`,{className:`size-2 rounded-full`,style:{background:e}}),t,` `,(0,H.jsxs)(`span`,{className:`text-fg`,children:[L(n),`/s`]})]})}function se({history:e,onClear:t}){if(!e.length)return null;let n=JSON.stringify(e,null,2);return(0,H.jsxs)(d,{padded:!1,className:`mt-6 overflow-hidden`,children:[(0,H.jsxs)(`div`,{className:`flex items-center justify-between border-b border-line px-5 py-3`,children:[(0,H.jsx)(`h3`,{className:`text-base font-semibold text-fg`,children:`Run history`}),(0,H.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,H.jsx)(g,{value:n}),(0,H.jsx)(l,{size:`sm`,variant:`ghost`,onClick:()=>{let e=new Blob([n],{type:`application/json`}),t=URL.createObjectURL(e),r=document.createElement(`a`);r.href=t,r.download=`benchmark-history-${Date.now()}.json`,r.click(),URL.revokeObjectURL(t)},children:`Export JSON`}),(0,H.jsx)(l,{size:`sm`,variant:`ghost`,onClick:t,children:`Clear`})]})]}),(0,H.jsx)(`div`,{className:`overflow-x-auto`,children:(0,H.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,H.jsx)(`thead`,{children:(0,H.jsxs)(`tr`,{className:`border-b border-line text-left text-[11px] uppercase tracking-wider text-faint`,children:[(0,H.jsx)(`th`,{className:`px-5 py-2 font-medium`,children:`Mode`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Prod`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Wkr`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Pushed`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Done`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Push/s`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Done/s`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`p95`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Dur`})]})}),(0,H.jsx)(`tbody`,{children:e.map(e=>(0,H.jsxs)(`tr`,{className:`border-b border-line last:border-0 hover:bg-surface-2/40`,children:[(0,H.jsx)(`td`,{className:`px-5 py-2 capitalize text-muted`,children:e.mode}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:e.producers}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:e.workers}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-fg`,children:h(e.pushed)}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-success`,children:h(e.completed)}),(0,H.jsxs)(`td`,{className:`px-5 py-2 text-right tnum text-accent`,children:[L(e.pushPerSec),`/s`]}),(0,H.jsxs)(`td`,{className:`px-5 py-2 text-right tnum text-success`,children:[L(e.donePerSec),`/s`]}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:m(e.p95)}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:m(e.durationMs)})]},e.id))})]})})]})}function ce(e=8){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`Benchmark compensation concurrency must be a positive whole number`);let t=0,n=[],r=()=>{--t,n.shift()?.()};return Object.freeze({run(i){return new Promise((a,o)=>{let s=()=>{t+=1,Promise.resolve().then(i).then(a,o).finally(r)};t<e?s():n.push(s)})}})}var le=[`waiting`,`prioritized`,`delayed`,`active`,`paused`,`waiting-children`,`completed`,`failed`];function K(e){return le.reduce((t,n)=>{let r=e[n];if(!Number.isSafeInteger(r)||r<0)throw Error(`Malformed queue count for "${n}".`);return t+r},0)}function q(e,t){if(!e||typeof e!=`object`||e.ok!==!0)throw Error(`${t} returned a malformed success response.`)}var J={pushed:0,completed:0,pushFailed:0,ackFailed:0,bytes:0,elapsedMs:0,pushPerSec:0,donePerSec:0,activeWorkers:0,pushSeries:[],doneSeries:[],etaMs:null,error:null},Y=()=>({pushed:0,completed:0,pushFailed:0,ackFailed:0,bytes:0,assigned:0,startedAt:0,pushLat:[],activeWorkers:0,error:null,lastAt:0,lastPushed:0,lastDone:0,pushSeries:[],doneSeries:[]});function ue(e,t,n,r){let i=r-e.startedAt,a=(r-e.lastAt)/1e3,o=a>0?Math.max(0,(e.pushed-e.lastPushed)/a):0,s=a>0?Math.max(0,(e.completed-e.lastDone)/a):0;e.lastAt=r,e.lastPushed=e.pushed,e.lastDone=e.completed,e.pushSeries=[...e.pushSeries,o].slice(-60),e.doneSeries=[...e.doneSeries,s].slice(-60);let c=null;if(t?.mode===`count`){let r=A(t.total,1,w.total);if(n===`draining`){let t=e.doneSeries.slice(-10),n=t.length?t.reduce((e,t)=>e+t,0)/t.length:0;n<=0&&i>0&&(n=e.completed/(i/1e3)),n>0&&(c=Math.max(0,r-e.completed)/n*1e3)}else o>0&&(c=Math.max(0,r-e.pushed)/o*1e3)}else t?.mode===`duration`&&(c=Math.max(0,A(t.durationS,1,w.durationS)*1e3-i));return{pushed:e.pushed,completed:e.completed,pushFailed:e.pushFailed,ackFailed:e.ackFailed,bytes:e.bytes,elapsedMs:i,pushPerSec:o,donePerSec:s,activeWorkers:e.activeWorkers,pushSeries:e.pushSeries,doneSeries:e.doneSeries,etaMs:c,error:e.error}}function de(e,t){let n=t/1e3||1,r=[...e.pushLat].sort((e,t)=>e-t);return{pushed:e.pushed,completed:e.completed,pushFailed:e.pushFailed,ackFailed:e.ackFailed,bytes:e.bytes,durationMs:t,pushPerSec:e.pushed/n,donePerSec:e.completed/n,mbPerSec:e.bytes/n,avg:r.length?r.reduce((e,t)=>e+t,0)/r.length:0,p50:M(r,50),p95:M(r,95),p99:M(r,99),max:r.length?r[r.length-1]:0,error:e.error}}function fe(e){let t=e.queue.trim(),n=j(t);return n?{ok:!1,error:n}:[`count`,`duration`].includes(e.mode)?typeof e.durable!=`boolean`||typeof e.removeOnComplete!=`boolean`?{ok:!1,error:`Benchmark job options are invalid.`}:{ok:!0,config:Object.freeze({...e,queue:t,total:A(e.total,1,w.total),durationS:A(e.durationS,1,w.durationS),batch:A(e.batch,1,w.batch),producers:A(e.producers,1,w.producers),payload:A(e.payload,0,w.payload),workers:A(e.workers,0,w.workers),workerBatch:A(e.workerBatch,1,w.workerBatch),processMs:A(e.processMs,0,w.processMs)})}:{ok:!1,error:`Benchmark mode is invalid.`}}async function pe(e,t,n){try{q(await e.overview(),`Dashboard preflight`)}catch(e){return{ok:!1,live:{...J,error:`Server unreachable — start it on the Server page first. (${I(e)})`}}}if(!n())return{ok:!0};try{let r=await e.counts(t);if(!n())return{ok:!0};if(q(r,`Queue-count preflight`),!r.counts||typeof r.counts!=`object`||Array.isArray(r.counts))throw Error(`Malformed queue-count response.`);let i=K(r.counts);if(i>0)return{ok:!1,live:{...J,error:`Dedicated benchmark queue "${t}" is not empty (${i} job(s) across all states). Clean it before running so no external work can be consumed or deleted.`}}}catch(e){return{ok:!1,live:{...J,error:`Could not verify that dedicated queue "${t}" is empty. (${I(e)})`}}}return{ok:!0}}function me(e){return async()=>{let{stats:t}=e;for(;e.shouldContinue();){let n;if(e.runConfig.mode===`count`){if(t.assigned>=e.total)break;n=Math.min(e.batch,e.total-t.assigned)}else{if(performance.now()>=e.deadline)break;n=e.batch}let r=t.assigned;t.assigned+=n;let i=N(r,n,e.blob,e.runConfig.durable,e.runConfig.removeOnComplete,e.runId,Math.max(12e4,e.processMs+6e4)),a=performance.now(),o=e.client.addJobsBulk(e.queue,i).then(r=>{if(!e.isCurrent())return;if(q(r,`Bulk enqueue`),!Array.isArray(r.ids)||r.ids.length!==n||r.ids.some(e=>typeof e!=`string`||e.length===0)||new Set(r.ids).size!==r.ids.length)throw Error(`Bulk enqueue returned invalid job IDs.`);let i=r.ids;for(let t of i)e.ownJobIds.add(t);t.pushed+=i.length,t.bytes+=i.length*e.payload;let a=Math.max(0,n-i.length);a>0&&(t.pushFailed+=a,t.error??=`Server created ${i.length} of ${n} jobs in a benchmark batch.`)}).catch(r=>{e.isCurrent()&&(t.pushFailed+=n,t.error??=I(r))});e.pendingPushes.add(o);try{await o}finally{e.pendingPushes.delete(o)}e.isCurrent()&&t.pushLat.push(performance.now()-a)}}}function he(e){return async()=>{let{stats:t}=e;for(;e.shouldContinue()&&!(e.runConfig.mode===`duration`&&performance.now()>=e.deadline||e.runConfig.mode===`count`&&t.completed>=e.total);){t.activeWorkers++;let n=[];try{if(n=await ge(e),!e.shouldContinue()){await X(e,n);break}n.length>0&&await _e(e,n)}finally{e.isCurrent()&&t.activeWorkers--}if(!e.shouldContinue())break;if(n.length===0){if(e.runConfig.mode===`count`&&e.producersDone()){e.ownJobIds.size>0&&(t.error??=`${e.ownJobIds.size} benchmark job(s) could not be drained.`);break}await F(50,e.shouldContinue)}}}}async function ge(e){try{let t=await e.client.pullBatch(e.queue,e.workerBatch,`dashboard-benchmark:${e.runId}`);if(t?.ok!==!0||!Array.isArray(t.jobs)||t.jobs.length>e.workerBatch||t.jobs.some(e=>!e||typeof e!=`object`||typeof e.id!=`string`||e.id.length===0)||!Array.isArray(t.tokens)||t.tokens.length!==t.jobs.length||t.tokens.some(e=>typeof e!=`string`||e.length===0)||new Set(t.jobs.map(e=>e.id)).size!==t.jobs.length)throw Error(`Malformed pull-batch response.`);let n=t.jobs.map((e,n)=>({id:e.id,token:t.tokens?.[n]}));return e.shouldContinue()?n:(await X(e,n),[])}catch(t){return e.isCurrent()&&(e.stats.error??=I(t)),[]}}async function _e(e,t){if(t.some(t=>!e.ownJobIds.has(t.id))&&e.pendingPushes.size>0&&await Promise.allSettled([...e.pendingPushes]),!e.shouldContinue())return X(e,t);let n=t.filter(t=>e.ownJobIds.has(t.id)),r=t.filter(t=>!e.ownJobIds.has(t.id));if(r.length>0){await ve(e,t,r.length);return}if(n.length===0)return;if(!e.shouldContinue()||(e.processMs>0&&await F(e.processMs,e.shouldContinue),!e.shouldContinue()))return X(e,n);let i=!1;try{let t=n.map(e=>e.id),r=n.map(e=>e.token),a=await e.client.heartbeatBatch(t,r);q(a,`Heartbeat benchmark jobs`);let o=a.data;if(!o||typeof o!=`object`||o.ok!==!0||!Number.isSafeInteger(o.count)||o.count!==n.length)throw Error(`Heartbeat confirmed ${String(o?.count)} of ${n.length} benchmark jobs; refusing a lossy batch ACK.`);if(!e.shouldContinue())return X(e,n);if(i=!0,q(await e.client.ackBatch(t,r),`Acknowledge benchmark jobs`),!e.isCurrent())return;e.stats.completed+=n.length;for(let t of n)e.ownJobIds.delete(t.id)}catch(t){e.isCurrent()&&(e.stats.ackFailed+=n.length,e.stats.error??=I(t)),i||await X(e,n)}}async function ve(e,t,n){let r=await Z(e,t);e.isCurrent()&&(e.stats.error??=r>0?`Detected ${n} foreign job(s); ${r} pulled job(s) could not be returned to waiting. Benchmark stopped.`:`Detected ${n} foreign job(s); returned the complete pulled batch to waiting and stopped without counting it.`),e.stop()}async function X(e,t){if(t.length===0)return;let n=await Z(e,t);n>0&&e.isCurrent()&&(e.stats.error??=`${n} interrupted benchmark job(s) could not be returned to waiting.`)}async function Z(e,t){return(await Promise.allSettled(t.map(t=>e.compensationQueue.run(async()=>{q(await e.client.retryJob(t.id,t.token),`Restore job ${t.id}`),e.ownJobIds.delete(t.id)})))).filter(e=>e.status===`rejected`).length}var ye=0;async function be(e,t,n){if(e.runningRef.current||!e.mountedRef.current)return;e.runningRef.current=!0;let r=++e.runGenerationRef.current,i=n??a.createServerTargetClient(a.captureServerRequestTarget()),o=()=>e.mountedRef.current&&e.runGenerationRef.current===r,s=()=>o()&&!e.stopRef.current;e.stopRef.current=!1;try{e.setSummary(null);let n=fe(t);if(!n.ok){e.setLive({...J,error:n.error}),Q(e,`error`);return}let r=n.config,a=await pe(i,r.queue,s);if(!o())return;if(!a.ok){e.setLive(a.live),Q(e,`error`);return}if(!s())return;e.producersDoneRef.current=!1,e.configRef.current=r,e.setRunConfig(r);let c=Y();c.startedAt=performance.now(),c.lastAt=performance.now(),e.statsRef.current=c,e.setLive({...J}),Q(e,`running`);let l={batch:r.batch,blob:`x`.repeat(r.payload),client:i,compensationQueue:ce(),deadline:performance.now()+r.durationS*1e3,isCurrent:o,ownJobIds:new Set,payload:r.payload,pendingPushes:new Set,processMs:r.processMs,producersDone:()=>e.producersDoneRef.current,queue:r.queue,runConfig:r,runId:`bqbench-${globalThis.crypto.randomUUID()}`,shouldContinue:s,stats:c,stop:()=>{e.stopRef.current=!0},total:r.total,workerBatch:r.workerBatch},u=me(l),d=he(l),f=Array.from({length:r.producers},u),p=Array.from({length:r.workers},d),m=Promise.all(f).then(()=>{o()&&(e.producersDoneRef.current=!0,s()&&r.mode===`count`&&r.workers>0&&e.phaseRef.current===`running`&&Q(e,`draining`))});if(await Promise.all([m,...p]),!o())return;xe(e,r,c)}finally{e.runningRef.current=!1}}function xe(e,t,n){let r=performance.now()-n.startedAt,i=de(n,r);e.setSummary(i),e.setLive(e=>({...e,pushed:n.pushed,completed:n.completed,pushFailed:n.pushFailed,ackFailed:n.ackFailed,bytes:n.bytes,elapsedMs:r,pushPerSec:0,donePerSec:0,activeWorkers:0,etaMs:0,error:n.error})),e.setHistory(e=>[{...i,id:++ye,at:Date.now(),mode:t.mode,producers:t.producers,workers:t.workers},...e].slice(0,12)),Q(e,e.stopRef.current?`stopped`:n.error?`error`:`done`)}function Q(e,t){e.phaseRef.current=t,e.setPhase(t)}function Se(){let[e,t]=(0,C.useState)(`idle`),[n,r]=(0,C.useState)(J),[i,a]=(0,C.useState)(null),[o,s]=(0,C.useState)([]),[c,l]=(0,C.useState)(null),u=(0,C.useRef)(!1),d=(0,C.useRef)(!0),f=(0,C.useRef)(0),p=(0,C.useRef)(!1),m=(0,C.useRef)(!1),h=(0,C.useRef)(null),g=(0,C.useRef)(`idle`),_=(0,C.useRef)(Y());return g.current=e,(0,C.useEffect)(()=>{let e=d,t=u,n=f;return d.current=!0,()=>{e.current=!1,t.current=!0,n.current++}},[]),(0,C.useEffect)(()=>{if(e!==`running`&&e!==`draining`)return;let t=setInterval(()=>{r(ue(_.current,h.current,g.current,performance.now()))},200);return()=>clearInterval(t)},[e]),{phase:e,live:n,summary:i,history:o,runCfg:c,run:(e,n)=>be({configRef:h,mountedRef:d,phaseRef:g,producersDoneRef:m,runningRef:p,runGenerationRef:f,setHistory:s,setLive:r,setPhase:t,setRunConfig:l,setSummary:a,statsRef:_,stopRef:u},e,n),stop:()=>{u.current=!0,(g.current===`running`||g.current===`draining`)&&(g.current=`stopping`,t(`stopping`))},reset:()=>{u.current=!0,f.current++,g.current=`idle`,a(null),r(J),t(`idle`)},clearHistory:()=>s([])}}function Ce({benchmark:e,counts:t,dedicatedQueue:n,draft:r,pollQueue:i,runTarget:a,setRunTarget:o}){let s=(0,C.useRef)(null),c=(0,C.useRef)(!0),[l,u]=(0,C.useState)(!1),[d,f]=(0,C.useState)(null);(0,C.useEffect)(()=>(c.current=!0,()=>{c.current=!1,s.current=null}),[]);let p=async()=>{let e=r.queue.trim();if(s.current||!e)return;if(!O(e)||e!==n){f({error:`refusing to clean a queue not owned by this dashboard session`});return}s.current=`clean`;let o=a??$(),l=t&&i===e?` Currently ${h(t.waiting??0)} waiting / ${h(t.completed??0)} completed.`:``;if(!window.confirm(`Clean every cleanable job from the dedicated dashboard benchmark queue "${e}" on ${o.target.baseUrl}? This is queue-wide; the cryptographic queue name is owned by this browser session.${l}`)){s.current=null;return}u(!0),f(null);try{let t=null;for(let n of z){if(!c.current)return;try{let t=await o.client.clean(e,{state:n,limit:w.total});if(q(t,`Clean ${n} jobs`),!Number.isSafeInteger(t.count)||t.count<0)throw Error(`Clean ${n} jobs returned an invalid count.`)}catch(e){t=e.message}}if(!c.current)return;let n=await o.client.counts(e).catch(e=>(t=e.message,null));if(!c.current)return;if(!n)f({error:t??`server unreachable — could not verify`});else if(n.ok!==!0||!n.counts||typeof n.counts!=`object`||Array.isArray(n.counts))f({error:`server returned a malformed queue-count response`});else try{let e=K(n.counts);f(t||e>0?{error:t??`${h(e)} job(s) still present`}:{remaining:0})}catch{f({error:`server returned malformed queue counts`})}}finally{c.current&&u(!1),s.current===`clean`&&(s.current=null)}},m=()=>{if(s.current)return;s.current=`run`;let n=V(r),c=n.queue.trim(),l=$();if(j(c)){e.run({...n,queue:c},l.client).finally(g);return}let u=0;if(!a&&t&&i===c)try{u=K(t)}catch{}let d=u>0?` Warning: it already holds ${h(u)} job(s) across all states; the safety preflight will refuse the run until the dedicated queue is empty.`:``;if(!window.confirm(`Enqueue real jobs into "${c}" on ${l.target.baseUrl}?${d}`)){s.current=null;return}o(l),e.run(n,l.client).finally(g)},g=()=>{s.current===`run`&&(s.current=null)};return{cleaning:l,cleanResult:d,cleanup:p,start:m}}function $(){let e=a.captureServerRequestTarget();return{target:e,client:a.createServerTargetClient(e)}}function we(){let[e]=(0,C.useState)(ee),[t,n]=(0,C.useState)(()=>ne({...T,queue:e})),r=Se(),u=i(e=>e.baseUrl),[d,p]=(0,C.useState)(null),m=(e,t)=>n(n=>({...n,[e]:t})),h=e=>t=>m(e,t.target.value),g=e=>n(t=>{let n={...t};for(let[t,r]of Object.entries(e))n[t]=te.includes(t)?String(r):r;return n}),[_,v]=(0,C.useState)(e);(0,C.useEffect)(()=>{let e=setTimeout(()=>v(t.queue.trim()||T.queue),400);return()=>clearTimeout(e)},[t.queue]);let y=d?.target.baseUrl??u,{data:b,error:x}=c(()=>d?d.client.counts(_):a.counts(_),[_,d],{intervalMs:1e3}),S=b?.counts??null,E=Ce({benchmark:r,counts:S,dedicatedQueue:e,draft:t,pollQueue:_,runTarget:d,setRunTarget:p}),{phase:D,live:O,summary:j,history:M}=r,N=D===`running`||D===`draining`||D===`stopping`,P=r.runCfg??V(t),F=A(P.total,1,w.total),I=A(P.workers,0,w.workers),L=P.mode===`count`&&(N||j)?Math.min(100,O.pushed/F*100):0,R=P.mode===`count`&&(N||j)?Math.min(100,O.completed/F*100):0,z=P.mode===`duration`?Math.min(100,O.elapsedMs/(A(P.durationS,1,w.durationS)*1e3)*100):0,B=Te(D,P.mode,!!j),U=Ee(D,O.etaMs);return(0,H.jsxs)(`div`,{children:[(0,H.jsx)(f,{title:`Benchmark`,description:`Drive real load against the server — producers bulk-enqueue jobs while simulated workers pull, process and ack them. Throughput is measured client-side; the queue genuinely fills and drains.`,actions:D===`stopping`?(0,H.jsxs)(l,{variant:`warning`,size:`sm`,disabled:!0,children:[(0,H.jsx)(o,{className:`size-3.5`}),` Stopping…`]}):N?(0,H.jsxs)(l,{variant:`warning`,size:`sm`,onClick:r.stop,children:[(0,H.jsx)(o,{className:`size-3.5`}),` Stop`]}):(0,H.jsxs)(l,{variant:`success`,size:`sm`,disabled:E.cleaning,onClick:E.start,children:[(0,H.jsx)(s,{className:`size-3.5`}),` Run benchmark`]})}),O.error&&(0,H.jsx)(`div`,{className:`mb-4 rounded-lg border border-red-500/20 bg-red-500/5 px-4 py-2 text-sm text-danger`,children:O.error}),(0,H.jsxs)(`div`,{className:`mb-4 flex flex-wrap items-center gap-2`,children:[(0,H.jsx)(`span`,{className:`text-xs font-medium uppercase tracking-wider text-faint`,children:`Presets`}),Object.keys(k).map(e=>(0,H.jsx)(`button`,{type:`button`,disabled:N,onClick:()=>g(k[e]),className:`rounded-md border border-line bg-surface-2 px-2.5 py-1 text-xs text-muted transition-colors hover:border-line-strong hover:text-fg disabled:opacity-40`,children:e},e))]}),(0,H.jsxs)(`div`,{className:`grid grid-cols-1 gap-6 lg:grid-cols-3`,children:[(0,H.jsx)(re,{draft:t,active:N,cleaning:E.cleaning,cleanResult:E.cleanResult,setField:m,numberInput:h,onClean:()=>void E.cleanup()}),(0,H.jsx)(ie,{heading:B,pollBaseUrl:y,etaText:U,shown:P,shownWorkers:I,producePct:L,drainPct:R,durationPct:z,live:O,summary:j})]}),(0,H.jsx)(ae,{summary:j}),(0,H.jsx)(oe,{pollQueue:_,pollBaseUrl:y,pinned:!!d,counts:S,error:x}),(0,H.jsx)(se,{history:M,onClear:r.clearHistory})]})}function Te(e,t,n){return e===`running`?t===`duration`?`Running…`:`Producing…`:e===`draining`?`Draining…`:e===`stopping`?`Stopping…`:e===`error`?`Error`:n?`Result`:`Ready`}function Ee(e,t){return(e===`running`||e===`draining`)&&t!=null?t>0?`ETA ${m(t)}`:`finishing…`:e===`stopped`?`stopped early`:e===`done`?`complete`:``}export{we as Benchmark};
|