bunqueue-dashboard 1.0.1 → 1.0.2
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 +5 -3
- package/agent/backup/routes.ts +20 -12
- package/agent/server/router.ts +5 -1
- package/dist/.vite/manifest.json +31 -31
- package/dist/assets/{Alerts-DzKKoA9U.js → Alerts-DCKl_C8D.js} +1 -1
- package/dist/assets/{Benchmark-CHYDL31K.js → Benchmark-DyHvEM8x.js} +1 -1
- package/dist/assets/{Database-BoxVpjts.js → Database-BBzyi7f0.js} +1 -1
- package/dist/assets/{Diagnostics-B3QSQuFF.js → Diagnostics-E_icHggb.js} +1 -1
- package/dist/assets/{Dlq-DHOfgORa.js → Dlq-DNs32CzV.js} +1 -1
- package/dist/assets/{DlqControl-BY1e55-x.js → DlqControl-DawwIsTI.js} +1 -1
- package/dist/assets/{DlqPro-CfwKDcpc.js → DlqPro-BEdxFiNN.js} +1 -1
- package/dist/assets/{Flows-CDCQrPit.js → Flows-DKZxI7ta.js} +1 -1
- package/dist/assets/{JobInspector-Bw3Gs1Iy.js → JobInspector-BARE52q3.js} +1 -1
- package/dist/assets/{Jobs-IrlneOy6.js → Jobs-CKzKpQ7i.js} +1 -1
- package/dist/assets/{JobsPro-vlPd6Ows.js → JobsPro-DxDw-akG.js} +1 -1
- package/dist/assets/{Logs-DG7cK-tF.js → Logs-Bj60G_FP.js} +1 -1
- package/dist/assets/{LogsPro-ClSqFKys.js → LogsPro-Hv8xwXDK.js} +1 -1
- package/dist/assets/{McpServer-DoNFY0Pj.js → McpServer-zxSMjG8K.js} +1 -1
- package/dist/assets/{Metrics-BkePZiCZ.js → Metrics-BBLdWOw1.js} +1 -1
- package/dist/assets/{MetricsPro-ooSvpQ3H.js → MetricsPro-D470b1aP.js} +1 -1
- package/dist/assets/{Overview-CuWmPRra.js → Overview-B_AOa5kW.js} +1 -1
- package/dist/assets/{QueueControl-C01l6bBJ.js → QueueControl-DaZjeZnw.js} +1 -1
- package/dist/assets/{QueueDetail-gSf9Tb7G.js → QueueDetail-Dgo9Ukmp.js} +1 -1
- package/dist/assets/{QueueDetailPro-BKszqI0M.js → QueueDetailPro-D7U8ahp2.js} +1 -1
- package/dist/assets/{Queues-BAg2nSz0.js → Queues-CS8LeZtJ.js} +1 -1
- package/dist/assets/{QueuesOverview-BLSjiYsA.js → QueuesOverview-BySSnCm0.js} +1 -1
- package/dist/assets/{S3BackupPro-DcjlrzC5.js → S3BackupPro-UwWu0yxr.js} +1 -1
- package/dist/assets/{ServerControl-SiZBNo9P.js → ServerControl-Ch1wZcKB.js} +1 -1
- package/dist/assets/{Settings-DR_iqW0T.js → Settings-FOzwrB25.js} +1 -1
- package/dist/assets/{Usage-ZWfB7PcR.js → Usage-DR90dCt0.js} +1 -1
- package/dist/assets/{UsagePro-BjdeS2zb.js → UsagePro-DAlE3oCE.js} +1 -1
- package/dist/assets/{Workers-C1h6hCPp.js → Workers-D1Ead02h.js} +1 -1
- package/dist/assets/{WorkersPro-B0f_Nfdz.js → WorkersPro-XuLxtU4A.js} +1 -1
- package/dist/assets/{index-Bat1DN5U.js → index-DZFZr0Mi.js} +2 -2
- package/dist/assets/{index-C9yGKhQ8.css → index-DaEyLvTR.css} +1 -1
- package/dist/index.html +2 -2
- package/package.json +21 -11
- package/scripts/bunVersion.ts +1 -1
- package/scripts/embedded.gen.ts +154 -154
package/README.md
CHANGED
|
@@ -36,9 +36,11 @@ The live demo uses sample data. The docs explain how to connect and operate a re
|
|
|
36
36
|
|
|
37
37
|
<br />
|
|
38
38
|
|
|
39
|
-
<
|
|
39
|
+
<a href="https://www.youtube.com/watch?v=1bQRFXGClcc" title="Watch the full bunqueue dashboard tour on YouTube"><img src="docs/public/video-tour.png" alt="Watch the narrated 4K tour of bunqueue dashboard on YouTube" width="900" /></a>
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
### [▶ Watch the full tour on YouTube](https://www.youtube.com/watch?v=1bQRFXGClcc)
|
|
42
|
+
|
|
43
|
+
<sub>A narrated 4K walkthrough (7:45, English with subtitles) recorded against a real bunqueue server under live traffic. <a href="https://egeominotti.github.io/bunqueue-dashboard/">Or try the live demo →</a></sub>
|
|
42
44
|
|
|
43
45
|
<br /><br />
|
|
44
46
|
|
|
@@ -134,7 +136,7 @@ contracts; it never patches Bunqueue internals.
|
|
|
134
136
|
|
|
135
137
|
## Quick start
|
|
136
138
|
|
|
137
|
-
**Prerequisites:** [Bun](https://bun.sh) 1.4.
|
|
139
|
+
**Prerequisites:** [Bun](https://bun.sh) 1.4.4 and a reachable bunqueue server (or let the control
|
|
138
140
|
agent start one for you from the **Server** page).
|
|
139
141
|
|
|
140
142
|
### Run from npm (no clone)
|
package/agent/backup/routes.ts
CHANGED
|
@@ -190,18 +190,26 @@ function assertDatabaseSnapshot(value: unknown, current: DbStats): void {
|
|
|
190
190
|
throw new Error('A database snapshot is required to authorize restore');
|
|
191
191
|
}
|
|
192
192
|
const expected = value as Record<string, unknown>;
|
|
193
|
-
const allowed = ['path', 'exists', 'size', 'walSize', 'shmSize', 'totalSize', 'mtimeMs'];
|
|
194
|
-
const unknown = Object.keys(expected).find((key) => !allowed.includes(key));
|
|
193
|
+
const allowed = ['path', 'exists', 'size', 'walSize', 'shmSize', 'totalSize', 'mtimeMs'] as const;
|
|
194
|
+
const unknown = Object.keys(expected).find((key) => !(allowed as readonly string[]).includes(key));
|
|
195
195
|
if (unknown) throw new Error(`Unknown database snapshot option: ${unknown}`);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
196
|
+
// Compare what reflects the data. The -shm file is SQLite's shared-memory WAL
|
|
197
|
+
// index: it appears and disappears as connections open and close (the last one
|
|
198
|
+
// deletes it) without any write, so it, and the total that includes it, would
|
|
199
|
+
// reject valid confirmations. Writes still move the main file or the WAL.
|
|
200
|
+
const compared = ['path', 'exists', 'size', 'walSize', 'mtimeMs'] as const;
|
|
201
|
+
// Name every field that moved so a rejected restore is diagnosable.
|
|
202
|
+
const changed = compared
|
|
203
|
+
.filter((field) => expected[field] !== current[field])
|
|
204
|
+
.map((field) => `${field} ${shown(expected[field])} → ${shown(current[field])}`);
|
|
205
|
+
if (changed.length > 0) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Database changed after restore confirmation (${changed.join(', ')}); reload status and confirm again`
|
|
208
|
+
);
|
|
206
209
|
}
|
|
207
210
|
}
|
|
211
|
+
|
|
212
|
+
function shown(value: unknown): string {
|
|
213
|
+
const text = String(value);
|
|
214
|
+
return text.length > 120 ? `${text.slice(0, 117)}...` : text;
|
|
215
|
+
}
|
package/agent/server/router.ts
CHANGED
|
@@ -123,12 +123,16 @@ export async function routeAgentRequest(
|
|
|
123
123
|
return sqliteOnly('S3 backup', currentStorageMode);
|
|
124
124
|
}
|
|
125
125
|
return manager.withStoppedMaintenance('restoring a backup', async () => {
|
|
126
|
+
// Measure what the operator confirmed before our own cleanup: releasing
|
|
127
|
+
// the Workflow Engine's connection checkpoints the WAL, which changes the
|
|
128
|
+
// files without any other writer and would void a valid confirmation.
|
|
129
|
+
const confirmedDatabase = await manager.dbStats(restoreConfig.dataPath);
|
|
126
130
|
await closeWorkflowRuntime(runtime);
|
|
127
131
|
return executePreparedBackupRequest(
|
|
128
132
|
prepared,
|
|
129
133
|
restoreConfig,
|
|
130
134
|
current.status === 'running',
|
|
131
|
-
|
|
135
|
+
confirmedDatabase,
|
|
132
136
|
backupRunner,
|
|
133
137
|
undefined,
|
|
134
138
|
managedTargetPolicy
|
package/dist/.vite/manifest.json
CHANGED
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
]
|
|
233
233
|
},
|
|
234
234
|
"index.html": {
|
|
235
|
-
"file": "assets/index-
|
|
235
|
+
"file": "assets/index-DZFZr0Mi.js",
|
|
236
236
|
"name": "index",
|
|
237
237
|
"src": "index.html",
|
|
238
238
|
"isEntry": true,
|
|
@@ -293,7 +293,7 @@
|
|
|
293
293
|
"src/lib/demo/install.ts"
|
|
294
294
|
],
|
|
295
295
|
"css": [
|
|
296
|
-
"assets/index-
|
|
296
|
+
"assets/index-DaEyLvTR.css"
|
|
297
297
|
],
|
|
298
298
|
"assets": [
|
|
299
299
|
"assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2",
|
|
@@ -325,7 +325,7 @@
|
|
|
325
325
|
"isDynamicEntry": true
|
|
326
326
|
},
|
|
327
327
|
"src/pages/Alerts.tsx": {
|
|
328
|
-
"file": "assets/Alerts-
|
|
328
|
+
"file": "assets/Alerts-DCKl_C8D.js",
|
|
329
329
|
"name": "Alerts",
|
|
330
330
|
"src": "src/pages/Alerts.tsx",
|
|
331
331
|
"isDynamicEntry": true,
|
|
@@ -363,7 +363,7 @@
|
|
|
363
363
|
]
|
|
364
364
|
},
|
|
365
365
|
"src/pages/Dlq.tsx": {
|
|
366
|
-
"file": "assets/Dlq-
|
|
366
|
+
"file": "assets/Dlq-DNs32CzV.js",
|
|
367
367
|
"name": "Dlq",
|
|
368
368
|
"src": "src/pages/Dlq.tsx",
|
|
369
369
|
"isDynamicEntry": true,
|
|
@@ -402,7 +402,7 @@
|
|
|
402
402
|
]
|
|
403
403
|
},
|
|
404
404
|
"src/pages/Jobs.tsx": {
|
|
405
|
-
"file": "assets/Jobs-
|
|
405
|
+
"file": "assets/Jobs-CKzKpQ7i.js",
|
|
406
406
|
"name": "Jobs",
|
|
407
407
|
"src": "src/pages/Jobs.tsx",
|
|
408
408
|
"isDynamicEntry": true,
|
|
@@ -425,7 +425,7 @@
|
|
|
425
425
|
]
|
|
426
426
|
},
|
|
427
427
|
"src/pages/Logs.tsx": {
|
|
428
|
-
"file": "assets/Logs-
|
|
428
|
+
"file": "assets/Logs-Bj60G_FP.js",
|
|
429
429
|
"name": "Logs",
|
|
430
430
|
"src": "src/pages/Logs.tsx",
|
|
431
431
|
"isDynamicEntry": true,
|
|
@@ -446,7 +446,7 @@
|
|
|
446
446
|
]
|
|
447
447
|
},
|
|
448
448
|
"src/pages/Metrics.tsx": {
|
|
449
|
-
"file": "assets/Metrics-
|
|
449
|
+
"file": "assets/Metrics-BBLdWOw1.js",
|
|
450
450
|
"name": "Metrics",
|
|
451
451
|
"src": "src/pages/Metrics.tsx",
|
|
452
452
|
"isDynamicEntry": true,
|
|
@@ -462,7 +462,7 @@
|
|
|
462
462
|
]
|
|
463
463
|
},
|
|
464
464
|
"src/pages/Overview.tsx": {
|
|
465
|
-
"file": "assets/Overview-
|
|
465
|
+
"file": "assets/Overview-B_AOa5kW.js",
|
|
466
466
|
"name": "Overview",
|
|
467
467
|
"src": "src/pages/Overview.tsx",
|
|
468
468
|
"isDynamicEntry": true,
|
|
@@ -479,7 +479,7 @@
|
|
|
479
479
|
]
|
|
480
480
|
},
|
|
481
481
|
"src/pages/QueueDetail.tsx": {
|
|
482
|
-
"file": "assets/QueueDetail-
|
|
482
|
+
"file": "assets/QueueDetail-Dgo9Ukmp.js",
|
|
483
483
|
"name": "QueueDetail",
|
|
484
484
|
"src": "src/pages/QueueDetail.tsx",
|
|
485
485
|
"isDynamicEntry": true,
|
|
@@ -503,7 +503,7 @@
|
|
|
503
503
|
]
|
|
504
504
|
},
|
|
505
505
|
"src/pages/Queues.tsx": {
|
|
506
|
-
"file": "assets/Queues-
|
|
506
|
+
"file": "assets/Queues-CS8LeZtJ.js",
|
|
507
507
|
"name": "Queues",
|
|
508
508
|
"src": "src/pages/Queues.tsx",
|
|
509
509
|
"isDynamicEntry": true,
|
|
@@ -536,7 +536,7 @@
|
|
|
536
536
|
]
|
|
537
537
|
},
|
|
538
538
|
"src/pages/Settings.tsx": {
|
|
539
|
-
"file": "assets/Settings-
|
|
539
|
+
"file": "assets/Settings-FOzwrB25.js",
|
|
540
540
|
"name": "Settings",
|
|
541
541
|
"src": "src/pages/Settings.tsx",
|
|
542
542
|
"isDynamicEntry": true,
|
|
@@ -553,7 +553,7 @@
|
|
|
553
553
|
]
|
|
554
554
|
},
|
|
555
555
|
"src/pages/Usage.tsx": {
|
|
556
|
-
"file": "assets/Usage-
|
|
556
|
+
"file": "assets/Usage-DR90dCt0.js",
|
|
557
557
|
"name": "Usage",
|
|
558
558
|
"src": "src/pages/Usage.tsx",
|
|
559
559
|
"isDynamicEntry": true,
|
|
@@ -569,7 +569,7 @@
|
|
|
569
569
|
]
|
|
570
570
|
},
|
|
571
571
|
"src/pages/Workers.tsx": {
|
|
572
|
-
"file": "assets/Workers-
|
|
572
|
+
"file": "assets/Workers-D1Ead02h.js",
|
|
573
573
|
"name": "Workers",
|
|
574
574
|
"src": "src/pages/Workers.tsx",
|
|
575
575
|
"isDynamicEntry": true,
|
|
@@ -587,7 +587,7 @@
|
|
|
587
587
|
]
|
|
588
588
|
},
|
|
589
589
|
"src/pages/control/Benchmark.tsx": {
|
|
590
|
-
"file": "assets/Benchmark-
|
|
590
|
+
"file": "assets/Benchmark-DyHvEM8x.js",
|
|
591
591
|
"name": "Benchmark",
|
|
592
592
|
"src": "src/pages/control/Benchmark.tsx",
|
|
593
593
|
"isDynamicEntry": true,
|
|
@@ -655,7 +655,7 @@
|
|
|
655
655
|
]
|
|
656
656
|
},
|
|
657
657
|
"src/pages/control/Database.tsx": {
|
|
658
|
-
"file": "assets/Database-
|
|
658
|
+
"file": "assets/Database-BBzyi7f0.js",
|
|
659
659
|
"name": "Database",
|
|
660
660
|
"src": "src/pages/control/Database.tsx",
|
|
661
661
|
"isDynamicEntry": true,
|
|
@@ -679,7 +679,7 @@
|
|
|
679
679
|
]
|
|
680
680
|
},
|
|
681
681
|
"src/pages/control/Diagnostics.tsx": {
|
|
682
|
-
"file": "assets/Diagnostics-
|
|
682
|
+
"file": "assets/Diagnostics-E_icHggb.js",
|
|
683
683
|
"name": "Diagnostics",
|
|
684
684
|
"src": "src/pages/control/Diagnostics.tsx",
|
|
685
685
|
"isDynamicEntry": true,
|
|
@@ -701,7 +701,7 @@
|
|
|
701
701
|
]
|
|
702
702
|
},
|
|
703
703
|
"src/pages/control/DlqControl.tsx": {
|
|
704
|
-
"file": "assets/DlqControl-
|
|
704
|
+
"file": "assets/DlqControl-DawwIsTI.js",
|
|
705
705
|
"name": "DlqControl",
|
|
706
706
|
"src": "src/pages/control/DlqControl.tsx",
|
|
707
707
|
"isDynamicEntry": true,
|
|
@@ -726,7 +726,7 @@
|
|
|
726
726
|
]
|
|
727
727
|
},
|
|
728
728
|
"src/pages/control/DlqPro.tsx": {
|
|
729
|
-
"file": "assets/DlqPro-
|
|
729
|
+
"file": "assets/DlqPro-BEdxFiNN.js",
|
|
730
730
|
"name": "DlqPro",
|
|
731
731
|
"src": "src/pages/control/DlqPro.tsx",
|
|
732
732
|
"isDynamicEntry": true,
|
|
@@ -751,7 +751,7 @@
|
|
|
751
751
|
]
|
|
752
752
|
},
|
|
753
753
|
"src/pages/control/Flows.tsx": {
|
|
754
|
-
"file": "assets/Flows-
|
|
754
|
+
"file": "assets/Flows-DKZxI7ta.js",
|
|
755
755
|
"name": "Flows",
|
|
756
756
|
"src": "src/pages/control/Flows.tsx",
|
|
757
757
|
"isDynamicEntry": true,
|
|
@@ -772,7 +772,7 @@
|
|
|
772
772
|
]
|
|
773
773
|
},
|
|
774
774
|
"src/pages/control/JobInspector.tsx": {
|
|
775
|
-
"file": "assets/JobInspector-
|
|
775
|
+
"file": "assets/JobInspector-BARE52q3.js",
|
|
776
776
|
"name": "JobInspector",
|
|
777
777
|
"src": "src/pages/control/JobInspector.tsx",
|
|
778
778
|
"isDynamicEntry": true,
|
|
@@ -797,7 +797,7 @@
|
|
|
797
797
|
]
|
|
798
798
|
},
|
|
799
799
|
"src/pages/control/JobsPro.tsx": {
|
|
800
|
-
"file": "assets/JobsPro-
|
|
800
|
+
"file": "assets/JobsPro-DxDw-akG.js",
|
|
801
801
|
"name": "JobsPro",
|
|
802
802
|
"src": "src/pages/control/JobsPro.tsx",
|
|
803
803
|
"isDynamicEntry": true,
|
|
@@ -822,7 +822,7 @@
|
|
|
822
822
|
]
|
|
823
823
|
},
|
|
824
824
|
"src/pages/control/LogsPro.tsx": {
|
|
825
|
-
"file": "assets/LogsPro-
|
|
825
|
+
"file": "assets/LogsPro-Hv8xwXDK.js",
|
|
826
826
|
"name": "LogsPro",
|
|
827
827
|
"src": "src/pages/control/LogsPro.tsx",
|
|
828
828
|
"isDynamicEntry": true,
|
|
@@ -844,7 +844,7 @@
|
|
|
844
844
|
]
|
|
845
845
|
},
|
|
846
846
|
"src/pages/control/McpServer.tsx": {
|
|
847
|
-
"file": "assets/McpServer-
|
|
847
|
+
"file": "assets/McpServer-zxSMjG8K.js",
|
|
848
848
|
"name": "McpServer",
|
|
849
849
|
"src": "src/pages/control/McpServer.tsx",
|
|
850
850
|
"isDynamicEntry": true,
|
|
@@ -856,7 +856,7 @@
|
|
|
856
856
|
]
|
|
857
857
|
},
|
|
858
858
|
"src/pages/control/MetricsPro.tsx": {
|
|
859
|
-
"file": "assets/MetricsPro-
|
|
859
|
+
"file": "assets/MetricsPro-D470b1aP.js",
|
|
860
860
|
"name": "MetricsPro",
|
|
861
861
|
"src": "src/pages/control/MetricsPro.tsx",
|
|
862
862
|
"isDynamicEntry": true,
|
|
@@ -877,7 +877,7 @@
|
|
|
877
877
|
]
|
|
878
878
|
},
|
|
879
879
|
"src/pages/control/QueueControl.tsx": {
|
|
880
|
-
"file": "assets/QueueControl-
|
|
880
|
+
"file": "assets/QueueControl-DaZjeZnw.js",
|
|
881
881
|
"name": "QueueControl",
|
|
882
882
|
"src": "src/pages/control/QueueControl.tsx",
|
|
883
883
|
"isDynamicEntry": true,
|
|
@@ -901,7 +901,7 @@
|
|
|
901
901
|
]
|
|
902
902
|
},
|
|
903
903
|
"src/pages/control/QueueDetailPro.tsx": {
|
|
904
|
-
"file": "assets/QueueDetailPro-
|
|
904
|
+
"file": "assets/QueueDetailPro-D7U8ahp2.js",
|
|
905
905
|
"name": "QueueDetailPro",
|
|
906
906
|
"src": "src/pages/control/QueueDetailPro.tsx",
|
|
907
907
|
"isDynamicEntry": true,
|
|
@@ -928,7 +928,7 @@
|
|
|
928
928
|
]
|
|
929
929
|
},
|
|
930
930
|
"src/pages/control/QueuesOverview.tsx": {
|
|
931
|
-
"file": "assets/QueuesOverview-
|
|
931
|
+
"file": "assets/QueuesOverview-BySSnCm0.js",
|
|
932
932
|
"name": "QueuesOverview",
|
|
933
933
|
"src": "src/pages/control/QueuesOverview.tsx",
|
|
934
934
|
"isDynamicEntry": true,
|
|
@@ -951,7 +951,7 @@
|
|
|
951
951
|
]
|
|
952
952
|
},
|
|
953
953
|
"src/pages/control/S3BackupPro.tsx": {
|
|
954
|
-
"file": "assets/S3BackupPro-
|
|
954
|
+
"file": "assets/S3BackupPro-UwWu0yxr.js",
|
|
955
955
|
"name": "S3BackupPro",
|
|
956
956
|
"src": "src/pages/control/S3BackupPro.tsx",
|
|
957
957
|
"isDynamicEntry": true,
|
|
@@ -970,7 +970,7 @@
|
|
|
970
970
|
]
|
|
971
971
|
},
|
|
972
972
|
"src/pages/control/ServerControl.tsx": {
|
|
973
|
-
"file": "assets/ServerControl-
|
|
973
|
+
"file": "assets/ServerControl-Ch1wZcKB.js",
|
|
974
974
|
"name": "ServerControl",
|
|
975
975
|
"src": "src/pages/control/ServerControl.tsx",
|
|
976
976
|
"isDynamicEntry": true,
|
|
@@ -992,7 +992,7 @@
|
|
|
992
992
|
]
|
|
993
993
|
},
|
|
994
994
|
"src/pages/control/UsagePro.tsx": {
|
|
995
|
-
"file": "assets/UsagePro-
|
|
995
|
+
"file": "assets/UsagePro-DAlE3oCE.js",
|
|
996
996
|
"name": "UsagePro",
|
|
997
997
|
"src": "src/pages/control/UsagePro.tsx",
|
|
998
998
|
"isDynamicEntry": true,
|
|
@@ -1030,7 +1030,7 @@
|
|
|
1030
1030
|
]
|
|
1031
1031
|
},
|
|
1032
1032
|
"src/pages/control/WorkersPro.tsx": {
|
|
1033
|
-
"file": "assets/WorkersPro-
|
|
1033
|
+
"file": "assets/WorkersPro-XuLxtU4A.js",
|
|
1034
1034
|
"name": "WorkersPro",
|
|
1035
1035
|
"src": "src/pages/control/WorkersPro.tsx",
|
|
1036
1036
|
"isDynamicEntry": true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{g as t,t as n}from"./react-vendor-CFL9l8LK.js";import{o as r}from"./connectionStore-CVjQyIZv.js";import{O as i,r as a}from"./icons-DH9SS1DA.js";import{n as o,t as s}from"./Button-_aSJvRoW.js";import{t as c}from"./Card-egIDLwVk.js";import{i as l,t as u}from"./feedback-CtGN_kqB.js";import{t as d}from"./PageHeader-CQzNsw8r.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-Bat1DN5U.js";import{a as v,n as y,o as b,t as x}from"./form-DG3X6cLL.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{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{g as t,t as n}from"./react-vendor-CFL9l8LK.js";import{o as r}from"./connectionStore-CVjQyIZv.js";import{O as i,r as a}from"./icons-DH9SS1DA.js";import{n as o,t as s}from"./Button-_aSJvRoW.js";import{t as c}from"./Card-egIDLwVk.js";import{i as l,t as u}from"./feedback-CtGN_kqB.js";import{t as d}from"./PageHeader-CQzNsw8r.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-DZFZr0Mi.js";import{a as v,n as y,o as b,t as x}from"./form-DG3X6cLL.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{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{g as t,t as n}from"./react-vendor-CFL9l8LK.js";import{o as r}from"./connectionStore-CVjQyIZv.js";import{t as i}from"./bq-DR2BTISO.js";import{b as a,x as o}from"./icons-DH9SS1DA.js";import{r as s}from"./StatusBadge-DereLzOj.js";import{n as c}from"./usePolledData-CEyyLRYa.js";import{t as l}from"./Button-_aSJvRoW.js";import{n as u,t as d}from"./Card-egIDLwVk.js";import{t as f}from"./PageHeader-CQzNsw8r.js";import{n as p,o as m,s as h}from"./format-Cg7VCIZW.js";import{a as g,r as _}from"./index-Bat1DN5U.js";import{i as v,n as y,o as b,t as x}from"./form-DG3X6cLL.js";import{t as S}from"./AreaChart-Db2UVnaK.js";var C=e(t(),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=n();function re({draft:e,active:t,cleaning:n,cleanResult:r,setField:i,numberInput:a,onClean:o}){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=>i(`mode`,e),disabled:t})}),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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`processMs`)})})]}),(0,H.jsx)(U,{label:`Durable (fsync each job)`,checked:e.durable,active:t,onChange:e=>i(`durable`,e)}),(0,H.jsx)(U,{label:`Remove on complete`,checked:e.removeOnComplete,active:t,onChange:e=>i(`removeOnComplete`,e)}),(0,H.jsxs)(`div`,{className:`flex items-center gap-3 pt-1`,children:[(0,H.jsx)(l,{variant:`ghost`,size:`sm`,disabled:t||n,onClick:o,children:n?`Cleaning…`:`Clean queue`}),r&&(`error`in r?(0,H.jsxs)(`span`,{className:`text-xs text-danger`,children:[`Clean unverified — `,r.error]}):(0,H.jsx)(`span`,{className:s(`text-xs`,r.remaining>0?`text-warning`:`text-success`),children:r.remaining>0?`Cleaned — ${h(r.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:t,tone:n}){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:[t.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(t),children:(0,H.jsx)(`div`,{className:s(`h-full rounded-full transition-[width] motion-reduce:transition-none`,n===`accent`?`bg-accent`:`bg-emerald-500`),style:{width:`${t}%`}})})]})}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}}var X=new WeakMap;function me(e){let t=X.get(e);return t||(t={next:0,inFlight:new Set},X.set(e,t)),t}function he(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 ge(e){return async()=>{let{stats:t}=e,n=me(e);for(;e.shouldContinue()&&!(e.runConfig.mode===`duration`&&performance.now()>=e.deadline||e.runConfig.mode===`count`&&t.completed>=e.total);){t.activeWorkers++;let r=++n.next;n.inFlight.add(r);let i=[];try{if(i=await _e(e),!e.shouldContinue()){await Z(e,i);break}i.length>0&&await ve(e,i)}finally{n.inFlight.delete(r),e.isCurrent()&&t.activeWorkers--}if(!e.shouldContinue())break;if(i.length===0){if(e.runConfig.mode===`count`&&e.producersDone()){let r=n.next;for(;e.ownJobIds.size>0&&e.shouldContinue()&&[...n.inFlight].some(e=>e<=r);)await F(50,e.shouldContinue);if(!e.shouldContinue())break;e.ownJobIds.size>0&&(t.error??=`${e.ownJobIds.size} benchmark job(s) could not be drained.`);break}await F(50,e.shouldContinue)}}}}async function _e(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 Z(e,n),[])}catch(t){return e.isCurrent()&&(e.stats.error??=I(t)),[]}}async function ve(e,t){if(t.some(t=>!e.ownJobIds.has(t.id))&&e.pendingPushes.size>0&&await Promise.allSettled([...e.pendingPushes]),!e.shouldContinue())return Z(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 ye(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 Z(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 Z(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 Z(e,n)}}async function ye(e,t,n){let r=await Q(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 Z(e,t){if(t.length===0)return;let n=await Q(e,t);n>0&&e.isCurrent()&&(e.stats.error??=`${n} interrupted benchmark job(s) could not be returned to waiting.`)}async function Q(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 be=0;async function xe(e,t,n){if(e.runningRef.current||!e.mountedRef.current)return;e.runningRef.current=!0;let r=++e.runGenerationRef.current,a=n??i.createServerTargetClient(i.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}),$(e,`error`);return}let r=n.config,i=await pe(a,r.queue,s);if(!o())return;if(!i.ok){e.setLive(i.live),$(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}),$(e,`running`);let l={batch:r.batch,blob:`x`.repeat(r.payload),client:a,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=he(l),d=ge(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`&&$(e,`draining`))});if(await Promise.all([m,...p]),!o())return;Se(e,r,c)}finally{e.runningRef.current=!1}}function Se(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:++be,at:Date.now(),mode:t.mode,producers:t.producers,workers:t.workers},...e].slice(0,12)),$(e,e.stopRef.current?`stopped`:n.error?`error`:`done`)}function $(e,t){e.phaseRef.current=t,e.setPhase(t)}function Ce(){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)=>xe({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 we({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??Te(),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=Te();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 Te(){let e=i.captureServerRequestTarget();return{target:e,client:i.createServerTargetClient(e)}}function Ee(){let[e]=(0,C.useState)(ee),[t,n]=(0,C.useState)(()=>ne({...T,queue:e})),s=Ce(),u=r(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(_):i.counts(_),[_,d],{intervalMs:1e3}),S=b?.counts??null,E=we({benchmark:s,counts:S,dedicatedQueue:e,draft:t,pollQueue:_,runTarget:d,setRunTarget:p}),{phase:D,live:O,summary:j,history:M}=s,N=D===`running`||D===`draining`||D===`stopping`,P=s.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=De(D,P.mode,!!j),U=Oe(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)(a,{className:`size-3.5`}),` Stopping…`]}):N?(0,H.jsxs)(l,{variant:`warning`,size:`sm`,onClick:s.stop,children:[(0,H.jsx)(a,{className:`size-3.5`}),` Stop`]}):(0,H.jsxs)(l,{variant:`success`,size:`sm`,disabled:E.cleaning,onClick:E.start,children:[(0,H.jsx)(o,{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:s.clearHistory})]})}function De(e,t,n){return e===`running`?t===`duration`?`Running…`:`Producing…`:e===`draining`?`Draining…`:e===`stopping`?`Stopping…`:e===`error`?`Error`:n?`Result`:`Ready`}function Oe(e,t){return(e===`running`||e===`draining`)&&t!=null?t>0?`ETA ${m(t)}`:`finishing…`:e===`stopped`?`stopped early`:e===`done`?`complete`:``}export{Ee as Benchmark};
|
|
1
|
+
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{g as t,t as n}from"./react-vendor-CFL9l8LK.js";import{o as r}from"./connectionStore-CVjQyIZv.js";import{t as i}from"./bq-DR2BTISO.js";import{b as a,x as o}from"./icons-DH9SS1DA.js";import{r as s}from"./StatusBadge-DereLzOj.js";import{n as c}from"./usePolledData-CEyyLRYa.js";import{t as l}from"./Button-_aSJvRoW.js";import{n as u,t as d}from"./Card-egIDLwVk.js";import{t as f}from"./PageHeader-CQzNsw8r.js";import{n as p,o as m,s as h}from"./format-Cg7VCIZW.js";import{a as g,r as _}from"./index-DZFZr0Mi.js";import{i as v,n as y,o as b,t as x}from"./form-DG3X6cLL.js";import{t as S}from"./AreaChart-Db2UVnaK.js";var C=e(t(),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=n();function re({draft:e,active:t,cleaning:n,cleanResult:r,setField:i,numberInput:a,onClean:o}){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=>i(`mode`,e),disabled:t})}),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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`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:t,onChange:a(`processMs`)})})]}),(0,H.jsx)(U,{label:`Durable (fsync each job)`,checked:e.durable,active:t,onChange:e=>i(`durable`,e)}),(0,H.jsx)(U,{label:`Remove on complete`,checked:e.removeOnComplete,active:t,onChange:e=>i(`removeOnComplete`,e)}),(0,H.jsxs)(`div`,{className:`flex items-center gap-3 pt-1`,children:[(0,H.jsx)(l,{variant:`ghost`,size:`sm`,disabled:t||n,onClick:o,children:n?`Cleaning…`:`Clean queue`}),r&&(`error`in r?(0,H.jsxs)(`span`,{className:`text-xs text-danger`,children:[`Clean unverified — `,r.error]}):(0,H.jsx)(`span`,{className:s(`text-xs`,r.remaining>0?`text-warning`:`text-success`),children:r.remaining>0?`Cleaned — ${h(r.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:t,tone:n}){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:[t.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(t),children:(0,H.jsx)(`div`,{className:s(`h-full rounded-full transition-[width] motion-reduce:transition-none`,n===`accent`?`bg-accent`:`bg-emerald-500`),style:{width:`${t}%`}})})]})}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}}var X=new WeakMap;function me(e){let t=X.get(e);return t||(t={next:0,inFlight:new Set},X.set(e,t)),t}function he(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 ge(e){return async()=>{let{stats:t}=e,n=me(e);for(;e.shouldContinue()&&!(e.runConfig.mode===`duration`&&performance.now()>=e.deadline||e.runConfig.mode===`count`&&t.completed>=e.total);){t.activeWorkers++;let r=++n.next;n.inFlight.add(r);let i=[];try{if(i=await _e(e),!e.shouldContinue()){await Z(e,i);break}i.length>0&&await ve(e,i)}finally{n.inFlight.delete(r),e.isCurrent()&&t.activeWorkers--}if(!e.shouldContinue())break;if(i.length===0){if(e.runConfig.mode===`count`&&e.producersDone()){let r=n.next;for(;e.ownJobIds.size>0&&e.shouldContinue()&&[...n.inFlight].some(e=>e<=r);)await F(50,e.shouldContinue);if(!e.shouldContinue())break;e.ownJobIds.size>0&&(t.error??=`${e.ownJobIds.size} benchmark job(s) could not be drained.`);break}await F(50,e.shouldContinue)}}}}async function _e(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 Z(e,n),[])}catch(t){return e.isCurrent()&&(e.stats.error??=I(t)),[]}}async function ve(e,t){if(t.some(t=>!e.ownJobIds.has(t.id))&&e.pendingPushes.size>0&&await Promise.allSettled([...e.pendingPushes]),!e.shouldContinue())return Z(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 ye(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 Z(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 Z(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 Z(e,n)}}async function ye(e,t,n){let r=await Q(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 Z(e,t){if(t.length===0)return;let n=await Q(e,t);n>0&&e.isCurrent()&&(e.stats.error??=`${n} interrupted benchmark job(s) could not be returned to waiting.`)}async function Q(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 be=0;async function xe(e,t,n){if(e.runningRef.current||!e.mountedRef.current)return;e.runningRef.current=!0;let r=++e.runGenerationRef.current,a=n??i.createServerTargetClient(i.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}),$(e,`error`);return}let r=n.config,i=await pe(a,r.queue,s);if(!o())return;if(!i.ok){e.setLive(i.live),$(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}),$(e,`running`);let l={batch:r.batch,blob:`x`.repeat(r.payload),client:a,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=he(l),d=ge(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`&&$(e,`draining`))});if(await Promise.all([m,...p]),!o())return;Se(e,r,c)}finally{e.runningRef.current=!1}}function Se(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:++be,at:Date.now(),mode:t.mode,producers:t.producers,workers:t.workers},...e].slice(0,12)),$(e,e.stopRef.current?`stopped`:n.error?`error`:`done`)}function $(e,t){e.phaseRef.current=t,e.setPhase(t)}function Ce(){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)=>xe({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 we({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??Te(),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=Te();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 Te(){let e=i.captureServerRequestTarget();return{target:e,client:i.createServerTargetClient(e)}}function Ee(){let[e]=(0,C.useState)(ee),[t,n]=(0,C.useState)(()=>ne({...T,queue:e})),s=Ce(),u=r(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(_):i.counts(_),[_,d],{intervalMs:1e3}),S=b?.counts??null,E=we({benchmark:s,counts:S,dedicatedQueue:e,draft:t,pollQueue:_,runTarget:d,setRunTarget:p}),{phase:D,live:O,summary:j,history:M}=s,N=D===`running`||D===`draining`||D===`stopping`,P=s.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=De(D,P.mode,!!j),U=Oe(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)(a,{className:`size-3.5`}),` Stopping…`]}):N?(0,H.jsxs)(l,{variant:`warning`,size:`sm`,onClick:s.stop,children:[(0,H.jsx)(a,{className:`size-3.5`}),` Stop`]}):(0,H.jsxs)(l,{variant:`success`,size:`sm`,disabled:E.cleaning,onClick:E.start,children:[(0,H.jsx)(o,{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:s.clearHistory})]})}function De(e,t,n){return e===`running`?t===`duration`?`Running…`:`Producing…`:e===`draining`?`Draining…`:e===`stopping`?`Stopping…`:e===`error`?`Error`:n?`Result`:`Ready`}function Oe(e,t){return(e===`running`||e===`draining`)&&t!=null?t>0?`ETA ${m(t)}`:`finishing…`:e===`stopped`?`stopped early`:e===`done`?`complete`:``}export{Ee as Benchmark};
|