bunqueue-dashboard 0.0.35 → 0.0.37

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 (176) hide show
  1. package/README.md +36 -16
  2. package/agent/backup/processExecutor.ts +90 -0
  3. package/agent/backup/resultValidation.ts +85 -0
  4. package/agent/backup/routes.ts +161 -0
  5. package/agent/backup/runner.ts +91 -0
  6. package/agent/backup/standaloneWorker.ts +23 -0
  7. package/agent/backup/workerExecutor.ts +40 -0
  8. package/agent/backup/workerFactory.ts +21 -0
  9. package/agent/backup/workerProtocol.ts +10 -0
  10. package/agent/db/core.ts +141 -0
  11. package/agent/db/csvExport.ts +172 -0
  12. package/agent/db/exportTimeout.ts +190 -0
  13. package/agent/db/query.ts +82 -0
  14. package/agent/db/queryTimeout.ts +76 -0
  15. package/agent/db/schema.ts +79 -0
  16. package/agent/db/tables.ts +157 -0
  17. package/agent/db/types.ts +118 -0
  18. package/agent/db/workerFactory.ts +25 -0
  19. package/agent/db.ts +43 -1099
  20. package/agent/errorMessage.ts +9 -0
  21. package/agent/flow/routes.ts +251 -0
  22. package/agent/flow/service.ts +266 -0
  23. package/agent/flow/types.ts +34 -0
  24. package/agent/flow/validation.ts +230 -0
  25. package/agent/index.ts +22 -21
  26. package/agent/logger.ts +121 -17
  27. package/agent/managedTarget.ts +82 -0
  28. package/agent/manager/config.ts +84 -0
  29. package/agent/manager/logs.ts +83 -0
  30. package/agent/manager/process.ts +233 -0
  31. package/agent/manager/storage.ts +28 -0
  32. package/agent/manager/types.ts +38 -0
  33. package/agent/manager.ts +10 -490
  34. package/agent/queue/routes.ts +135 -0
  35. package/agent/queue/runtime.ts +119 -0
  36. package/agent/queue/types.ts +34 -0
  37. package/agent/queue/validation.ts +100 -0
  38. package/agent/server/controlRoutes.ts +185 -0
  39. package/agent/server/controlTarget.ts +116 -0
  40. package/agent/server/databaseRoutes.ts +165 -0
  41. package/agent/server/errors.ts +49 -0
  42. package/agent/server/handler.ts +155 -0
  43. package/agent/server/jsonBody.ts +109 -0
  44. package/agent/server/lifecycle.ts +72 -0
  45. package/agent/server/managedRuntime.ts +41 -0
  46. package/agent/server/policy.ts +60 -0
  47. package/agent/server/router.ts +161 -0
  48. package/agent/server/types.ts +46 -0
  49. package/agent/server/workflowReadRoutes.ts +80 -0
  50. package/agent/server.ts +26 -441
  51. package/agent/shutdown.ts +156 -0
  52. package/agent/workflow/codec.ts +78 -0
  53. package/agent/workflow/detail.ts +63 -0
  54. package/agent/workflow/list.ts +87 -0
  55. package/agent/workflow/runtime.ts +274 -0
  56. package/agent/workflow/runtimeConfig.ts +103 -0
  57. package/agent/workflow/runtimeRoutes.ts +163 -0
  58. package/agent/workflow/sqlite.ts +52 -0
  59. package/agent/workflow/stats.ts +79 -0
  60. package/agent/workflow/types.ts +81 -0
  61. package/agent/workflow/validation.ts +114 -0
  62. package/agent/workflows.ts +14 -489
  63. package/dist/.vite/manifest.json +611 -250
  64. package/dist/assets/AddJob-CWkkVHmf.js +3 -0
  65. package/dist/assets/Alerts-Gh1pVbqj.js +1 -0
  66. package/dist/assets/{AreaChart-REK4ZsTC.js → AreaChart-w0ruozXH.js} +1 -1
  67. package/dist/assets/Benchmark-Dir2A1CQ.js +1 -0
  68. package/dist/assets/BulkAddJobs-Di_IgRSH.js +5 -0
  69. package/dist/assets/Button-BPqpRZJD.js +1 -0
  70. package/dist/assets/Card-C7NMhqUt.js +1 -0
  71. package/dist/assets/{CopilotPanel-COLw-atS.js → CopilotPanel-Db5nYpo1.js} +3 -3
  72. package/dist/assets/Cron-BRgWcBGk.js +1 -0
  73. package/dist/assets/CronManager-CbM0x_s0.js +1 -0
  74. package/dist/assets/Database-BeoEreW0.js +3 -0
  75. package/dist/assets/Diagnostics-Cw9iJ84f.js +1 -0
  76. package/dist/assets/Dlq-DfCg04Jd.js +1 -0
  77. package/dist/assets/DlqControl-BRCkUXvb.js +1 -0
  78. package/dist/assets/DlqPro-BHJwBYsN.js +1 -0
  79. package/dist/assets/Flows-BAzbFP_W.js +2 -0
  80. package/dist/assets/JobInspector-qvW_APO6.js +2 -0
  81. package/dist/assets/Jobs-URpnFjC6.js +1 -0
  82. package/dist/assets/JobsPro-pQ_RFSU3.js +1 -0
  83. package/dist/assets/Logs-C5jX2OPE.js +1 -0
  84. package/dist/assets/LogsPro-Cq_npmmS.js +1 -0
  85. package/dist/assets/{McpServer-CbaU52l2.js → McpServer-DtCTmQBu.js} +2 -2
  86. package/dist/assets/Metrics-BdxitePr.js +1 -0
  87. package/dist/assets/MetricsPro-orHfW0mg.js +1 -0
  88. package/dist/assets/Overview-DaNns1aj.js +1 -0
  89. package/dist/assets/PageHeader-BXlZSBcc.js +1 -0
  90. package/dist/assets/Pagination-Q-vf4hsX.js +1 -0
  91. package/dist/assets/QueueActions-jiTzndPH.js +1 -0
  92. package/dist/assets/QueueControl-l5bSdMgb.js +1 -0
  93. package/dist/assets/QueueDetail-URK2pjtH.js +1 -0
  94. package/dist/assets/QueueDetailPro-C9M5CV4K.js +1 -0
  95. package/dist/assets/Queues-DeLXlww2.js +1 -0
  96. package/dist/assets/QueuesOverview-DoQ-MUfT.js +1 -0
  97. package/dist/assets/S3Backup-D2agV9_z.js +1 -0
  98. package/dist/assets/S3BackupPro--WSZWTQn.js +3 -0
  99. package/dist/assets/ServerControl-QVd6m_iR.js +3 -0
  100. package/dist/assets/Settings-CiJRj_SM.js +1 -0
  101. package/dist/assets/StatusBadge-B0I7hNBX.js +1 -0
  102. package/dist/assets/Usage-DbHJgA4t.js +1 -0
  103. package/dist/assets/UsagePro-LoPo3k1L.js +1 -0
  104. package/dist/assets/Webhooks-CuzIJHwk.js +1 -0
  105. package/dist/assets/Workers-BpUgpwUH.js +1 -0
  106. package/dist/assets/WorkersPro-BYY0P1Pt.js +1 -0
  107. package/dist/assets/Workflows-ByJw4FFH.js +1 -0
  108. package/dist/assets/api-V_sVpHDo.js +1 -0
  109. package/dist/assets/bq-DYmbVkgR.js +1 -0
  110. package/dist/assets/cn-zFEt5SO8.js +1 -0
  111. package/dist/assets/connectionStore-CKFQrxDo.js +1 -0
  112. package/dist/assets/controlConnectionEpoch-JM4omZ6j.js +1 -0
  113. package/dist/assets/feedback-BnGE2oW2.js +1 -0
  114. package/dist/assets/{flowMutationSafety-CoOp94wS.js → flowMutationSafety-DObp9nJo.js} +1 -1
  115. package/dist/assets/{form-DsUq0w8s.js → form-tC20r4Ma.js} +1 -1
  116. package/dist/assets/format-Cg7VCIZW.js +1 -0
  117. package/dist/assets/icons-CIvFfU2b.js +1 -0
  118. package/dist/assets/index-D64v2vNO.js +8 -0
  119. package/dist/assets/index-DOze4TKg.css +1 -0
  120. package/dist/assets/install-jXi1SOYY.js +3 -0
  121. package/dist/assets/{react-vendor-Bjui4wS4.js → react-vendor-CyjgmGTq.js} +3 -3
  122. package/dist/assets/responses-CBLyy1Ob.js +1 -0
  123. package/dist/assets/toastStore-B7n0zS0y.js +1 -0
  124. package/dist/assets/{upstreamPaths-D_LPsv7b.js → upstreamPaths-CDVsu6IM.js} +1 -1
  125. package/dist/assets/usePolledData-c53tx1jw.js +1 -0
  126. package/dist/assets/{useServerActionGuard-CItj3hvh.js → useServerActionGuard-OTv6_fU0.js} +1 -1
  127. package/dist/assets/useThroughputSeries-DWHQl7cM.js +1 -0
  128. package/dist/index.html +17 -6
  129. package/package.json +15 -4
  130. package/scripts/embedded.gen.ts +160 -134
  131. package/scripts/serve.ts +127 -484
  132. package/scripts/serveHandler.ts +237 -0
  133. package/scripts/servePolicy.ts +187 -0
  134. package/dist/assets/AddJob-CG28MSxU.js +0 -3
  135. package/dist/assets/Alerts-D9JojJrj.js +0 -1
  136. package/dist/assets/Benchmark-BVjUH9dG.js +0 -1
  137. package/dist/assets/BulkAddJobs-BHzUCMj_.js +0 -5
  138. package/dist/assets/Cron-DoEHDHwy.js +0 -1
  139. package/dist/assets/CronManager-vulcesqJ.js +0 -1
  140. package/dist/assets/Database-DlB_t04Z.js +0 -3
  141. package/dist/assets/Diagnostics-DddCUgeR.js +0 -1
  142. package/dist/assets/Dlq-DMKrsSMP.js +0 -1
  143. package/dist/assets/DlqControl-9m0Re_TH.js +0 -1
  144. package/dist/assets/DlqPro-Dmw-0wta.js +0 -1
  145. package/dist/assets/Flows-C3-9nXW4.js +0 -1
  146. package/dist/assets/JobInspector-DwIfABmI.js +0 -2
  147. package/dist/assets/Jobs-j8tTS3NC.js +0 -1
  148. package/dist/assets/JobsPro-CS__CJJl.js +0 -1
  149. package/dist/assets/Logs-BB-VDPA1.js +0 -1
  150. package/dist/assets/LogsPro-RNtSQZRT.js +0 -1
  151. package/dist/assets/Metrics-ivcddPwT.js +0 -1
  152. package/dist/assets/MetricsPro-xjZno4vg.js +0 -1
  153. package/dist/assets/Overview-D84aD2ph.js +0 -1
  154. package/dist/assets/Pagination-Bfi4vkpp.js +0 -1
  155. package/dist/assets/QueueActions-DTbD6uek.js +0 -1
  156. package/dist/assets/QueueControl-B5xK_SQ-.js +0 -1
  157. package/dist/assets/QueueDetail-BH59kwm9.js +0 -1
  158. package/dist/assets/QueueDetailPro-DoN25ndy.js +0 -1
  159. package/dist/assets/Queues-CKMJqFHX.js +0 -1
  160. package/dist/assets/QueuesOverview-BX1lzojl.js +0 -1
  161. package/dist/assets/S3Backup-Bz3GfoZ9.js +0 -1
  162. package/dist/assets/S3BackupPro-BC_6-uX-.js +0 -2
  163. package/dist/assets/ServerControl-CSSrN8G5.js +0 -3
  164. package/dist/assets/Settings-DkqmZUIS.js +0 -1
  165. package/dist/assets/Usage-Ba20p0xd.js +0 -1
  166. package/dist/assets/UsagePro-C2pQwjN9.js +0 -1
  167. package/dist/assets/Webhooks-CVuuvb0L.js +0 -1
  168. package/dist/assets/Workers-C1wPU6CS.js +0 -1
  169. package/dist/assets/WorkersPro-B0vEkC0T.js +0 -1
  170. package/dist/assets/Workflows-C8Qmkflf.js +0 -1
  171. package/dist/assets/cn-CMFYpumL.js +0 -1
  172. package/dist/assets/connectionStore-Cd9-3Fw0.js +0 -1
  173. package/dist/assets/index-BCNzbhhD.css +0 -1
  174. package/dist/assets/index-CAtsVmyh.js +0 -8
  175. package/dist/assets/install-D0eRLf9F.js +0 -3
  176. package/dist/assets/useThroughputSeries-DjxGN8ob.js +0 -1
