bunqueue-dashboard 0.0.47 → 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.
Files changed (133) hide show
  1. package/README.md +11 -9
  2. package/agent/backup/routes.ts +20 -12
  3. package/agent/managedTarget.ts +3 -1
  4. package/agent/manager/commandLine.ts +33 -0
  5. package/agent/manager/errors.ts +4 -0
  6. package/agent/manager/process.ts +4 -2
  7. package/agent/manager.ts +1 -0
  8. package/agent/server/controlRoutes.ts +10 -0
  9. package/agent/server/controlStatus.ts +26 -5
  10. package/agent/server/errors.ts +3 -0
  11. package/agent/server/router.ts +7 -2
  12. package/agent/shutdown.ts +3 -2
  13. package/dist/.vite/manifest.json +538 -576
  14. package/dist/assets/AddJob-CiwWycYG.js +3 -0
  15. package/dist/assets/Alerts-DCKl_C8D.js +1 -0
  16. package/dist/assets/AreaChart-Db2UVnaK.js +1 -0
  17. package/dist/assets/Benchmark-DyHvEM8x.js +1 -0
  18. package/dist/assets/BulkAddJobs-GRMVnf0j.js +5 -0
  19. package/dist/assets/Button-_aSJvRoW.js +1 -0
  20. package/dist/assets/Card-egIDLwVk.js +1 -0
  21. package/dist/assets/Cron-XL0PdUEc.js +1 -0
  22. package/dist/assets/CronManager-Cqtg9Zr8.js +1 -0
  23. package/dist/assets/{Database-DV2pcDLL.js → Database-BBzyi7f0.js} +3 -3
  24. package/dist/assets/Diagnostics-E_icHggb.js +1 -0
  25. package/dist/assets/Dlq-DNs32CzV.js +1 -0
  26. package/dist/assets/DlqControl-DawwIsTI.js +1 -0
  27. package/dist/assets/DlqPro-BEdxFiNN.js +1 -0
  28. package/dist/assets/Fleet-Bkbq0dFB.js +1 -0
  29. package/dist/assets/Flows-DKZxI7ta.js +2 -0
  30. package/dist/assets/JobInspector-BARE52q3.js +2 -0
  31. package/dist/assets/Jobs-CKzKpQ7i.js +1 -0
  32. package/dist/assets/JobsPro-DxDw-akG.js +1 -0
  33. package/dist/assets/Logs-Bj60G_FP.js +1 -0
  34. package/dist/assets/LogsPro-Hv8xwXDK.js +1 -0
  35. package/dist/assets/{McpServer-fVy27Mof.js → McpServer-zxSMjG8K.js} +1 -1
  36. package/dist/assets/{Metrics-Ba46pdzT.js → Metrics-BBLdWOw1.js} +1 -1
  37. package/dist/assets/MetricsPro-D470b1aP.js +1 -0
  38. package/dist/assets/{Overview-DV2qo5v3.js → Overview-B_AOa5kW.js} +1 -1
  39. package/dist/assets/{PageHeader-CvIyaFOO.js → PageHeader-CQzNsw8r.js} +1 -1
  40. package/dist/assets/Pagination-CSkzhNW6.js +1 -0
  41. package/dist/assets/QueueActions-BBxRt-t0.js +1 -0
  42. package/dist/assets/QueueControl-DaZjeZnw.js +1 -0
  43. package/dist/assets/QueueDetail-Dgo9Ukmp.js +1 -0
  44. package/dist/assets/QueueDetailPro-D7U8ahp2.js +1 -0
  45. package/dist/assets/Queues-CS8LeZtJ.js +1 -0
  46. package/dist/assets/QueuesOverview-BySSnCm0.js +1 -0
  47. package/dist/assets/{S3Backup-DNABHR2Q.js → S3Backup-Ci4dJPP_.js} +1 -1
  48. package/dist/assets/S3BackupPro-UwWu0yxr.js +3 -0
  49. package/dist/assets/ServerControl-Ch1wZcKB.js +3 -0
  50. package/dist/assets/Settings-FOzwrB25.js +1 -0
  51. package/dist/assets/StatusBadge-DereLzOj.js +1 -0
  52. package/dist/assets/{Usage-1KxG1mJ9.js → Usage-DR90dCt0.js} +1 -1
  53. package/dist/assets/UsagePro-DAlE3oCE.js +1 -0
  54. package/dist/assets/Webhooks-BonYUgMo.js +1 -0
  55. package/dist/assets/Workers-D1Ead02h.js +1 -0
  56. package/dist/assets/WorkersPro-XuLxtU4A.js +1 -0
  57. package/dist/assets/Workflows-CqZZ96EK.js +1 -0
  58. package/dist/assets/{api-BgWE-705.js → api-D9aT0ND7.js} +1 -1
  59. package/dist/assets/bq-DR2BTISO.js +1 -0
  60. package/dist/assets/classicJobsData-Csj4iihY.js +1 -0
  61. package/dist/assets/connectionStore-CVjQyIZv.js +1 -0
  62. package/dist/assets/controlConnectionEpoch-ChAeMMLk.js +1 -0
  63. package/dist/assets/feedback-CtGN_kqB.js +1 -0
  64. package/dist/assets/form-DG3X6cLL.js +1 -0
  65. package/dist/assets/{icons-BVzwsfbG.js → icons-DH9SS1DA.js} +1 -1
  66. package/dist/assets/index-DZFZr0Mi.js +8 -0
  67. package/dist/assets/{index-C9yGKhQ8.css → index-DaEyLvTR.css} +1 -1
  68. package/dist/assets/react-vendor-CFL9l8LK.js +11 -0
  69. package/dist/assets/{cn-zFEt5SO8.js → rolldown-runtime-QTnfLwEv.js} +1 -1
  70. package/dist/assets/{toastStore-IZn6OPLZ.js → toastStore-XmMX7A5W.js} +1 -1
  71. package/dist/assets/useControlActionGuard-1i7aFA7E.js +1 -0
  72. package/dist/assets/usePolledData-CEyyLRYa.js +1 -0
  73. package/dist/assets/useServerActionGuard-CUx8TOAC.js +1 -0
  74. package/dist/assets/useThroughputSeries-CeFx-Mwq.js +1 -0
  75. package/dist/index.html +15 -15
  76. package/package.json +29 -16
  77. package/scripts/bunVersion.ts +1 -1
  78. package/scripts/embedded.gen.ts +154 -164
  79. package/scripts/serveHandler.ts +28 -1
  80. package/dist/assets/AddJob-FNaPbs_V.js +0 -3
  81. package/dist/assets/Alerts-vzIYtFpW.js +0 -1
  82. package/dist/assets/AreaChart-DEAalJ6P.js +0 -1
  83. package/dist/assets/Benchmark-C16tT4pF.js +0 -1
  84. package/dist/assets/BulkAddJobs-IiL1uud0.js +0 -5
  85. package/dist/assets/Button-Ch1W94fJ.js +0 -1
  86. package/dist/assets/Card-CWxrC8OY.js +0 -1
  87. package/dist/assets/CopilotPanel-FlAW3RlL.js +0 -44
  88. package/dist/assets/Cron-1DZTZWXO.js +0 -1
  89. package/dist/assets/CronManager-B6xLLgI5.js +0 -1
  90. package/dist/assets/Diagnostics-B6apZrC5.js +0 -1
  91. package/dist/assets/Dlq-MRmkQrNC.js +0 -1
  92. package/dist/assets/DlqControl-DNJPVOTF.js +0 -1
  93. package/dist/assets/DlqPro-CwtTDwGN.js +0 -1
  94. package/dist/assets/Fleet-Bdrj9bLI.js +0 -1
  95. package/dist/assets/Flows-1dC8F_wx.js +0 -2
  96. package/dist/assets/JobInspector-Ct9Ga-UT.js +0 -2
  97. package/dist/assets/Jobs-Chqv0RuL.js +0 -1
  98. package/dist/assets/JobsPro-CLzOhKAL.js +0 -1
  99. package/dist/assets/Logs-C5PNP9Wq.js +0 -1
  100. package/dist/assets/LogsPro-B3KwTSsn.js +0 -1
  101. package/dist/assets/MetricsPro-B9rD_KQc.js +0 -1
  102. package/dist/assets/Pagination-x-Cwgrul.js +0 -1
  103. package/dist/assets/QueueActions-BWnmNEqV.js +0 -1
  104. package/dist/assets/QueueControl-DqBvuDnF.js +0 -1
  105. package/dist/assets/QueueDetail-COSH9bJl.js +0 -1
  106. package/dist/assets/QueueDetailPro-ClKtyHIf.js +0 -1
  107. package/dist/assets/Queues-D2-R8nYh.js +0 -1
  108. package/dist/assets/QueuesOverview-tokquA3g.js +0 -1
  109. package/dist/assets/S3BackupPro-CDX5WQUY.js +0 -3
  110. package/dist/assets/ServerControl-BmccMNW9.js +0 -3
  111. package/dist/assets/Settings-UcujwvKv.js +0 -1
  112. package/dist/assets/StatusBadge-BoOVTnKu.js +0 -1
  113. package/dist/assets/UsagePro-BGLufsE7.js +0 -1
  114. package/dist/assets/Webhooks-DT2LK17V.js +0 -1
  115. package/dist/assets/Workers-BX1qspaQ.js +0 -1
  116. package/dist/assets/WorkersPro-BsUQVgQZ.js +0 -1
  117. package/dist/assets/Workflows-DZZA6aGW.js +0 -1
  118. package/dist/assets/bq-F5BbTG1T.js +0 -1
  119. package/dist/assets/connectionStore-BJLihGaC.js +0 -1
  120. package/dist/assets/controlConnectionEpoch-CZsS0wAb.js +0 -1
  121. package/dist/assets/dist-1f1fMHVO.js +0 -11
  122. package/dist/assets/dist-BlWBaCN6.js +0 -11
  123. package/dist/assets/dist-DZL8soAh.js +0 -79
  124. package/dist/assets/dist-DjvZiKhB.js +0 -2
  125. package/dist/assets/dist-DpZcbP9W.js +0 -11
  126. package/dist/assets/feedback-QDN1AsYt.js +0 -1
  127. package/dist/assets/form-C4oby9pI.js +0 -1
  128. package/dist/assets/index-8Ii-_Rml.js +0 -8
  129. package/dist/assets/react-vendor-CvwrP0tU.js +0 -11
  130. package/dist/assets/useControlActionGuard-B-bzKZKI.js +0 -1
  131. package/dist/assets/usePolledData-0CVelkl2.js +0 -1
  132. package/dist/assets/useServerActionGuard-uGePilQQ.js +0 -1
  133. package/dist/assets/useThroughputSeries-DgO8uicf.js +0 -1