@@ -0,0 +1,3 @@
1
+ import{i as e,r as t}from"./cn-zFEt5SO8.js";import{g as n,l as r,r as i,t as a}from"./react-vendor-CyjgmGTq.js";import{i as o}from"./upstreamPaths-CDVsu6IM.js";import{r as s,t as c}from"./bq-DYmbVkgR.js";import{t as l}from"./toastStore-B7n0zS0y.js";import{n as u}from"./usePolledData-c53tx1jw.js";import{t as d}from"./Button-BPqpRZJD.js";import{n as f,t as p}from"./Card-C7NMhqUt.js";import{t as m}from"./PageHeader-BXlZSBcc.js";import{a as h,n as g,o as _,t as v}from"./form-tC20r4Ma.js";import{r as y}from"./useServerActionGuard-OTv6_fU0.js";import{a as b,c as x,d as S,i as C,l as w,m as T,n as E,o as D,p as O,r as k,s as A,t as j,u as M}from"./responses-CBLyy1Ob.js";var N=e(n(),1),P=a();function F({values:e,setValue:t,queueNames:n,queueDiscoveryError:r,onRetryQueues:i,jsonError:a,setJsonError:o}){return(0,P.jsxs)(p,{children:[(0,P.jsx)(f,{title:`Job`}),(0,P.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,P.jsxs)(v,{label:`Queue`,children:[(0,P.jsx)(g,{list:`queue-options`,"aria-label":`Queue`,name:`target-queue`,autoComplete:`off`,value:e.queue,onChange:e=>t(`queue`,e.target.value),placeholder:`queue name (existing or new)`}),(0,P.jsx)(`datalist`,{id:`queue-options`,children:n.map(e=>(0,P.jsx)(`option`,{value:e},e))})]}),(0,P.jsx)(v,{label:`Job name`,hint:`Worker routing name in Bunqueue 2.8.59; separate from the JSON payload.`,children:(0,P.jsx)(g,{name:`job-name`,autoComplete:`off`,maxLength:256,value:e.name,onChange:e=>t(`name`,e.target.value),placeholder:`default`})}),r&&(0,P.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,P.jsx)(`button`,{type:`button`,onClick:i,className:`font-medium underline underline-offset-2 hover:text-fg`,children:`Retry`})]}),(0,P.jsxs)(`div`,{children:[(0,P.jsx)(v,{label:`Data (JSON)`,children:(0,P.jsx)(`textarea`,{name:`job-data`,value:e.dataText,onChange:e=>t(`dataText`,e.target.value),onBlur:()=>{let t=T(e.dataText);o(t.ok?null:t.msg)},maxLength:O,spellCheck:!1,rows:7,className:`w-full rounded-lg border border-line bg-surface-2 p-3 font-mono text-sm text-fg focus:border-accent/60 focus:outline-none focus:ring-2 focus:ring-accent/30`})}),a&&(0,P.jsx)(`p`,{role:`alert`,className:`mt-2 text-xs text-danger`,children:a})]})]})]})}function I({values:e,setValue:t}){return(0,P.jsxs)(p,{children:[(0,P.jsx)(f,{title:`Options`}),(0,P.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 md:grid-cols-3`,children:[(0,P.jsx)(z,{label:`Priority`,name:`priority`,value:e.priority,min:-1e6,max:1e6,placeholder:`0`,onChange:e=>t(`priority`,e)}),(0,P.jsx)(z,{label:`Delay (ms)`,name:`delay`,value:e.delay,min:0,max:C,placeholder:`0`,disabled:e.runAt.trim()!==``,onChange:e=>t(`delay`,e)}),(0,P.jsx)(v,{label:`Run at`,hint:e.runAt.trim()?`Overrides Delay — derived from this time (local time).`:`(local time)`,children:(0,P.jsx)(g,{type:`datetime-local`,name:`run-at`,value:e.runAt,onChange:e=>t(`runAt`,e.target.value)})}),(0,P.jsx)(z,{label:`Max attempts`,hint:`blank = server default`,name:`max-attempts`,value:e.maxAttempts,min:1,max:1e3,placeholder:`3`,onChange:e=>t(`maxAttempts`,e)}),(0,P.jsx)(z,{label:`Backoff (ms)`,hint:`blank = server default`,name:`backoff`,value:e.backoff,min:0,max:b,placeholder:`1000`,onChange:e=>t(`backoff`,e)}),(0,P.jsx)(v,{label:`Backoff strategy`,hint:`flat delay unless set`,children:(0,P.jsxs)(h,{name:`backoff-strategy`,value:e.backoffType,onChange:e=>t(`backoffType`,e.target.value),children:[(0,P.jsx)(`option`,{value:``,children:`flat`}),(0,P.jsx)(`option`,{value:`fixed`,children:`fixed`}),(0,P.jsx)(`option`,{value:`exponential`,children:`exponential`})]})}),(0,P.jsx)(z,{label:`Timeout (ms)`,name:`timeout`,value:e.timeout,min:0,max:b,placeholder:`—`,onChange:e=>t(`timeout`,e)}),(0,P.jsx)(z,{label:`TTL (ms)`,hint:`blank = no expiry`,name:`ttl`,value:e.ttl,min:0,max:C,placeholder:`—`,onChange:e=>t(`ttl`,e)}),(0,P.jsx)(v,{label:`Custom job ID`,children:(0,P.jsx)(g,{name:`custom-job-id`,value:e.jobId,onChange:e=>t(`jobId`,e.target.value),placeholder:`idempotency key`})})]}),(0,P.jsxs)(`div`,{className:`mt-4 flex flex-wrap gap-6`,children:[(0,P.jsx)(B,{label:`removeOnComplete`,checked:e.removeOnComplete,onChange:e=>t(`removeOnComplete`,e)}),(0,P.jsx)(B,{label:`removeOnFail`,checked:e.removeOnFail,onChange:e=>t(`removeOnFail`,e)}),(0,P.jsx)(B,{label:`durable`,checked:e.durable,onChange:e=>t(`durable`,e)}),(0,P.jsx)(B,{label:`lifo`,checked:e.lifo,onChange:e=>t(`lifo`,e)})]}),(0,P.jsx)(L,{values:e,setValue:t})]})}function L({values:e,setValue:t}){return(0,P.jsxs)(`div`,{className:`mt-4 border-t border-line pt-4`,children:[(0,P.jsx)(`div`,{className:`mb-3 text-[11px] uppercase tracking-wider text-faint`,children:`Advanced`}),(0,P.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 md:grid-cols-3`,children:[(0,P.jsx)(R,{label:`Tags`,hint:`comma-separated`,name:`tags`,value:e.tags,placeholder:`email, urgent`,onChange:e=>t(`tags`,e)}),(0,P.jsx)(R,{label:`Group ID`,name:`group-id`,value:e.groupId,placeholder:`—`,onChange:e=>t(`groupId`,e)}),(0,P.jsx)(R,{label:`Unique key`,hint:`dedup key`,name:`unique-key`,value:e.uniqueKey,placeholder:`—`,onChange:e=>t(`uniqueKey`,e)}),(0,P.jsx)(R,{label:`Depends on`,hint:`parent job ids, comma-separated`,name:`depends-on`,value:e.dependsOn,placeholder:`job-id-1, job-id-2`,onChange:e=>t(`dependsOn`,e)}),(0,P.jsx)(`div`,{className:`col-span-2 md:col-span-3`,children:(0,P.jsx)(v,{label:`Repeat policy (JSON)`,hint:`v2.8.59-safe form: e.g. {"every":60000,"limit":10}. Use Cron Manager for cron patterns.`,children:(0,P.jsx)(`textarea`,{name:`repeat-policy`,value:e.repeatText,onChange:e=>t(`repeatText`,e.target.value),rows:2,spellCheck:!1,placeholder:`{"every": 60000}`,className:`w-full rounded-lg border border-line bg-surface-2 px-3 py-2 font-mono text-xs text-fg placeholder:text-faint focus:border-accent/60 focus:outline-none focus:ring-2 focus:ring-accent/30`})})})]})]})}function R({label:e,hint:t,name:n,value:r,placeholder:i,onChange:a}){return(0,P.jsx)(v,{label:e,hint:t,children:(0,P.jsx)(g,{name:n,value:r,onChange:e=>a(e.target.value),placeholder:i})})}function z({label:e,hint:t,name:n,value:r,min:i,max:a,placeholder:o,disabled:s,onChange:c}){return(0,P.jsx)(v,{label:e,hint:t,children:(0,P.jsx)(g,{type:`number`,min:i,max:a,step:1,name:n,value:r,onChange:e=>c(e.target.value),placeholder:o,disabled:s})})}function B({label:e,checked:t,onChange:n}){return(0,P.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,P.jsx)(_,{checked:t,onChange:n,label:e}),(0,P.jsx)(`span`,{className:`font-mono text-xs text-muted`,children:e})]})}function V(e){let t=e?.options??{},n=D(t);return{queue:e?.queue??``,name:e?.name??`default`,dataText:e?.dataText??`{
2
+ "hello": "world"
3
+ }`,count:`1`,priority:A(t.priority),delay:``,runAt:``,maxAttempts:A(t.maxAttempts),backoff:n.backoff,timeout:A(t.timeout),ttl:n.ttl,jobId:``,removeOnComplete:t.removeOnComplete??!1,removeOnFail:t.removeOnFail??!1,durable:!1,lifo:!1,backoffType:n.backoffType,tags:n.tags,groupId:n.groupId,dependsOn:``,uniqueKey:``,repeatText:``}}function H(e,t=Date.now()){let n=e.queue.trim(),r=M(n);if(r)return U(r);let i=e.name.trim();if(!i||i.length>256)return U(`Job name must be a non-empty string of at most 256 characters`);let a=T(e.dataText);if(!a.ok)return{ok:!1,msg:a.msg,field:a.kind===`json`?`data`:`result`};let s=x({priority:e.priority,delay:e.runAt.trim()?``:e.delay,maxAttempts:e.maxAttempts,backoff:e.backoff,timeout:e.timeout,ttl:e.ttl});if(!s.ok)return U(s.msg);let c=s.options.delay;if(e.runAt.trim()){let n=new Date(e.runAt).getTime();if(!Number.isFinite(n))return U(`Run at is not a valid date/time`);if(c=Math.max(0,n-t),c>31536e6)return U(`Run at must be within the next 365 days`)}let l=S(s.options.backoff,e.backoffType);if(!l.ok)return U(l.msg);let u=W(e.tags),d=W(e.dependsOn),f=e.jobId.trim();if(f){let e=o(f);if(e)return U(`Job ID: ${e}`)}for(let e of d){let t=o(e);if(t)return U(`Dependency ID "${e}": ${t}`)}let p=w(e.repeatText);if(!p.ok)return U(p.msg);let m=Number(e.count);return!Number.isSafeInteger(m)||m<1||m>1e4?U(`Count must be a whole number from 1 to 10000`):{ok:!0,target:n,count:m,body:{name:i,data:a.data,priority:s.options.priority,delay:c,maxAttempts:s.options.maxAttempts,backoff:l.backoff,timeout:s.options.timeout,ttl:s.options.ttl,jobId:f||void 0,removeOnComplete:e.removeOnComplete||void 0,removeOnFail:e.removeOnFail||void 0,durable:e.durable||void 0,lifo:e.lifo||void 0,tags:u.length?u:void 0,groupId:e.groupId.trim()||void 0,dependsOn:d.length?d:void 0,uniqueKey:e.uniqueKey.trim()||void 0,repeat:p.repeat}}}var U=e=>({ok:!1,msg:e,field:`result`}),W=e=>e.split(`,`).map(e=>e.trim()).filter(Boolean),G=t({AddJob:()=>K,MAX_JOB_DATA_BYTES:()=>MAX_JOB_DATA_BYTES,MAX_JOB_DATA_CHARS:()=>O});function K(){let{data:e,error:t,refetch:n}=u(()=>c.queues(),[],{intervalMs:3e4}),a=r().state?.clone,[o,f]=(0,N.useState)(()=>V(a)),[p,h]=(0,N.useState)(!1),[_,b]=(0,N.useState)(null),[x,S]=(0,N.useState)(null),C=y(`add-job`),w=(e,t)=>f(n=>({...n,[e]:t}));(0,N.useEffect)(()=>{h(!1),b(null),S(null)},[C.scopeKey]);let T=async()=>{b(null),S(null);let e=H(o);if(!e.ok){e.field===`data`?S(e.msg):b({ok:!1,msg:e.msg});return}let t=C.begin();if(!t)return;h(!0);let{target:n,body:r,count:i}=e;try{if(i===1){let e=E(await c.addJob(n,r));if(!t.isCurrent())return;b({ok:!0,msg:`Accepted job ID ${e} (it may be an existing deduplicated job)`}),l.success(`Job submission accepted`,`${n} · ${e}`)}else{let e=Array.from({length:i},()=>r),a=s(e);if(a){t.isCurrent()&&b({ok:!1,msg:a});return}let o=j(await c.addJobsBulk(n,e),i);if(!t.isCurrent())return;let u=k(new Set(o).size,i);b(u),l.success(u.msg,`in ${n}`)}}catch(e){if(!t.isCurrent())return;b({ok:!1,msg:e.message}),l.error(`Add job failed`,e.message)}finally{t.finish()&&h(!1)}};return(0,P.jsxs)(`div`,{children:[(0,P.jsx)(m,{title:`Add Job`,description:`Enqueue a job with full options.`,actions:(0,P.jsx)(i,{to:`/jobs/bulk-add`,className:`rounded-lg border border-line px-3 py-1.5 text-sm text-muted hover:bg-surface-2 hover:text-fg`,children:`Bulk import`})}),a&&(0,P.jsx)(`div`,{className:`mb-4 flex items-center gap-2 rounded-lg border border-accent/30 bg-accent/[0.06] px-4 py-2 text-sm text-accent`,children:`Pre-filled from an existing job. Review the data and options, then enqueue a fresh job.`}),(0,P.jsxs)(`form`,{className:`grid grid-cols-1 gap-6 lg:grid-cols-2`,onSubmit:e=>{e.preventDefault(),T()},children:[(0,P.jsx)(F,{values:o,setValue:w,queueNames:(e?.queues??[]).map(e=>e.name),queueDiscoveryError:t,onRetryQueues:()=>void n(),jsonError:x,setJsonError:S}),(0,P.jsx)(I,{values:o,setValue:w}),(0,P.jsxs)(`div`,{className:`flex flex-wrap items-end gap-3 lg:col-span-2`,children:[(0,P.jsx)(`div`,{className:`w-28`,children:(0,P.jsx)(v,{label:`Count`,hint:`1–10000`,children:(0,P.jsx)(g,{type:`number`,min:1,max:1e4,step:1,name:`count`,value:o.count,onChange:e=>w(`count`,e.target.value)})})}),(0,P.jsx)(d,{type:`submit`,variant:`accent`,disabled:p,children:p?`Adding…`:`Add job`}),_&&(0,P.jsx)(`span`,{role:_.ok?`status`:`alert`,className:_.ok?`text-sm text-success`:`text-sm text-danger`,children:_.msg})]})]})]})}export{G as t};
@@ -0,0 +1 @@
1
+ import{i as e}from"./cn-zFEt5SO8.js";import{g as t,t as n}from"./react-vendor-CyjgmGTq.js";import{o as r}from"./connectionStore-CKFQrxDo.js";import{O as i,r as a}from"./icons-CIvFfU2b.js";import{n as o,t as s}from"./Button-BPqpRZJD.js";import{t as c}from"./Card-C7NMhqUt.js";import{i as l,t as u}from"./feedback-BnGE2oW2.js";import{t as d}from"./PageHeader-BXlZSBcc.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-D64v2vNO.js";import{a as v,n as y,o as b,t as x}from"./form-tC20r4Ma.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"./cn-CMFYpumL.js";import{g as t,t as n}from"./react-vendor-Bjui4wS4.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
+ import{i as e}from"./cn-zFEt5SO8.js";import{g as t,t as n}from"./react-vendor-CyjgmGTq.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};
@@ -0,0 +1 @@
1
+ import{i as e,t}from"./cn-zFEt5SO8.js";import{g as n,t as r}from"./react-vendor-CyjgmGTq.js";import{o as i}from"./connectionStore-CKFQrxDo.js";import{t as a}from"./bq-DYmbVkgR.js";import{b as o,x as s}from"./icons-CIvFfU2b.js";import{n as c}from"./usePolledData-c53tx1jw.js";import{t as l}from"./Button-BPqpRZJD.js";import{n as u,t as d}from"./Card-C7NMhqUt.js";import{t as f}from"./PageHeader-BXlZSBcc.js";import{n as p,o as m,s as h}from"./format-Cg7VCIZW.js";import{a as g,n as _}from"./index-D64v2vNO.js";import{i as v,n as y,o as b,t as x}from"./form-tC20r4Ma.js";import{t as S}from"./AreaChart-w0ruozXH.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`],V=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)}),H=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)}),U=r();function ne({draft:e,active:n,cleaning:r,cleanResult:i,setField:a,numberInput:o,onClean:s}){return(0,U.jsxs)(d,{className:`lg:col-span-1`,children:[(0,U.jsx)(u,{title:`Configuration`}),(0,U.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,U.jsx)(x,{label:`Dedicated queue`,hint:`Cryptographically generated for this browser tab; production queue names cannot be entered here.`,children:(0,U.jsx)(y,{name:`benchmark-queue`,autoComplete:`off`,value:e.queue,readOnly:!0,"aria-readonly":`true`})}),(0,U.jsx)(x,{label:`Mode`,hint:e.mode===`count`?`Push a fixed number of jobs.`:`Run producers + workers for a fixed time.`,children:(0,U.jsx)(v,{options:R,value:e.mode,onChange:e=>a(`mode`,e),disabled:n})}),e.mode===`count`?(0,U.jsx)(x,{label:`Total jobs`,hint:`max ${h(w.total)}`,children:(0,U.jsx)(y,{name:`benchmark-total`,autoComplete:`off`,type:`number`,min:1,max:w.total,value:e.total,disabled:n,onChange:o(`total`)})}):(0,U.jsx)(x,{label:`Duration (s)`,hint:`max ${w.durationS}s`,children:(0,U.jsx)(y,{name:`benchmark-duration-seconds`,autoComplete:`off`,type:`number`,min:1,max:w.durationS,value:e.durationS,disabled:n,onChange:o(`durationS`)})}),(0,U.jsx)(`div`,{className:`mt-1 text-[11px] font-semibold uppercase tracking-wider text-faint`,children:`Producers`}),(0,U.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,U.jsx)(x,{label:`Producers`,hint:`parallel, max ${w.producers}`,children:(0,U.jsx)(y,{name:`benchmark-producers`,autoComplete:`off`,type:`number`,min:1,max:w.producers,value:e.producers,disabled:n,onChange:o(`producers`)})}),(0,U.jsx)(x,{label:`Push batch`,hint:`jobs/req, max ${w.batch}`,children:(0,U.jsx)(y,{name:`benchmark-push-batch`,autoComplete:`off`,type:`number`,min:1,max:w.batch,value:e.batch,disabled:n,onChange:o(`batch`)})}),(0,U.jsx)(x,{label:`Payload`,hint:`bytes/job, max ${h(w.payload)}`,children:(0,U.jsx)(y,{name:`benchmark-payload-bytes`,autoComplete:`off`,type:`number`,min:0,max:w.payload,value:e.payload,disabled:n,onChange:o(`payload`)})})]}),(0,U.jsx)(`div`,{className:`mt-1 text-[11px] font-semibold uppercase tracking-wider text-faint`,children:`Workers (simulated)`}),(0,U.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,U.jsx)(x,{label:`Workers`,hint:`0 = produce only, max ${w.workers}`,children:(0,U.jsx)(y,{name:`benchmark-workers`,autoComplete:`off`,type:`number`,min:0,max:w.workers,value:e.workers,disabled:n,onChange:o(`workers`)})}),(0,U.jsx)(x,{label:`Pull batch`,hint:`jobs/pull, max ${w.workerBatch}`,children:(0,U.jsx)(y,{name:`benchmark-pull-batch`,autoComplete:`off`,type:`number`,min:1,max:w.workerBatch,value:e.workerBatch,disabled:n,onChange:o(`workerBatch`)})}),(0,U.jsx)(x,{label:`Process (ms)`,hint:`simulated work per pull`,children:(0,U.jsx)(y,{name:`benchmark-process-ms`,autoComplete:`off`,type:`number`,min:0,max:w.processMs,value:e.processMs,disabled:n,onChange:o(`processMs`)})})]}),(0,U.jsx)(W,{label:`Durable (fsync each job)`,checked:e.durable,active:n,onChange:e=>a(`durable`,e)}),(0,U.jsx)(W,{label:`Remove on complete`,checked:e.removeOnComplete,active:n,onChange:e=>a(`removeOnComplete`,e)}),(0,U.jsxs)(`div`,{className:`flex items-center gap-3 pt-1`,children:[(0,U.jsx)(l,{variant:`ghost`,size:`sm`,disabled:n||r,onClick:s,children:r?`Cleaning…`:`Clean queue`}),i&&(`error`in i?(0,U.jsxs)(`span`,{className:`text-xs text-danger`,children:[`Clean unverified — `,i.error]}):(0,U.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 W({label:e,checked:t,active:n,onChange:r}){return(0,U.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,U.jsx)(b,{checked:t,onChange:r,disabled:n,label:e}),(0,U.jsx)(`span`,{className:`text-sm text-muted`,children:e})]})}function re({heading:e,pollBaseUrl:t,etaText:n,shown:r,shownWorkers:i,producePct:a,drainPct:o,durationPct:s,live:c,summary:l}){return(0,U.jsxs)(`div`,{className:`flex flex-col gap-6 lg:col-span-2`,children:[(0,U.jsxs)(d,{children:[(0,U.jsxs)(`div`,{className:`mb-3 flex items-center justify-between`,children:[(0,U.jsx)(`h3`,{className:`text-base font-semibold text-fg`,children:e}),(0,U.jsxs)(`span`,{className:`text-right text-xs text-faint`,children:[(0,U.jsxs)(`span`,{className:`block`,children:[`Benchmark target: `,t]}),n&&(0,U.jsx)(`span`,{className:`block`,children:n})]})]}),(0,U.jsx)(G,{label:r.mode===`duration`?`Elapsed`:`Produced`,pct:r.mode===`duration`?s:a,tone:`accent`}),i>0&&r.mode===`count`&&(0,U.jsx)(G,{label:`Completed`,pct:o,tone:`emerald`}),(0,U.jsxs)(`div`,{className:`mt-4 grid grid-cols-2 gap-4 md:grid-cols-4`,children:[(0,U.jsx)(_,{label:`Pushed`,value:h(c.pushed),tone:`accent`,compact:!0}),(0,U.jsx)(_,{label:`Completed`,value:h(c.completed),tone:`green`,compact:!0}),(0,U.jsx)(_,{label:`Push/sec`,value:L(c.pushPerSec),compact:!0}),(0,U.jsx)(_,{label:`Done/sec`,value:L(c.donePerSec),tone:`green`,compact:!0}),(0,U.jsx)(_,{label:`Elapsed`,value:m(c.elapsedMs),compact:!0}),(0,U.jsx)(_,{label:`Active workers`,value:`${c.activeWorkers}/${i}`,tone:`blue`,compact:!0}),(0,U.jsx)(_,{label:`Data`,value:p(l?.bytes??c.bytes),compact:!0}),(0,U.jsx)(_,{label:`Errors`,value:h((l?.pushFailed??c.pushFailed)+(l?.ackFailed??c.ackFailed)),tone:c.pushFailed+c.ackFailed?`red`:`default`,compact:!0})]})]}),(0,U.jsxs)(d,{children:[(0,U.jsx)(u,{title:`Throughput`}),(0,U.jsx)(`p`,{className:`-mt-3 mb-4 text-xs text-faint`,children:`Enqueue (client → server) vs completion (workers) per second`}),(0,U.jsx)(S,{series:[{label:`Push/sec`,color:`#38bdf8`,points:c.pushSeries,area:!0},{label:`Done/sec`,color:`#34d399`,points:c.doneSeries,area:!0}]}),(0,U.jsxs)(`div`,{className:`mt-2 flex items-center gap-4 font-mono text-[11px] text-faint`,children:[(0,U.jsx)(K,{color:`#38bdf8`,label:`Push/sec`,value:c.pushPerSec}),(0,U.jsx)(K,{color:`#34d399`,label:`Done/sec`,value:c.donePerSec})]})]})]})}function ie({summary:e}){return e?(0,U.jsxs)(d,{className:`mt-6`,children:[(0,U.jsx)(u,{title:`Summary`}),(0,U.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 md:grid-cols-4 xl:grid-cols-8`,children:[(0,U.jsx)(_,{label:`Pushed`,value:h(e.pushed),tone:`accent`,compact:!0}),(0,U.jsx)(_,{label:`Completed`,value:h(e.completed),tone:`green`,compact:!0}),(0,U.jsx)(_,{label:`Duration`,value:m(e.durationMs),compact:!0}),(0,U.jsx)(_,{label:`Avg push/s`,value:L(e.pushPerSec),tone:`accent`,compact:!0}),(0,U.jsx)(_,{label:`Avg done/s`,value:L(e.donePerSec),tone:`green`,compact:!0}),(0,U.jsx)(_,{label:`Data rate`,value:`${p(e.mbPerSec)}/s`,compact:!0}),(0,U.jsx)(_,{label:`Push p95`,value:m(e.p95),compact:!0}),(0,U.jsx)(_,{label:`Push p99`,value:m(e.p99),compact:!0})]}),(0,U.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 ae({pollQueue:e,pollBaseUrl:t,pinned:n,counts:r,error:i}){return(0,U.jsxs)(d,{className:`mt-6`,children:[(0,U.jsx)(u,{title:`Server queue: ${e}`,action:i?(0,U.jsx)(`span`,{className:`text-xs font-medium text-warning`,children:`stale — server unreachable`}):void 0}),(0,U.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,U.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 md:grid-cols-5`,children:[(0,U.jsx)(_,{label:`Waiting`,value:h(r.waiting??0),tone:`amber`,compact:!0}),(0,U.jsx)(_,{label:`Active`,value:h(r.active??0),tone:`blue`,compact:!0}),(0,U.jsx)(_,{label:`Completed`,value:h(r.completed??0),tone:`green`,compact:!0}),(0,U.jsx)(_,{label:`Failed`,value:h(r.failed??0),tone:r.failed?`red`:`default`,compact:!0}),(0,U.jsx)(_,{label:`Delayed`,value:h(r.delayed??0),compact:!0})]}):(0,U.jsx)(`p`,{className:`text-sm text-faint`,children:`No counts yet — waiting for the server to answer for this queue.`})]})}function G({label:e,pct:n,tone:r}){return(0,U.jsxs)(`div`,{className:`mb-2`,children:[(0,U.jsxs)(`div`,{className:`mb-1 flex items-center justify-between text-[11px] text-faint`,children:[(0,U.jsx)(`span`,{children:e}),(0,U.jsxs)(`span`,{className:`tnum`,children:[n.toFixed(0),`%`]})]}),(0,U.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,U.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 K({color:e,label:t,value:n}){return(0,U.jsxs)(`span`,{className:`flex items-center gap-1.5`,children:[(0,U.jsx)(`span`,{className:`size-2 rounded-full`,style:{background:e}}),t,` `,(0,U.jsxs)(`span`,{className:`text-fg`,children:[L(n),`/s`]})]})}function q({history:e,onClear:t}){if(!e.length)return null;let n=JSON.stringify(e,null,2);return(0,U.jsxs)(d,{padded:!1,className:`mt-6 overflow-hidden`,children:[(0,U.jsxs)(`div`,{className:`flex items-center justify-between border-b border-line px-5 py-3`,children:[(0,U.jsx)(`h3`,{className:`text-base font-semibold text-fg`,children:`Run history`}),(0,U.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,U.jsx)(g,{value:n}),(0,U.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,U.jsx)(l,{size:`sm`,variant:`ghost`,onClick:t,children:`Clear`})]})]}),(0,U.jsx)(`div`,{className:`overflow-x-auto`,children:(0,U.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,U.jsx)(`thead`,{children:(0,U.jsxs)(`tr`,{className:`border-b border-line text-left text-[11px] uppercase tracking-wider text-faint`,children:[(0,U.jsx)(`th`,{className:`px-5 py-2 font-medium`,children:`Mode`}),(0,U.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Prod`}),(0,U.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Wkr`}),(0,U.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Pushed`}),(0,U.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Done`}),(0,U.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Push/s`}),(0,U.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Done/s`}),(0,U.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`p95`}),(0,U.jsx)(`th`,{className:`px-5 py-2 text-right font-medium`,children:`Dur`})]})}),(0,U.jsx)(`tbody`,{children:e.map(e=>(0,U.jsxs)(`tr`,{className:`border-b border-line last:border-0 hover:bg-surface-2/40`,children:[(0,U.jsx)(`td`,{className:`px-5 py-2 capitalize text-muted`,children:e.mode}),(0,U.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:e.producers}),(0,U.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:e.workers}),(0,U.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-fg`,children:h(e.pushed)}),(0,U.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-success`,children:h(e.completed)}),(0,U.jsxs)(`td`,{className:`px-5 py-2 text-right tnum text-accent`,children:[L(e.pushPerSec),`/s`]}),(0,U.jsxs)(`td`,{className:`px-5 py-2 text-right tnum text-success`,children:[L(e.donePerSec),`/s`]}),(0,U.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:m(e.p95)}),(0,U.jsx)(`td`,{className:`px-5 py-2 text-right tnum text-muted`,children:m(e.durationMs)})]},e.id))})]})})]})}var oe=[`waiting`,`prioritized`,`delayed`,`active`,`paused`,`waiting-children`,`completed`,`failed`];function J(e){return oe.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 Y(e,t){if(!e||typeof e!=`object`||e.ok!==!0)throw Error(`${t} returned a malformed success response.`)}var X={pushed:0,completed:0,pushFailed:0,ackFailed:0,bytes:0,elapsedMs:0,pushPerSec:0,donePerSec:0,activeWorkers:0,pushSeries:[],doneSeries:[],etaMs:null,error:null},Z=()=>({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 se(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 ce(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 le(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 ue(e,t,n){try{Y(await e.overview(),`Dashboard preflight`)}catch(e){return{ok:!1,live:{...X,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(Y(r,`Queue-count preflight`),!r.counts||typeof r.counts!=`object`||Array.isArray(r.counts))throw Error(`Malformed queue-count response.`);let i=J(r.counts);if(i>0)return{ok:!1,live:{...X,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:{...X,error:`Could not verify that dedicated queue "${t}" is empty. (${I(e)})`}}}return{ok:!0}}function de(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(Y(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 fe(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 pe(e),!e.shouldContinue())break;n.length>0&&await me(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 pe(e){try{let t=await e.client.pullBatch(e.queue,e.workerBatch);if(!e.shouldContinue())return[];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)||new Set(t.jobs.map(e=>e.id)).size!==t.jobs.length)throw Error(`Malformed pull-batch response.`);return t.jobs}catch(t){return e.isCurrent()&&(e.stats.error??=I(t)),[]}}async function me(e,t){if(t.some(t=>!e.ownJobIds.has(t.id))&&e.pendingPushes.size>0&&await Promise.allSettled([...e.pendingPushes]),!e.shouldContinue())return;let n=t.filter(t=>e.ownJobIds.has(t.id)),r=t.filter(t=>!e.ownJobIds.has(t.id));if(r.length>0&&await he(e,r),!(n.length===0||!e.shouldContinue())&&(e.processMs>0&&await F(e.processMs,e.shouldContinue),e.shouldContinue()))try{let t=n.map(e=>e.id),r=await e.client.heartbeatBatch(t);Y(r,`Heartbeat benchmark jobs`);let i=r.data;if(!i||typeof i!=`object`||i.ok!==!0||!Number.isSafeInteger(i.count)||i.count!==n.length)throw Error(`Heartbeat confirmed ${String(i?.count)} of ${n.length} benchmark jobs; refusing a lossy batch ACK.`);if(!e.shouldContinue()||(Y(await e.client.ackBatch(t),`Acknowledge benchmark jobs`),!e.isCurrent()))return;e.stats.completed+=n.length;for(let t of n)e.ownJobIds.delete(t.id)}catch(t){if(!e.isCurrent())return;e.stats.ackFailed+=n.length,e.stats.error??=I(t)}}async function he(e,t){if(!e.shouldContinue())return;let n=await Promise.allSettled(t.map(async t=>{Y(await e.client.retryJob(t.id),`Restore job ${t.id}`)}));if(!e.isCurrent())return;let r=n.filter(e=>e.status===`rejected`);e.stats.error??=r.length>0?`Detected ${t.length} foreign job(s); ${r.length} could not be returned to waiting. Benchmark stopped.`:`Detected ${t.length} foreign job(s); returned them to waiting and stopped without counting them.`,e.stop()}var ge=0;async function _e(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=le(t);if(!n.ok){e.setLive({...X,error:n.error}),Q(e,`error`);return}let r=n.config,a=await ue(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=Z();c.startedAt=performance.now(),c.lastAt=performance.now(),e.statsRef.current=c,e.setLive({...X}),Q(e,`running`);let l={batch:r.batch,blob:`x`.repeat(r.payload),client:i,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=de(l),d=fe(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;ve(e,r,c)}finally{e.runningRef.current=!1}}function ve(e,t,n){let r=performance.now()-n.startedAt,i=ce(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:++ge,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 ye(){let[e,t]=(0,C.useState)(`idle`),[n,r]=(0,C.useState)(X),[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)(Z());return g.current=e,(0,C.useEffect)(()=>(d.current=!0,()=>{d.current=!1,u.current=!0,f.current++}),[]),(0,C.useEffect)(()=>{if(e!==`running`&&e!==`draining`)return;let t=setInterval(()=>{r(se(_.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)=>_e({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(X),t(`idle`)},clearHistory:()=>s([])}}function be({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(Y(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=J(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=H(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=J(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 xe(){let[e]=(0,C.useState)(ee),[t,n]=(0,C.useState)(()=>V({...T,queue:e})),r=ye(),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=be({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??H(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=Se(D,P.mode,!!j),W=Ce(D,O.etaMs);return(0,U.jsxs)(`div`,{children:[(0,U.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,U.jsxs)(l,{variant:`warning`,size:`sm`,disabled:!0,children:[(0,U.jsx)(o,{className:`size-3.5`}),` Stopping…`]}):N?(0,U.jsxs)(l,{variant:`warning`,size:`sm`,onClick:r.stop,children:[(0,U.jsx)(o,{className:`size-3.5`}),` Stop`]}):(0,U.jsxs)(l,{variant:`success`,size:`sm`,disabled:E.cleaning,onClick:E.start,children:[(0,U.jsx)(s,{className:`size-3.5`}),` Run benchmark`]})}),O.error&&(0,U.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,U.jsxs)(`div`,{className:`mb-4 flex flex-wrap items-center gap-2`,children:[(0,U.jsx)(`span`,{className:`text-xs font-medium uppercase tracking-wider text-faint`,children:`Presets`}),Object.keys(k).map(e=>(0,U.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,U.jsxs)(`div`,{className:`grid grid-cols-1 gap-6 lg:grid-cols-3`,children:[(0,U.jsx)(ne,{draft:t,active:N,cleaning:E.cleaning,cleanResult:E.cleanResult,setField:m,numberInput:h,onClean:()=>void E.cleanup()}),(0,U.jsx)(re,{heading:B,pollBaseUrl:y,etaText:W,shown:P,shownWorkers:I,producePct:L,drainPct:R,durationPct:z,live:O,summary:j})]}),(0,U.jsx)(ie,{summary:j}),(0,U.jsx)(ae,{pollQueue:_,pollBaseUrl:y,pinned:!!d,counts:S,error:x}),(0,U.jsx)(q,{history:M,onClear:r.clearHistory})]})}function Se(e,t,n){return e===`running`?t===`duration`?`Running…`:`Producing…`:e===`draining`?`Draining…`:e===`stopping`?`Stopping…`:e===`error`?`Error`:n?`Result`:`Ready`}function Ce(e,t){return(e===`running`||e===`draining`)&&t!=null?t>0?`ETA ${m(t)}`:`finishing…`:e===`stopped`?`stopped early`:e===`done`?`complete`:``}export{xe as Benchmark};
@@ -0,0 +1,5 @@
1
+ import{i as e}from"./cn-zFEt5SO8.js";import{g as t,t as n}from"./react-vendor-CyjgmGTq.js";import{i as r}from"./upstreamPaths-CDVsu6IM.js";import{r as i,t as a}from"./bq-DYmbVkgR.js";import{t as o}from"./toastStore-B7n0zS0y.js";import{n as s}from"./usePolledData-c53tx1jw.js";import{t as c}from"./Button-BPqpRZJD.js";import{n as l,t as u}from"./Card-C7NMhqUt.js";import{t as d}from"./PageHeader-BXlZSBcc.js";import{i as f,n as p,t as m}from"./form-tC20r4Ma.js";import{r as h}from"./useServerActionGuard-OTv6_fU0.js";import{f as g,h as _,l as v,t as y,u as b}from"./responses-CBLyy1Ob.js";var x=e(t(),1),S=1e4,C=365*24*60*60*1e3,w=1440*60*1e3,T=64*1024*1024,E=64*1024*1024,D=`[
2
+ { "data": { "to": "a@example.com", "template": "welcome" }, "priority": 1 },
3
+ { "data": { "to": "b@example.com", "template": "welcome" } }
4
+ ]`,O=n();function k({values:e,setValue:t,parsedCount:n,parseError:r,validation:i,onFile:a,onBeforeTextChange:o}){return(0,O.jsxs)(u,{className:`lg:col-span-2`,children:[(0,O.jsx)(l,{title:`Jobs`,action:(0,O.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,O.jsx)(`input`,{type:`file`,name:`jobs-file`,accept:`.json,.ndjson,.txt,application/json`,onChange:a,className:`hidden`})]})}),(0,O.jsx)(`textarea`,{name:`jobs-json`,value:e.text,onChange:e=>{o(),t(`text`,e.target.value)},maxLength:T,spellCheck:!1,rows:16,placeholder:D,"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,O.jsxs)(`fieldset`,{className:`mt-3 flex flex-wrap items-center gap-3`,children:[(0,O.jsx)(`legend`,{className:`sr-only`,children:`Import interpretation`}),(0,O.jsx)(`span`,{"aria-hidden":`true`,className:`text-xs font-medium text-faint`,children:`Interpret each item as`}),(0,O.jsx)(f,{options:[`spec`,`raw`],value:e.mode,onChange:e=>t(`mode`,e)}),(0,O.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,O.jsx)(`p`,{className:`mt-2 text-xs text-faint`,children:`Accepts a JSON array, a single object, or newline-delimited JSON.`}),(0,O.jsx)(`div`,{className:`mt-2 text-sm`,children:r?(0,O.jsx)(`span`,{role:`alert`,className:`text-danger`,children:r}):n>1e4?(0,O.jsxs)(`span`,{role:`alert`,className:`text-danger`,children:[`Too many jobs (`,n,`). Limit is `,S,`.`]}):n>0?(0,O.jsxs)(`span`,{className:`text-success`,children:[n,` job(s) parsed ✓`]}):(0,O.jsx)(`span`,{className:`text-faint`,children:`Nothing parsed yet.`})}),!r&&n>0&&!i.ok&&(0,O.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 A({values:e,setValue:t,queueNames:n,queueDiscoveryError:r,onRetryQueues:i,busy:a,parsedCount:o,parseError:s,validationOk:d,result:f,onSubmit:h}){return(0,O.jsxs)(u,{children:[(0,O.jsx)(l,{title:`Target & defaults`}),(0,O.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,O.jsxs)(m,{label:`Queue`,children:[(0,O.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,O.jsx)(`datalist`,{id:`bulk-queue-options`,children:n.map(e=>(0,O.jsx)(`option`,{value:e},e))})]}),r&&(0,O.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,O.jsx)(`button`,{type:`button`,onClick:i,className:`font-medium underline underline-offset-2 hover:text-fg`,children:`Retry`})]}),(0,O.jsx)(`p`,{className:`-mt-2 text-xs text-faint`,children:`Defaults below fill any field an item omits.`}),(0,O.jsxs)(`div`,{className:`grid grid-cols-2 gap-3`,children:[(0,O.jsx)(j,{label:`Priority`,name:`default-priority`,value:e.priority,min:-1e6,max:1e6,placeholder:`0`,onChange:e=>t(`priority`,e)}),(0,O.jsx)(j,{label:`Max attempts`,name:`default-max-attempts`,value:e.maxAttempts,min:1,max:1e3,placeholder:`3`,onChange:e=>t(`maxAttempts`,e)}),(0,O.jsx)(j,{label:`Backoff (ms)`,name:`default-backoff`,value:e.backoff,min:0,max:w,placeholder:`1000`,onChange:e=>t(`backoff`,e)}),(0,O.jsx)(j,{label:`Timeout (ms)`,name:`default-timeout`,value:e.timeout,min:0,max:w,placeholder:`—`,onChange:e=>t(`timeout`,e)})]}),(0,O.jsx)(`div`,{className:`flex items-center gap-3`,children:(0,O.jsx)(c,{variant:`accent`,disabled:a||o===0||o>1e4||s!=null||!d,onClick:h,children:a?`Importing…`:`Import ${o||``}`})}),f&&(0,O.jsx)(`span`,{role:f.ok?`status`:`alert`,className:f.ok?`text-sm text-success`:`text-sm text-danger`,children:f.msg})]})]})}function j({label:e,name:t,value:n,min:r,max:i,placeholder:a,onChange:o}){return(0,O.jsx)(m,{label:e,children:(0,O.jsx)(p,{type:`number`,min:r,max:i,step:1,name:t,value:n,onChange:e=>o(e.target.value),placeholder:a})})}function M(e){return e.length>67108864||_(e,67108864)>67108864?`Import text exceeds the 64 MiB UTF-8 safety limit`:null}function N(e,t=E){return i(e,t)}function P(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 F(e){let t=M(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 I({text:e,setText:t,setResult:n}){let r=(0,x.useRef)(null),i=(0,x.useRef)(0),a=(0,x.useCallback)(()=>{i.current+=1;let e=r.current;r.current=null;try{e?.abort()}catch{}},[]);return(0,x.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=M(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 L=e=>{let t=typeof e==`string`&&e.trim()!==``?Number(e.trim()):e;return typeof t==`number`&&Number.isFinite(t)?t:void 0},R=e=>typeof e==`boolean`?e:void 0,z=e=>typeof e==`string`?e.trim()===``?void 0:e.trim():typeof e==`number`&&Number.isFinite(e)?String(e):void 0,B=e=>{if(!Array.isArray(e))return;let t=e.map(z);return t.every(e=>e!==void 0)?t:void 0},V=e=>typeof e==`object`&&e&&!Array.isArray(e)?e:void 0,H=e=>{let t=L(e);if(t!==void 0)return t;let n=V(e);if(!n||n.type!==`fixed`&&n.type!==`exponential`||Object.keys(n).some(e=>e!==`type`&&e!==`delay`))return;let r=L(n.delay);return r===void 0?void 0:{type:n.type,delay:r}},U=e=>{let t=V(e);if(!t)return;let n=v(JSON.stringify(t));return n.ok?n.repeat:void 0};function W(e){let t=V(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 ${C} 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 G=e=>{let t=W(e);return t.ok?t.dedup:void 0};function K(e,t,n){if(n===`spec`&&e&&typeof e==`object`&&!Array.isArray(e)&&`data`in e){let n=e;return{name:z(n.name),data:n.data,priority:L(n.priority)??t.priority,delay:L(n.delay),maxAttempts:L(n.maxAttempts)??t.maxAttempts,backoff:H(n.backoff)??t.backoff,timeout:L(n.timeout)??t.timeout,jobId:z(n.jobId)??z(n.customId),removeOnComplete:R(n.removeOnComplete),removeOnFail:R(n.removeOnFail),durable:R(n.durable),lifo:R(n.lifo),ttl:L(n.ttl),uniqueKey:z(n.uniqueKey),tags:B(n.tags),groupId:z(n.groupId),dependsOn:B(n.dependsOn),stallTimeout:L(n.stallTimeout),repeat:U(n.repeat),dedup:G(n.dedup),stackTraceLimit:L(n.stackTraceLimit),timestamp:L(n.timestamp)}}return{data:e,...t}}var q=new Set([`name`,`data`,`priority`,`delay`,`maxAttempts`,`backoff`,`timeout`,`jobId`,`customId`,`removeOnComplete`,`removeOnFail`,`durable`,`lifo`,`ttl`,`uniqueKey`,`tags`,`groupId`,`dependsOn`,`stallTimeout`,`repeat`,`dedup`,`stackTraceLimit`,`timestamp`]);function J(e,t,n,r){if(e===void 0)return null;let i=L(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 Y(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,w],[`timeout`,`Default timeout`,0,w]],r={};for(let[e,i,a,o]of n){let n=J(t[e],i,a,o);if(n)return{ok:!1,msg:n};let s=L(t[e]);s!==void 0&&(r[e]=s)}return{ok:!0,defaults:r}}function X(e){let t=Object.keys(e).filter(e=>!q.has(e));if(t.length)return`unknown job option(s): ${t.join(`, `)}`;let n=[[`priority`,`priority`,-1e6,1e6],[`delay`,`delay`,0,C],[`maxAttempts`,`maxAttempts`,1,1e3],[`timeout`,`timeout`,0,w],[`ttl`,`ttl`,0,C],[`stallTimeout`,`stallTimeout`,0,w],[`stackTraceLimit`,`stackTraceLimit`,0,1e4],[`timestamp`,`timestamp`,0,2**53-1]];for(let[t,r,i,a]of n){let n=J(e[t],r,i,a);if(n)return n}if(e.backoff!==void 0){let t=H(e.backoff);if(t===void 0)return`backoff must be a number or {"type":"fixed|exponential","delay":number} with no extra keys`;let n=J(typeof t==`number`?t:t.delay,`backoff delay`,0,w);if(n)return n}if(e.repeat!==void 0){let t=V(e.repeat);if(!t)return`repeat must be an object`;let n=v(JSON.stringify(t));if(!n.ok)return n.msg}if(e.dedup!==void 0){let t=W(e.dedup);if(!t.ok)return`dedup ${t.msg}`;if(z(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=z(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=z(e.jobId),a=z(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=z(e.groupId);if(t===void 0||t.length>256)return`groupId must be a non-empty string of at most 256 characters`}for(let t of[`tags`,`dependsOn`]){if(e[t]===void 0)continue;let n=B(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 Z(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 Q(e,t,n){if(e.length>1e4)return{ok:!1,msg:`Too many jobs (${e.length}). Limit is ${S}.`};let r=[],i=0;for(let a=0;a<e.length;a++){let o=e[a];if(n===`spec`&&$(o)){let e=X(o);if(e)return{ok:!1,msg:`Job ${a+1}: ${e}`}}let s=K(o,t,n),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,g);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=Z(r);return a?{ok:!1,msg:a}:{ok:!0,bodies:r}}var $=e=>typeof e==`object`&&!!e&&!Array.isArray(e)&&`data`in e,ee={queue:``,text:``,mode:`spec`,priority:``,maxAttempts:``,backoff:``,timeout:``};function te(){let{data:e,error:t,refetch:n}=s(()=>a.queues(),[],{intervalMs:3e4}),[r,i]=(0,x.useState)(ee),[c,l]=(0,x.useState)(!1),[u,f]=(0,x.useState)(null),p=h(`bulk-add-jobs`),m=(e,t)=>i(n=>({...n,[e]:t})),g=I({text:r.text,setText:e=>m(`text`,e),setResult:f});(0,x.useEffect)(()=>{l(!1),f(null)},[p.scopeKey]);let _=(0,x.useMemo)(()=>F(r.text),[r.text]),v=(0,x.useMemo)(()=>{if(_.error)return{ok:!1,msg:_.error};if(_.items.length>1e4)return{ok:!1,msg:`Too many jobs (${_.items.length}). Limit is ${S}.`};let e=Y(r);return e.ok?Q(_.items,e.defaults,r.mode):e},[_,r]),C=async()=>{f(null);let e=r.queue.trim(),t=b(e);if(t){f({ok:!1,msg:t});return}let n=M(r.text);if(n){f({ok:!1,msg:n});return}if(_.error){f({ok:!1,msg:_.error});return}if(_.items.length===0){f({ok:!1,msg:`Nothing to enqueue — paste a JSON array or NDJSON.`});return}if(_.items.length>1e4){f({ok:!1,msg:`Too many jobs (${_.items.length}). Limit is ${S}.`});return}if(!v.ok){f({ok:!1,msg:v.msg});return}let i=N(v.bodies);if(i){f({ok:!1,msg:i});return}let s=p.begin();if(s){l(!0);try{let t=y(await a.addJobsBulk(e,v.bodies),v.bodies.length);if(!s.isCurrent())return;let n=P(new Set(t).size,v.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,O.jsxs)(`div`,{children:[(0,O.jsx)(d,{title:`Bulk Add Jobs`,description:`Import many distinct jobs at once from a JSON array, NDJSON, or a file.`}),(0,O.jsxs)(`div`,{className:`grid grid-cols-1 gap-6 lg:grid-cols-3`,children:[(0,O.jsx)(k,{values:r,setValue:m,parsedCount:_.items.length,parseError:_.error,validation:v,onFile:g.onFile,onBeforeTextChange:g.cancelPending}),(0,O.jsx)(A,{values:r,setValue:m,queueNames:(e?.queues??[]).map(e=>e.name),queueDiscoveryError:t,onRetryQueues:()=>void n(),busy:c,parsedCount:_.items.length,parseError:_.error,validationOk:v.ok,result:u,onSubmit:()=>void C()})]})]})}export{te as BulkAddJobs};
@@ -0,0 +1 @@
1
+ import{t as e}from"./cn-zFEt5SO8.js";import{t}from"./react-vendor-CyjgmGTq.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};
@@ -0,0 +1 @@
1
+ import{t as e}from"./cn-zFEt5SO8.js";import{t}from"./react-vendor-CyjgmGTq.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};