@@ -1 +0,0 @@
1
- import{i as e}from"./cn-zFEt5SO8.js";import{g as t,t as n}from"./react-vendor-CvwrP0tU.js";import{o as r}from"./connectionStore-BJLihGaC.js";import{O as i,r as a}from"./icons-BVzwsfbG.js";import{n as o,t as s}from"./Button-Ch1W94fJ.js";import{t as c}from"./Card-CWxrC8OY.js";import{i as l,t as u}from"./feedback-QDN1AsYt.js";import{t as d}from"./PageHeader-CvIyaFOO.js";import{l as f}from"./format-Cg7VCIZW.js";import{d as p,f as m,l as h,p as g,u as _}from"./index-8Ii-_Rml.js";import{a as v,n as y,o as b,t as x}from"./form-C4oby9pI.js";var S=e(t(),1),C=[`error_rate`,`p99_latency`,`waiting`,`failed`,`dlq`],w=[`>=`,`>`,`<=`,`<`],T=[`email`,`webhook`,`slack`],E=/^[a-zA-Z0-9_\-.:]+$/,D={error_rate:{unit:`% (0–100)`,placeholder:`e.g. 5`},p99_latency:{unit:`ms`,placeholder:`e.g. 250`},waiting:{unit:`jobs`,placeholder:`e.g. 100`},failed:{unit:`jobs`,placeholder:`e.g. 10`},dlq:{unit:`jobs`,placeholder:`e.g. 1`}};function O(e){let t=e.name.trim();if(!C.includes(e.metric))return{ok:!1,error:`Invalid metric`,field:`metric`};if(!w.includes(e.operator))return{ok:!1,error:`Invalid operator`,field:`operator`};let n=e.metric===`p99_latency`?``:e.queue.trim();if(!t)return{ok:!1,error:`Rule name is required`,field:`name`};if(t.length>200)return{ok:!1,error:`Rule name must be 200 characters or fewer`,field:`name`};if(!e.threshold.trim())return{ok:!1,error:`Threshold is required`,field:`threshold`};let r=Number(e.threshold);return!Number.isFinite(r)||r<0?{ok:!1,error:`Threshold must be a non-negative number`,field:`threshold`}:e.metric===`error_rate`&&r>100?{ok:!1,error:`Error-rate threshold must be between 0 and 100%`,field:`threshold`}:[`waiting`,`failed`,`dlq`].includes(e.metric)&&!Number.isSafeInteger(r)?{ok:!1,error:`Job-count thresholds must be whole numbers`,field:`threshold`}:n&&(n.length>256||!E.test(n))?{ok:!1,error:`Queue contains unsupported characters or is too long`,field:`queue`}:T.includes(e.channel)?{ok:!0,rule:{name:t,metric:e.metric,operator:e.operator,threshold:r,queue:n,channel:e.channel,enabled:!0}}:{ok:!1,error:`Invalid channel type`,field:`channel`}}var k=n();function A({onAdd:e}){let[t,n]=(0,S.useState)(``),[r,i]=(0,S.useState)(`error_rate`),[a,o]=(0,S.useState)(`>=`),[c,l]=(0,S.useState)(``),[u,d]=(0,S.useState)(``),[f,p]=(0,S.useState)(null),h=(0,S.useRef)(!1),g=(0,S.useId)(),_=e=>f?.field===e;return(0,k.jsxs)(`div`,{className:`grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-5`,children:[(0,k.jsx)(x,{label:`Name`,children:(0,k.jsx)(y,{name:`alert-rule-name`,autoComplete:`off`,maxLength:200,value:t,"aria-invalid":_(`name`),"aria-describedby":_(`name`)?g:void 0,onChange:e=>{n(e.target.value),p(null)},placeholder:`High error rate`})}),(0,k.jsx)(x,{label:`Metric`,children:(0,k.jsx)(v,{name:`alert-rule-metric`,value:r,"aria-invalid":_(`metric`),"aria-describedby":_(`metric`)?g:void 0,onChange:e=>{i(e.target.value),p(null)},children:C.map(e=>(0,k.jsx)(`option`,{value:e,children:m[e]},e))})}),(0,k.jsx)(x,{label:`Operator`,children:(0,k.jsx)(v,{name:`alert-rule-operator`,value:a,"aria-invalid":_(`operator`),"aria-describedby":_(`operator`)?g:void 0,onChange:e=>{o(e.target.value),p(null)},children:w.map(e=>(0,k.jsx)(`option`,{value:e,children:e},e))})}),(0,k.jsx)(x,{label:`Threshold`,hint:D[r].unit,children:(0,k.jsx)(y,{name:`alert-rule-threshold`,autoComplete:`off`,type:`number`,min:0,max:r===`error_rate`?100:void 0,step:[`waiting`,`failed`,`dlq`].includes(r)?1:`any`,value:c,"aria-invalid":_(`threshold`),"aria-describedby":_(`threshold`)?g:void 0,onChange:e=>{l(e.target.value),p(null)},placeholder:D[r].placeholder})}),(0,k.jsx)(x,{label:`Queue (optional)`,hint:r===`p99_latency`?`global only — latency is not per queue`:void 0,children:(0,k.jsx)(y,{name:`alert-rule-queue`,autoComplete:`off`,spellCheck:!1,maxLength:256,value:u,"aria-invalid":_(`queue`),"aria-describedby":_(`queue`)?g:void 0,onChange:e=>{d(e.target.value),p(null)},disabled:r===`p99_latency`,placeholder:r===`p99_latency`?`Global`:`All queues`,className:`disabled:cursor-not-allowed disabled:opacity-40`})}),(0,k.jsxs)(`div`,{className:`col-span-full`,children:[(0,k.jsx)(s,{variant:`accent`,disabled:!t.trim()||!c.trim(),onClick:()=>{if(h.current)return;let n=O({name:t,metric:r,operator:a,threshold:c,queue:u,channel:`email`});if(!n.ok)return p({field:n.field,message:n.error});h.current=!0;try{let t=e(n.rule);t.ok||(h.current=!1,p({field:`name`,message:t.error}))}catch(e){h.current=!1,p({field:`name`,message:e.message})}},children:`Save rule`}),f&&(0,k.jsx)(`span`,{id:g,role:`alert`,className:`ml-3 text-xs text-danger`,children:f.message})]})]})}function j(){return typeof Notification>`u`?`unsupported`:Notification.permission}function M(){let{rules:e,addRule:t,removeRule:n,toggleRule:v}=g(),y=_(e=>e.breaching),x=_(e=>e.status),C=_(e=>e.error),w=_(e=>e.connectionIdentity),T=r(e=>p(e.baseUrl,e.token)),E=e.some(e=>e.enabled),D=w===T,O=D?y:[],M=D?x:E?`checking`:`idle`,N=D?C:null,[P,F]=(0,S.useState)(!1),[I,L]=(0,S.useState)(j);return(0,k.jsxs)(`div`,{children:[(0,k.jsx)(d,{title:`Alerts`,description:`Threshold rules evaluated live in your browser.`,live:E&&M===`live`,actions:(0,k.jsxs)(k.Fragment,{children:[I===`granted`?(0,k.jsxs)(`span`,{className:`flex items-center gap-1.5 text-sm text-success`,children:[(0,k.jsx)(a,{className:`size-4`}),` Desktop alerts on`]}):I===`unsupported`?(0,k.jsx)(`span`,{className:`text-sm text-faint`,children:`Notifications unsupported`}):I===`denied`?(0,k.jsx)(`span`,{className:`text-sm text-faint`,children:`Desktop alerts blocked — allow notifications in browser settings`}):(0,k.jsx)(s,{size:`sm`,onClick:async()=>{L(await h())},children:`Enable desktop notifications`}),(0,k.jsx)(s,{variant:`accent`,size:`sm`,"aria-expanded":P,"aria-controls":`alert-rule-form`,onClick:()=>F(e=>!e),children:`+ Create Alert Rule`})]})}),E&&M===`checking`&&(0,k.jsx)(`p`,{role:`status`,className:`mb-4 text-sm text-muted`,children:`Checking alert metrics for the connected server…`}),E&&M===`degraded`&&(0,k.jsx)(l,{message:N??`Alert metrics are unavailable; current rule results may be stale.`}),(0,k.jsx)(`div`,{className:`mb-6 rounded-lg border border-amber-500/20 bg-amber-500/5 px-4 py-3 text-sm text-amber-400/90`,children:`Rules are evaluated in this browser while a tab is open (even backgrounded), raising an in-app toast and — if enabled — a desktop notification. bunqueue OSS has no alerting backend, so email/webhook/slack delivery still needs your own monitoring or hosted bunqueue Cloud.`}),(0,k.jsxs)(`section`,{className:`mb-8`,children:[(0,k.jsx)(`h2`,{className:`mb-3 text-lg font-semibold text-fg`,children:`Triggered Alerts`}),O.length===0?(0,k.jsx)(u,{icon:(0,k.jsx)(a,{}),title:`No triggered alerts`,hint:E?M===`live`?`All enabled rules are within their thresholds.`:M===`checking`?`Alert metrics are still being checked.`:`Alert metrics are unavailable; no all-clear result is available.`:`No enabled rules yet. Create one below.`}):(0,k.jsx)(`div`,{className:`overflow-x-auto rounded-xl border border-danger/30 bg-red-500/5`,children:(0,k.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,k.jsx)(`thead`,{children:(0,k.jsxs)(`tr`,{className:`border-b border-line text-left text-[11px] uppercase tracking-wider text-faint`,children:[(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Rule`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Condition`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Queue`}),(0,k.jsx)(`th`,{className:`px-5 py-3 text-right font-medium`,children:`Current`}),(0,k.jsx)(`th`,{className:`px-5 py-3 text-right font-medium`,children:`Since`})]})}),(0,k.jsx)(`tbody`,{children:O.map(e=>(0,k.jsxs)(`tr`,{className:`border-b border-line last:border-0`,children:[(0,k.jsx)(`td`,{className:`px-5 py-3 font-medium text-danger`,children:e.ruleName}),(0,k.jsxs)(`td`,{className:`px-5 py-3 text-muted`,children:[e.metricLabel,` `,e.operator,` `,(0,k.jsx)(`span`,{className:`font-semibold text-fg`,children:e.threshold})]}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-muted`,children:e.queue||`All queues`}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-right tnum font-semibold text-danger`,children:Math.round(e.value*100)/100}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-right text-faint`,children:f(e.at)})]},e.ruleId))})]})})]}),(0,k.jsxs)(`section`,{children:[(0,k.jsx)(`h2`,{className:`mb-3 text-lg font-semibold text-fg`,children:`Alert Rules`}),P&&(0,k.jsx)(`div`,{id:`alert-rule-form`,children:(0,k.jsx)(c,{className:`mb-4`,children:(0,k.jsx)(A,{onAdd:e=>{let n=t(e);return n.ok&&F(!1),n}})})}),e.length===0?(0,k.jsx)(u,{title:`No alert rules`,hint:`Create a rule to be notified when a threshold is crossed.`}):(0,k.jsx)(`div`,{className:`overflow-x-auto rounded-xl border border-line bg-surface`,children:(0,k.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,k.jsx)(`thead`,{children:(0,k.jsxs)(`tr`,{className:`border-b border-line text-left text-[11px] uppercase tracking-wider text-faint`,children:[(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Rule`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Condition`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Queue`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Delivery`}),(0,k.jsx)(`th`,{className:`px-5 py-3 font-medium`,children:`Status`}),(0,k.jsx)(`th`,{className:`w-12 px-5 py-3`})]})}),(0,k.jsx)(`tbody`,{children:e.map(e=>(0,k.jsxs)(`tr`,{className:`border-b border-line last:border-0`,children:[(0,k.jsx)(`td`,{className:`px-5 py-3 font-medium text-fg`,children:e.name}),(0,k.jsxs)(`td`,{className:`px-5 py-3 text-muted`,children:[m[e.metric],` `,e.operator,` `,(0,k.jsx)(`span`,{className:`font-semibold text-accent`,children:e.threshold})]}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-muted`,children:e.queue||`All queues`}),(0,k.jsx)(`td`,{className:`px-5 py-3`,children:(0,k.jsx)(`span`,{className:`rounded-md bg-surface-2 px-2 py-0.5 text-xs text-muted`,children:`Browser`})}),(0,k.jsx)(`td`,{className:`px-5 py-3`,children:(0,k.jsx)(b,{checked:e.enabled,onChange:()=>v(e.id),label:`Enabled`})}),(0,k.jsx)(`td`,{className:`px-5 py-3 text-right`,children:(0,k.jsx)(o,{"aria-label":`Delete rule ${e.name}`,onClick:()=>window.confirm(`Delete alert rule "${e.name}"?`)&&n(e.id),children:(0,k.jsx)(i,{className:`size-3.5`})})})]},e.id))})]})})]})]})}export{M as Alerts};
@@ -1 +0,0 @@
1
- import{i as e}from"./cn-zFEt5SO8.js";import{g as t,t as n}from"./react-vendor-CvwrP0tU.js";var r=e(t(),1),i=n();function a({series:e,height:t=180,xLabels:n,ariaLabel:a=`throughput chart`,formatValue:o}){let s=(0,r.useId)().replace(/:/g,``),c=`${s}-desc`,l=t,u=e=>Number.isFinite(e)?e:0,d=Math.max(1,...e.map(e=>e.points.length)),f=Math.max(1,...e.flatMap(e=>e.points.map(u))),p=e=>d<=1?0:e/(d-1)*600,m=e=>l-10-u(e)/f*(l-20),h=[0,.25,.5,.75,1].map(e=>10+e*(l-20)),g=o??(e=>Number.isInteger(e)?String(e):e.toFixed(1)),_=e.some(e=>e.points.length>0),v=e.find(e=>e.points.length>0),y=v?u(v.points[v.points.length-1]):0;return(0,i.jsxs)(`div`,{className:`w-full`,children:[(0,i.jsxs)(`div`,{className:`relative`,children:[(0,i.jsxs)(`svg`,{viewBox:`0 0 600 ${l}`,preserveAspectRatio:`none`,className:`block w-full`,style:{height:t},role:`img`,"aria-label":a,"aria-describedby":c,children:[(0,i.jsx)(`defs`,{children:e.map((e,t)=>(0,i.jsxs)(`linearGradient`,{id:`${s}-g${t}`,x1:`0`,y1:`0`,x2:`0`,y2:`1`,children:[(0,i.jsx)(`stop`,{offset:`0%`,stopColor:e.color,stopOpacity:`0.28`}),(0,i.jsx)(`stop`,{offset:`100%`,stopColor:e.color,stopOpacity:`0`})]},e.label))}),h.map(e=>(0,i.jsx)(`line`,{x1:`0`,y1:e,x2:600,y2:e,stroke:`var(--line)`,strokeWidth:`1`},e)),e.map((e,t)=>{if(e.points.length===0)return null;let n=e.points.length===1?`M0,${m(e.points[0])} L600,${m(e.points[0])}`:e.points.map((e,t)=>`${t===0?`M`:`L`}${p(t)},${m(e)}`).join(` `),r=e.points.length===1?`${n} L600,${l} L0,${l} Z`:`${n} L${p(e.points.length-1)},${l} L${p(0)},${l} Z`;return(0,i.jsxs)(`g`,{children:[e.area&&(0,i.jsx)(`path`,{d:r,fill:`url(#${s}-g${t})`}),(0,i.jsx)(`path`,{d:n,fill:`none`,stroke:e.color,strokeWidth:`2`,strokeLinejoin:`round`,vectorEffect:`non-scaling-stroke`})]},e.label)})]}),_&&(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(`span`,{className:`pointer-events-none absolute left-1 -translate-y-full font-mono text-[10px] leading-none text-faint`,style:{top:10},children:g(f)}),(0,i.jsx)(`span`,{className:`pointer-events-none absolute left-1 top-1/2 -translate-y-1/2 font-mono text-[10px] leading-none text-faint`,children:g(f/2)})]})]}),n&&(0,i.jsx)(`div`,{className:`mt-1 flex justify-between px-1 font-mono text-[10px] text-faint`,children:n.map(e=>(0,i.jsx)(`span`,{children:e},e))}),(0,i.jsx)(`p`,{id:c,className:`sr-only`,children:_?`latest ${g(y)}, max ${g(f)}`:`no data yet`})]})}export{a as t};
@@ -1 +0,0 @@
1
- import{i as e,t}from"./cn-zFEt5SO8.js";import{g as n,t as r}from"./react-vendor-CvwrP0tU.js";import{o as i}from"./connectionStore-BJLihGaC.js";import{t as a}from"./bq-F5BbTG1T.js";import{b as o,x as s}from"./icons-BVzwsfbG.js";import{n as c}from"./usePolledData-0CVelkl2.js";import{t as l}from"./Button-Ch1W94fJ.js";import{n as u,t as d}from"./Card-CWxrC8OY.js";import{t as f}from"./PageHeader-CvIyaFOO.js";import{n as p,o as m,s as h}from"./format-Cg7VCIZW.js";import{a as g,n as _}from"./index-8Ii-_Rml.js";import{i as v,n as y,o as b,t as x}from"./form-C4oby9pI.js";import{t as S}from"./AreaChart-DEAalJ6P.js";var C=e(n(),1),w={total:1e6,batch:5e3,producers:64,payload:65536,workers:64,workerBatch:1e3,processMs:6e4,durationS:600},T={queue:`benchmark`,mode:`count`,total:5e3,durationS:30,batch:100,producers:4,payload:128,workers:4,workerBatch:100,processMs:5,durable:!1,removeOnComplete:!0},E=`bq-dashboard-benchmark-`;function D(){if(typeof globalThis.crypto?.randomUUID!=`function`)throw Error(`A cryptographically secure browser is required to create a benchmark queue.`);return`${E}${globalThis.crypto.randomUUID()}`}function O(e){return RegExp(`^${E}[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`,`i`).test(e)}var k={Smoke:{mode:`count`,total:200,batch:50,producers:2,workers:1,workerBatch:50,processMs:0},Standard:{mode:`count`,total:5e3,batch:100,producers:4,workers:4,workerBatch:100,processMs:5},Stress:{mode:`count`,total:5e4,batch:500,producers:16,workers:16,workerBatch:200,processMs:0},Soak:{mode:`duration`,durationS:30,batch:100,producers:4,workers:8,workerBatch:100,processMs:10}},A=(e,t,n)=>Number.isFinite(e)?Math.max(t,Math.min(n,Math.floor(e))):t;function j(e){return e?e.length>256?`Queue name is too long (maximum 256 characters).`:/^[a-zA-Z0-9_\-.:]+$/.test(e)?null:`Queue name contains unsupported characters.`:`Queue name is required.`}var M=(e,t)=>e.length?e[Math.min(e.length-1,Math.max(0,Math.ceil(t/100*e.length)-1))]:0,N=(e,t,n,r,i,a,o)=>Array.from({length:t},(t,s)=>({data:{i:e+s,blob:n},durable:r,removeOnComplete:i,...o===void 0?{}:{stallTimeout:o},...a?{jobId:`${a}-${e+s}`,tags:[`bunqueue-dashboard-benchmark`,a]}:{}})),P=e=>new Promise(t=>setTimeout(t,e)),F=async(e,t)=>{let n=performance.now()+e;for(;t()&&performance.now()<n;)await P(Math.min(100,Math.max(0,n-performance.now())))},I=e=>e instanceof Error?e.message:String(e),L=e=>e>=1e6?`${(e/1e6).toFixed(2)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:`${Math.round(e)}`,R=[`count`,`duration`],z=[`waiting`,`completed`,`failed`],B=null;function ee(){return B??=D(),B}var te=[`total`,`durationS`,`batch`,`producers`,`payload`,`workers`,`workerBatch`,`processMs`],ne=e=>({...e,total:String(e.total),durationS:String(e.durationS),batch:String(e.batch),producers:String(e.producers),payload:String(e.payload),workers:String(e.workers),workerBatch:String(e.workerBatch),processMs:String(e.processMs)}),V=e=>({...e,total:Number(e.total),durationS:Number(e.durationS),batch:Number(e.batch),producers:Number(e.producers),payload:Number(e.payload),workers:Number(e.workers),workerBatch:Number(e.workerBatch),processMs:Number(e.processMs)}),H=r();function re({draft:e,active:n,cleaning:r,cleanResult:i,setField:a,numberInput:o,onClean:s}){return(0,H.jsxs)(d,{className:`lg:col-span-1`,children:[(0,H.jsx)(u,{title:`Configuration`}),(0,H.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,H.jsx)(x,{label:`Dedicated queue`,hint:`Cryptographically generated for this browser tab; production queue names cannot be entered here.`,children:(0,H.jsx)(y,{name:`benchmark-queue`,autoComplete:`off`,value:e.queue,readOnly:!0,"aria-readonly":`true`})}),(0,H.jsx)(x,{label:`Mode`,hint:e.mode===`count`?`Push a fixed number of jobs.`:`Run producers + workers for a fixed time.`,children:(0,H.jsx)(v,{options:R,value:e.mode,onChange:e=>a(`mode`,e),disabled:n})}),e.mode===`count`?(0,H.jsx)(x,{label:`Total jobs`,hint:`max ${h(w.total)}`,children:(0,H.jsx)(y,{name:`benchmark-total`,autoComplete:`off`,type:`number`,min:1,max:w.total,value:e.total,disabled:n,onChange:o(`total`)})}):(0,H.jsx)(x,{label:`Duration (s)`,hint:`max ${w.durationS}s`,children:(0,H.jsx)(y,{name:`benchmark-duration-seconds`,autoComplete:`off`,type:`number`,min:1,max:w.durationS,value:e.durationS,disabled:n,onChange:o(`durationS`)})}),(0,H.jsx)(`div`,{className:`mt-1 text-[11px] font-semibold uppercase tracking-wider text-faint`,children:`Producers`}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,H.jsx)(x,{label:`Producers`,hint:`parallel, max ${w.producers}`,children:(0,H.jsx)(y,{name:`benchmark-producers`,autoComplete:`off`,type:`number`,min:1,max:w.producers,value:e.producers,disabled:n,onChange:o(`producers`)})}),(0,H.jsx)(x,{label:`Push batch`,hint:`jobs/req, max ${w.batch}`,children:(0,H.jsx)(y,{name:`benchmark-push-batch`,autoComplete:`off`,type:`number`,min:1,max:w.batch,value:e.batch,disabled:n,onChange:o(`batch`)})}),(0,H.jsx)(x,{label:`Payload`,hint:`bytes/job, max ${h(w.payload)}`,children:(0,H.jsx)(y,{name:`benchmark-payload-bytes`,autoComplete:`off`,type:`number`,min:0,max:w.payload,value:e.payload,disabled:n,onChange:o(`payload`)})})]}),(0,H.jsx)(`div`,{className:`mt-1 text-[11px] font-semibold uppercase tracking-wider text-faint`,children:`Workers (simulated)`}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,H.jsx)(x,{label:`Workers`,hint:`0 = produce only, max ${w.workers}`,children:(0,H.jsx)(y,{name:`benchmark-workers`,autoComplete:`off`,type:`number`,min:0,max:w.workers,value:e.workers,disabled:n,onChange:o(`workers`)})}),(0,H.jsx)(x,{label:`Pull batch`,hint:`jobs/pull, max ${w.workerBatch}`,children:(0,H.jsx)(y,{name:`benchmark-pull-batch`,autoComplete:`off`,type:`number`,min:1,max:w.workerBatch,value:e.workerBatch,disabled:n,onChange:o(`workerBatch`)})}),(0,H.jsx)(x,{label:`Process (ms)`,hint:`simulated work per pull`,children:(0,H.jsx)(y,{name:`benchmark-process-ms`,autoComplete:`off`,type:`number`,min:0,max:w.processMs,value:e.processMs,disabled:n,onChange:o(`processMs`)})})]}),(0,H.jsx)(U,{label:`Durable (fsync each job)`,checked:e.durable,active:n,onChange:e=>a(`durable`,e)}),(0,H.jsx)(U,{label:`Remove on complete`,checked:e.removeOnComplete,active:n,onChange:e=>a(`removeOnComplete`,e)}),(0,H.jsxs)(`div`,{className:`flex items-center gap-3 pt-1`,children:[(0,H.jsx)(l,{variant:`ghost`,size:`sm`,disabled:n||r,onClick:s,children:r?`Cleaning…`:`Clean queue`}),i&&(`error`in i?(0,H.jsxs)(`span`,{className:`text-xs text-danger`,children:[`Clean unverified — `,i.error]}):(0,H.jsx)(`span`,{className:t(`text-xs`,i.remaining>0?`text-warning`:`text-success`),children:i.remaining>0?`Cleaned — ${h(i.remaining)} active job(s) remain (requeued after the stall timeout)`:`Cleaned — 0 jobs remain`}))]})]})]})}function U({label:e,checked:t,active:n,onChange:r}){return(0,H.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,H.jsx)(b,{checked:t,onChange:r,disabled:n,label:e}),(0,H.jsx)(`span`,{className:`text-sm text-muted`,children:e})]})}function ie({heading:e,pollBaseUrl:t,etaText:n,shown:r,shownWorkers:i,producePct:a,drainPct:o,durationPct:s,live:c,summary:l}){return(0,H.jsxs)(`div`,{className:`flex flex-col gap-6 lg:col-span-2`,children:[(0,H.jsxs)(d,{children:[(0,H.jsxs)(`div`,{className:`mb-3 flex items-center justify-between`,children:[(0,H.jsx)(`h3`,{className:`text-base font-semibold text-fg`,children:e}),(0,H.jsxs)(`span`,{className:`text-right text-xs text-faint`,children:[(0,H.jsxs)(`span`,{className:`block`,children:[`Benchmark target: `,t]}),n&&(0,H.jsx)(`span`,{className:`block`,children:n})]})]}),(0,H.jsx)(W,{label:r.mode===`duration`?`Elapsed`:`Produced`,pct:r.mode===`duration`?s:a,tone:`accent`}),i>0&&r.mode===`count`&&(0,H.jsx)(W,{label:`Completed`,pct:o,tone:`emerald`}),(0,H.jsxs)(`div`,{className:`mt-4 grid grid-cols-2 gap-4 md:grid-cols-4`,children:[(0,H.jsx)(_,{label:`Pushed`,value:h(c.pushed),tone:`accent`,compact:!0}),(0,H.jsx)(_,{label:`Completed`,value:h(c.completed),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Push/sec`,value:L(c.pushPerSec),compact:!0}),(0,H.jsx)(_,{label:`Done/sec`,value:L(c.donePerSec),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Elapsed`,value:m(c.elapsedMs),compact:!0}),(0,H.jsx)(_,{label:`Active workers`,value:`${c.activeWorkers}/${i}`,tone:`blue`,compact:!0}),(0,H.jsx)(_,{label:`Data`,value:p(l?.bytes??c.bytes),compact:!0}),(0,H.jsx)(_,{label:`Errors`,value:h((l?.pushFailed??c.pushFailed)+(l?.ackFailed??c.ackFailed)),tone:c.pushFailed+c.ackFailed?`red`:`default`,compact:!0})]})]}),(0,H.jsxs)(d,{children:[(0,H.jsx)(u,{title:`Throughput`}),(0,H.jsx)(`p`,{className:`-mt-3 mb-4 text-xs text-faint`,children:`Enqueue (client → server) vs completion (workers) per second`}),(0,H.jsx)(S,{series:[{label:`Push/sec`,color:`#38bdf8`,points:c.pushSeries,area:!0},{label:`Done/sec`,color:`#34d399`,points:c.doneSeries,area:!0}]}),(0,H.jsxs)(`div`,{className:`mt-2 flex items-center gap-4 font-mono text-[11px] text-faint`,children:[(0,H.jsx)(G,{color:`#38bdf8`,label:`Push/sec`,value:c.pushPerSec}),(0,H.jsx)(G,{color:`#34d399`,label:`Done/sec`,value:c.donePerSec})]})]})]})}function ae({summary:e}){return e?(0,H.jsxs)(d,{className:`mt-6`,children:[(0,H.jsx)(u,{title:`Summary`}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 md:grid-cols-4 xl:grid-cols-8`,children:[(0,H.jsx)(_,{label:`Pushed`,value:h(e.pushed),tone:`accent`,compact:!0}),(0,H.jsx)(_,{label:`Completed`,value:h(e.completed),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Duration`,value:m(e.durationMs),compact:!0}),(0,H.jsx)(_,{label:`Avg push/s`,value:L(e.pushPerSec),tone:`accent`,compact:!0}),(0,H.jsx)(_,{label:`Avg done/s`,value:L(e.donePerSec),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Data rate`,value:`${p(e.mbPerSec)}/s`,compact:!0}),(0,H.jsx)(_,{label:`Push p95`,value:m(e.p95),compact:!0}),(0,H.jsx)(_,{label:`Push p99`,value:m(e.p99),compact:!0})]}),(0,H.jsxs)(`p`,{className:`mt-3 text-xs text-faint`,children:[`Push-batch latency avg `,m(e.avg),` · p50 `,m(e.p50),` · p95`,` `,m(e.p95),` · p99 `,m(e.p99),` · max `,m(e.max),`.`,e.error?` First error: ${e.error}`:``]})]}):null}function oe({pollQueue:e,pollBaseUrl:t,pinned:n,counts:r,error:i}){return(0,H.jsxs)(d,{className:`mt-6`,children:[(0,H.jsx)(u,{title:`Server queue: ${e}`,action:i?(0,H.jsx)(`span`,{className:`text-xs font-medium text-warning`,children:`stale — server unreachable`}):void 0}),(0,H.jsxs)(`p`,{className:`-mt-3 mb-4 text-xs text-faint`,children:[`Live counts from `,t,` (poll 1s).`,n?` Pinned to the current or last benchmark run.`:``]}),r?(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 md:grid-cols-5`,children:[(0,H.jsx)(_,{label:`Waiting`,value:h(r.waiting??0),tone:`amber`,compact:!0}),(0,H.jsx)(_,{label:`Active`,value:h(r.active??0),tone:`blue`,compact:!0}),(0,H.jsx)(_,{label:`Completed`,value:h(r.completed??0),tone:`green`,compact:!0}),(0,H.jsx)(_,{label:`Failed`,value:h(r.failed??0),tone:r.failed?`red`:`default`,compact:!0}),(0,H.jsx)(_,{label:`Delayed`,value:h(r.delayed??0),compact:!0})]}):(0,H.jsx)(`p`,{className:`text-sm text-faint`,children:`No counts yet — waiting for the server to answer for this queue.`})]})}function W({label:e,pct:n,tone:r}){return(0,H.jsxs)(`div`,{className:`mb-2`,children:[(0,H.jsxs)(`div`,{className:`mb-1 flex items-center justify-between text-[11px] text-faint`,children:[(0,H.jsx)(`span`,{children:e}),(0,H.jsxs)(`span`,{className:`tnum`,children:[n.toFixed(0),`%`]})]}),(0,H.jsx)(`div`,{className:`h-1.5 overflow-hidden rounded-full bg-surface-2`,role:`progressbar`,"aria-label":e,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":Math.round(n),children:(0,H.jsx)(`div`,{className:t(`h-full rounded-full transition-[width] motion-reduce:transition-none`,r===`accent`?`bg-accent`:`bg-emerald-500`),style:{width:`${n}%`}})})]})}function G({color:e,label:t,value:n}){return(0,H.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,H.jsx)(`span`,{className:`size-2 rounded-full`,style:{background:e}}),t,` `,(0,H.jsxs)(`span`,{className:`text-fg`,children:[L(n),`/s`]})]})}function se({history:e,onClear:t}){if(!e.length)return null;let n=JSON.stringify(e,null,2);return(0,H.jsxs)(d,{padded:!1,className:`mt-6 overflow-hidden`,children:[(0,H.jsxs)(`div`,{className:`flex items-center justify-between border-b border-line px-5 py-3`,children:[(0,H.jsx)(`h3`,{className:`text-base font-semibold text-fg`,children:`Run history`}),(0,H.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,H.jsx)(g,{value:n}),(0,H.jsx)(l,{size:`sm`,variant:`ghost`,onClick:()=>{let e=new Blob([n],{type:`application/json`}),t=URL.createObjectURL(e),r=document.createElement(`a`);r.href=t,r.download=`benchmark-history-${Date.now()}.json`,r.click(),URL.revokeObjectURL(t)},children:`Export JSON`}),(0,H.jsx)(l,{size:`sm`,variant:`ghost`,onClick:t,children:`Clear`})]})]}),(0,H.jsx)(`div`,{className:`overflow-x-auto`,children:(0,H.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,H.jsx)(`thead`,{children:(0,H.jsxs)(`tr`,{className:`border-b border-line text-left text-[11px] uppercase tracking-wider text-faint`,children:[(0,H.jsx)(`th`,{className:`px-5 py-2 font-medium`,children:`Mode`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Prod`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Wkr`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Pushed`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Done`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Push/s`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Done/s`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`p95`}),(0,H.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Dur`})]})}),(0,H.jsx)(`tbody`,{children:e.map(e=>(0,H.jsxs)(`tr`,{className:`border-b border-line last:border-0 hover:bg-surface-2/40`,children:[(0,H.jsx)(`td`,{className:`px-5 py-2 capitalize text-muted`,children:e.mode}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:e.producers}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:e.workers}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-fg`,children:h(e.pushed)}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-success`,children:h(e.completed)}),(0,H.jsxs)(`td`,{className:`px-5 py-2 text-right tnum text-accent`,children:[L(e.pushPerSec),`/s`]}),(0,H.jsxs)(`td`,{className:`px-5 py-2 text-right tnum text-success`,children:[L(e.donePerSec),`/s`]}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:m(e.p95)}),(0,H.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:m(e.durationMs)})]},e.id))})]})})]})}function ce(e=8){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`Benchmark compensation concurrency must be a positive whole number`);let t=0,n=[],r=()=>{--t,n.shift()?.()};return Object.freeze({run(i){return new Promise((a,o)=>{let s=()=>{t+=1,Promise.resolve().then(i).then(a,o).finally(r)};t<e?s():n.push(s)})}})}var le=[`waiting`,`prioritized`,`delayed`,`active`,`paused`,`waiting-children`,`completed`,`failed`];function K(e){return le.reduce((t,n)=>{let r=e[n];if(!Number.isSafeInteger(r)||r<0)throw Error(`Malformed queue count for "${n}".`);return t+r},0)}function q(e,t){if(!e||typeof e!=`object`||e.ok!==!0)throw Error(`${t} returned a malformed success response.`)}var J={pushed:0,completed:0,pushFailed:0,ackFailed:0,bytes:0,elapsedMs:0,pushPerSec:0,donePerSec:0,activeWorkers:0,pushSeries:[],doneSeries:[],etaMs:null,error:null},Y=()=>({pushed:0,completed:0,pushFailed:0,ackFailed:0,bytes:0,assigned:0,startedAt:0,pushLat:[],activeWorkers:0,error:null,lastAt:0,lastPushed:0,lastDone:0,pushSeries:[],doneSeries:[]});function ue(e,t,n,r){let i=r-e.startedAt,a=(r-e.lastAt)/1e3,o=a>0?Math.max(0,(e.pushed-e.lastPushed)/a):0,s=a>0?Math.max(0,(e.completed-e.lastDone)/a):0;e.lastAt=r,e.lastPushed=e.pushed,e.lastDone=e.completed,e.pushSeries=[...e.pushSeries,o].slice(-60),e.doneSeries=[...e.doneSeries,s].slice(-60);let c=null;if(t?.mode===`count`){let r=A(t.total,1,w.total);if(n===`draining`){let t=e.doneSeries.slice(-10),n=t.length?t.reduce((e,t)=>e+t,0)/t.length:0;n<=0&&i>0&&(n=e.completed/(i/1e3)),n>0&&(c=Math.max(0,r-e.completed)/n*1e3)}else o>0&&(c=Math.max(0,r-e.pushed)/o*1e3)}else t?.mode===`duration`&&(c=Math.max(0,A(t.durationS,1,w.durationS)*1e3-i));return{pushed:e.pushed,completed:e.completed,pushFailed:e.pushFailed,ackFailed:e.ackFailed,bytes:e.bytes,elapsedMs:i,pushPerSec:o,donePerSec:s,activeWorkers:e.activeWorkers,pushSeries:e.pushSeries,doneSeries:e.doneSeries,etaMs:c,error:e.error}}function de(e,t){let n=t/1e3||1,r=[...e.pushLat].sort((e,t)=>e-t);return{pushed:e.pushed,completed:e.completed,pushFailed:e.pushFailed,ackFailed:e.ackFailed,bytes:e.bytes,durationMs:t,pushPerSec:e.pushed/n,donePerSec:e.completed/n,mbPerSec:e.bytes/n,avg:r.length?r.reduce((e,t)=>e+t,0)/r.length:0,p50:M(r,50),p95:M(r,95),p99:M(r,99),max:r.length?r[r.length-1]:0,error:e.error}}function fe(e){let t=e.queue.trim(),n=j(t);return n?{ok:!1,error:n}:[`count`,`duration`].includes(e.mode)?typeof e.durable!=`boolean`||typeof e.removeOnComplete!=`boolean`?{ok:!1,error:`Benchmark job options are invalid.`}:{ok:!0,config:Object.freeze({...e,queue:t,total:A(e.total,1,w.total),durationS:A(e.durationS,1,w.durationS),batch:A(e.batch,1,w.batch),producers:A(e.producers,1,w.producers),payload:A(e.payload,0,w.payload),workers:A(e.workers,0,w.workers),workerBatch:A(e.workerBatch,1,w.workerBatch),processMs:A(e.processMs,0,w.processMs)})}:{ok:!1,error:`Benchmark mode is invalid.`}}async function pe(e,t,n){try{q(await e.overview(),`Dashboard preflight`)}catch(e){return{ok:!1,live:{...J,error:`Server unreachable — start it on the Server page first. (${I(e)})`}}}if(!n())return{ok:!0};try{let r=await e.counts(t);if(!n())return{ok:!0};if(q(r,`Queue-count preflight`),!r.counts||typeof r.counts!=`object`||Array.isArray(r.counts))throw Error(`Malformed queue-count response.`);let i=K(r.counts);if(i>0)return{ok:!1,live:{...J,error:`Dedicated benchmark queue "${t}" is not empty (${i} job(s) across all states). Clean it before running so no external work can be consumed or deleted.`}}}catch(e){return{ok:!1,live:{...J,error:`Could not verify that dedicated queue "${t}" is empty. (${I(e)})`}}}return{ok:!0}}function me(e){return async()=>{let{stats:t}=e;for(;e.shouldContinue();){let n;if(e.runConfig.mode===`count`){if(t.assigned>=e.total)break;n=Math.min(e.batch,e.total-t.assigned)}else{if(performance.now()>=e.deadline)break;n=e.batch}let r=t.assigned;t.assigned+=n;let i=N(r,n,e.blob,e.runConfig.durable,e.runConfig.removeOnComplete,e.runId,Math.max(12e4,e.processMs+6e4)),a=performance.now(),o=e.client.addJobsBulk(e.queue,i).then(r=>{if(!e.isCurrent())return;if(q(r,`Bulk enqueue`),!Array.isArray(r.ids)||r.ids.length!==n||r.ids.some(e=>typeof e!=`string`||e.length===0)||new Set(r.ids).size!==r.ids.length)throw Error(`Bulk enqueue returned invalid job IDs.`);let i=r.ids;for(let t of i)e.ownJobIds.add(t);t.pushed+=i.length,t.bytes+=i.length*e.payload;let a=Math.max(0,n-i.length);a>0&&(t.pushFailed+=a,t.error??=`Server created ${i.length} of ${n} jobs in a benchmark batch.`)}).catch(r=>{e.isCurrent()&&(t.pushFailed+=n,t.error??=I(r))});e.pendingPushes.add(o);try{await o}finally{e.pendingPushes.delete(o)}e.isCurrent()&&t.pushLat.push(performance.now()-a)}}}function he(e){return async()=>{let{stats:t}=e;for(;e.shouldContinue()&&!(e.runConfig.mode===`duration`&&performance.now()>=e.deadline||e.runConfig.mode===`count`&&t.completed>=e.total);){t.activeWorkers++;let n=[];try{if(n=await ge(e),!e.shouldContinue()){await X(e,n);break}n.length>0&&await _e(e,n)}finally{e.isCurrent()&&t.activeWorkers--}if(!e.shouldContinue())break;if(n.length===0){if(e.runConfig.mode===`count`&&e.producersDone()){e.ownJobIds.size>0&&(t.error??=`${e.ownJobIds.size} benchmark job(s) could not be drained.`);break}await F(50,e.shouldContinue)}}}}async function ge(e){try{let t=await e.client.pullBatch(e.queue,e.workerBatch,`dashboard-benchmark:${e.runId}`);if(t?.ok!==!0||!Array.isArray(t.jobs)||t.jobs.length>e.workerBatch||t.jobs.some(e=>!e||typeof e!=`object`||typeof e.id!=`string`||e.id.length===0)||!Array.isArray(t.tokens)||t.tokens.length!==t.jobs.length||t.tokens.some(e=>typeof e!=`string`||e.length===0)||new Set(t.jobs.map(e=>e.id)).size!==t.jobs.length)throw Error(`Malformed pull-batch response.`);let n=t.jobs.map((e,n)=>({id:e.id,token:t.tokens?.[n]}));return e.shouldContinue()?n:(await X(e,n),[])}catch(t){return e.isCurrent()&&(e.stats.error??=I(t)),[]}}async function _e(e,t){if(t.some(t=>!e.ownJobIds.has(t.id))&&e.pendingPushes.size>0&&await Promise.allSettled([...e.pendingPushes]),!e.shouldContinue())return X(e,t);let n=t.filter(t=>e.ownJobIds.has(t.id)),r=t.filter(t=>!e.ownJobIds.has(t.id));if(r.length>0){await ve(e,t,r.length);return}if(n.length===0)return;if(!e.shouldContinue()||(e.processMs>0&&await F(e.processMs,e.shouldContinue),!e.shouldContinue()))return X(e,n);let i=!1;try{let t=n.map(e=>e.id),r=n.map(e=>e.token),a=await e.client.heartbeatBatch(t,r);q(a,`Heartbeat benchmark jobs`);let o=a.data;if(!o||typeof o!=`object`||o.ok!==!0||!Number.isSafeInteger(o.count)||o.count!==n.length)throw Error(`Heartbeat confirmed ${String(o?.count)} of ${n.length} benchmark jobs; refusing a lossy batch ACK.`);if(!e.shouldContinue())return X(e,n);if(i=!0,q(await e.client.ackBatch(t,r),`Acknowledge benchmark jobs`),!e.isCurrent())return;e.stats.completed+=n.length;for(let t of n)e.ownJobIds.delete(t.id)}catch(t){e.isCurrent()&&(e.stats.ackFailed+=n.length,e.stats.error??=I(t)),i||await X(e,n)}}async function ve(e,t,n){let r=await Z(e,t);e.isCurrent()&&(e.stats.error??=r>0?`Detected ${n} foreign job(s); ${r} pulled job(s) could not be returned to waiting. Benchmark stopped.`:`Detected ${n} foreign job(s); returned the complete pulled batch to waiting and stopped without counting it.`),e.stop()}async function X(e,t){if(t.length===0)return;let n=await Z(e,t);n>0&&e.isCurrent()&&(e.stats.error??=`${n} interrupted benchmark job(s) could not be returned to waiting.`)}async function Z(e,t){return(await Promise.allSettled(t.map(t=>e.compensationQueue.run(async()=>{q(await e.client.retryJob(t.id,t.token),`Restore job ${t.id}`),e.ownJobIds.delete(t.id)})))).filter(e=>e.status===`rejected`).length}var ye=0;async function be(e,t,n){if(e.runningRef.current||!e.mountedRef.current)return;e.runningRef.current=!0;let r=++e.runGenerationRef.current,i=n??a.createServerTargetClient(a.captureServerRequestTarget()),o=()=>e.mountedRef.current&&e.runGenerationRef.current===r,s=()=>o()&&!e.stopRef.current;e.stopRef.current=!1;try{e.setSummary(null);let n=fe(t);if(!n.ok){e.setLive({...J,error:n.error}),Q(e,`error`);return}let r=n.config,a=await pe(i,r.queue,s);if(!o())return;if(!a.ok){e.setLive(a.live),Q(e,`error`);return}if(!s())return;e.producersDoneRef.current=!1,e.configRef.current=r,e.setRunConfig(r);let c=Y();c.startedAt=performance.now(),c.lastAt=performance.now(),e.statsRef.current=c,e.setLive({...J}),Q(e,`running`);let l={batch:r.batch,blob:`x`.repeat(r.payload),client:i,compensationQueue:ce(),deadline:performance.now()+r.durationS*1e3,isCurrent:o,ownJobIds:new Set,payload:r.payload,pendingPushes:new Set,processMs:r.processMs,producersDone:()=>e.producersDoneRef.current,queue:r.queue,runConfig:r,runId:`bqbench-${globalThis.crypto.randomUUID()}`,shouldContinue:s,stats:c,stop:()=>{e.stopRef.current=!0},total:r.total,workerBatch:r.workerBatch},u=me(l),d=he(l),f=Array.from({length:r.producers},u),p=Array.from({length:r.workers},d),m=Promise.all(f).then(()=>{o()&&(e.producersDoneRef.current=!0,s()&&r.mode===`count`&&r.workers>0&&e.phaseRef.current===`running`&&Q(e,`draining`))});if(await Promise.all([m,...p]),!o())return;xe(e,r,c)}finally{e.runningRef.current=!1}}function xe(e,t,n){let r=performance.now()-n.startedAt,i=de(n,r);e.setSummary(i),e.setLive(e=>({...e,pushed:n.pushed,completed:n.completed,pushFailed:n.pushFailed,ackFailed:n.ackFailed,bytes:n.bytes,elapsedMs:r,pushPerSec:0,donePerSec:0,activeWorkers:0,etaMs:0,error:n.error})),e.setHistory(e=>[{...i,id:++ye,at:Date.now(),mode:t.mode,producers:t.producers,workers:t.workers},...e].slice(0,12)),Q(e,e.stopRef.current?`stopped`:n.error?`error`:`done`)}function Q(e,t){e.phaseRef.current=t,e.setPhase(t)}function Se(){let[e,t]=(0,C.useState)(`idle`),[n,r]=(0,C.useState)(J),[i,a]=(0,C.useState)(null),[o,s]=(0,C.useState)([]),[c,l]=(0,C.useState)(null),u=(0,C.useRef)(!1),d=(0,C.useRef)(!0),f=(0,C.useRef)(0),p=(0,C.useRef)(!1),m=(0,C.useRef)(!1),h=(0,C.useRef)(null),g=(0,C.useRef)(`idle`),_=(0,C.useRef)(Y());return g.current=e,(0,C.useEffect)(()=>{let e=d,t=u,n=f;return d.current=!0,()=>{e.current=!1,t.current=!0,n.current++}},[]),(0,C.useEffect)(()=>{if(e!==`running`&&e!==`draining`)return;let t=setInterval(()=>{r(ue(_.current,h.current,g.current,performance.now()))},200);return()=>clearInterval(t)},[e]),{phase:e,live:n,summary:i,history:o,runCfg:c,run:(e,n)=>be({configRef:h,mountedRef:d,phaseRef:g,producersDoneRef:m,runningRef:p,runGenerationRef:f,setHistory:s,setLive:r,setPhase:t,setRunConfig:l,setSummary:a,statsRef:_,stopRef:u},e,n),stop:()=>{u.current=!0,(g.current===`running`||g.current===`draining`)&&(g.current=`stopping`,t(`stopping`))},reset:()=>{u.current=!0,f.current++,g.current=`idle`,a(null),r(J),t(`idle`)},clearHistory:()=>s([])}}function Ce({benchmark:e,counts:t,dedicatedQueue:n,draft:r,pollQueue:i,runTarget:a,setRunTarget:o}){let s=(0,C.useRef)(null),c=(0,C.useRef)(!0),[l,u]=(0,C.useState)(!1),[d,f]=(0,C.useState)(null);(0,C.useEffect)(()=>(c.current=!0,()=>{c.current=!1,s.current=null}),[]);let p=async()=>{let e=r.queue.trim();if(s.current||!e)return;if(!O(e)||e!==n){f({error:`refusing to clean a queue not owned by this dashboard session`});return}s.current=`clean`;let o=a??$(),l=t&&i===e?` Currently ${h(t.waiting??0)} waiting / ${h(t.completed??0)} completed.`:``;if(!window.confirm(`Clean every cleanable job from the dedicated dashboard benchmark queue "${e}" on ${o.target.baseUrl}? This is queue-wide; the cryptographic queue name is owned by this browser session.${l}`)){s.current=null;return}u(!0),f(null);try{let t=null;for(let n of z){if(!c.current)return;try{let t=await o.client.clean(e,{state:n,limit:w.total});if(q(t,`Clean ${n} jobs`),!Number.isSafeInteger(t.count)||t.count<0)throw Error(`Clean ${n} jobs returned an invalid count.`)}catch(e){t=e.message}}if(!c.current)return;let n=await o.client.counts(e).catch(e=>(t=e.message,null));if(!c.current)return;if(!n)f({error:t??`server unreachable — could not verify`});else if(n.ok!==!0||!n.counts||typeof n.counts!=`object`||Array.isArray(n.counts))f({error:`server returned a malformed queue-count response`});else try{let e=K(n.counts);f(t||e>0?{error:t??`${h(e)} job(s) still present`}:{remaining:0})}catch{f({error:`server returned malformed queue counts`})}}finally{c.current&&u(!1),s.current===`clean`&&(s.current=null)}},m=()=>{if(s.current)return;s.current=`run`;let n=V(r),c=n.queue.trim(),l=$();if(j(c)){e.run({...n,queue:c},l.client).finally(g);return}let u=0;if(!a&&t&&i===c)try{u=K(t)}catch{}let d=u>0?` Warning: it already holds ${h(u)} job(s) across all states; the safety preflight will refuse the run until the dedicated queue is empty.`:``;if(!window.confirm(`Enqueue real jobs into "${c}" on ${l.target.baseUrl}?${d}`)){s.current=null;return}o(l),e.run(n,l.client).finally(g)},g=()=>{s.current===`run`&&(s.current=null)};return{cleaning:l,cleanResult:d,cleanup:p,start:m}}function $(){let e=a.captureServerRequestTarget();return{target:e,client:a.createServerTargetClient(e)}}function we(){let[e]=(0,C.useState)(ee),[t,n]=(0,C.useState)(()=>ne({...T,queue:e})),r=Se(),u=i(e=>e.baseUrl),[d,p]=(0,C.useState)(null),m=(e,t)=>n(n=>({...n,[e]:t})),h=e=>t=>m(e,t.target.value),g=e=>n(t=>{let n={...t};for(let[t,r]of Object.entries(e))n[t]=te.includes(t)?String(r):r;return n}),[_,v]=(0,C.useState)(e);(0,C.useEffect)(()=>{let e=setTimeout(()=>v(t.queue.trim()||T.queue),400);return()=>clearTimeout(e)},[t.queue]);let y=d?.target.baseUrl??u,{data:b,error:x}=c(()=>d?d.client.counts(_):a.counts(_),[_,d],{intervalMs:1e3}),S=b?.counts??null,E=Ce({benchmark:r,counts:S,dedicatedQueue:e,draft:t,pollQueue:_,runTarget:d,setRunTarget:p}),{phase:D,live:O,summary:j,history:M}=r,N=D===`running`||D===`draining`||D===`stopping`,P=r.runCfg??V(t),F=A(P.total,1,w.total),I=A(P.workers,0,w.workers),L=P.mode===`count`&&(N||j)?Math.min(100,O.pushed/F*100):0,R=P.mode===`count`&&(N||j)?Math.min(100,O.completed/F*100):0,z=P.mode===`duration`?Math.min(100,O.elapsedMs/(A(P.durationS,1,w.durationS)*1e3)*100):0,B=Te(D,P.mode,!!j),U=Ee(D,O.etaMs);return(0,H.jsxs)(`div`,{children:[(0,H.jsx)(f,{title:`Benchmark`,description:`Drive real load against the server — producers bulk-enqueue jobs while simulated workers pull, process and ack them. Throughput is measured client-side; the queue genuinely fills and drains.`,actions:D===`stopping`?(0,H.jsxs)(l,{variant:`warning`,size:`sm`,disabled:!0,children:[(0,H.jsx)(o,{className:`size-3.5`}),` Stopping…`]}):N?(0,H.jsxs)(l,{variant:`warning`,size:`sm`,onClick:r.stop,children:[(0,H.jsx)(o,{className:`size-3.5`}),` Stop`]}):(0,H.jsxs)(l,{variant:`success`,size:`sm`,disabled:E.cleaning,onClick:E.start,children:[(0,H.jsx)(s,{className:`size-3.5`}),` Run benchmark`]})}),O.error&&(0,H.jsx)(`div`,{className:`mb-4 rounded-lg border border-red-500/20 bg-red-500/5 px-4 py-2 text-sm text-danger`,children:O.error}),(0,H.jsxs)(`div`,{className:`mb-4 flex flex-wrap items-center gap-2`,children:[(0,H.jsx)(`span`,{className:`text-xs font-medium uppercase tracking-wider text-faint`,children:`Presets`}),Object.keys(k).map(e=>(0,H.jsx)(`button`,{type:`button`,disabled:N,onClick:()=>g(k[e]),className:`rounded-md border border-line bg-surface-2 px-2.5 py-1 text-xs text-muted transition-colors hover:border-line-strong hover:text-fg disabled:opacity-40`,children:e},e))]}),(0,H.jsxs)(`div`,{className:`grid grid-cols-1 gap-6 lg:grid-cols-3`,children:[(0,H.jsx)(re,{draft:t,active:N,cleaning:E.cleaning,cleanResult:E.cleanResult,setField:m,numberInput:h,onClean:()=>void E.cleanup()}),(0,H.jsx)(ie,{heading:B,pollBaseUrl:y,etaText:U,shown:P,shownWorkers:I,producePct:L,drainPct:R,durationPct:z,live:O,summary:j})]}),(0,H.jsx)(ae,{summary:j}),(0,H.jsx)(oe,{pollQueue:_,pollBaseUrl:y,pinned:!!d,counts:S,error:x}),(0,H.jsx)(se,{history:M,onClear:r.clearHistory})]})}function Te(e,t,n){return e===`running`?t===`duration`?`Running…`:`Producing…`:e===`draining`?`Draining…`:e===`stopping`?`Stopping…`:e===`error`?`Error`:n?`Result`:`Ready`}function Ee(e,t){return(e===`running`||e===`draining`)&&t!=null?t>0?`ETA ${m(t)}`:`finishing…`:e===`stopped`?`stopped early`:e===`done`?`complete`:``}export{we as Benchmark};
@@ -1,5 +0,0 @@
1
- import{i as e}from"./cn-zFEt5SO8.js";import{g as t,t as n}from"./react-vendor-CvwrP0tU.js";import{i as r}from"./upstreamPaths-DQeKSxGc.js";import{r as i,t as a}from"./bq-F5BbTG1T.js";import{t as o}from"./toastStore-IZn6OPLZ.js";import{n as s}from"./usePolledData-0CVelkl2.js";import{t as c}from"./Button-Ch1W94fJ.js";import{n as l,t as u}from"./Card-CWxrC8OY.js";import{t as d}from"./PageHeader-CvIyaFOO.js";import{i as f,n as p,t as m}from"./form-C4oby9pI.js";import{r as h}from"./useServerActionGuard-uGePilQQ.js";import{d as g,g as _,o as v,p as y,t as b,u as x}from"./responses-BkR2JTH9.js";var S=e(t(),1),C=1e4,w=365*24*60*60*1e3,T=1440*60*1e3,E=64*1024*1024,D=64*1024*1024,O=`[
2
- { "data": { "to": "a@example.com", "template": "welcome" }, "groupId": "tenant-a", "priority": 1, "groupMaxSize": 100 },
3
- { "data": { "to": "b@example.com", "template": "welcome" }, "groupId": "tenant-b" }
4
- ]`,k=n();function A({values:e,setValue:t,parsedCount:n,parseError:r,validation:i,onFile:a,onBeforeTextChange:o}){return(0,k.jsxs)(u,{className:`lg:col-span-2`,children:[(0,k.jsx)(l,{title:`Jobs`,action:(0,k.jsxs)(`label`,{className:`cursor-pointer rounded-lg border border-line px-3 py-1.5 text-xs text-muted hover:bg-surface-2 hover:text-fg`,children:[`Upload file`,(0,k.jsx)(`input`,{type:`file`,name:`jobs-file`,accept:`.json,.ndjson,.txt,application/json`,onChange:a,className:`hidden`})]})}),(0,k.jsx)(`textarea`,{name:`jobs-json`,value:e.text,onChange:e=>{o(),t(`text`,e.target.value)},maxLength:E,spellCheck:!1,rows:16,placeholder:O,"aria-label":`Jobs JSON`,className:`w-full rounded-lg border border-line bg-surface-2 p-3 font-mono text-xs text-fg placeholder:text-faint focus:border-accent/60 focus:outline-none focus:ring-2 focus:ring-accent/30`}),(0,k.jsxs)(`fieldset`,{className:`mt-3 flex flex-wrap items-center gap-3`,children:[(0,k.jsx)(`legend`,{className:`sr-only`,children:`Import interpretation`}),(0,k.jsx)(`span`,{"aria-hidden":`true`,className:`text-xs font-medium text-faint`,children:`Interpret each item as`}),(0,k.jsx)(f,{options:[`spec`,`raw`],value:e.mode,onChange:e=>t(`mode`,e)}),(0,k.jsx)(`span`,{className:`text-xs text-faint`,children:e.mode===`spec`?`job spec: an object with a "data" key sets options from its sibling fields.`:`raw data: the whole item becomes the job payload, untouched.`})]}),(0,k.jsx)(`p`,{className:`mt-2 text-xs text-faint`,children:`Accepts a JSON array, a single object, or newline-delimited JSON.`}),(0,k.jsx)(`div`,{className:`mt-2 text-sm`,children:r?(0,k.jsx)(`span`,{role:`alert`,className:`text-danger`,children:r}):n>1e4?(0,k.jsxs)(`span`,{role:`alert`,className:`text-danger`,children:[`Too many jobs (`,n,`). Limit is `,C,`.`]}):n>0?(0,k.jsxs)(`span`,{className:`text-success`,children:[n,` job(s) parsed ✓`]}):(0,k.jsx)(`span`,{className:`text-faint`,children:`Nothing parsed yet.`})}),!r&&n>0&&!i.ok&&(0,k.jsxs)(`p`,{role:`alert`,className:`mt-2 text-xs text-danger`,children:[`Import blocked — `,i.msg,`. Fix the spec or switch to “raw” when the whole object is job data.`]})]})}function j({values:e,setValue:t,queueNames:n,queueDiscoveryError:r,onRetryQueues:i,busy:a,parsedCount:o,parseError:s,validationOk:d,result:f,onSubmit:h}){return(0,k.jsxs)(u,{children:[(0,k.jsx)(l,{title:`Target & defaults`}),(0,k.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,k.jsxs)(m,{label:`Queue`,children:[(0,k.jsx)(p,{list:`bulk-queue-options`,"aria-label":`Queue`,name:`bulk-target-queue`,autoComplete:`off`,value:e.queue,onChange:e=>t(`queue`,e.target.value),placeholder:`queue name (existing or new)`}),(0,k.jsx)(`datalist`,{id:`bulk-queue-options`,children:n.map(e=>(0,k.jsx)(`option`,{value:e},e))})]}),r&&(0,k.jsxs)(`p`,{role:`status`,className:`-mt-2 text-xs text-warning`,children:[`Existing queue suggestions unavailable — `,r.message,`. You can still enter a queue name manually.`,` `,(0,k.jsx)(`button`,{type:`button`,onClick:i,className:`font-medium underline underline-offset-2 hover:text-fg`,children:`Retry`})]}),(0,k.jsx)(`p`,{className:`-mt-2 text-xs text-faint`,children:`Defaults below fill any field an item omits.`}),(0,k.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,k.jsx)(M,{label:`Priority`,name:`default-priority`,value:e.priority,min:-1e6,max:1e6,placeholder:`0`,onChange:e=>t(`priority`,e)}),(0,k.jsx)(M,{label:`Max attempts`,name:`default-max-attempts`,value:e.maxAttempts,min:1,max:1e3,placeholder:`3`,onChange:e=>t(`maxAttempts`,e)}),(0,k.jsx)(M,{label:`Backoff (ms)`,name:`default-backoff`,value:e.backoff,min:0,max:T,placeholder:`1000`,onChange:e=>t(`backoff`,e)}),(0,k.jsx)(M,{label:`Timeout (ms)`,name:`default-timeout`,value:e.timeout,min:0,max:T,placeholder:`—`,onChange:e=>t(`timeout`,e)})]}),(0,k.jsx)(`div`,{className:`flex items-center gap-3`,children:(0,k.jsx)(c,{variant:`accent`,disabled:a||o===0||o>1e4||s!=null||!d,onClick:h,children:a?`Importing…`:`Import ${o||``}`})}),f&&(0,k.jsx)(`span`,{role:f.ok?`status`:`alert`,className:f.ok?`text-sm text-success`:`text-sm text-danger`,children:f.msg})]})]})}function M({label:e,name:t,value:n,min:r,max:i,placeholder:a,onChange:o}){return(0,k.jsx)(m,{label:e,children:(0,k.jsx)(p,{type:`number`,min:r,max:i,step:1,name:t,value:n,onChange:e=>o(e.target.value),placeholder:a})})}function N(e){return e.length>67108864||_(e,67108864)>67108864?`Import text exceeds the 64 MiB UTF-8 safety limit`:null}function P(e,t=D){return i(e,t)}function F(e,t,n){return{ok:!0,msg:`Accepted ${t} job submission${t===1?``:`s`} in ${n}; server returned ${e} distinct job ID${e===1?``:`s`} (deduplication may reuse existing jobs)`}}function I(e){let t=N(e);if(t)return{items:[],error:t};let n=e.trim();if(!n)return{items:[],error:null};try{let e=JSON.parse(n);return{items:Array.isArray(e)?e:[e],error:null}}catch{}let r=[],i=e.split(`
5
- `);for(let e=0;e<i.length;e++){let t=i[e].trim();if(t)try{r.push(JSON.parse(t))}catch(t){return{items:[],error:`Line ${e+1}: ${t.message}`}}}return{items:r,error:null}}function L({text:e,setText:t,setResult:n}){let r=(0,S.useRef)(null),i=(0,S.useRef)(0),a=(0,S.useCallback)(()=>{i.current+=1;let e=r.current;r.current=null;try{e?.abort()}catch{}},[]);return(0,S.useEffect)(()=>a,[a]),{cancelPending:a,onFile:a=>{let o=a.currentTarget,s=o.files?.[0];if(!s)return;let c=i.current+1;i.current=c;let l=r.current;r.current=null;try{l?.abort()}catch{}if(o.value=``,!Number.isSafeInteger(s.size)||s.size<0||s.size>67108864){n({ok:!1,msg:`File is too large; maximum is 64 MiB (${s.name})`});return}if(e.trim()!==``&&!window.confirm(`Replace the current input with "${s.name}"?`))return;let u=new FileReader,d=()=>i.current===c&&r.current===u,f=()=>{d()&&(r.current=null)};u.onload=()=>{if(!d())return;if(typeof u.result!=`string`){n({ok:!1,msg:`Could not decode file as text (${s.name})`}),f();return}let e=N(u.result);e?n({ok:!1,msg:`${e} (${s.name})`}):(t(u.result),n(null)),f()},u.onerror=()=>{d()&&(n({ok:!1,msg:`Could not read file (${s.name})`}),f())},u.onabort=f,r.current=u,n(null);try{u.readAsText(s)}catch{if(!d())return;n({ok:!1,msg:`Could not read file (${s.name})`}),f()}}}}var R=e=>{let t=typeof e==`string`&&e.trim()!==``?Number(e.trim()):e;return typeof t==`number`&&Number.isFinite(t)?t:void 0},z=e=>typeof e==`boolean`?e:void 0,B=e=>typeof e==`string`?e.trim()===``?void 0:e.trim():typeof e==`number`&&Number.isFinite(e)?String(e):void 0,V=e=>{if(!Array.isArray(e))return;let t=e.map(B);return t.every(e=>e!==void 0)?t:void 0},H=e=>typeof e==`object`&&e&&!Array.isArray(e)?e:void 0,U=e=>{let t=R(e);if(t!==void 0)return t;let n=H(e);if(!n||n.type!==`fixed`&&n.type!==`exponential`||Object.keys(n).some(e=>e!==`type`&&e!==`delay`))return;let r=R(n.delay);return r===void 0?void 0:{type:n.type,delay:r}},W=e=>{let t=H(e);if(!t)return;let n=x(JSON.stringify(t));return n.ok?n.repeat:void 0};function G(e){let t=H(e);if(!t)return{ok:!1,msg:`must be an object`};let n=new Set([`ttl`,`extend`,`replace`]),r=Object.keys(t).filter(e=>!n.has(e));if(r.length)return{ok:!1,msg:`has unknown option(s): ${r.join(`, `)}`};if(t.ttl!==void 0&&(!Number.isSafeInteger(t.ttl)||t.ttl<=0||t.ttl>31536e6))return{ok:!1,msg:`ttl must be a whole number from 1 to ${w} ms`};for(let e of[`extend`,`replace`])if(t[e]!==void 0&&typeof t[e]!=`boolean`)return{ok:!1,msg:`${e} must be a boolean`};return t.extend===!0&&t.replace===!0?{ok:!1,msg:`extend and replace are mutually exclusive`}:t.extend===!0&&t.ttl===void 0?{ok:!1,msg:`extend requires ttl`}:{ok:!0,dedup:{...typeof t.ttl==`number`?{ttl:t.ttl}:{},...typeof t.extend==`boolean`?{extend:t.extend}:{},...typeof t.replace==`boolean`?{replace:t.replace}:{}}}}var K=e=>{let t=G(e);return t.ok?t.dedup:void 0};function q(e,t,n){if(n===`spec`&&e&&typeof e==`object`&&!Array.isArray(e)&&`data`in e){let n=e;return{name:B(n.name),data:n.data,priority:R(n.priority)??t.priority,delay:R(n.delay),maxAttempts:R(n.maxAttempts)??t.maxAttempts,backoff:U(n.backoff)??t.backoff,timeout:R(n.timeout)??t.timeout,jobId:B(n.jobId)??B(n.customId),removeOnComplete:z(n.removeOnComplete),removeOnFail:z(n.removeOnFail),durable:z(n.durable),lifo:z(n.lifo),ttl:R(n.ttl),uniqueKey:B(n.uniqueKey),tags:V(n.tags),groupId:B(n.groupId),groupMaxSize:R(n.groupMaxSize),dependsOn:V(n.dependsOn),stallTimeout:R(n.stallTimeout),repeat:W(n.repeat),dedup:K(n.dedup),stackTraceLimit:R(n.stackTraceLimit),timestamp:R(n.timestamp)}}return{data:e,...t}}var J=new Set([`name`,`data`,`priority`,`delay`,`maxAttempts`,`backoff`,`timeout`,`jobId`,`customId`,`removeOnComplete`,`removeOnFail`,`durable`,`lifo`,`ttl`,`uniqueKey`,`tags`,`groupId`,`groupMaxSize`,`dependsOn`,`stallTimeout`,`repeat`,`dedup`,`stackTraceLimit`,`timestamp`]);function Y(e,t,n,r){if(e===void 0)return null;let i=R(e);return i===void 0?`${t} must be a number`:Number.isSafeInteger(i)?i<n||i>r?`${t} must be between ${n} and ${r}`:null:`${t} must be a whole, safe integer`}function X(e){let t={priority:e.priority.trim()||void 0,maxAttempts:e.maxAttempts.trim()||void 0,backoff:e.backoff.trim()||void 0,timeout:e.timeout.trim()||void 0},n=[[`priority`,`Default priority`,-1e6,1e6],[`maxAttempts`,`Default max attempts`,1,1e3],[`backoff`,`Default backoff`,0,T],[`timeout`,`Default timeout`,0,T]],r={};for(let[e,i,a,o]of n){let n=Y(t[e],i,a,o);if(n)return{ok:!1,msg:n};let s=R(t[e]);s!==void 0&&(r[e]=s)}return{ok:!0,defaults:r}}function Z(e){let t=Object.keys(e).filter(e=>!J.has(e));if(t.length)return`unknown job option(s): ${t.join(`, `)}`;let n=[[`delay`,`delay`,0,w],[`maxAttempts`,`maxAttempts`,1,1e3],[`timeout`,`timeout`,0,T],[`ttl`,`ttl`,0,w],[`stallTimeout`,`stallTimeout`,0,T],[`stackTraceLimit`,`stackTraceLimit`,0,1e4],[`timestamp`,`timestamp`,0,2**53-1],[`groupMaxSize`,`groupMaxSize`,1,2**53-1]];for(let[t,r,i,a]of n){let n=Y(e[t],r,i,a);if(n)return n}if(e.backoff!==void 0){let t=U(e.backoff);if(t===void 0)return`backoff must be a number or {"type":"fixed|exponential","delay":number} with no extra keys`;let n=Y(typeof t==`number`?t:t.delay,`backoff delay`,0,T);if(n)return n}if(e.repeat!==void 0){let t=H(e.repeat);if(!t)return`repeat must be an object`;let n=x(JSON.stringify(t));if(!n.ok)return n.msg}if(e.dedup!==void 0){let t=G(e.dedup);if(!t.ok)return`dedup ${t.msg}`;if(B(e.uniqueKey)===void 0)return`dedup requires a non-empty uniqueKey`}for(let t of[`removeOnComplete`,`removeOnFail`,`durable`,`lifo`])if(e[t]!==void 0&&typeof e[t]!=`boolean`)return`${t} must be a boolean`;for(let t of[`jobId`,`customId`,`uniqueKey`]){let n=e[t];if(n===void 0)continue;let i=B(n);if(i===void 0||i.length>1024)return`${t} must be a non-empty string (or finite numeric id) of at most 1024 characters`;if((t===`jobId`||t===`customId`)&&r(i))return`${t}: ${r(i)}`}let i=B(e.jobId),a=B(e.customId);if(i!==void 0&&a!==void 0&&i!==a)return`jobId and customId disagree; provide only one spelling`;if(e.groupId!==void 0){let t=B(e.groupId);if(t===void 0||t.length>256||t.includes(`\0`))return`groupId must be a non-empty string of at most 256 characters without NUL`}if(e.groupMaxSize!==void 0&&e.groupId===void 0)return`groupMaxSize requires groupId`;let o=Y(e.priority,e.groupId===void 0?`priority`:`group priority`,e.groupId===void 0?-1e6:0,e.groupId===void 0?1e6:v);if(o)return o;for(let t of[`tags`,`dependsOn`]){if(e[t]===void 0)continue;let n=V(e[t]),i=t===`tags`?256:1024;if(!n||n.some(e=>e.length>i))return`${t} must be an array of non-empty strings of at most ${i} characters`;if(t===`dependsOn`){let e=n.find(e=>r(e));if(e)return`dependsOn ID "${e}": ${r(e)}`}}return null}function Q(e){let t=new Map;for(let n of e)if(n.jobId){if(t.has(n.jobId))return`duplicate jobId/customId "${n.jobId}" makes dependency resolution ambiguous`;t.set(n.jobId,n)}let n=new Set,r=new Set,i=e=>{if(n.has(e))return!0;if(r.has(e))return!1;n.add(e);for(let n of t.get(e)?.dependsOn??[])if(t.has(n)&&i(n))return!0;return n.delete(e),r.add(e),!1};for(let e of t.keys())if(i(e))return`dependency cycle detected in this batch (involving "${e}")`;return null}function $(e,t,n){if(e.length>1e4)return{ok:!1,msg:`Too many jobs (${e.length}). Limit is ${C}.`};let r=[],i=0;for(let a=0;a<e.length;a++){let o=e[a];if(n===`spec`&&ee(o)){let e=Z(o);if(e)return{ok:!1,msg:`Job ${a+1}: ${e}`}}let s=q(o,t,n);if(s.groupId!==void 0){let e=Y(s.priority,`group priority`,0,v);if(e)return{ok:!1,msg:`Job ${a+1}: ${e}`}}if(s.groupMaxSize!==void 0&&s.groupId===void 0)return{ok:!1,msg:`Job ${a+1}: groupMaxSize requires groupId`};let c;try{c=JSON.stringify(s.data)}catch{return{ok:!1,msg:`Job ${a+1}: data must be JSON serializable`}}if(c===void 0)return{ok:!1,msg:`Job ${a+1}: data must be JSON serializable`};if(c.length>10485760)return{ok:!1,msg:`Job ${a+1}: data exceeds the 10 MiB UTF-8 limit`};let l=_(c,y);if(l>10485760)return{ok:!1,msg:`Job ${a+1}: data exceeds the 10 MiB UTF-8 limit`};if(i+=l,i>67108864)return{ok:!1,msg:`Combined job data exceeds the 64 MiB import safety limit`};r.push(s)}let a=Q(r);return a?{ok:!1,msg:a}:{ok:!0,bodies:r}}var ee=e=>typeof e==`object`&&!!e&&!Array.isArray(e)&&`data`in e,te={queue:``,text:``,mode:`spec`,priority:``,maxAttempts:``,backoff:``,timeout:``};function ne(){let{data:e,error:t,refetch:n}=s(()=>a.queues(),[],{intervalMs:3e4}),[r,i]=(0,S.useState)(te),[c,l]=(0,S.useState)(!1),[u,f]=(0,S.useState)(null),p=h(`bulk-add-jobs`),m=(e,t)=>i(n=>({...n,[e]:t})),_=L({text:r.text,setText:e=>m(`text`,e),setResult:f});(0,S.useEffect)(()=>{l(!1),f(null)},[p.scopeKey]);let v=(0,S.useMemo)(()=>I(r.text),[r.text]),y=(0,S.useMemo)(()=>{if(v.error)return{ok:!1,msg:v.error};if(v.items.length>1e4)return{ok:!1,msg:`Too many jobs (${v.items.length}). Limit is ${C}.`};let e=X(r);return e.ok?$(v.items,e.defaults,r.mode):e},[v,r]),x=async()=>{f(null);let e=r.queue.trim(),t=g(e);if(t){f({ok:!1,msg:t});return}let n=N(r.text);if(n){f({ok:!1,msg:n});return}if(v.error){f({ok:!1,msg:v.error});return}if(v.items.length===0){f({ok:!1,msg:`Nothing to enqueue — paste a JSON array or NDJSON.`});return}if(v.items.length>1e4){f({ok:!1,msg:`Too many jobs (${v.items.length}). Limit is ${C}.`});return}if(!y.ok){f({ok:!1,msg:y.msg});return}let i=P(y.bodies);if(i){f({ok:!1,msg:i});return}let s=p.begin();if(s){l(!0);try{let t=b(await a.addJobsBulk(e,y.bodies),y.bodies.length);if(!s.isCurrent())return;let n=F(new Set(t).size,y.bodies.length,e);f(n),o.success(n.msg)}catch(e){if(!s.isCurrent())return;let t=e.message;f({ok:!1,msg:t}),o.error(`Bulk import failed`,t)}finally{s.finish()&&l(!1)}}};return(0,k.jsxs)(`div`,{children:[(0,k.jsx)(d,{title:`Bulk Add Jobs`,description:`Import many distinct jobs at once from a JSON array, NDJSON, or a file.`}),(0,k.jsxs)(`div`,{className:`grid grid-cols-1 gap-6 lg:grid-cols-3`,children:[(0,k.jsx)(A,{values:r,setValue:m,parsedCount:v.items.length,parseError:v.error,validation:y,onFile:_.onFile,onBeforeTextChange:_.cancelPending}),(0,k.jsx)(j,{values:r,setValue:m,queueNames:(e?.queues??[]).map(e=>e.name),queueDiscoveryError:t,onRetryQueues:()=>void n(),busy:c,parsedCount:v.items.length,parseError:v.error,validationOk:y.ok,result:u,onSubmit:()=>void x()})]})]})}export{ne as BulkAddJobs};
@@ -1 +0,0 @@
1
- import{t as e}from"./cn-zFEt5SO8.js";import{t}from"./react-vendor-CvwrP0tU.js";var n=t(),r={default:`border border-line bg-surface-2 text-fg hover:bg-surface-2/70 hover:border-line-strong`,ghost:`text-muted hover:text-fg hover:bg-surface-2`,accent:`bg-accent text-accent-fg hover:opacity-90 border border-transparent`,danger:`border border-red-500/30 text-danger hover:bg-red-500/10 hover:border-red-500/50`,warning:`border border-amber-500/30 text-warning hover:bg-amber-500/10 hover:border-amber-500/50`,success:`bg-emerald-500 text-white hover:bg-emerald-500/90 border border-transparent`},i={sm:`h-8 px-3 text-xs gap-1.5`,md:`h-9 px-4 text-sm gap-2`};function a({children:t,variant:a=`default`,size:o=`md`,className:s,...c}){return(0,n.jsx)(`button`,{type:`button`,className:e(`inline-flex items-center justify-center rounded-lg font-medium transition-colors`,`focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent`,`disabled:cursor-not-allowed disabled:opacity-50`,r[a],i[o],s),...c,children:t})}function o({children:t,className:i,variant:a=`ghost`,...o}){return(0,n.jsx)(`button`,{type:`button`,className:e(`inline-flex size-8 items-center justify-center rounded-lg transition-colors`,`focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent`,`disabled:cursor-not-allowed disabled:opacity-50`,r[a],i),...o,children:t})}export{o as n,a as t};
@@ -1 +0,0 @@
1
- import{t as e}from"./cn-zFEt5SO8.js";import{t}from"./react-vendor-CvwrP0tU.js";var n=t();function r({children:t,className:r,padded:i=!0}){return(0,n.jsx)(`div`,{className:e(`rounded-xl border border-line bg-surface`,i&&`p-5`,r),children:t})}function i({title:e,action:t,icon:r,headingLevel:i=2}){return(0,n.jsxs)(`div`,{className:`mb-4 flex items-center justify-between gap-3`,children:[(0,n.jsxs)(`h${i}`,{className:`flex items-center gap-2 text-base font-semibold text-fg`,children:[r,e]}),t]})}export{i as n,r as t};