@kingsleyweb/bun-ui 0.0.0-stage → 3.0.0-rc.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 (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -2
  3. package/app/docs/App.tsx +72 -0
  4. package/app/docs/api/client.ts +303 -0
  5. package/app/docs/api/contract.ts +24 -0
  6. package/app/docs/api/docs.ts +38 -0
  7. package/app/docs/api/errors.ts +144 -0
  8. package/app/docs/boot.tsx +58 -0
  9. package/app/docs/components/Badge.tsx +65 -0
  10. package/app/docs/components/Button.tsx +37 -0
  11. package/app/docs/components/Card.tsx +41 -0
  12. package/app/docs/components/ConfirmDialog.tsx +173 -0
  13. package/app/docs/components/CopyButton.tsx +68 -0
  14. package/app/docs/components/Dialog.tsx +230 -0
  15. package/app/docs/components/EmptyState.tsx +34 -0
  16. package/app/docs/components/ErrorView.tsx +70 -0
  17. package/app/docs/components/Field.tsx +93 -0
  18. package/app/docs/components/JsonEditor.tsx +151 -0
  19. package/app/docs/components/JsonView.tsx +338 -0
  20. package/app/docs/components/KeyValue.tsx +63 -0
  21. package/app/docs/components/ProblemBanner.tsx +116 -0
  22. package/app/docs/components/ScreenErrorBoundary.tsx +141 -0
  23. package/app/docs/components/Spinner.tsx +33 -0
  24. package/app/docs/components/Table.tsx +33 -0
  25. package/app/docs/components/Tabs.tsx +226 -0
  26. package/app/docs/components/classNames.ts +4 -0
  27. package/app/docs/components/components.css +691 -0
  28. package/app/docs/components/copy.ts +89 -0
  29. package/app/docs/components/fieldContext.ts +29 -0
  30. package/app/docs/components/inputValues.ts +142 -0
  31. package/app/docs/components/inputs.tsx +387 -0
  32. package/app/docs/components/jsonParse.ts +338 -0
  33. package/app/docs/components/listKeys.ts +30 -0
  34. package/app/docs/config.ts +86 -0
  35. package/app/docs/context.ts +33 -0
  36. package/app/docs/embed.ts +74 -0
  37. package/app/docs/env.d.ts +2 -0
  38. package/app/docs/format.ts +9 -0
  39. package/app/docs/hooks/useUrlTab.ts +16 -0
  40. package/app/docs/main.tsx +11 -0
  41. package/app/docs/queryClient.ts +54 -0
  42. package/app/docs/router.tsx +194 -0
  43. package/app/docs/routing.ts +194 -0
  44. package/app/docs/screens/docs/document.ts +27 -0
  45. package/app/docs/screens/docs/http/OperationView.tsx +407 -0
  46. package/app/docs/screens/docs/http/Sidebar.tsx +190 -0
  47. package/app/docs/screens/docs/http/TryItPanel.tsx +603 -0
  48. package/app/docs/screens/docs/http/http.css +405 -0
  49. package/app/docs/screens/docs/http/index.tsx +395 -0
  50. package/app/docs/screens/docs/http/model.ts +604 -0
  51. package/app/docs/screens/docs/http/tryIt.ts +455 -0
  52. package/app/docs/screens/docs/permissions.ts +6 -0
  53. package/app/docs/screens/docs/schema/Prose.tsx +43 -0
  54. package/app/docs/screens/docs/schema/SchemaTree.tsx +319 -0
  55. package/app/docs/screens/docs/schema/index.ts +30 -0
  56. package/app/docs/screens/docs/schema/resolve.ts +466 -0
  57. package/app/docs/screens/docs/schema/schema.css +137 -0
  58. package/app/docs/screens/docs/schema/skeleton.ts +92 -0
  59. package/app/docs/screens/docs/viewer.tsx +154 -0
  60. package/app/docs/screens/docs/ws/InlineText.tsx +25 -0
  61. package/app/docs/screens/docs/ws/PayloadSchema.tsx +39 -0
  62. package/app/docs/screens/docs/ws/format.ts +82 -0
  63. package/app/docs/screens/docs/ws/index.tsx +286 -0
  64. package/app/docs/screens/docs/ws/items.tsx +786 -0
  65. package/app/docs/screens/docs/ws/model.ts +1178 -0
  66. package/app/docs/screens/docs/ws/panels.tsx +440 -0
  67. package/app/docs/screens/docs/ws/ws.css +231 -0
  68. package/app/docs/shortcuts.ts +94 -0
  69. package/app/docs/styles/base.css +847 -0
  70. package/app/docs/styles/tokens.css +143 -0
  71. package/app/docs/theme.ts +56 -0
  72. package/app/kit/unauthorized.ts +222 -0
  73. package/dist/docs/assets/main-0rchxtv5.js +18 -0
  74. package/dist/docs/assets/main-0rchxtv5.js.map +99 -0
  75. package/dist/docs/assets/main-cqgyvs86.css +1 -0
  76. package/dist/docs/manifest.json +26 -0
  77. package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js +6 -0
  78. package/dist/jobs/assets/AddFlowDialog-h6xkgx9b.js.map +11 -0
  79. package/dist/jobs/assets/AddFlowDialog-n7gtc84v.css +1 -0
  80. package/dist/jobs/assets/added-cbwsxv3e.js +4 -0
  81. package/dist/jobs/assets/added-cbwsxv3e.js.map +11 -0
  82. package/dist/jobs/assets/bodies-yyw5hmrp.js +6 -0
  83. package/dist/jobs/assets/bodies-yyw5hmrp.js.map +10 -0
  84. package/dist/jobs/assets/channel-tgkvqm8y.js +4 -0
  85. package/dist/jobs/assets/channel-tgkvqm8y.js.map +10 -0
  86. package/dist/jobs/assets/channelChecks-s4yz4yhw.js +4 -0
  87. package/dist/jobs/assets/channelChecks-s4yz4yhw.js.map +10 -0
  88. package/dist/jobs/assets/index-02427smp.css +1 -0
  89. package/dist/jobs/assets/index-09z2cjft.js +4 -0
  90. package/dist/jobs/assets/index-09z2cjft.js.map +16 -0
  91. package/dist/jobs/assets/index-18bn0gvy.css +1 -0
  92. package/dist/jobs/assets/index-47pxmyxn.js +4 -0
  93. package/dist/jobs/assets/index-47pxmyxn.js.map +10 -0
  94. package/dist/jobs/assets/index-4e55z986.js +4 -0
  95. package/dist/jobs/assets/index-4e55z986.js.map +11 -0
  96. package/dist/jobs/assets/index-5c933424.js +4 -0
  97. package/dist/jobs/assets/index-5c933424.js.map +18 -0
  98. package/dist/jobs/assets/index-5kbyqspm.css +1 -0
  99. package/dist/jobs/assets/index-5ng37hk6.css +1 -0
  100. package/dist/jobs/assets/index-5w8w7fvc.css +1 -0
  101. package/dist/jobs/assets/index-71qs5qyx.css +1 -0
  102. package/dist/jobs/assets/index-btpg4r8b.css +1 -0
  103. package/dist/jobs/assets/index-d5r5n0mm.js +4 -0
  104. package/dist/jobs/assets/index-d5r5n0mm.js.map +23 -0
  105. package/dist/jobs/assets/index-e0yy2skq.css +1 -0
  106. package/dist/jobs/assets/index-hs60b25v.js +10 -0
  107. package/dist/jobs/assets/index-hs60b25v.js.map +62 -0
  108. package/dist/jobs/assets/index-k2smrr70.js +4 -0
  109. package/dist/jobs/assets/index-k2smrr70.js.map +9 -0
  110. package/dist/jobs/assets/index-q24jw3w9.js +4 -0
  111. package/dist/jobs/assets/index-q24jw3w9.js.map +32 -0
  112. package/dist/jobs/assets/index-qfb80xen.css +1 -0
  113. package/dist/jobs/assets/index-rtynx6wj.js +4 -0
  114. package/dist/jobs/assets/index-rtynx6wj.js.map +10 -0
  115. package/dist/jobs/assets/live-6k7s6358.js +4 -0
  116. package/dist/jobs/assets/live-6k7s6358.js.map +10 -0
  117. package/dist/jobs/assets/main-0jmeh6mq.js +6 -0
  118. package/dist/jobs/assets/main-0jmeh6mq.js.map +12 -0
  119. package/dist/jobs/assets/main-3yj6bv90.js +5 -0
  120. package/dist/jobs/assets/main-3yj6bv90.js.map +10 -0
  121. package/dist/jobs/assets/main-4g69fn3x.js +5 -0
  122. package/dist/jobs/assets/main-4g69fn3x.js.map +10 -0
  123. package/dist/jobs/assets/main-4gte3eqq.js +5 -0
  124. package/dist/jobs/assets/main-4gte3eqq.js.map +10 -0
  125. package/dist/jobs/assets/main-4j1fcevh.js +5 -0
  126. package/dist/jobs/assets/main-4j1fcevh.js.map +10 -0
  127. package/dist/jobs/assets/main-54qwkz28.css +1 -0
  128. package/dist/jobs/assets/main-5grjvsy7.js +5 -0
  129. package/dist/jobs/assets/main-5grjvsy7.js.map +11 -0
  130. package/dist/jobs/assets/main-5zbx60gq.js +5 -0
  131. package/dist/jobs/assets/main-5zbx60gq.js.map +11 -0
  132. package/dist/jobs/assets/main-9prgjhg4.js +5 -0
  133. package/dist/jobs/assets/main-9prgjhg4.js.map +10 -0
  134. package/dist/jobs/assets/main-a250ffk7.js +17 -0
  135. package/dist/jobs/assets/main-a250ffk7.js.map +115 -0
  136. package/dist/jobs/assets/main-d6ge7w08.js +5 -0
  137. package/dist/jobs/assets/main-d6ge7w08.js.map +10 -0
  138. package/dist/jobs/assets/main-gngzht43.js +5 -0
  139. package/dist/jobs/assets/main-gngzht43.js.map +10 -0
  140. package/dist/jobs/assets/main-hg0hrg8j.js +5 -0
  141. package/dist/jobs/assets/main-hg0hrg8j.js.map +10 -0
  142. package/dist/jobs/assets/main-hm1ng3yg.js +5 -0
  143. package/dist/jobs/assets/main-hm1ng3yg.js.map +12 -0
  144. package/dist/jobs/assets/main-jkvd92z9.js +5 -0
  145. package/dist/jobs/assets/main-jkvd92z9.js.map +10 -0
  146. package/dist/jobs/assets/main-jv5xqd13.js +5 -0
  147. package/dist/jobs/assets/main-jv5xqd13.js.map +21 -0
  148. package/dist/jobs/assets/main-jxre7w93.js +5 -0
  149. package/dist/jobs/assets/main-jxre7w93.js.map +23 -0
  150. package/dist/jobs/assets/main-kwmqkjfx.js +5 -0
  151. package/dist/jobs/assets/main-kwmqkjfx.js.map +11 -0
  152. package/dist/jobs/assets/main-n9cf6bxm.js +5 -0
  153. package/dist/jobs/assets/main-n9cf6bxm.js.map +11 -0
  154. package/dist/jobs/assets/main-pegp0ygb.js +5 -0
  155. package/dist/jobs/assets/main-pegp0ygb.js.map +10 -0
  156. package/dist/jobs/assets/main-qr573853.js +5 -0
  157. package/dist/jobs/assets/main-qr573853.js.map +12 -0
  158. package/dist/jobs/assets/main-rq97rdr4.js +4 -0
  159. package/dist/jobs/assets/main-rq97rdr4.js.map +22 -0
  160. package/dist/jobs/assets/main-szjdmydc.js +5 -0
  161. package/dist/jobs/assets/main-szjdmydc.js.map +13 -0
  162. package/dist/jobs/assets/main-tnbhrm9n.js +7 -0
  163. package/dist/jobs/assets/main-tnbhrm9n.js.map +11 -0
  164. package/dist/jobs/assets/main-ts19j4n0.js +5 -0
  165. package/dist/jobs/assets/main-ts19j4n0.js.map +25 -0
  166. package/dist/jobs/assets/main-w93x1dzs.js +5 -0
  167. package/dist/jobs/assets/main-w93x1dzs.js.map +10 -0
  168. package/dist/jobs/assets/sections-0ksdxs8m.css +1 -0
  169. package/dist/jobs/assets/sections-0y7w4fb4.js +4 -0
  170. package/dist/jobs/assets/sections-0y7w4fb4.js.map +10 -0
  171. package/dist/jobs/assets/sse-mn0t0fh7.js +5 -0
  172. package/dist/jobs/assets/sse-mn0t0fh7.js.map +11 -0
  173. package/dist/jobs/assets/sseChecks-t7tcm7ra.js +4 -0
  174. package/dist/jobs/assets/sseChecks-t7tcm7ra.js.map +10 -0
  175. package/dist/jobs/assets/validation-ysjfy072.js +4 -0
  176. package/dist/jobs/assets/validation-ysjfy072.js.map +9 -0
  177. package/dist/jobs/manifest.json +511 -0
  178. package/dts/docs/assets.d.ts +144 -0
  179. package/dts/docs/assets.d.ts.map +1 -0
  180. package/dts/docs/browser/config.d.ts +61 -0
  181. package/dts/docs/browser/config.d.ts.map +1 -0
  182. package/dts/docs/browser/index.d.ts +12 -0
  183. package/dts/docs/browser/index.d.ts.map +1 -0
  184. package/dts/docs/docsUi.d.ts +42 -0
  185. package/dts/docs/docsUi.d.ts.map +1 -0
  186. package/dts/docs/hosted.d.ts +22 -0
  187. package/dts/docs/hosted.d.ts.map +1 -0
  188. package/dts/docs/index.d.ts +18 -0
  189. package/dts/docs/index.d.ts.map +1 -0
  190. package/dts/docs/shell.d.ts +57 -0
  191. package/dts/docs/shell.d.ts.map +1 -0
  192. package/dts/docs/types.d.ts +137 -0
  193. package/dts/docs/types.d.ts.map +1 -0
  194. package/dts/index.d.ts +21 -0
  195. package/dts/index.d.ts.map +1 -0
  196. package/dts/jobs/assets.d.ts +157 -0
  197. package/dts/jobs/assets.d.ts.map +1 -0
  198. package/dts/jobs/browser/config.d.ts +67 -0
  199. package/dts/jobs/browser/config.d.ts.map +1 -0
  200. package/dts/jobs/browser/index.d.ts +10 -0
  201. package/dts/jobs/browser/index.d.ts.map +1 -0
  202. package/dts/jobs/browser/unauthorized.d.ts +9 -0
  203. package/dts/jobs/browser/unauthorized.d.ts.map +1 -0
  204. package/dts/jobs/index.d.ts +24 -0
  205. package/dts/jobs/index.d.ts.map +1 -0
  206. package/dts/jobs/jobsUi.d.ts +63 -0
  207. package/dts/jobs/jobsUi.d.ts.map +1 -0
  208. package/dts/jobs/shell.d.ts +77 -0
  209. package/dts/jobs/shell.d.ts.map +1 -0
  210. package/dts/jobs/types.d.ts +146 -0
  211. package/dts/jobs/types.d.ts.map +1 -0
  212. package/dts/kit/browser/shared.d.ts +10 -0
  213. package/dts/kit/browser/shared.d.ts.map +1 -0
  214. package/dts/kit/browser/unauthorized.d.ts +74 -0
  215. package/dts/kit/browser/unauthorized.d.ts.map +1 -0
  216. package/dts/kit/unauthorized.d.ts +47 -0
  217. package/dts/kit/unauthorized.d.ts.map +1 -0
  218. package/guides/docs.md +170 -0
  219. package/guides/jobs.md +1120 -0
  220. package/lib/docs/assets.ts +427 -0
  221. package/lib/docs/browser/config.ts +66 -0
  222. package/lib/docs/browser/index.ts +24 -0
  223. package/lib/docs/docsUi.ts +623 -0
  224. package/lib/docs/hosted.ts +31 -0
  225. package/lib/docs/index.ts +37 -0
  226. package/lib/docs/shell.ts +156 -0
  227. package/lib/docs/types.ts +149 -0
  228. package/lib/index.ts +27 -0
  229. package/lib/jobs/assets.ts +448 -0
  230. package/lib/jobs/browser/config.ts +65 -0
  231. package/lib/jobs/browser/index.ts +9 -0
  232. package/lib/jobs/browser/unauthorized.ts +11 -0
  233. package/lib/jobs/index.ts +41 -0
  234. package/lib/jobs/jobsUi.ts +709 -0
  235. package/lib/jobs/shell.ts +205 -0
  236. package/lib/jobs/types.ts +162 -0
  237. package/lib/kit/browser/shared.ts +11 -0
  238. package/lib/kit/browser/unauthorized.ts +114 -0
  239. package/lib/kit/unauthorized.ts +222 -0
  240. package/package.json +198 -4
@@ -0,0 +1,18 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["app/jobs/screens/workers/WorkerAnalyticsCard.tsx", "app/jobs/screens/workers/WorkerConfigCard.tsx", "app/jobs/screens/workers/workerJobs.ts", "app/jobs/screens/workers/WorkerJobsCard.tsx", "app/jobs/screens/workers/WorkerSummonCard.tsx", "app/jobs/screens/workers/WorkerTargetCard.tsx", "app/jobs/screens/workers/WorkerScreen.tsx", "app/jobs/screens/workers/filters.ts", "app/jobs/screens/workers/WorkersListScreen.tsx"],
4
+ "sourcesContent": [
5
+ "import { useQuery } from \"@tanstack/react-query\";\nimport { AnalyticsError } from \"../../analytics/AnalyticsError\";\nimport { RangeCaption } from \"../../analytics/RangeCaption\";\nimport { RangePicker } from \"../../analytics/RangePicker\";\nimport { useAnalyticsRange } from \"../../analytics/useAnalyticsRange\";\nimport { useRange } from \"../../analytics/useRange\";\nimport {\n analyticsKeys,\n describeAxis,\n getWorkerAnalytics,\n} from \"../../api/analytics\";\nimport { Card } from \"../../components/Card\";\nimport { Sparkline } from \"../../components/Sparkline\";\nimport { Spinner } from \"../../components/Spinner\";\nimport { useApiClient } from \"../../context\";\nimport { formatNumber } from \"../../format\";\nimport { usePollInterval } from \"../../live\";\nimport { POLL_INTERVAL_MS } from \"../../queryClient\";\nimport { useAnalyticsGate } from \"../overview/gate\";\n\n/** Props of {@link WorkerAnalyticsCard}. */\nexport interface WorkerAnalyticsCardProps {\n /** The queue the key's workers consume. */\n queue: string;\n /** The stable key the series is recorded under — never an incarnation id. */\n workerKey: string;\n}\n\n/**\n * One worker key's throughput and busyness over a chosen range\n * (`GET /queues/:queue/analytics/workers/:key`), keyed by the stable key so a\n * redeploy does not end the series.\n *\n * Throughput and busyness come back with **their own** ranges: busyness is\n * sampled from heartbeats and served coarser. Each is captioned from its own\n * response's `range`, never from what was asked for. Rendered only where the\n * Overview's Workers section would be (`metrics.read`,\n * `features.workerMetrics`, `meta.analytics`, recording workers).\n */\nexport function WorkerAnalyticsCard({\n queue,\n workerKey,\n}: WorkerAnalyticsCardProps) {\n const api = useApiClient();\n const enabled = useAnalyticsGate(\"workers\");\n const [range, setRange] = useRange(\"range\");\n const { analytics, key, request } = useAnalyticsRange(range);\n const refetchInterval = usePollInterval(POLL_INTERVAL_MS);\n const series = useQuery({\n queryKey: analyticsKeys.worker(queue, workerKey, key),\n queryFn: ({ signal }) =>\n getWorkerAnalytics(api, queue, workerKey, request(), signal),\n enabled,\n refetchInterval,\n });\n\n if (!enabled) {\n return null;\n }\n return (\n <Card\n title=\"Throughput and busyness\"\n className=\"analytics-section\"\n actions={\n <RangePicker\n range={range}\n onChange={setRange}\n label=\"Range for this worker's numbers\"\n maxSpan={analytics?.maxSpanMs}\n />\n }\n >\n <div\n className=\"worker-analytics\"\n data-testid=\"worker-analytics\"\n >\n {series.isPending ? (\n <Spinner\n label=\"Loading this worker's numbers\"\n showLabel\n />\n ) : series.isError ? (\n <AnalyticsError\n error={series.error}\n title=\"Could not load this worker's numbers\"\n onRetry={() => void series.refetch()}\n testId=\"worker-range-not-retained\"\n />\n ) : (\n <>\n <section aria-label=\"Throughput\">\n <h3 className=\"worker-analytics-title\">Throughput</h3>\n <RangeCaption\n range={series.data.jobs.range}\n always\n testId=\"worker-jobs-range-caption\"\n />\n <div className=\"analytics-headline\">\n <dl\n className=\"stat-grid\"\n aria-label=\"Throughput totals\"\n >\n <div className=\"stat\">\n <dt className=\"stat-label\">Completed</dt>\n <dd className=\"stat-value\">\n {formatNumber(series.data.jobs.totals.completed)}\n </dd>\n </div>\n <div className=\"stat\">\n <dt className=\"stat-label\">Failed</dt>\n <dd className=\"stat-value\">\n {formatNumber(series.data.jobs.totals.failed)}\n </dd>\n <dd className=\"stat-hint\">Attempts that failed.</dd>\n </div>\n </dl>\n <div className=\"analytics-charts\">\n <Sparkline\n values={series.data.jobs.buckets.map(\n (bucket) => bucket.completed,\n )}\n secondary={series.data.jobs.buckets.map(\n (bucket) => bucket.failed,\n )}\n label={`${workerKey}: ${formatNumber(series.data.jobs.totals.completed)} completed, ${formatNumber(series.data.jobs.totals.failed)} failed, ${describeAxis(series.data.jobs.range)}`}\n width={240}\n />\n </div>\n </div>\n </section>\n {series.data.busyness ? (\n <section aria-label=\"Busyness\">\n <h3 className=\"worker-analytics-title\">Busyness</h3>\n <RangeCaption\n range={series.data.busyness.range}\n always\n testId=\"worker-busyness-range-caption\"\n />\n {series.data.busyness.totals.samples === 0 ? (\n <p\n className=\"muted\"\n data-testid=\"worker-busyness-no-samples\"\n >\n No heartbeat landed in this range, so nothing was sampled —\n the worker was not reporting, which is not the same as idle.\n </p>\n ) : (\n <div className=\"analytics-headline\">\n <dl\n className=\"stat-grid\"\n aria-label=\"Busyness totals\"\n >\n <div className=\"stat\">\n <dt className=\"stat-label\">Mean in flight</dt>\n <dd className=\"stat-value\">\n {series.data.busyness.totals.activeMean.toFixed(1)} /{\" \"}\n {formatNumber(\n series.data.busyness.totals.concurrency,\n )}\n </dd>\n <dd className=\"stat-hint\">\n Against the concurrency of the last sample.\n </dd>\n </div>\n <div className=\"stat\">\n <dt className=\"stat-label\">Most in flight</dt>\n <dd className=\"stat-value\">\n {formatNumber(series.data.busyness.totals.activeMax)}\n </dd>\n </div>\n <div className=\"stat\">\n <dt className=\"stat-label\">Samples</dt>\n <dd className=\"stat-value\">\n {formatNumber(series.data.busyness.totals.samples)}\n </dd>\n <dd className=\"stat-hint\">Heartbeats in the range.</dd>\n </div>\n </dl>\n <div className=\"analytics-charts\">\n <Sparkline\n values={series.data.busyness.buckets.map(\n (bucket) => bucket.activeMean,\n )}\n secondary={series.data.busyness.buckets.map(\n (bucket) => bucket.concurrency,\n )}\n label={`${workerKey}: mean jobs in flight against its concurrency, ${describeAxis(series.data.busyness.range)}`}\n width={240}\n />\n </div>\n </div>\n )}\n </section>\n ) : (\n <p\n className=\"muted\"\n data-testid=\"worker-busyness-absent\"\n >\n This backend records no busyness for workers.\n </p>\n )}\n </>\n )}\n </div>\n </Card>\n );\n}\n",
6
+ "import type { WorkerConfigKey } from \"../../api/contract\";\nimport type { WorkerConfigOverrideDto, WorkerDto } from \"../../api/types\";\nimport { useState } from \"react\";\nimport { WORKER_CONFIG_KEYS } from \"../../api/contract\";\nimport { resetWorkerConfig, workerInvalidations } from \"../../api/workers\";\nimport { Badge } from \"../../components/Badge\";\nimport { Button } from \"../../components/Button\";\nimport { Card } from \"../../components/Card\";\nimport { ConfirmDialog } from \"../../components/ConfirmDialog\";\nimport { KeyValue } from \"../../components/KeyValue\";\nimport { RelativeTime } from \"../../components/RelativeTime\";\nimport { Table } from \"../../components/Table\";\nimport { useApiClient } from \"../../context\";\nimport { formatNumber } from \"../../format\";\nimport { useApiMutation } from \"../../hooks/useApiMutation\";\nimport { useCanControlWorkers, workerActionGates } from \"./actions/gating\";\nimport { SETTING_HINT } from \"./actions/settingHints\";\nimport { WorkerConfigDialog } from \"./actions/WorkerConfigDialog\";\n\n/**\n * The settings that are counts; every other worker setting is in\n * milliseconds (as `WORKER_CONFIG_BOUNDS` documents).\n */\nconst COUNT_SETTINGS: ReadonlySet<WorkerConfigKey> = new Set([\n \"concurrency\",\n \"maxStalledCount\",\n]);\n\n/** A setting's value with its unit. */\nfunction formatSetting(setting: WorkerConfigKey, value: number): string {\n return COUNT_SETTINGS.has(setting)\n ? formatNumber(value)\n : `${formatNumber(value)} ms`;\n}\n\n/** Whether two instances run with the same effective values. */\nfunction sameEffective(a: WorkerDto, b: WorkerDto): boolean {\n return WORKER_CONFIG_KEYS.every(\n (setting) => a.config?.effective[setting] === b.config?.effective[setting],\n );\n}\n\n/** Props of {@link WorkerConfigCard}. */\nexport interface WorkerConfigCardProps {\n /** The queue the key's workers consume: where the override is stored. */\n queue: string;\n /** The stable key the override is stored against. */\n workerKey: string;\n /** The key's live instances, oldest first (`GET /workers?key=&queue=`). */\n instances: readonly WorkerDto[];\n /**\n * The override stored for the key when no instance is live (the listing's\n * `offline`, read with `includeOffline`): `null` when the listing says none\n * is stored — no entry for the key, or one with no values, which is what a\n * reset leaves behind — and `undefined` when it cannot tell (an API that\n * sent no `offline`).\n */\n stored: WorkerConfigOverrideDto | null | undefined;\n}\n\n/**\n * A worker key's full configuration: every setting with the value it runs\n * with, the value its code asked for, and whether an override replaces it.\n *\n * The override is stored against the key, so every instance shares it; the\n * values are read from the first instance that reports a `config`, and a note\n * says when another instance runs with different ones (a change still being\n * taken up, or replicas whose code differs). Editing opens the same Settings\n * dialog a worker row's Settings… opens.\n *\n * With no live instance there are no effective or code values to show — only\n * a running worker reports them — but the override lives on, so a caller\n * holding `workers.configure` can still reset it.\n */\nexport function WorkerConfigCard({\n queue,\n workerKey,\n instances,\n stored,\n}: WorkerConfigCardProps) {\n const canMutate = useCanControlWorkers();\n const [editing, setEditing] = useState(false);\n const source = instances.find((worker) => worker.config !== undefined);\n\n if (instances.length === 0) {\n return (\n <Card title=\"Configuration\">\n <NoInstanceConfig\n queue={queue}\n workerKey={workerKey}\n stored={stored}\n canReset={canMutate(\"workers.configure\")}\n />\n </Card>\n );\n }\n\n const config = source?.config;\n const pending = instances.some((worker) => worker.control?.pending === true);\n const canEdit =\n source !== undefined && workerActionGates(source, canMutate).configure;\n const differs =\n source !== undefined &&\n instances.some(\n (worker) => worker.config !== undefined && !sameEffective(worker, source),\n );\n\n return (\n <Card\n title=\"Configuration\"\n actions={\n <>\n {pending && (\n <Badge\n tone=\"accent\"\n title=\"A change was recorded and an instance has not taken it up yet. It applies within seconds.\"\n >\n Change pending\n </Badge>\n )}\n {canEdit && (\n <Button\n size=\"sm\"\n onClick={() => setEditing(true)}\n >\n Edit settings…\n </Button>\n )}\n </>\n }\n >\n <div data-testid=\"worker-config\">\n {config === undefined ? (\n <p className=\"muted\">\n No instance of this key reports its settings (it predates remote\n configuration, or the backend keeps no config), so there is nothing\n to show.\n </p>\n ) : (\n <>\n <p\n className=\"muted worker-config-summary\"\n data-testid=\"worker-config-summary\"\n >\n {config.overridden.length === 0 ? (\n \"No override is stored: every value is what the code asks for.\"\n ) : (\n <>\n An override (version {config.seq}\n {config.updatedAt !== undefined && (\n <>\n , written <RelativeTime value={config.updatedAt} />\n </>\n )}\n ) replaces {config.overridden.length} of the{\" \"}\n {WORKER_CONFIG_KEYS.length} settings, for every instance of\n this key.\n </>\n )}\n </p>\n {differs && (\n <p\n className=\"notice\"\n role=\"note\"\n data-testid=\"worker-config-differs\"\n >\n Not every instance runs with these values (they are{\" \"}\n <code>{source.id}</code>’s): a change is still being taken up,\n or the instances’ code differs. Each instance’s Settings… shows\n its own.\n </p>\n )}\n <Table label={`Settings of ${workerKey}`}>\n <thead>\n <tr>\n <th scope=\"col\">Setting</th>\n <th\n scope=\"col\"\n className=\"num\"\n >\n Running with\n </th>\n <th\n scope=\"col\"\n className=\"num\"\n >\n Code asks for\n </th>\n <th scope=\"col\">Source</th>\n </tr>\n </thead>\n <tbody>\n {WORKER_CONFIG_KEYS.map((setting) => {\n const overridden = config.overridden.includes(setting);\n const derived = config.derived?.includes(setting) === true;\n return (\n <tr\n key={setting}\n data-testid={`worker-setting-${setting}`}\n >\n <th\n scope=\"row\"\n title={SETTING_HINT[setting]}\n >\n <code>{setting}</code>\n </th>\n <td className=\"num\">\n {formatSetting(setting, config.effective[setting])}\n </td>\n <td className=\"num\">\n {formatSetting(setting, config.code[setting])}\n {derived && (\n <span\n className=\"muted\"\n title=\"Derived rather than given: a third of lockDuration.\"\n >\n {\" \"}\n (derived)\n </span>\n )}\n </td>\n <td>\n {overridden ? (\n <Badge\n tone=\"accent\"\n title=\"Replaced by the override stored for this key.\"\n >\n Overridden\n </Badge>\n ) : (\n <span className=\"muted\">Code</span>\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n </Table>\n </>\n )}\n </div>\n {editing && source !== undefined && (\n <WorkerConfigDialog\n worker={source}\n onClose={() => setEditing(false)}\n />\n )}\n </Card>\n );\n}\n\n/** Props of {@link NoInstanceConfig}. */\ninterface NoInstanceConfigProps {\n /** The queue the override is stored on. */\n queue: string;\n /** The stable key it is stored against. */\n workerKey: string;\n /** The stored override, `null` when none is, `undefined` when unknown. */\n stored: WorkerConfigOverrideDto | null | undefined;\n /** Whether the caller may reset it (mutation `workers.configure`, with worker control supported). */\n canReset: boolean;\n}\n\n/** The configuration of a key no instance of which is live: what is stored, and Reset. */\nfunction NoInstanceConfig({\n queue,\n workerKey,\n stored,\n canReset,\n}: NoInstanceConfigProps) {\n const api = useApiClient();\n const [confirming, setConfirming] = useState(false);\n const reset = useApiMutation({\n mutationFn: () => resetWorkerConfig(api, queue, workerKey),\n invalidate: workerInvalidations(queue),\n successMessage: `Reset ${workerKey} to the values in its code`,\n toastErrors: false,\n });\n const values = stored\n ? WORKER_CONFIG_KEYS.flatMap((setting) => {\n const value = stored.values[setting];\n return value === undefined ? [] : [{ setting, value }];\n })\n : [];\n // Nothing to reset when the listing says nothing is stored.\n const offerReset = canReset && stored !== null;\n\n return (\n <div data-testid=\"worker-config-offline\">\n <p className=\"muted\">\n No instance of this key is live, so its configuration cannot be shown\n until one reports: the values a worker runs with and the ones its code\n asks for come from the running worker.\n </p>\n {stored === null ? (\n <p data-testid=\"worker-config-none-stored\">\n No override is stored for this key.\n </p>\n ) : stored !== undefined ? (\n <>\n <p data-testid=\"worker-config-stored\">\n An override is stored for this key (version {stored.seq}, written{\" \"}\n <RelativeTime value={stored.updatedAt} />\n ). The next instance to start takes it up.\n </p>\n {values.length > 0 && (\n <KeyValue\n items={values.map(({ setting, value }) => ({\n key: setting,\n label: <code>{setting}</code>,\n value: formatSetting(setting, value),\n }))}\n />\n )}\n </>\n ) : null}\n {offerReset && (\n <Button\n size=\"sm\"\n variant=\"danger\"\n onClick={() => setConfirming(true)}\n >\n Reset to code values…\n </Button>\n )}\n <ConfirmDialog\n open={confirming}\n onClose={() => setConfirming(false)}\n title={`Reset ${workerKey}?`}\n description=\"Drops the override stored for this key, so every instance started from now on runs with what its code asks for.\"\n confirmLabel=\"Reset\"\n pendingLabel=\"Resetting…\"\n variant=\"danger\"\n onConfirm={() => reset.mutateAsync()}\n />\n </div>\n );\n}\n",
7
+ "import type { PresetRange, TimeRange } from \"../../analytics/range\";\nimport type { JobState } from \"../../api/contract\";\nimport type { FinishedWindow, JobListFilters } from \"../../api/queues\";\nimport type { StateTab } from \"../queues/jobFilters\";\nimport { readRange } from \"../../analytics/range\";\nimport { ALL_STATES } from \"../queues/jobFilters\";\nimport { clampLimit, intParam, splitList } from \"../queues/urlState\";\n\n/**\n * The URL state of a worker page's jobs section. Its parameters carry a\n * `job` prefix (and the range is `finished`), since the page's own `range`\n * is its throughput's and a bare `state` would read as the worker's.\n */\nexport const WORKER_JOB_PARAMS = {\n /** The state tab. */\n state: \"jobState\",\n /** Exact names, comma-separated. */\n name: \"jobName\",\n /** Id/name substring. */\n search: \"jobSearch\",\n /** `asc` for oldest first; absent means newest first. */\n order: \"jobOrder\",\n /** Rows skipped. */\n offset: \"jobOffset\",\n /** Page size. */\n limit: \"jobLimit\",\n /** The `finishedOn` range. */\n finished: \"finished\",\n} as const;\n\n/** The range a worker's jobs are listed over by default: the last 24 hours. */\nexport const DEFAULT_FINISHED_RANGE: PresetRange = {\n kind: \"preset\",\n seconds: 86_400,\n};\n\n/**\n * The states a `finishedOn` range can match: only a completed or dead job has\n * a `finishedOn`. A waiting, delayed, active or waiting-children job has none,\n * and neither has a failed one (it is waiting for its retry), so the API would\n * match none of them to any range.\n */\nconst FINISHED_STATES: ReadonlySet<JobState> = new Set<JobState>([\n \"completed\",\n \"dead\",\n]);\n\n/**\n * Whether the range applies to a state tab: to All (which then lists the\n * finished jobs alone) and to the finished states, never to one a job waits\n * or runs in.\n */\nexport function rangeApplies(tab: StateTab): boolean {\n return tab === ALL_STATES || FINISHED_STATES.has(tab);\n}\n\n/**\n * The jobs range as the URL holds it: `<seconds>s` for a preset, `<from>-<to>`\n * in epoch ms for a custom span, and absent for the default (the last 24\n * hours). Every preset but the default is written, including the analytics\n * default (`3600s`), since this section's default is another one.\n */\nexport function writeFinishedRange(range: TimeRange): string | null {\n if (range.kind === \"preset\") {\n return range.seconds === DEFAULT_FINISHED_RANGE.seconds\n ? null\n : `${range.seconds}s`;\n }\n return `${range.from}-${range.to}`;\n}\n\n/**\n * The jobs range read back from the URL. A preset reads as the analytics\n * presets do; a custom span needs only its end after its start (the API\n * bounds no `finishedOn` window). Anything else is the default.\n */\nexport function readFinishedRange(raw: string | null): TimeRange {\n if (raw === null || raw === \"\") {\n return DEFAULT_FINISHED_RANGE;\n }\n const custom = /^(\\d+)-(\\d+)$/.exec(raw);\n if (custom) {\n const from = Number(custom[1]);\n const to = Number(custom[2]);\n return Number.isSafeInteger(from) && Number.isSafeInteger(to) && to > from\n ? { kind: \"custom\", from, to }\n : DEFAULT_FINISHED_RANGE;\n }\n if (/^\\d+s$/.test(raw)) {\n const preset = readRange(raw);\n // `readRange` falls back to its own default for a preset nobody offers.\n return preset.kind === \"preset\" && raw === `${preset.seconds}s`\n ? preset\n : DEFAULT_FINISHED_RANGE;\n }\n return DEFAULT_FINISHED_RANGE;\n}\n\n/** The `finishedOn` window a range stands for, kept rolling for a preset. */\nexport function finishedWindow(range: TimeRange): FinishedWindow {\n return range.kind === \"preset\"\n ? { kind: \"last\", ms: range.seconds * 1_000 }\n : { kind: \"between\", from: range.from, to: range.to };\n}\n\n/**\n * Reads a worker page's jobs filters from the URL: the queue table's filters\n * under their prefixed names, newest first by default, never a total, plus\n * the key and — when the tab is one it applies to — the range.\n */\nexport function readWorkerJobFilters(\n params: URLSearchParams,\n tab: StateTab,\n workerKey: string,\n limits: { defaultPageSize: number; maxPageSize: number },\n): JobListFilters {\n const range = readFinishedRange(params.get(WORKER_JOB_PARAMS.finished));\n return {\n state: tab === ALL_STATES ? null : tab,\n offset: intParam(params, WORKER_JOB_PARAMS.offset, 0),\n limit: clampLimit(\n intParam(params, WORKER_JOB_PARAMS.limit, limits.defaultPageSize),\n limits.maxPageSize,\n ),\n order: params.get(WORKER_JOB_PARAMS.order) === \"asc\" ? \"asc\" : \"desc\",\n names: splitList(params.get(WORKER_JOB_PARAMS.name) ?? \"\"),\n search: params.get(WORKER_JOB_PARAMS.search) ?? \"\",\n // Never counted: a queue usually has one key, so the count would walk\n // the whole range the page is kept fast by.\n total: false,\n attribution: {\n workerKeys: [workerKey],\n workerIds: [],\n finished: rangeApplies(tab) ? finishedWindow(range) : null,\n },\n };\n}\n",
8
+ "import type { TimeRange } from \"../../analytics/range\";\nimport type { StateTab } from \"../queues/jobFilters\";\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { describeRange } from \"../../analytics/range\";\nimport { RangePicker } from \"../../analytics/RangePicker\";\nimport { JOB_STATES } from \"../../api/contract\";\nimport { isSendableFilterValue, listJobs, queueKeys } from \"../../api/queues\";\nimport { Card } from \"../../components/Card\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { ErrorView } from \"../../components/ErrorView\";\nimport { Pager } from \"../../components/Pager\";\nimport { Spinner } from \"../../components/Spinner\";\nimport { UrlTabs } from \"../../components/Tabs\";\nimport { useApiClient } from \"../../context\";\nimport { STATE_HINTS, STATE_LABELS } from \"../../format\";\nimport { useUrlTab } from \"../../hooks/useUrlTab\";\nimport {\n useCan,\n useFeature,\n useMeta,\n useUntargetedCanFn,\n} from \"../../meta/hooks\";\nimport { ALL_STATES } from \"../queues/jobFilters\";\nimport { JobFiltersBar, JobRows } from \"../queues/JobsTable\";\nimport { refreshInterval } from \"../queues/live\";\nimport { useUrlParams } from \"../queues/urlState\";\nimport {\n rangeApplies,\n readFinishedRange,\n readWorkerJobFilters,\n WORKER_JOB_PARAMS,\n writeFinishedRange,\n} from \"./workerJobs\";\nimport \"../queues/queues.css\";\n\n/** The card's title: honest about what attribution records — the last attempt only. */\nconst TITLE = \"Jobs whose last attempt this key ran\";\n\n/** Query parameters that restart paging when they change. */\nconst PAGING_PARAMS = [WORKER_JOB_PARAMS.offset] as const;\n\n/** The state tabs, with no counts: nothing counts one key's jobs per state. */\nconst TABS = [\n { value: ALL_STATES as StateTab, label: \"All\" },\n ...JOB_STATES.map((state) => ({\n value: state,\n label: STATE_LABELS[state],\n title: STATE_HINTS[state],\n })),\n];\n\n/** Props of {@link WorkerJobsCard}. */\nexport interface WorkerJobsCardProps {\n /** The queue the key's workers consume: the jobs listed are this queue's. */\n queue: string;\n /** The stable key whose jobs are listed: every instance's, not one incarnation's. */\n workerKey: string;\n}\n\n/**\n * The jobs whose **last** attempt a worker key ran\n * (`GET /queues/:queue/jobs?workerKey=<key>`), with the queue table's state\n * tabs, filters, rows and pager, over a `finishedOn` range that defaults to\n * the last 24 hours.\n *\n * Shown only where the backend records attribution\n * (`features.jobAttribution`): an API without it refuses the filters, so\n * nothing is read then. It needs `jobs.list` on the queue's answer. A key\n * holding a comma cannot be sent as one filter value (the API splits values\n * at commas), so for one the card says so instead of listing another key's\n * jobs.\n */\nexport function WorkerJobsCard({ queue, workerKey }: WorkerJobsCardProps) {\n const attribution = useFeature(\"jobAttribution\");\n const canList = useCan(\"jobs.list\");\n return (\n <Card title={TITLE}>\n <div data-testid=\"worker-jobs\">\n {!attribution ? (\n <div data-testid=\"worker-jobs-unrecorded\">\n <EmptyState\n title=\"Not recorded by this backend\"\n description=\"This backend does not record which worker ran a job, so a worker's jobs cannot be listed.\"\n />\n </div>\n ) : !canList ? (\n <div data-testid=\"worker-jobs-hidden\">\n <EmptyState\n title=\"Jobs hidden\"\n description=\"You may not list this queue's jobs.\"\n />\n </div>\n ) : !isSendableFilterValue(workerKey) ? (\n <div data-testid=\"worker-jobs-unfilterable\">\n <EmptyState\n title=\"Cannot be filtered to this key\"\n description=\"The API splits every filter value at commas, and this key contains one, so the job list cannot be filtered to it.\"\n />\n </div>\n ) : (\n <WorkerJobsList\n queue={queue}\n workerKey={workerKey}\n />\n )}\n </div>\n </Card>\n );\n}\n\n/** The list itself, once the card has decided it may read. */\nfunction WorkerJobsList({ queue, workerKey }: WorkerJobsCardProps) {\n const api = useApiClient();\n const meta = useMeta();\n // The job screen is routed with the Queues nav entry, which the untargeted\n // map decided; without it an id is plain text rather than a dead link.\n const linkJobs = useUntargetedCanFn()(\"queues.list\");\n const [params, update] = useUrlParams();\n const tab = useUrlTab<StateTab>(WORKER_JOB_PARAMS.state, TABS, ALL_STATES);\n const filters = readWorkerJobFilters(params, tab, workerKey, meta.limits);\n const range = readFinishedRange(params.get(WORKER_JOB_PARAMS.finished));\n const ranged = rangeApplies(tab);\n\n const jobs = useQuery({\n queryKey: queueKeys.jobs(queue, filters),\n queryFn: ({ signal }) =>\n listJobs(api, queue, filters, signal, {\n attribution: meta.features.jobAttribution,\n // Newest added first on every tab where the backend sorts by\n // `createdAt`; otherwise no `sort` (the API would refuse it).\n createdOrder: meta.features.addedByState,\n }),\n // No queue event reaches this page, so it polls at the base interval.\n refetchInterval: refreshInterval(\"jobs\"),\n placeholderData: keepPreviousData,\n });\n\n const setRange = (next: TimeRange) =>\n update({\n [WORKER_JOB_PARAMS.finished]: writeFinishedRange(next),\n [WORKER_JOB_PARAMS.offset]: null,\n });\n const selectionKey = JSON.stringify([\n filters.state,\n filters.order,\n filters.names,\n filters.search,\n ranged ? params.get(WORKER_JOB_PARAMS.finished) : null,\n ]);\n\n return (\n <section\n className=\"jobs-section\"\n aria-label=\"Jobs this key ran\"\n >\n <p\n className=\"muted\"\n data-testid=\"worker-jobs-last-attempt\"\n >\n Only a job&apos;s last attempt is recorded: a job that failed on another\n worker and then ran here is listed on this page alone.\n </p>\n <UrlTabs\n tabs={TABS}\n param={WORKER_JOB_PARAMS.state}\n defaultValue={ALL_STATES}\n resetParams={PAGING_PARAMS}\n label=\"Job states\"\n />\n {ranged ? (\n <div data-testid=\"worker-jobs-range\">\n <RangePicker\n range={range}\n onChange={setRange}\n label=\"Range these jobs finished in\"\n maxSpan={null}\n />\n <p className=\"muted\">\n {tab === ALL_STATES\n ? `Finished in: ${describeRange(range)}. Only a completed or dead job has a finish time, so a range lists those alone; choose Active for the jobs this key is running now.`\n : `Finished in: ${describeRange(range)}.`}\n </p>\n </div>\n ) : (\n <p\n className=\"muted\"\n data-testid=\"worker-jobs-no-range\"\n >\n {filters.state === null ? \"These\" : STATE_LABELS[filters.state]} jobs\n have not finished, so no date range applies to them: this lists every\n one whose last attempt this key ran, whenever that was.\n </p>\n )}\n <JobFiltersBar\n key={`${params.get(WORKER_JOB_PARAMS.name) ?? \"\"}|${filters.search}`}\n filters={filters}\n searchIndexed={meta.features.search}\n onApply={(patch) =>\n update({\n [WORKER_JOB_PARAMS.name]: patch.name,\n [WORKER_JOB_PARAMS.search]: patch.search,\n [WORKER_JOB_PARAMS.offset]: null,\n })\n }\n onOrder={(order) =>\n update({\n [WORKER_JOB_PARAMS.order]: order === \"asc\" ? \"asc\" : null,\n [WORKER_JOB_PARAMS.offset]: null,\n })\n }\n />\n {jobs.isPending ? (\n <Spinner\n label=\"Loading this key's jobs\"\n showLabel\n />\n ) : jobs.isError ? (\n <ErrorView\n error={jobs.error}\n title=\"Could not load this key's jobs\"\n onRetry={() => void jobs.refetch()}\n />\n ) : (\n <>\n <JobRows\n key={selectionKey}\n queue={queue}\n items={jobs.data.items}\n label={`Jobs whose last attempt ${workerKey} ran`}\n linkJobs={linkJobs}\n processedBy={false}\n empty={\n <div data-testid=\"worker-jobs-empty\">\n <EmptyState\n title=\"No jobs\"\n description=\"No job whose last attempt this key ran matches the state, range and filters. Which worker ran a job is kept only as long as the job itself: jobs removed on completion are not listed.\"\n />\n </div>\n }\n />\n <Pager\n label=\"Pages of this key's jobs\"\n offset={filters.offset}\n limit={filters.limit}\n itemCount={jobs.data.items.length}\n hasMore={jobs.data.page.hasMore}\n maxPageSize={meta.limits.maxPageSize}\n disabled={jobs.isFetching && jobs.isPlaceholderData}\n onChange={(next) =>\n update({\n [WORKER_JOB_PARAMS.offset]:\n next.offset > 0 ? String(next.offset) : null,\n [WORKER_JOB_PARAMS.limit]: String(next.limit),\n })\n }\n />\n </>\n )}\n </section>\n );\n}\n",
9
+ "import type { WorkerDto, WorkerSummonProvenanceDto } from \"../../api/types\";\nimport { Card } from \"../../components/Card\";\nimport { RelativeTime } from \"../../components/RelativeTime\";\nimport { Table } from \"../../components/Table\";\nimport { displayText, plural } from \"../../format\";\nimport {\n SUMMON_NOTE,\n summonModeHint,\n summonModeLabel,\n summonUnitHint,\n} from \"./summon\";\n\n/** Props of {@link WorkerSummonCard}. */\nexport interface WorkerSummonCardProps {\n /** The stable key, for the table's accessible name. */\n workerKey: string;\n /** The key's live instances, as the Instances card lists them. */\n instances: readonly WorkerDto[];\n}\n\n/** An instance that reports a summon, with it. */\ninterface Summoned {\n /** The instance's incarnation id. */\n id: string;\n /** What it reports. */\n summon: WorkerSummonProvenanceDto;\n}\n\n/**\n * Where a worker key's summoned instances came from: one row per instance\n * reporting `summon`, with the attempt's id, the summoner's kind, the summon\n * group whose shared unit it runs in, the mode and deadline the summoner\n * **requested**, and the platform's handle when the API sends it\n * (`serialize.exposeSummonHandles`). The group's column shows \"—\" for an\n * instance summoned for one queue, beside one that names a group.\n *\n * **Rendered only when an instance reports a summon.** An instance with no\n * `summon` was not summoned or is too old to say which, so it has no row, and\n * a key none of whose instances report one has no card at all. When some do\n * and some do not, a line counts the rest without claiming why.\n *\n * One row per instance, not per value: every summon is its own attempt with\n * its own id, so instances never share one the way they share a target.\n */\nexport function WorkerSummonCard({\n workerKey,\n instances,\n}: WorkerSummonCardProps) {\n const summoned: Summoned[] = [];\n for (const worker of instances) {\n if (worker.summon !== undefined) {\n summoned.push({ id: worker.id, summon: worker.summon });\n }\n }\n if (summoned.length === 0) {\n return null;\n }\n const others = instances.length - summoned.length;\n const showKind = summoned.some(({ summon }) => summon.kind !== undefined);\n // The summon group whose shared unit it runs in: absent for a unit\n // summoned for one queue, so the column exists only when one names a group.\n const showGroup = summoned.some(({ summon }) => summon.group !== undefined);\n // The mode it actually runs in, beside the one requested: absent on a\n // worker not run by `runSummoned`, or too old to say.\n const showResolved = summoned.some(\n ({ summon }) => summon.resolvedMode !== undefined,\n );\n const showMode = summoned.some(({ summon }) => summon.mode !== undefined);\n const showDeadline = summoned.some(\n ({ summon }) => summon.deadlineAt !== undefined,\n );\n // Only when the API sent one (`serialize.exposeSummonHandles`): nothing\n // hints at a handle it withheld.\n const showHandle = summoned.some(({ summon }) => summon.handle !== undefined);\n return (\n <Card title=\"Summoned\">\n <div data-testid=\"worker-summon\">\n <p className=\"muted worker-target-note\">{SUMMON_NOTE}</p>\n <Table label={`Summoned instances of ${workerKey}`}>\n <thead>\n <tr>\n <th scope=\"col\">Instance</th>\n <th scope=\"col\">Summon</th>\n {showKind && <th scope=\"col\">Summoner</th>}\n {showGroup && <th scope=\"col\">Unit of</th>}\n {showResolved && <th scope=\"col\">Runs as</th>}\n {showMode && <th scope=\"col\">Requested mode</th>}\n {showDeadline && <th scope=\"col\">Requested deadline</th>}\n {showHandle && <th scope=\"col\">Handle</th>}\n </tr>\n </thead>\n <tbody>\n {summoned.map(({ id, summon }) => (\n <tr\n key={id}\n data-testid={`worker-summon-row-${id}`}\n >\n <th scope=\"row\">\n <code>{id}</code>\n </th>\n <td>\n <code>{displayText(summon.id)}</code>\n </td>\n {showKind && (\n <td>\n {summon.kind !== undefined && displayText(summon.kind)}\n </td>\n )}\n {showGroup && (\n <td\n title={\n summon.group === undefined\n ? undefined\n : summonUnitHint(summon.group)\n }\n >\n {summon.group === undefined\n ? \"—\"\n : displayText(summon.group)}\n </td>\n )}\n {showResolved && (\n <td\n title={\n summon.resolvedMode === undefined\n ? undefined\n : \"The mode it actually runs in: its own option, else the requested mode, else exit when idle.\"\n }\n >\n {summon.resolvedMode !== undefined &&\n summonModeLabel(summon.resolvedMode)}\n </td>\n )}\n {showMode && (\n <td\n title={\n summon.mode === undefined\n ? undefined\n : summonModeHint(summon.mode)\n }\n >\n {summon.mode !== undefined && summonModeLabel(summon.mode)}\n </td>\n )}\n {showDeadline && (\n <td>\n {summon.deadlineAt !== undefined && (\n <RelativeTime\n value={summon.deadlineAt}\n hint=\"As requested by the summoner.\"\n />\n )}\n </td>\n )}\n {showHandle && (\n <td>\n {summon.handle !== undefined && (\n <code className=\"worker-target-file\">\n {displayText(summon.handle)}\n </code>\n )}\n </td>\n )}\n </tr>\n ))}\n </tbody>\n </Table>\n {others > 0 && (\n <p\n className=\"muted\"\n data-testid=\"worker-summon-others\"\n >\n {plural(others, \"other instance\")} of this key{\" \"}\n {others === 1 ? \"reports\" : \"report\"} no summon.\n </p>\n )}\n </div>\n </Card>\n );\n}\n",
10
+ "import type { WorkerDto, WorkerTargetInfoDto } from \"../../api/types\";\nimport type { TargetGroup } from \"./target\";\nimport { Card } from \"../../components/Card\";\nimport { KeyValue } from \"../../components/KeyValue\";\nimport { Table } from \"../../components/Table\";\nimport { displayText, plural } from \"../../format\";\nimport {\n groupTargets,\n processorLabel,\n TARGET_FIXED_NOTE,\n targetKindHint,\n targetKindLabel,\n} from \"./target\";\n\n/** How many instance ids a row of the mixed-targets table names before counting the rest. */\nconst NAMED_INSTANCES = 5;\n\n/** What a card says for instances too old to report a target. */\nconst PREDATES =\n \"This worker predates target reporting, so it does not say where its attempts run.\";\n\n/** Props of {@link WorkerTargetCard}. */\nexport interface WorkerTargetCardProps {\n /** The stable key, for the mixed-targets table's accessible name. */\n workerKey: string;\n /** The key's live instances, as the Instances card lists them. */\n instances: readonly WorkerDto[];\n}\n\n/** A muted dash for a worker that reports no target, explained on hover. */\nfunction Unknown({ testId }: { testId?: string }) {\n return (\n <span\n className=\"muted\"\n title=\"Not reported: this worker predates target reporting.\"\n data-testid={testId}\n >\n —\n </span>\n );\n}\n\n/** One target as label/value rows: where it runs, what it runs, and what the API sent besides. */\nfunction TargetFacts({\n target,\n}: {\n /** The target every instance reports, or `undefined` when none reports one. */\n target: WorkerTargetInfoDto | undefined;\n}) {\n if (target === undefined) {\n return (\n <KeyValue\n items={[\n {\n label: \"Runs in\",\n value: <Unknown testId=\"worker-target-kind\" />,\n hint: <span data-testid=\"worker-target-predates\">{PREDATES}</span>,\n },\n ]}\n />\n );\n }\n return (\n <KeyValue\n items={[\n {\n label: \"Runs in\",\n value: (\n <span data-testid=\"worker-target-kind\">\n {targetKindLabel(target.kind)}\n </span>\n ),\n hint: targetKindHint(target.kind),\n },\n {\n label: \"Processor\",\n value: (\n <span data-testid=\"worker-target-processor\">\n {processorLabel(target.processor)}\n </span>\n ),\n },\n // Only a custom target carries a name.\n target.name !== undefined && {\n label: \"Name\",\n value: (\n <code data-testid=\"worker-target-name\">\n {displayText(target.name)}\n </code>\n ),\n },\n // Only when the API sent it (`serialize.exposeProcessorFiles`):\n // nothing hints at a path it withheld.\n target.file !== undefined && {\n label: \"File\",\n value: (\n <code\n className=\"worker-target-file\"\n data-testid=\"worker-target-file\"\n >\n {displayText(target.file)}\n </code>\n ),\n },\n ]}\n />\n );\n}\n\n/** The instances a mixed-targets row covers: a count, and the first few ids. */\nfunction InstanceIds({ ids }: { ids: readonly string[] }) {\n const named = ids.slice(0, NAMED_INSTANCES);\n const rest = ids.length - named.length;\n return (\n <>\n {plural(ids.length, \"instance\")}:{\" \"}\n {named.map((id, index) => (\n <span key={id}>\n {index > 0 && \", \"}\n <code>{id}</code>\n </span>\n ))}\n {rest > 0 && ` and ${plural(rest, \"more\", \"more\")}`}\n </>\n );\n}\n\n/** One row per target the instances report, when they do not all report one. */\nfunction MixedTargets({\n workerKey,\n groups,\n}: {\n /** The key, for the table's accessible name. */\n workerKey: string;\n /** The groups, two or more. */\n groups: readonly TargetGroup[];\n}) {\n const showName = groups.some((group) => group.target?.name !== undefined);\n const showFile = groups.some((group) => group.target?.file !== undefined);\n return (\n <>\n <p\n className=\"notice\"\n role=\"note\"\n data-testid=\"worker-target-differs\"\n >\n Not every instance of this key runs the same way: a redeploy is still\n rolling out, or the instances were built from different code. Each row\n is one way, with the instances that report it.\n </p>\n <Table label={`Targets of ${workerKey}`}>\n <thead>\n <tr>\n <th scope=\"col\">Runs in</th>\n <th scope=\"col\">Processor</th>\n {showName && <th scope=\"col\">Name</th>}\n {showFile && <th scope=\"col\">File</th>}\n <th scope=\"col\">Instances</th>\n </tr>\n </thead>\n <tbody>\n {groups.map(({ target, ids }) => (\n <tr\n key={ids[0]}\n data-testid=\"worker-target-group\"\n >\n <th\n scope=\"row\"\n title={\n target === undefined ? undefined : targetKindHint(target.kind)\n }\n >\n {target === undefined ? (\n <Unknown />\n ) : (\n targetKindLabel(target.kind)\n )}\n </th>\n <td>\n {target === undefined ? (\n <Unknown />\n ) : (\n processorLabel(target.processor)\n )}\n </td>\n {showName && (\n <td>\n {target?.name !== undefined && (\n <code>{displayText(target.name)}</code>\n )}\n </td>\n )}\n {showFile && (\n <td>\n {target?.file !== undefined && (\n <code className=\"worker-target-file\">\n {displayText(target.file)}\n </code>\n )}\n </td>\n )}\n <td>\n <InstanceIds ids={ids} />\n </td>\n </tr>\n ))}\n </tbody>\n </Table>\n </>\n );\n}\n\n/**\n * Where a worker key's attempts run: in process, on a worker thread, in a\n * child process or on a custom target, whether they run a function or a\n * processor file, a custom target's name, and the file's path when the API\n * sends it.\n *\n * **A fact about how the worker was built, not a setting**: the target is not\n * among the settings an override replaces, and changing it is a redeploy, so\n * the card offers nothing to change and says so.\n *\n * **A worker too old to report it shows \"—\"** and a line saying so, never\n * \"In process\": that is only the default, and reading absence as it would\n * mislabel every worker not yet upgraded.\n *\n * **Instances that disagree are all shown.** One key's instances normally run\n * one build and report one target; when they report different ones — a\n * redeploy mid-rollout — the card lists each target with the instances that\n * report it, rather than the first instance's alone, which would state one\n * build's answer for all of them.\n */\nexport function WorkerTargetCard({\n workerKey,\n instances,\n}: WorkerTargetCardProps) {\n const groups = groupTargets(instances);\n return (\n <Card title=\"Target\">\n <div data-testid=\"worker-target\">\n {groups.length === 0 ? (\n <p className=\"muted\">\n Where this key's attempts run is known once an instance reports.\n </p>\n ) : (\n <>\n <p className=\"muted worker-target-note\">{TARGET_FIXED_NOTE}</p>\n {groups.length === 1 ? (\n <TargetFacts target={groups[0].target} />\n ) : (\n <MixedTargets\n workerKey={workerKey}\n groups={groups}\n />\n )}\n </>\n )}\n </div>\n </Card>\n );\n}\n",
11
+ "import type { WorkerConfigOverrideDto } from \"../../api/types\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { queuePath } from \"../../api/queues\";\nimport { listWorkers, workerKeys } from \"../../api/workers\";\nimport { Card } from \"../../components/Card\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { ErrorView } from \"../../components/ErrorView\";\nimport { KeyValue } from \"../../components/KeyValue\";\nimport { Spinner } from \"../../components/Spinner\";\nimport { useApiClient } from \"../../context\";\nimport { plural } from \"../../format\";\nimport { useCan, useUntargetedCanFn } from \"../../meta/hooks\";\nimport { Link } from \"../../router\";\nimport { useParams } from \"../../routing\";\nimport { useQueueWorkersLive, useWorkerRefetchInterval } from \"./live\";\nimport { WorkerAnalyticsCard } from \"./WorkerAnalyticsCard\";\nimport { WorkerConfigCard } from \"./WorkerConfigCard\";\nimport { WorkerJobsCard } from \"./WorkerJobsCard\";\nimport { WorkerSummonCard } from \"./WorkerSummonCard\";\nimport { WorkerTable } from \"./WorkerTable\";\nimport { WorkerTargetCard } from \"./WorkerTargetCard\";\nimport \"./workers.css\";\n\n/**\n * Instances per page in the Instances card.\n *\n * Ten, not the 25 a worker *list* pages at: these are the live instances of\n * **one** key, which is one or a few on most deployments, so the card almost\n * never shows a pager — and a key replicated across a large fleet stays a card\n * rather than taking over the page above its configuration.\n */\nconst INSTANCE_PAGE_SIZE = 10;\n\n/**\n * The override a listing's `offline` entry stores, or `null` when it stores\n * nothing. Resetting a key no instance of which is live **empties** its entry\n * rather than deleting it — the entry keeps its `seq`, so a later write still\n * conflicts correctly — and an entry with no values overrides nothing.\n */\nfunction storedOverride(\n entry: WorkerConfigOverrideDto | undefined,\n): WorkerConfigOverrideDto | null {\n return entry !== undefined &&\n Object.values(entry.values).some((value) => value !== undefined)\n ? entry\n : null;\n}\n\n/**\n * `/workers/:queue/:key`: one **stable worker key** — every live instance of\n * it with its controls, where its attempts run, the configuration the key's\n * override gives them, its throughput and busyness, and the jobs whose last\n * attempt it ran (where the backend records that, `features.jobAttribution`).\n *\n * Addressed by the pair, because a key is unique only within its queue. The\n * route sits inside the queue's `PermissionScope`, since worker actions\n * authorize against the queue. One read, `GET /workers?queue=&key=` with\n * `includeOffline`, gives the instances and, when none is live, the override\n * stored for the key.\n */\nexport function WorkerScreen() {\n const { queue = \"\", key = \"\" } = useParams<{ queue: string; key: string }>();\n const api = useApiClient();\n const canList = useCan(\"workers.list\");\n // The queue link leads to another section, whose route the untargeted map\n // decided; the queue's own answer says nothing about it.\n const canQueues = useUntargetedCanFn()(\"queues.list\");\n const refetchInterval = useWorkerRefetchInterval();\n // A worker event is authorized as its queue's, like the queue panel's.\n useQueueWorkersLive(queue, useCan(\"queues.read\") && canList);\n const workers = useQuery({\n queryKey: workerKeys.ofKey(queue, key),\n queryFn: ({ signal }) =>\n listWorkers(\n api,\n { queue: [queue], key: [key], includeOffline: true },\n signal,\n ),\n refetchInterval,\n enabled: canList,\n });\n\n if (!canList) {\n return (\n <div className=\"screen\">\n <h1 className=\"screen-title\">{key}</h1>\n <EmptyState\n title=\"Instances hidden\"\n description=\"You may not list workers on this queue, so its instances and configuration are not shown.\"\n />\n {/*\n Neither needs `workers.list`: the analytics are `metrics.read` and\n the workers-metrics gate, the jobs `jobs.list`. Each section\n degrades on its own, so hiding the instances hides only them.\n */}\n <WorkerAnalyticsCard\n queue={queue}\n workerKey={key}\n />\n <WorkerJobsCard\n queue={queue}\n workerKey={key}\n />\n </div>\n );\n }\n\n const instances = workers.data?.items ?? [];\n // `offline` is present only when the API honoured `includeOffline`.\n const offline = workers.data?.offline;\n const stored =\n offline === undefined\n ? undefined\n : storedOverride(\n offline.find(\n (override) => override.queue === queue && override.key === key,\n ),\n );\n const service = instances.find(\n (worker) => worker.service !== undefined,\n )?.service;\n const showHost = instances.some(\n (worker) => worker.host !== undefined || worker.pid !== undefined,\n );\n\n return (\n <div\n className=\"screen\"\n data-testid=\"worker-screen\"\n >\n <div className=\"worker-header\">\n <h1 className=\"screen-title\">\n <span className=\"visually-hidden\">Worker </span>\n {key}\n </h1>\n <KeyValue\n className=\"worker-header-facts\"\n items={[\n {\n label: \"Queue\",\n value: canQueues ? (\n <Link to={queuePath(queue)}>{queue}</Link>\n ) : (\n queue\n ),\n },\n {\n label: \"Service\",\n value:\n instances.length === 0\n ? null\n : (service ?? <span className=\"muted\">None named</span>),\n hint:\n instances.length === 0\n ? \"Known once an instance reports.\"\n : undefined,\n },\n ]}\n />\n </div>\n {workers.isPending ? (\n <Spinner\n label=\"Loading this worker\"\n showLabel\n />\n ) : workers.isError ? (\n <ErrorView\n error={workers.error}\n title=\"Could not load this worker\"\n onRetry={() => void workers.refetch()}\n />\n ) : (\n <>\n <Card\n title=\"Instances\"\n actions={\n <span className=\"muted\">\n {plural(instances.length, \"live instance\")}\n </span>\n }\n >\n <div data-testid=\"worker-instances\">\n {instances.length === 0 ? (\n <EmptyState\n title=\"No live instance\"\n description=\"No worker carrying this key is reporting now. One appears here when a process starts it; a stopped worker that lapsed is not listed.\"\n />\n ) : (\n <WorkerTable\n workers={instances}\n label={`Instances of ${key}`}\n showQueue={false}\n showHost={showHost}\n showMemory\n pageSize={INSTANCE_PAGE_SIZE}\n />\n )}\n </div>\n </Card>\n <WorkerTargetCard\n workerKey={key}\n instances={instances}\n />\n <WorkerSummonCard\n workerKey={key}\n instances={instances}\n />\n <WorkerConfigCard\n queue={queue}\n workerKey={key}\n instances={instances}\n stored={stored}\n />\n </>\n )}\n <WorkerAnalyticsCard\n queue={queue}\n workerKey={key}\n />\n <WorkerJobsCard\n queue={queue}\n workerKey={key}\n />\n </div>\n );\n}\n",
12
+ "import type { WorkerState } from \"../../api/contract\";\nimport type { WorkerListQuery } from \"../../api/types\";\nimport { WORKER_STATES } from \"../../api/contract\";\n\n/**\n * The Workers page's server-side filters, as the URL carries them: one value\n * each, `queue`, `service`, `host` and `state`. `GET /workers` takes each as\n * an exact, repeatable filter and ANDs them; the page offers one value per\n * filter, which is what a picker over the live list can offer.\n */\nexport interface WorkerFilterValues {\n /** `?queue=`: only workers consuming this queue; `\"\"` for any. */\n queue: string;\n /** `?service=`: only workers in this service; `\"\"` for any. */\n service: string;\n /** `?host=`: only workers on this host; `\"\"` for any. Sent only when the API exposes hosts. */\n host: string;\n /** `?state=`: only workers in this state; `\"\"` for any. A value that is not a worker state is ignored. */\n state: WorkerState | \"\";\n}\n\n/** The URL parameters the filters live in, in the order the filter row shows them. */\nexport const WORKER_FILTER_PARAMS = [\n \"queue\",\n \"service\",\n \"host\",\n \"state\",\n] as const satisfies readonly (keyof WorkerFilterValues)[];\n\n/** Whether `value` is one of the contract's worker states. */\nfunction isWorkerState(value: string): value is WorkerState {\n return (WORKER_STATES as readonly string[]).includes(value);\n}\n\n/** Reads the filters from the query string; a missing or unknown value means \"any\". */\nexport function readWorkerFilters(params: URLSearchParams): WorkerFilterValues {\n const state = params.get(\"state\")?.trim() ?? \"\";\n return {\n queue: params.get(\"queue\")?.trim() ?? \"\",\n service: params.get(\"service\")?.trim() ?? \"\",\n host: params.get(\"host\")?.trim() ?? \"\",\n state: isWorkerState(state) ? state : \"\",\n };\n}\n\n/** Whether any filter is set. */\nexport function hasWorkerFilters(filters: WorkerFilterValues): boolean {\n return WORKER_FILTER_PARAMS.some((name) => filters[name] !== \"\");\n}\n\n/**\n * The `GET /workers` query for these filters. `host` is left out unless\n * `hostsShown`: the API answers 400 `INVALID_ARGUMENT` to a host filter when\n * it hides hosts, and a link carrying `?host=` must not break the page.\n */\nexport function workerListQuery(\n filters: WorkerFilterValues,\n hostsShown: boolean,\n): WorkerListQuery {\n const query: WorkerListQuery = {};\n if (filters.queue) {\n query.queue = [filters.queue];\n }\n if (filters.service) {\n query.service = [filters.service];\n }\n if (filters.host && hostsShown) {\n query.host = [filters.host];\n }\n if (filters.state) {\n query.state = [filters.state];\n }\n return query;\n}\n",
13
+ "import type { WorkerState } from \"../../api/contract\";\nimport type { WorkerServer, WorkerServiceGroup } from \"../../api/workers\";\nimport type { WorkerFilterValues } from \"./filters\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { useDeferredValue, useId } from \"react\";\nimport { WORKER_STATES } from \"../../api/contract\";\nimport {\n distinctValues,\n groupByService,\n hostsExposed,\n listWorkers,\n matchesWorker,\n workerKeys,\n} from \"../../api/workers\";\nimport { Button } from \"../../components/Button\";\nimport { Card } from \"../../components/Card\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { ErrorView } from \"../../components/ErrorView\";\nimport { Field } from \"../../components/Field\";\nimport { Select } from \"../../components/inputs\";\nimport { Spinner } from \"../../components/Spinner\";\nimport { useApiClient } from \"../../context\";\nimport { plural } from \"../../format\";\nimport { SEARCH_SHORTCUT } from \"../../layout/shortcuts\";\nimport { useCan } from \"../../meta/hooks\";\nimport { useUrlParams } from \"../queues/urlState\";\nimport {\n hasWorkerFilters,\n readWorkerFilters,\n WORKER_FILTER_PARAMS,\n workerListQuery,\n} from \"./filters\";\nimport { useWorkerListLive, useWorkerRefetchInterval } from \"./live\";\nimport { useWorkerPagesRouted } from \"./routed\";\nimport { WorkerTable } from \"./WorkerTable\";\nimport \"./workers.css\";\n\n/**\n * Workers per page in a server's section.\n *\n * Twenty-five: a server usually runs a handful, so the common section is one\n * page with no pager at all, while a host running a worker per queue — dozens\n * of rows carrying state, counts, memory and controls — is cut down to a\n * readable block without hiding which server it belongs to. The pager is per\n * **server section**, the table it pages: a pager per service card, or one\n * over the whole page, would have to page across the host headings that say\n * where a worker runs.\n */\nconst SERVER_PAGE_SIZE = 25;\n\n/** A server's heading: `host · pid N`, or a note when the API hides hosts. */\nfunction serverTitle(server: WorkerServer): string {\n if (server.host === null && server.pid === null) {\n return \"Hosts hidden by the API\";\n }\n return `${server.host ?? \"unknown host\"} · pid ${server.pid ?? \"?\"}`;\n}\n\n/** One server's workers, as a section inside its service's card. */\nfunction ServerSection({\n server,\n linkQueues,\n linkKeys,\n}: {\n /** The server and its workers. */\n server: WorkerServer;\n /** Whether queue names link to the queue screen (`queues.list`). */\n linkQueues: boolean;\n /** Whether stable keys link to their worker page. */\n linkKeys: boolean;\n}) {\n const title = serverTitle(server);\n return (\n <section\n className=\"worker-server\"\n data-testid={`worker-server-${server.key}`}\n >\n <h3 className=\"worker-server-title\">\n {title}{\" \"}\n <span className=\"muted\">{plural(server.workers.length, \"worker\")}</span>\n </h3>\n <WorkerTable\n workers={server.workers}\n label={`Workers on ${title}`}\n linkQueues={linkQueues}\n linkKeys={linkKeys}\n showMemory\n pageSize={SERVER_PAGE_SIZE}\n />\n </section>\n );\n}\n\n/** One service's servers: a card per service, a section per server inside it. */\nfunction ServiceCard({\n group,\n linkQueues,\n linkKeys,\n}: {\n /** The service, its servers and its worker count. */\n group: WorkerServiceGroup;\n /** Whether queue names link to the queue screen (`queues.list`). */\n linkQueues: boolean;\n /** Whether stable keys link to their worker page. */\n linkKeys: boolean;\n}) {\n return (\n <Card\n title={group.service ?? \"No service named\"}\n actions={<span className=\"muted\">{plural(group.count, \"worker\")}</span>}\n >\n <div data-testid={`worker-service-${group.service ?? \"none\"}`}>\n {group.service === null && (\n <p className=\"muted worker-service-note\">\n These processes set no <code>service</code> on their BunJobs\n context, so they are grouped by host and pid alone.\n </p>\n )}\n {group.servers.map((server) => (\n <ServerSection\n key={server.key}\n server={server}\n linkQueues={linkQueues}\n linkKeys={linkKeys}\n />\n ))}\n </div>\n </Card>\n );\n}\n\n/** How each worker state reads in the State filter. */\nconst STATE_LABELS: Readonly<Record<WorkerState, string>> = {\n running: \"Running\",\n paused: \"Paused\",\n stopping: \"Stopping\",\n stopped: \"Stopped\",\n restarting: \"Restarting\",\n};\n\n/** Props of {@link FilterSelect}. */\ninterface FilterSelectProps {\n /** The visible label, e.g. \"Queue\". */\n label: string;\n /** What \"no filter\" reads as, e.g. \"Any queue\". */\n any: string;\n /** The value in force; `\"\"` for any. */\n value: string;\n /** The values to offer, from the unfiltered list. The value in force is offered too, even when no live worker has it any more. */\n choices: readonly string[];\n /** Sets the filter; `\"\"` clears it. */\n onChange: (value: string) => void;\n /** How a value reads, when not as itself. */\n labelOf?: (value: string) => string;\n}\n\n/** One server-side filter: a select over the values the live workers have. */\nfunction FilterSelect({\n label,\n any,\n value,\n choices,\n onChange,\n labelOf,\n}: FilterSelectProps) {\n const values =\n value !== \"\" && !choices.includes(value) ? [value, ...choices] : choices;\n return (\n <Field label={label}>\n <Select\n options={[\n { value: \"\", label: any },\n ...values.map((choice) => ({\n value: choice,\n label: labelOf ? labelOf(choice) : choice,\n })),\n ]}\n value={value}\n onChange={onChange}\n />\n </Field>\n );\n}\n\n/**\n * `/workers`: every live worker in the namespace, grouped by service, then by\n * the server hosting it.\n *\n * Queue, service, host and state are **server-side** filters, sent to\n * `GET /workers` and kept in the URL; the search box narrows what came back,\n * in the browser. The choices each filter offers come from the unfiltered\n * list, which is the same read (and cache entry) the page shows when nothing\n * is filtered, so an unfiltered page costs one request.\n */\nexport function WorkersListScreen() {\n const api = useApiClient();\n const canList = useCan(\"workers.list\");\n const canQueues = useCan(\"queues.list\");\n const linkKeys = useWorkerPagesRouted();\n const [params, update] = useUrlParams();\n const filterId = useId();\n const filter = params.get(\"search\") ?? \"\";\n const deferredFilter = useDeferredValue(filter);\n const filters = readWorkerFilters(params);\n\n const refetchInterval = useWorkerRefetchInterval();\n useWorkerListLive(canList);\n // The unfiltered list: what each filter offers, and whether hosts show.\n const everything = useQuery({\n queryKey: workerKeys.list(),\n queryFn: ({ signal }) => listWorkers(api, {}, signal),\n refetchInterval,\n enabled: canList,\n });\n // Hosts are hidden when the workers carry none. Until the list answers the\n // page cannot tell, so a `?host=` waits for it; if it failed, the API\n // decides (and says so).\n const hostsShown = everything.isSuccess\n ? (hostsExposed(everything.data.items) ?? false)\n : everything.isError;\n const hostKnown = everything.isSuccess || everything.isError;\n const query = workerListQuery(filters, hostsShown);\n const workers = useQuery({\n queryKey: workerKeys.list(query),\n queryFn: ({ signal }) => listWorkers(api, query, signal),\n refetchInterval,\n enabled: canList && (filters.host === \"\" || hostKnown),\n });\n\n if (!canList) {\n return (\n <div className=\"screen\">\n <h1 className=\"screen-title\">Workers</h1>\n <EmptyState\n title=\"Nothing to show\"\n description=\"You may not list workers on this API.\"\n />\n </div>\n );\n }\n\n const live = everything.data?.items ?? [];\n const filtered = hasWorkerFilters(filters);\n const all = workers.data?.items ?? [];\n const shown = all.filter((worker) => matchesWorker(worker, deferredFilter));\n const services = groupByService(shown);\n const serverCount = services.reduce(\n (total, group) => total + group.servers.length,\n 0,\n );\n const total = everything.data ? live.length : all.length;\n const setFilter = (name: keyof WorkerFilterValues) => (value: string) =>\n update({ [name]: value });\n const clearFilters = () =>\n update(\n Object.fromEntries(WORKER_FILTER_PARAMS.map((name) => [name, null])),\n );\n\n return (\n <div\n className=\"screen\"\n data-testid=\"workers-list\"\n >\n <h1 className=\"screen-title\">Workers</h1>\n {/*\n * A ROW, never a bare child of the column-flex `.screen`: there a\n * `.search`'s `flex: 0 1 260px` is a height, and the box grew 260px\n * tall. `__tests__/app/workers/filters.test.tsx` pins both.\n */}\n <div\n className=\"worker-filters\"\n role=\"search\"\n aria-label=\"Filter workers\"\n data-testid=\"worker-filters\"\n >\n <div className=\"search\">\n <label\n htmlFor={filterId}\n className=\"visually-hidden\"\n >\n Filter workers by id, queue or host\n </label>\n <input\n id={filterId}\n type=\"search\"\n className=\"input\"\n placeholder=\"Filter workers\"\n {...SEARCH_SHORTCUT}\n value={filter}\n maxLength={200}\n onChange={(event) => update({ search: event.target.value })}\n />\n </div>\n <FilterSelect\n label=\"Queue\"\n any=\"Any queue\"\n value={filters.queue}\n choices={distinctValues(live, \"queue\")}\n onChange={setFilter(\"queue\")}\n />\n <FilterSelect\n label=\"Service\"\n any=\"Any service\"\n value={filters.service}\n choices={distinctValues(live, \"service\")}\n onChange={setFilter(\"service\")}\n />\n {hostsShown && (\n <FilterSelect\n label=\"Host\"\n any=\"Any host\"\n value={filters.host}\n choices={distinctValues(live, \"host\")}\n onChange={setFilter(\"host\")}\n />\n )}\n <FilterSelect\n label=\"State\"\n any=\"Any state\"\n value={filters.state}\n choices={WORKER_STATES}\n onChange={setFilter(\"state\")}\n labelOf={(state) => STATE_LABELS[state as WorkerState]}\n />\n {filtered && (\n <Button\n size=\"sm\"\n onClick={clearFilters}\n >\n Clear filters\n </Button>\n )}\n </div>\n {filters.host !== \"\" && hostKnown && !hostsShown && (\n <p\n className=\"notice\"\n role=\"note\"\n data-testid=\"workers-host-ignored\"\n >\n This API hides worker hosts, so the host filter in this link (\n <code>{filters.host}</code>) is ignored.\n </p>\n )}\n {workers.isPending ? (\n <Spinner\n label=\"Loading workers\"\n showLabel\n />\n ) : workers.isError ? (\n <ErrorView\n error={workers.error}\n title=\"Could not load workers\"\n onRetry={() => void workers.refetch()}\n />\n ) : all.length === 0 && (!filtered || total === 0) ? (\n <EmptyState\n title=\"No live workers\"\n description=\"Workers appear here once a process runs one and it reports.\"\n />\n ) : all.length === 0 ? (\n <EmptyState\n title=\"No workers match these filters\"\n description=\"No live worker has every value chosen above.\"\n action={\n <Button\n size=\"sm\"\n onClick={clearFilters}\n >\n Clear filters\n </Button>\n }\n />\n ) : services.length === 0 ? (\n <EmptyState\n title=\"No workers match\"\n description={`No worker id, queue or host contains “${deferredFilter.trim()}”.`}\n />\n ) : (\n <>\n <p\n className=\"muted\"\n data-testid=\"workers-count\"\n >\n {plural(shown.length, \"worker\")} on {plural(serverCount, \"server\")}\n {shown.length < total && ` (of ${total})`}\n </p>\n {services.map((group) => (\n <ServiceCard\n key={group.service ?? \"\"}\n group={group}\n linkQueues={canQueues}\n linkKeys={linkKeys}\n />\n ))}\n </>\n )}\n </div>\n );\n}\n"
14
+ ],
15
+ "mappings": "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",
16
+ "debugId": "AC0C8BC200BBDE9064756E2164756E21",
17
+ "names": []
18
+ }
@@ -0,0 +1 @@
1
+ .providers-list{display:grid;gap:var(--space-3)}.provider-test,.provider-schema{display:grid;gap:var(--space-2);margin-top:var(--space-3);justify-items:start}.provider-test-result p{margin:0}.provider-test-fail{color:var(--danger)}.provider-checks{margin:var(--space-1)0 0;padding-left:var(--space-3)}
@@ -0,0 +1 @@
1
+ .events-screen{display:flex;gap:var(--space-4);flex-direction:column}.events-picker{display:flex;align-items:flex-end;gap:var(--space-2)var(--space-3);flex-wrap:wrap}.events-picker .field{min-width:12rem;margin:0}.events-types{margin-top:var(--space-3)}.events-types summary{cursor:pointer;overflow-wrap:anywhere}.events-types fieldset{margin:var(--space-2)0 0;border:0;padding:0}.events-type-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(9rem,1fr));gap:var(--space-1)var(--space-3);margin-bottom:var(--space-2)}.events-types-ignored{margin:var(--space-3)0 0;padding:var(--space-2)var(--space-3);background:var(--warning-bg);color:var(--warning-text);font-size:var(--text-sm);overflow-wrap:anywhere;border-radius:6px}.events-channel{margin:var(--space-3)0 0;font-size:var(--text-sm);overflow-wrap:anywhere}.events-rejected{margin-top:var(--space-3);padding:var(--space-2)var(--space-3);background:var(--warning-bg);color:var(--warning-text);border-radius:6px}.events-rejected p{overflow-wrap:anywhere;margin:0}.events-actions{display:flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);flex-wrap:wrap}.events-held{font-variant-numeric:tabular-nums}.events-table td{vertical-align:top}.events-time{font-variant-numeric:tabular-nums;white-space:nowrap}.events-kind{white-space:nowrap}.events-target,.events-id{overflow-wrap:anywhere}.events-payload{min-width:12rem}.events-gap td{font-style:italic}.events-summary{margin:0 0 var(--space-1)}
@@ -0,0 +1 @@
1
+ .range-picker{display:flex;align-items:flex-end;gap:var(--space-3);flex-wrap:wrap}.range-picker>.field{min-width:0}.range-scope{margin-top:var(--space-2)}.range-caption{font-size:var(--text-sm);color:var(--text-muted);margin:0}.range-not-retained{padding:var(--space-3);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-muted)}.range-not-retained-title{margin:0 0 var(--space-1);color:var(--text);font-weight:600}.range-not-retained-detail{font-size:var(--text-sm);margin:0}.breadcrumb{font-size:var(--text-sm);color:var(--text-muted)}.queue-header{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--space-3);flex-wrap:wrap}.queue-heading{display:flex;align-items:baseline;gap:var(--space-2)var(--space-3);flex-wrap:wrap;min-width:0}.queue-heading .screen-title{overflow-wrap:anywhere}.queue-total{font-variant-numeric:tabular-nums;color:var(--text-muted)}.queue-updated{font-size:var(--text-sm)}.queue-toolbar,.queue-actions{display:flex;gap:var(--space-2);flex-wrap:wrap}.jobs-section{display:flex;gap:var(--space-3);flex-direction:column;min-width:0}.jobs-filters{display:flex;align-items:flex-end;gap:var(--space-2)var(--space-3);flex-wrap:wrap}.jobs-filter{display:flex;gap:var(--space-1);font-size:var(--text-sm);flex-direction:column;min-width:0}.jobs-total{align-self:center}.bulk-bar{display:flex;align-items:center;gap:var(--space-2)var(--space-3);padding:var(--space-2)var(--space-3);border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-2);flex-wrap:wrap}.bulk-count{font-size:var(--text-sm);font-variant-numeric:tabular-nums}.select-cell{text-align:center;width:2rem}.job-id{display:flex;align-items:center;gap:var(--space-2);font-family:var(--font-mono);max-width:22rem;font-weight:400}.job-id a{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.job-processed-by-col{white-space:nowrap;width:12rem}.job-processed-by-text{display:inline-block;overflow:hidden;text-overflow:ellipsis;vertical-align:bottom;white-space:nowrap;max-width:12rem}.job-failure{color:var(--text-muted);overflow-wrap:anywhere;max-width:24rem}tr[aria-selected=true]{background:var(--info-bg)}.queue-panels{min-width:0}.field-row{display:flex;gap:var(--space-3);flex-wrap:wrap}.field-row>.field{flex:10rem;min-width:0}.limits-editor{display:flex;gap:var(--space-3);flex-direction:column}.limits-names{display:flex;gap:var(--space-3);border:1px solid var(--border);border-radius:var(--radius);padding:var(--space-3);flex-direction:column}.limits-name{border-top:1px solid var(--border);padding-top:var(--space-3)}.form-actions{display:flex;gap:var(--space-2);flex-wrap:wrap}.limits-summary{display:grid;grid-template-columns:max-content 1fr;gap:var(--space-1)var(--space-3);margin:0}.limits-summary>div{display:contents}.limits-summary dd{margin:0}.throughput-controls{display:flex;align-items:center;gap:var(--space-2);margin-bottom:var(--space-3)}.throughput-chart{margin:0}.throughput-legend{display:flex;gap:var(--space-3);font-size:var(--text-sm);margin-bottom:var(--space-2);flex-wrap:wrap}.swatch{display:inline-block;vertical-align:middle;border-radius:2px;width:10px;height:10px}.swatch-completed,.bar-completed{background:var(--state-completed);fill:var(--state-completed)}.swatch-failed,.bar-failed{background:var(--state-failed);fill:var(--state-failed)}.throughput-svg{display:block;width:100%;height:160px}.throughput-baseline{stroke:var(--border);stroke-width:1;vector-effect:non-scaling-stroke}.throughput-hit{fill:transparent}.throughput-hit:hover{fill:var(--surface-2)}.throughput-axis{display:flex;font-size:var(--text-xs);color:var(--text-muted);justify-content:space-between}.repeatable-data{max-width:24rem}.repeatable-actions{display:flex;gap:var(--space-2);flex-wrap:wrap}.demand-panel{display:grid;gap:var(--space-3)}.demand-urls{display:grid;gap:var(--space-2)}.demand-urls p{margin:0}.demand-url{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;min-width:0}.demand-url-label{min-width:6rem;font-weight:600}.demand-url code{overflow-wrap:anywhere}.summon-panel{display:grid;gap:var(--space-3)}.summon-heading{margin:0 0 var(--space-2);font-size:1rem}.summon-actions{display:flex;gap:var(--space-2);flex-wrap:wrap}.summoning-unit{display:block;font-size:var(--text-sm);font-weight:400}.summon-budget{display:inline-grid;gap:var(--space-1)}.summon-budget-window{display:flex;align-items:center;gap:var(--space-2);font-variant-numeric:tabular-nums;flex-wrap:wrap}.summon-budget-window.is-exhausted{color:var(--warning-text);font-weight:600}.summon-budget-meter{flex:none;width:80px;height:8px}.summon-group{display:grid;gap:var(--space-2);padding:var(--space-3);border:1px solid var(--border);border-radius:var(--radius);background:var(--surface)}.summon-subheading{font-size:var(--text-sm);margin:0;font-weight:600}.summon-group-circuit.is-open{color:var(--warning-text)}.summon-group tr.is-current,.summon-groups tr.is-current{background:var(--surface-2);box-shadow:inset 3px 0 0 var(--accent)}.summon-groups{display:grid;gap:var(--space-2);margin-top:var(--space-5)}.summon-groups-title{font-size:var(--text-lg);margin:0}.summon-group-list{list-style:none;margin:0;padding:0}.summon-group-count{font-variant-numeric:tabular-nums;color:var(--text-muted)}.summon-coverage{display:flex;gap:var(--space-1)var(--space-2);list-style:none;flex-wrap:wrap;margin:0;padding:0}.summon-last-missing{display:block;color:var(--danger-text)}.summon-group-note{display:block;font-size:var(--text-sm);margin:0}.summon-policy-tag{font-size:var(--text-xs);font-style:italic}.summon-policy-names{display:inline-flex;gap:var(--space-1);flex-wrap:wrap}.summon-policy-names code{padding:0 var(--space-1);border:1px solid var(--border);border-radius:var(--radius-sm);overflow-wrap:anywhere}.worker-actions{display:flex;align-items:center;gap:var(--space-1);flex-wrap:wrap}.worker-actions-hint{display:inline-block;font-size:var(--text-sm);max-width:24rem}.worker-config-form{display:grid;gap:var(--space-3)}.worker-config-row{display:flex;align-items:flex-end;gap:var(--space-2);flex-wrap:wrap}.worker-config-row>.field{flex:12rem;min-width:0}.worker-config-note{padding:var(--space-2)var(--space-3);border-radius:var(--radius-sm);background:var(--surface-2);font-size:var(--text-sm);margin:0}.worker-config-instances{padding-left:var(--space-4);font-size:var(--text-sm);color:var(--text-muted);margin:0}.worker-server+.worker-server{margin-top:var(--space-4)}.worker-server-title{margin:0 0 var(--space-2);font-size:var(--text-sm);font-weight:600}.worker-service-note{margin:0 0 var(--space-3);font-size:var(--text-sm)}.workers-sweep-note{margin:0 0 var(--space-3);padding:var(--space-2)var(--space-3);border-left:3px solid var(--warning-text);border-radius:var(--radius-sm);background:var(--warning-bg);color:var(--warning-text);font-size:var(--text-sm)}.worker-filters{display:flex;align-items:flex-end;gap:var(--space-2)var(--space-3);flex-flow:wrap}.worker-filters .field{min-width:10rem;margin:0}.worker-key{margin-top:var(--space-1);font-size:var(--text-sm);font-weight:400}.worker-header{display:flex;gap:var(--space-2);flex-direction:column}.worker-header .screen-title{overflow-wrap:anywhere}.worker-config-summary{margin:0 0 var(--space-3)}.worker-analytics-title{margin:0 0 var(--space-2);font-size:var(--text-sm);font-weight:600}.worker-analytics>*+*{margin-top:var(--space-4)}.worker-target-note{margin:0 0 var(--space-3)}.worker-target-file{overflow-wrap:anywhere}
@@ -0,0 +1 @@
1
+ .job-defaults{display:flex;gap:var(--space-3);flex-direction:column}.job-defaults p{margin:0}.job-defaults-heading{font-size:var(--text-md);margin:0}.job-defaults-form{display:flex;gap:var(--space-3);flex-direction:column}.job-defaults-row{display:flex;align-items:flex-start;gap:var(--space-2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:var(--space-3);flex-direction:column;margin:0}.job-defaults-row>.field,.job-defaults-row>.field-row{align-self:stretch}.job-defaults-row legend{font-weight:600}.job-defaults-values,.job-defaults-note,.job-defaults-uses-code{font-size:var(--text-sm);margin:0}.job-defaults-reset{display:flex;align-items:center;gap:var(--space-2)var(--space-3);margin-top:var(--space-3);flex-wrap:wrap}.job-defaults-reset-note{font-size:var(--text-sm);color:var(--text-muted);flex:16rem;margin:0}.job-defaults-conflict,.job-defaults-warning{border-radius:var(--radius-sm);padding:var(--space-2)var(--space-3);background:var(--warning-bg);color:var(--warning-text)}.job-defaults-conflict{display:flex;align-items:flex-start;gap:var(--space-2);margin-bottom:var(--space-3);flex-direction:column}.job-defaults-conflict p{margin:0}.job-defaults-apply-states{display:flex;gap:var(--space-1);border:1px solid var(--border);border-radius:var(--radius-sm);padding:var(--space-3);flex-direction:column}.job-defaults-apply-keys{padding-left:var(--space-4);margin:0}.job-defaults-preview h3{margin:var(--space-2)0;font-size:var(--text-md)}[data-testid=apply-progress] progress{width:100%}
@@ -0,0 +1 @@
1
+ .runner-actions{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}.runner-actions-hint{font-size:var(--text-sm);color:var(--text-muted);flex-basis:100%;margin:0}.runner-form{display:grid;gap:var(--space-3)}.runner-form-row{display:flex;gap:var(--space-3);flex-wrap:wrap}.runner-form-row>.field{flex:10rem;min-width:0}.runner-note{padding:var(--space-2)var(--space-3);border-radius:var(--radius-sm);background:var(--surface-2);font-size:var(--text-sm);margin:0}.runner-note-pending{background:var(--warning-bg);color:var(--warning-text)}.schedule-modes{display:flex;gap:var(--space-2)var(--space-4);padding:var(--space-2)var(--space-3);border:1px solid var(--border);border-radius:var(--radius);flex-wrap:wrap;margin:0}.schedule-modes>legend{padding:0 var(--space-1);font-weight:600;font-size:var(--text-sm)}.schedule-mode{display:inline-flex;align-items:center;gap:var(--space-1);cursor:pointer}.run-id{display:inline-flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}.run-id code{overflow-wrap:anywhere}.runner-note-refused{background:var(--danger-bg);color:var(--danger-text)}.config-source{display:flex;align-items:center;gap:var(--space-2);margin:calc(var(--space-2)*-1)0 0;font-size:var(--text-sm);color:var(--text-muted);flex-wrap:wrap}.log-lines{margin:0 0 var(--space-3);padding:var(--space-2)0;overflow:auto;list-style:none;background:var(--surface-2);border-radius:var(--radius-sm);font-family:var(--font-mono);font-size:var(--text-xs);max-height:480px}.log-line{display:flex;gap:var(--space-3);padding:0 var(--space-3)}.log-number{text-align:right;color:var(--text-muted);user-select:none;flex:none;min-width:4ch}.log-text{white-space:pre-wrap;overflow-wrap:anywhere}.run-log-line{align-items:baseline;gap:var(--space-2)}.run-log-time,.run-log-stream{color:var(--text-muted);user-select:none;flex:none}.run-log-stream{text-transform:uppercase;font-size:calc(var(--text-xs)*.9);letter-spacing:.04em;min-width:6ch}.run-log-line-stderr .run-log-stream,.run-log-line-stderr .log-text{color:var(--danger-text)}.run-log-level,.run-log-cut{flex:none;align-self:center}.run-log-notes{display:grid;gap:var(--space-2);margin:0 0 var(--space-3)}.run-log-notes p{font-size:var(--text-xs);margin:0}.run-log-filter{display:inline-flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm)}.run-log-toggle,.run-log-section{margin-top:var(--space-3)}.runner-link{overflow-wrap:anywhere;font-weight:600}.runner-id{margin-left:var(--space-2);font-size:var(--text-sm);overflow-wrap:anywhere}.runners-where{white-space:nowrap;width:7rem}.runners-status{white-space:nowrap;width:16rem}.runners-count{margin:var(--space-2)0 0;font-size:var(--text-sm)}.runner-screen{display:flex;gap:var(--space-4);flex-direction:column}.runner-header{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--space-3);flex-wrap:wrap}.runner-heading{display:flex;align-items:baseline;gap:var(--space-2)var(--space-3);flex-wrap:wrap;min-width:0}.runner-title{overflow-wrap:anywhere}.runner-badges{display:inline-flex;gap:var(--space-1);flex-wrap:wrap}.runner-last-error{color:var(--danger-text);overflow-wrap:anywhere}.run-list{display:flex;gap:var(--space-3);list-style:none;flex-direction:column;margin:0;padding:0}.run-list>li+li{padding-top:var(--space-3);border-top:1px solid var(--border)}.run-record{display:flex;gap:var(--space-3);flex-direction:column;min-width:0}.run-id-line{display:inline-flex;align-items:center;gap:var(--space-2);overflow-wrap:anywhere;flex-wrap:wrap}.run-id{font-size:var(--text-sm);overflow-wrap:anywhere}.run-toggle{display:inline-flex;border-radius:var(--radius-sm);color:inherit;cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center;align-items:center;min-width:1.75rem;min-height:1.75rem;padding:0}.run-toggle:hover{border-color:var(--border)}.run-toggle:focus-visible{outline:2px solid var(--focus);outline-offset:1px}.run-details-row>td{background:var(--surface-2)}.history-table .numeric{text-align:right;font-variant-numeric:tabular-nums}.history-actions{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap}.history-limit{display:flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm)}.history-open-log-note{display:flex;align-items:center;gap:var(--space-2);margin:var(--space-3)0 0;font-size:var(--text-xs);flex-wrap:wrap}.run-logs-cell{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}
@@ -0,0 +1,4 @@
1
+ import{Me,c,T,r,Z,K,b,n,e,t,f,i,gt,yt,m,y,ie,Y,a,_,s,de,E,ve,d,p,ee,ce,ls,ds,cs,ms,fs,ps,hs,gs,Ns,Os,As,Rs,Ds,Ts,vs,Ss,R,L,o,z,h,u,te,l,M,V,S,w,I,ne,le,Cs,Ls,Zt,Ps,Us,fr,pr,hr,jr,kt,zr,xs,Zr}from"./main-a250ffk7.js";import{wn}from"./main-rq97rdr4.js";import{rn}from"./main-5zbx60gq.js";import"./main-szjdmydc.js";import"./main-qr573853.js";import"./main-tnbhrm9n.js";import{Ye,Rt,mn,Eo,bo,fn,Dt}from"./main-ts19j4n0.js";import{Oe}from"./main-kwmqkjfx.js";import{Lo,U,Po}from"./main-hg0hrg8j.js";import{k}from"./main-4g69fn3x.js";import{O}from"./main-4j1fcevh.js";import{W,me,we}from"./main-3yj6bv90.js";import{ke,$n,Uo,xo,xe,yn,Ze,Bo}from"./main-jxre7w93.js";import{vt,En,wr,qo,kr,Ho,Go,Qo,jo,Ko,$o,Wo,zo,Zo,Xo}from"./main-w93x1dzs.js";import{A}from"./main-0jmeh6mq.js";import{Kt,bn,Nn}from"./main-n9cf6bxm.js";import{ae}from"./main-d6ge7w08.js";import{re,ts}from"./main-jkvd92z9.js";import{D}from"./main-hm1ng3yg.js";import{ge}from"./main-5grjvsy7.js";import{pt}from"./main-9prgjhg4.js";import{F}from"./main-pegp0ygb.js";function Le({queue:g}){let v=i(),P=le("demand"),N=a({queryKey:Zt.queue(g),queryFn:({signal:C})=>Ps(v,g,C),refetchInterval:P,placeholderData:Y});if(N.isPending)return e(p,{label:"Loading demand",showLabel:!0});if(N.isError)return e(D,{error:N.error,title:"Could not load demand",onRetry:()=>void N.refetch()});return e(ct,{reading:N.data,urls:Us(v.base,g,window.location.origin)})}function ct({reading:g,urls:v}){let P=jr.some(({figure:N})=>fr(g,N).approximate);return t("div",{className:"demand-panel","data-testid":"queue-demand",children:[g.paused&&e("p",{className:"notice",role:"note","data-testid":"demand-paused",children:"The queue is paused, so it demands nothing: demand and outstanding read 0 whatever is waiting, and a scaler polling them scales to zero."}),g.capped&&e("p",{className:"notice",role:"note","data-testid":"demand-capped",children:"At least these many: a count reached the backend's cap, so every figure is a lower bound (≥)."}),P&&e("p",{className:"notice",role:"note","data-testid":"demand-approximate",children:"Approximate (≈): this backend cannot count demand directly, so due now is only 1 or 0, stalled counts every active job while no worker is live, and the next due time is known only while nothing is due. Right as a trigger, not as a count."}),e(F,{items:[...jr.map(({figure:N,label:C,hint:x})=>({key:N,label:C,value:e("span",{className:"num",title:hr(g,N),"data-testid":`demand-${N}`,children:pr(g,N)}),hint:x})),{key:"workers",label:"Workers",value:e("span",{className:"num","data-testid":"demand-workers",children:o(g.workers)}),hint:"Live workers on the queue, from their heartbeats, paused ones included. 0 on a backend that keeps no worker records."},{key:"nextDueAt",label:"Next due",value:e("span",{"data-testid":"demand-next-due",children:g.nextDueAt===null?"None scheduled":e(A,{value:g.nextDueAt,hint:g.exact?void 0:"Approximate: known only while nothing is due."})}),hint:"The earliest delayed job or retry still in the future."},{key:"at",label:"Read",value:e(A,{value:g.at})}]}),t("div",{className:"demand-urls",children:[e("p",{className:"muted",children:"A scaler reads the same figures from these URLs (JSON, and the Prometheus text format), with the credentials this API asks for."}),e(Ae,{label:"JSON",url:v.json,testId:"demand-url-json"}),e(Ae,{label:"Prometheus",url:v.prometheus,testId:"demand-url-prometheus"})]})]})}function Ae({label:g,url:v,testId:P}){return t("div",{className:"demand-url",children:[e("span",{className:"demand-url-label",children:g}),e("code",{"data-testid":P,children:v}),e(ge,{text:v,ariaLabel:`Copy the ${g} demand URL`})]})}function Ee(){let g=m(),v=y("queues.read"),P=re(),N=g.features.jobDefaults&&v;return{shown:N,canEdit:N&&P("queues.defaults"),canApply:N&&g.features.jobDefaultsApply&&P("queues.applyDefaults")}}function qe(g){return{rateMax:g?.rate?.max,rateDuration:g?.rate?String(g.rate.duration):"",concurrency:g?.concurrency}}function Ie(g){return{queue:qe(g??void 0),names:Object.entries(g?.names??{}).map(([v,P],N)=>({key:N,name:v,...qe(P)}))}}function $e(g){let v={},P=Po(g.rateDuration);if(g.rateMax!==void 0||P!==void 0)v.rate={max:g.rateMax??0,duration:P??0};if(g.concurrency!==void 0)v.concurrency=g.concurrency;return v}function Be(g){let v=$e(g.queue),P={};for(let N of g.names){let C=N.name.trim();if(C)P[C]=$e(N)}if(Object.keys(P).length>0)v.names=P;return v}function Fe({queue:g,limits:v,editable:P}){if(!P)return e(ft,{limits:v});return e(ht,{queue:g,limits:v},JSON.stringify(v))}function ft({limits:g}){if(!g||Object.keys(g).length===0)return e("p",{className:"muted",children:"No limits are set on this queue."});let v=(P)=>[P.rate&&`${P.rate.max} per ${U(P.rate.duration)}`,P.concurrency!==void 0&&`${P.concurrency} at once`].filter(Boolean).join(", ")||"none";return t("dl",{className:"limits-summary",children:[e("dt",{children:"Queue"}),e("dd",{children:v(g)}),Object.entries(g.names??{}).map(([P,N])=>t("div",{children:[e("dt",{children:P}),e("dd",{children:v(N)})]},P))]})}function Qe({row:g,onChange:v,prefix:P,errors:N,label:C,scope:x}){let Q=(H)=>N[`${P}${H}`],B=x.kind==="queue"?"jobs on this queue":x.name?`jobs named “${x.name}”`:"jobs with this name";return t("div",{className:"field-row limits-row",children:[e(l,{label:`${C}: rate max`,hint:`The most ${B} that may START in one rate window, across every worker. Set it with the window beside it; leave both empty for no rate limit.`,error:Q("rate.max")??Q("rate"),children:e(V,{value:g.rateMax,onChange:(H)=>v({...g,rateMax:H}),min:1})}),e(l,{label:`${C}: rate window`,hint:"The length of the rate window: milliseconds (60000), or a duration such as “1 minute” or “30 seconds”. Starts are counted over this window.",error:Q("rate.duration"),children:e(M,{value:g.rateDuration,onChange:(H)=>v({...g,rateDuration:H}),maxLength:100})}),e(l,{label:`${C}: concurrency`,hint:`The most ${B} that may RUN at once, counted across every worker — on top of each worker's own concurrency, which still applies. A worker that dies holds its share until its lock expires. Empty means no limit.`,error:Q("concurrency"),children:e(V,{value:g.concurrency,onChange:(H)=>v({...g,concurrency:H}),min:1})})]})}function ht({queue:g,limits:v}){let P=i(),[N,C]=r(()=>Ie(v)),[x,Q]=r(!1),B=O({mutationFn:(q)=>q===null?Os(P,g):Ns(P,g,q),successMessage:(q,se)=>se===null?`Removed the limits of ${g}`:`Saved the limits of ${g}`,invalidate:ce(g),toastErrors:!1}),H=(q)=>{q?.preventDefault(),B.mutate(Be(N))},G=b(B.error)&&B.error.code==="LIMITS_CONTENDED",oe=B.error&&(Object.keys(B.fieldErrors).length===0||B.unplacedIssues.length>0)?B.error:null,X=N.names.reduce((q,se)=>Math.max(q,se.key+1),0);return t(n,{children:[t("form",{className:"limits-editor","aria-label":"Queue limits",onSubmit:H,children:[e(Qe,{row:N.queue,onChange:(q)=>C({...N,queue:q}),prefix:"",errors:B.fieldErrors,label:"Queue",scope:{kind:"queue"}}),t("fieldset",{className:"limits-names",children:[e("legend",{children:"Per-name limits"}),N.names.length===0&&e("p",{className:"muted",children:"None."}),N.names.map((q,se)=>t("div",{className:"limits-name",children:[e(l,{label:`Name ${se+1}`,hint:"The job name these limits apply to, exactly as jobs are added with it (e.g. “send-email”). They apply on top of the queue's own. A name at its limit is skipped, not waited behind, so other names keep running.",children:e(M,{value:q.name,onChange:(j)=>C({...N,names:N.names.map((ue)=>ue.key===q.key?{...ue,name:j}:ue)}),maxLength:200})}),e(Qe,{row:q,onChange:(j)=>C({...N,names:N.names.map((ue)=>ue.key===q.key?{...ue,...j}:ue)}),prefix:`names.${q.name.trim()}.`,errors:B.fieldErrors,label:q.name.trim()||`Name ${se+1}`,scope:{kind:"name",name:q.name.trim()}}),t(s,{size:"sm",variant:"ghost",onClick:()=>C({...N,names:N.names.filter((j)=>j.key!==q.key)}),children:["Remove ",q.name.trim()||`name ${se+1}`]})]},q.key)),e(s,{size:"sm",onClick:()=>C({...N,names:[...N.names,{key:X,name:"",rateMax:void 0,rateDuration:"",concurrency:void 0}]}),children:"Add a name"})]}),oe!==null&&e(D,{error:oe,title:G?"The limits changed while saving. Retry to save again.":"Could not save the limits",onRetry:G?()=>H():void 0}),t("div",{className:"form-actions",children:[e(s,{type:"submit",variant:"primary",disabled:B.isPending,"aria-busy":B.isPending||void 0,children:B.isPending?"Saving…":"Save limits"}),v!==null&&e(s,{variant:"danger",onClick:()=>Q(!0),disabled:B.isPending,children:"Remove all limits…"})]})]}),e(k,{open:x,onClose:()=>Q(!1),title:`Remove every limit on ${g}?`,description:"Workers stop rate-limiting and capping this queue once they refresh their limits.",variant:"danger",confirmLabel:"Remove limits",onConfirm:()=>B.mutateAsync(null)})]})}var bt=20;function wt(g,v){if(!b(g)||g.code!=="REPEATABLE_NOT_FOUND")return g;return new K({kind:g.kind,status:g.status,code:g.code,title:g.title,detail:`The repeat series “${v}” no longer exists: somebody removed it, or it ended. Refresh to see the series there are now.`,type:g.type,issues:g.issues,context:g.context,instance:g.instance,cause:g})}function _e({queue:g,canRemove:v,canDisable:P=!1,canEnable:N=!1}){let C=i(),[x,Q]=r(null),B=le("repeatables"),H=a({queryKey:ee.repeatables(g),queryFn:({signal:j})=>Ds(C,g,j),refetchInterval:B}),G=O({mutationFn:(j)=>Ts(C,g,j),successMessage:(j,ue)=>`Removed the repeat series ${ue}`,invalidate:ce(g),toastErrors:!1}),oe=O({mutationFn:async({key:j,to:ue})=>{try{return ue==="disable"?await vs(C,g,j):await Ss(C,g,j)}catch(pe){throw wt(pe,j)}},successMessage:(j,{key:ue,to:pe})=>pe==="disable"?`Disabled the repeat series ${ue}: it schedules nothing until enabled`:`Enabled the repeat series ${ue}: its next occurrence is scheduled from now`,errorTitle:"Could not change the repeat series",invalidate:ce(g)}),X=H.data?.items??[],q=kt(X,bt);if(H.isPending)return e(p,{label:"Loading repeatables",showLabel:!0});if(H.isError)return e(D,{error:H.error,title:"Could not load repeatables",onRetry:()=>void H.refetch()});let se=v||X.some((j)=>j.disabled?N:P);if(X.length===0)return e(d,{title:"No repeatables",description:"No repeat series is scheduled on this queue."});return t(n,{children:[t(I,{label:`Repeatables of ${g}`,children:[e("thead",{children:t("tr",{children:[e("th",{scope:"col",children:"Key"}),e("th",{scope:"col",children:"Name"}),e("th",{scope:"col",children:"Schedule"}),e("th",{scope:"col",children:"Next run"}),e("th",{scope:"col",className:"num",children:"Runs"}),e("th",{scope:"col",children:"Data"}),se&&e("th",{scope:"col",children:e("span",{className:"visually-hidden",children:"Actions"})})]})}),e("tbody",{children:q.rows.map((j)=>t("tr",{"data-testid":`repeatable-row-${j.key}`,children:[t("th",{scope:"row",children:[e("code",{children:j.key}),j.disabled&&t(n,{children:[" ",e(f,{tone:"warning",title:"Schedules nothing until it is enabled",children:"Disabled"})]})]}),e("td",{children:j.name}),e("td",{children:Eo(j)}),e("td",{"data-testid":`repeatable-next-${j.key}`,children:j.disabled?"paused (disabled)":e(A,{value:j.nextRunAt})}),t("td",{className:"num",children:[o(j.count),j.limit!==void 0&&` / ${o(j.limit)}`]}),e("td",{className:"repeatable-data",children:j.data===void 0?"—":e(ae,{value:j.data,label:`Data of ${j.key}`,expandDepth:0})}),se&&e("td",{children:t("div",{className:"repeatable-actions",children:[P&&!j.disabled&&e(We,{series:j,to:"disable",pending:oe.isPending&&oe.variables?.key===j.key,onClick:()=>oe.mutate({key:j.key,to:"disable"})}),N&&j.disabled&&e(We,{series:j,to:"enable",pending:oe.isPending&&oe.variables?.key===j.key,onClick:()=>oe.mutate({key:j.key,to:"enable"})}),v&&e(s,{size:"sm",variant:"danger","aria-label":`Remove repeatable ${j.key}`,onClick:()=>Q(j),children:"Remove…"})]})})]},j.key))})]}),q.paged&&e(ne,{label:"Repeatable pages",offset:q.offset,limit:q.limit,total:q.total,itemCount:q.rows.length,onChange:q.onChange}),e(k,{open:x!==null,onClose:()=>Q(null),title:`Remove the repeat series ${x?.key??""}?`,description:"No further occurrences are scheduled. Jobs it already produced stay.",variant:"danger",confirmLabel:"Remove",onConfirm:()=>x?G.mutateAsync(x.key):void 0})]})}function We({series:g,to:v,pending:P,onClick:N}){let C=v==="disable"?"Disable":"Enable";return e(s,{size:"sm","aria-label":`${C} repeatable ${g.key}`,disabled:P,"aria-busy":P||void 0,onClick:N,children:P?`${v==="disable"?"Disabling":"Enabling"}…`:C})}var Ke="Its policy is known only where its controller runs.",Pt="(provider default)",St="While the summoner is not ready, the configured values; once it is, they may be lowered to a wake pool's size.",He="The policy does not set this: the default applies.",Ge="Set for this queue by the unit's overrides.";function Je(g,v){if(!g)return"none";if(!v.local)return"elsewhere";return v.policy===void 0?"none":"shown"}function fe(g,v,P="",N=!1){return{joiner:P,text:g,keys:v,muted:N}}var Ue={onAdd:"On add",events:"Events",poll:"Poll"},Nt={onAdd:"triggers.onAdd",events:"triggers.events",poll:"triggers.poll"},je={"any-worker":"any live worker","summoned-only":"summoned workers only"};function Ve(g,v){return typeof v==="string"&&Object.hasOwn(g,v)}function Ct(g){let v=Object.entries(g).filter(([N])=>N!=="kind").map(([N,C])=>`${N}: ${u(C)}`),P=u(g.kind);return v.length===0?P:`${P} (${v.join(", ")})`}function xt(g){let v=[];return g.forEach((P,N)=>{let C=v.length===0?"":" · ";if(!Ve(Ue,P.kind)){v.push(fe(Ct(P),[],C,P.on===!1));return}let x=Ue[P.kind],Q=Nt[P.kind];if(P.on!==!0){v.push(fe(`${x} off`,[Q],C,!0));return}if(P.kind==="poll"){v.push(fe(typeof P.every==="number"?`${x} every ${U(P.every)}`:x,[Q],C));return}v.push(fe(x,[Q],C));let B=P.debounce,H=g.slice(N+1).some((G)=>G.on===!0&&(G.kind==="onAdd"||G.kind==="events")&&G.debounce===B);if(typeof B==="number"&&!H)v.push(fe(`(coalesced ${U(B)})`,["triggers.debounce"]," "))}),v}function ze(g){return g===null?"one regardless of depth":`${h(g,"job")} per worker`}function Tt(g){if(g.signal==="none")return"no stop signal (in-invocation)";let v=typeof g.graceMaxMs==="number"?` (up to ${U(g.graceMaxMs)})`:"";return`${u(g.signal)}, ${U(g.graceMs)} grace${v}`}function At(g,v){let P=(C)=>C.length>0&&C.every((x)=>!v.has(x)),N=g.pieces.length>0&&g.pieces.every((C)=>P(C.keys));return{id:g.id,label:g.label,...g.hint===void 0?{}:{hint:g.hint},tag:N,parts:g.pieces.map((C)=>({...C,muted:C.muted===!0,tag:!N&&P(C.keys)}))}}function Xe(g,v){let P=new Set(Array.isArray(g.explicit)?g.explicit:[]),N=g.circuit.group;return[{id:"triggers",label:"Triggers",pieces:xt(g.triggers),hint:"What makes the controller check for work to summon for."},{id:"workers",label:"Workers",pieces:[fe(`at most ${o(g.maxWorkers)}`,["maxWorkers"]),fe(ze(g.jobsPerWorker),["jobsPerWorker"]," · "),fe(`at most ${o(g.maxPending)} starting`,["maxPending"]," · ")],hint:St},{id:"cooldown",label:"Cooldown",pieces:[fe(U(g.cooldown),["cooldown"])],hint:"The least time between two attempts."},{id:"backoff",label:"Backoff",pieces:[fe(U(g.backoff.initial),["backoff.initial"]),fe(`doubling to ${U(g.backoff.max)}`,["backoff.max"],", ")],hint:"The wait after a failed or lost attempt, doubling with each one in a row."},{id:"circuit",label:"Circuit",pieces:[fe(`opens after ${h(g.circuit.failures,"failure")}`,["circuit.failures"]),fe(`for ${U(g.circuit.resetAfter)}`,["circuit.resetAfter"],", ")],hint:"Failed or lost attempts in a row that stop summoning, and for how long before one trial attempt."},N!==void 0&&{id:"group-circuit",label:"Group circuit",pieces:[fe(`opens after ${h(N.failures,"failure")} across the group`,["group.circuit.failures"]),fe(`for ${U(N.resetAfter)}`,["group.circuit.resetAfter"],", ")],hint:"The summon group's shared circuit: failures of this summoner's kind on any of its queues."},{id:"boot-budget",label:"Boot budget",pieces:[typeof g.bootBudget==="number"?fe(U(g.bootBudget),["bootBudget"]):fe(Pt,[])],hint:"How long an attempt counts as a worker on its way before it is lost."},{id:"lifetime",label:"Longest life",pieces:[fe(U(g.maxLifetime),["maxLifetime"]),...typeof v?.maxLifetimeMs==="number"?[fe(`platform cap ${U(v.maxLifetimeMs)}`,[]," · ")]:[]],hint:"The longest a summoned worker may live."},v!==void 0&&{id:"shutdown",label:"Shutdown",pieces:[fe(Tt(v.shutdown),[])],hint:"What the platform sends to stop a unit, and how long it waits: declared by the provider, not set by the policy."},v?.style==="scale"&&{id:"scale-down",label:"Scale down",pieces:[fe(`to zero after ${U(g.scaleDown.after)} idle`,["scaleDown.after"])]},{id:"summon-timeout",label:"Summon timeout",pieces:[fe(U(g.summonTimeout),["summonTimeout"])],hint:"How long one call to the summoner may take."},{id:"served-by",label:"Served by",pieces:[fe(Ve(je,g.servedBy)?je[g.servedBy]:u(g.servedBy),["servedBy"])],hint:"Which live workers count as serving the queue."},{id:"env",label:"Env",pieces:[{...fe("none",["env"]),names:g.env.map((x)=>u(x))}],hint:"The names the policy adds to every summoned worker's environment. Their values are never sent."},{id:"from-summoned",label:"From summoned workers",pieces:[fe(g.fromSummoned?"yes":"no",["fromSummoned"])],hint:"Whether this controller may run in a summoned process or a runner child."},{id:"failure-hook",label:"Failure hook",pieces:[fe(g.onSummonFailed?"set":"not set",["onSummonFailed"])],hint:"Whether an onSummonFailed hook is called on failed attempts."}].filter((x)=>x!==!1).map((x)=>At(x,P))}function et(g){if(g.perQueue===void 0)return;return Object.entries(g.perQueue).map(([v,P])=>{let N=Array.isArray(P.explicit)?P.explicit:[];return{queue:u(v),jobsPerWorker:ze(P.jobsPerWorker),maxWorkers:o(P.maxWorkers),jobsPerWorkerOverride:N.includes("jobsPerWorker"),maxWorkersOverride:N.includes("maxWorkers")}})}function tt(g){return g!==void 0&&Array.isArray(g.explicit)&&!g.explicit.includes("budget")}function nt({queue:g}){let v=i(),P=re(),N=le("summon"),[C,x]=r(null),[Q,B]=r(!0),[H,G]=r(!1),oe=ie("summonResetBudget")===!0,X=ie("summonPolicy")===!0,q=a({queryKey:ke.status(v,g),queryFn:({signal:ye})=>$n(v,g,ye),refetchInterval:N,placeholderData:Y}),se=te(),j=Lt(q.data?.queues),ue=(ye)=>{if(q.data?.queues!==void 0&&b(ye)&&ye.status===403)se.error(Zo,{description:ye.detail??ye.title})},pe=[...ke.changed(v,g),Zt.queue(g),ee.workers(g)],he=O({mutationFn:(ye)=>Uo(v,g,ye),successMessage:(ye)=>zo(ye),invalidate:pe,toastErrors:!1,onError:ue}),be=O({mutationFn:(ye)=>xo(v,g,ye?{budget:!0}:{}),successMessage:(ye,dt)=>dt?`Reset the summon state of ${g}: failures, backoff, circuit and budget usage cleared`:`Reset the summon state of ${g}: failures, backoff and circuit cleared`,invalidate:pe,toastErrors:!1,onError:ue});if(q.isPending)return e(p,{label:"Loading summon status",showLabel:!0});if(q.isError)return e(D,{error:q.error,title:"Could not load the summon status",onRetry:()=>void q.refetch()});if(q.data===null)return e(d,{title:"No summoner here",description:"No summon controller for this queue runs in this API's process."});let J=q.data,Re=Je(X,J),Se=Xo(J,j??P("queues.summon"));return t("div",{className:"summon-panel","data-testid":"queue-summon",children:[J.inert&&e("p",{className:"notice",role:"note","data-testid":"summon-inert",children:En(J.inertReason)}),!J.local&&e("p",{className:"notice",role:"note","data-testid":"summon-stored",children:"This queue's controller runs in another process: its state is read from the store, and it can be summoned or reset only from there."}),J.overlaps!==void 0&&J.overlaps.length>0&&e("p",{className:"notice",role:"note","data-testid":"summon-overlaps",children:J.queues===void 0?`Workers summoned for another summon group (${J.overlaps.map(u).join(", ")}) were seen on this queue at the last check: two units may be serving the same queue.`:`Workers summoned for another summon group (${J.overlaps.map(u).join(", ")}) were seen on this unit's queues at the last check: two units may be serving the same queues.`}),e(Et,{status:J,queue:g,policy:Re==="shown"?J.policy:void 0}),J.group!==void 0&&e(Bo,{group:J.group,queue:g,kind:J.summoner?.provider.kind}),J.summoner&&e($t,{summoner:J.summoner}),Re==="shown"&&J.policy!==void 0&&e(It,{policy:J.policy,capabilities:J.summoner?.capabilities,queue:g}),Re==="elsewhere"&&e("p",{className:"muted","data-testid":"summon-policy-elsewhere",children:Ke}),wr(J)&&J.pending.length>0&&e(Ft,{queue:g,queues:J.queues,pending:J.pending}),Se&&t("div",{className:"summon-actions",role:"group","aria-label":"Summon actions",children:[e(s,{variant:"primary",onClick:()=>x("summon"),disabled:he.isPending,children:"Summon now…"}),e(s,{onClick:()=>{G(!1),x("reset")},disabled:be.isPending,children:"Reset…"})]}),e(k,{open:C==="summon",onClose:()=>x(null),title:`Summon a worker for ${g} now?`,description:"Runs one check of the queue's summon controller now. The circuit, the budget, the attempts already on their way and the guard against two controllers summoning at once all still apply.",confirmLabel:"Summon now",onConfirm:()=>he.mutateAsync(Q),children:e(w,{checked:Q,onChange:B,label:"Skip the cooldown",hint:"Summon even if the last attempt was recent. Nothing else is skipped."})}),e(k,{open:C==="reset",onClose:()=>x(null),title:`Reset the summon state of ${g}?`,description:"Clears the consecutive failures, the backoff and an open circuit, so the next check may summon. Attempts already on their way are kept.",variant:"danger",confirmLabel:"Reset",onConfirm:()=>be.mutateAsync(oe&&H),children:oe&&e(w,{checked:H,onChange:G,label:"Also clear budget usage",hint:"Sets the attempts counted this UTC hour and today back to 0, so the budget allows its full limits again. Every controller on the queue shares the counts."})})]})}function Lt(g){let v=i(),{readOnly:P}=m(),N=rn({queries:(g??[]).map((C)=>({queryKey:_.permissions({queue:C}),queryFn:({signal:x})=>v.getPermissions({queue:C},x)}))});if(g===void 0)return;return!P&&N.length>0&&N.every((C)=>C.data!==void 0&&yt(C.data,"queues.summon"))}function Et({status:g,queue:v,policy:P}){let N=g.last===void 0?void 0:vt(g.last.outcome),C=g.last===void 0?[]:kr(g.last),x=g.last===void 0?void 0:Ho(g.last),Q=wr(g);return e(F,{items:[g.queues!==void 0&&{key:"unit",label:"Shared unit",value:e(qt,{queue:v,queues:g.queues}),hint:"One summoned process serves these queues, and this status is the unit's. Only the queues you may read are listed."},{key:"last",label:"Last outcome",value:g.last===void 0||N===void 0?e("span",{"data-testid":"summon-last",children:"None yet"}):t("span",{"data-testid":"summon-last",children:[e(f,{tone:N.tone,title:N.hint,children:N.label})," ",e(A,{value:g.last.at}),x!==void 0&&t("span",{className:"muted",children:[" — ",x]}),C.length>0&&e("span",{className:"summon-last-missing","data-testid":"summon-last-missing",children:Go(C)})]}),hint:g.last!==void 0&&C.length>0?Qo:void 0},Q&&{key:"pending",label:"On their way",value:e("span",{"data-testid":"summon-pending-count",children:o(g.pending.length)}),hint:"Summon attempts still counting as workers on their way."},Q&&{key:"failures",label:"Consecutive failures",value:e("span",{"data-testid":"summon-failures",children:o(g.failures)}),hint:"Failed, unavailable or lost attempts in a row, towards the circuit. A throttled call is not counted, and an auth or misconfiguration error counts enough to open it at once. A registration or a reset clears them."},Q&&g.backoffUntil!==void 0&&{key:"backoff",label:"Backing off until",value:e("span",{"data-testid":"summon-backoff",children:e(A,{value:g.backoffUntil})})},Q&&g.circuitOpenUntil!==void 0&&{key:"circuit",label:"Circuit open until",value:e("span",{"data-testid":"summon-circuit",children:e(A,{value:g.circuitOpenUntil})}),hint:"After repeated failures nothing is summoned until then, apart from one trial attempt."},Q&&g.budget!==void 0&&{key:"budget",label:"Budget",value:t(n,{children:[e(Ze,{budget:g.budget,testId:"summon-budget",group:g.group!==void 0&&g.group.budget.off!==!0}),tt(P)&&t(n,{children:[" ",e(Ce,{})]})]}),hint:yn(g.budget)}]})}function qt({queue:g,queues:v}){return t("span",{"data-testid":"summon-unit",children:["One process for ",h(v.length,"queue"),":"," ",v.map((P,N)=>{let C=P===g;return t("span",{children:[N>0&&", ",e(E,{to:xe(P),title:`The Summon tab of ${P}`,children:u(P)}),C&&e("span",{className:"muted",children:" (this queue)"})]},P)})]})}function $t({summoner:g}){let{provider:v,capabilities:P,facts:N}=g,C=Object.entries(N),x=pt(g.readiness),Q=Mt(g.providerId),B=re(),H=u(v.displayName??v.kind);return t("div",{className:"summon-summoner","data-testid":"summon-summoner",children:[e("h3",{className:"summon-heading",children:"Summoner"}),e(F,{items:[{key:"provider",label:"Provider",value:t("span",{"data-testid":"summon-provider",children:[u(v.displayName??v.kind)," ",t("span",{className:"muted",children:[u(v.name)," ",u(v.version)]})]})},{key:"readiness",label:"Readiness",value:e(f,{tone:x.tone,title:x.hint,testId:"summon-readiness",children:x.label}),hint:x.hint},...P===void 0?[]:[{key:"style",label:"Style",value:qo(P.style)},{key:"boot",label:"Provider default boot budget",value:U(P.bootBudgetMs),hint:"How long an attempt counts as a worker on its way before it is lost, unless the policy sets its own."},{key:"lifetime",label:"Longest life",value:P.maxLifetimeMs===null?"No platform limit known":U(P.maxLifetimeMs)}],...C.map(([G,oe])=>({key:`fact-${G}`,label:u(G),value:e("code",{children:u(oe)})}))]}),g.providerId!==void 0&&Q&&B("providers.validate")&&e(Nn,{providerId:g.providerId,label:H})]})}function It({policy:g,capabilities:v,queue:P}){let N=Xe(g,v),C=et(g),x=N.flatMap((Q)=>[{key:Q.id,label:Q.label,value:e(Bt,{row:Q}),hint:Q.hint},...Q.id==="workers"&&C!==void 0?[{key:"per-queue",label:"Per queue",value:e(Qt,{queue:P,rows:C}),hint:"Each of the unit's queues' own capacity: what its demand alone may ask for."}]:[]]);return t("div",{className:"summon-policy","data-testid":"summon-policy",children:[e("h3",{className:"summon-heading",children:"Policy"}),e(F,{items:x})]})}function Bt({row:g}){return t("span",{"data-testid":`summon-policy-${g.id}`,children:[g.parts.map((v,P)=>t("span",{children:[v.joiner,e(Ot,{part:v}),v.tag&&t(n,{children:[" ",e(Ce,{})]})]},P)),g.tag&&t(n,{children:[" ",e(Ce,{})]})]})}function Ot({part:g}){if(g.names!==void 0&&g.names.length>0)return e("span",{className:"summon-policy-names",children:g.names.map((v)=>e("code",{children:v},v))});return e("span",{className:g.muted?"muted":void 0,children:g.text})}function Ce(){return e("span",{className:"summon-policy-tag muted",title:He,"data-testid":"summon-policy-default",children:"default"})}function Qt({queue:g,rows:v}){return e("div",{"data-testid":"summon-policy-per-queue",children:t(I,{label:`Per-queue capacity of the unit serving ${g}`,children:[e("thead",{children:t("tr",{children:[e("th",{scope:"col",children:"Queue"}),e("th",{scope:"col",children:"Jobs per worker"}),e("th",{scope:"col",className:"num",children:"Max workers"})]})}),e("tbody",{children:v.map((P)=>t("tr",{"data-testid":`summon-policy-per-queue-${P.queue}`,children:[e("th",{scope:"row",children:P.queue}),t("td",{children:[P.jobsPerWorker,P.jobsPerWorkerOverride&&e(ot,{})]}),t("td",{className:"num",children:[P.maxWorkers,P.maxWorkersOverride&&e(ot,{})]})]},P.queue))})]})})}function ot(){return t(n,{children:[" ",e(f,{tone:"accent",title:Ge,testId:"summon-policy-override",children:"override"})]})}function Mt(g){let v=i(),P=m(),N=y("providers.read"),C=g!==void 0&&P.features.providers&&N,x=a({queryKey:Kt.list,queryFn:({signal:Q})=>bn(v,Q),enabled:C,staleTime:30000});return C&&x.data?.providers.some((Q)=>Q.id===g&&Q.preflight)===!0}function Ft({queue:g,queues:v,pending:P}){let N=P.some((x)=>x.handles!==void 0),C=Wo(P);return t(I,{label:`Summon attempts on their way for ${g}`,children:[e("thead",{children:t("tr",{children:[e("th",{scope:"col",children:"Attempt"}),e("th",{scope:"col",children:"Started"}),e("th",{scope:"col",children:"Lost unless a worker registers"}),e("th",{scope:"col",className:"num",children:"Workers"}),e("th",{scope:"col",children:"Summoner"}),C&&e("th",{scope:"col",children:jo}),N&&e("th",{scope:"col",children:"Handles"})]})}),e("tbody",{children:P.map((x)=>t("tr",{"data-testid":`summon-pending-${x.id}`,children:[e("th",{scope:"row",children:e("code",{children:u(x.id)})}),e("td",{children:e(A,{value:x.at})}),e("td",{children:e(A,{value:x.until})}),e("td",{className:"num",children:o(x.count)}),e("td",{children:u(x.kind)}),C&&e("td",{children:e(Wt,{attempt:x.id,badges:$o(x.coverage,v,g)})}),N&&e("td",{children:x.handles?.map((Q)=>e("code",{className:"worker-target-file",children:u(Q)},Q))})]},x.id))})]})}function Wt({attempt:g,badges:v}){if(v===void 0||v.length===0)return e("span",{className:"muted",children:Ko});return e("ul",{className:"summon-coverage",children:v.map((P)=>t("li",{children:[e(f,{tone:P.tone,title:P.title,testId:`summon-coverage-${g}-${P.queue}`,children:P.label}),P.current&&e("span",{className:"muted",children:" (this queue)"})]},P.queue))})}var De=[15,60,360,1440],rt=60,Te={15:"Last 15 minutes",60:"Last hour",360:"Last 6 hours",1440:"Last 24 hours"},Pe=120,at=2,_t=new Intl.DateTimeFormat(void 0,{hour:"2-digit",minute:"2-digit"});function Ne(g){return _t.format(new Date(g))}function it(g){let v=Number(g);return De.find((P)=>P===v)??60}function st({queue:g}){let v=i(),[P,N]=W(),C=it(P.get("window")),x=c(),Q=le("throughput"),B=a({queryKey:ee.throughput(g,C),queryFn:({signal:H})=>Rs(v,g,C,H),refetchInterval:Q,placeholderData:Y});return t("div",{className:"throughput-panel",children:[t("div",{className:"throughput-controls",children:[e("label",{htmlFor:x,children:"Window"}),e(S,{id:x,options:De.map((H)=>({value:String(H),label:Te[H]})),value:String(C),onChange:(H)=>N({window:H===String(rt)?null:H})})]}),B.isPending?e(p,{label:"Loading throughput",showLabel:!0}):B.isError?e(D,{error:B.error,title:"Could not load throughput",onRetry:()=>void B.refetch()}):e(Ut,{data:B.data,label:`${g}: jobs per minute, ${Te[C].toLowerCase()}`})]})}function Ut({data:g,label:v}){let{buckets:P}=g,N=Math.max(1,...P.map((B)=>B.completed+B.failed)),C=Math.max(1,P.length)*10,x=(B)=>B/N*Pe,Q=`${v}: ${o(g.completed)} completed, ${o(g.failed)} failed, peak ${o(N)} per minute.`;return t("figure",{className:"throughput-chart",children:[t("div",{className:"throughput-legend","aria-hidden":"true",children:[t("span",{children:[e("span",{className:"swatch swatch-completed"})," Completed"," ",o(g.completed)]}),t("span",{children:[e("span",{className:"swatch swatch-failed"})," Failed"," ",o(g.failed)]}),t("span",{className:"muted",children:["Peak ",o(N),"/min"]})]}),t("svg",{role:"img","aria-label":Q,viewBox:`0 0 ${C} ${Pe}`,preserveAspectRatio:"none",className:"throughput-svg",children:[e("line",{x1:0,x2:C,y1:Pe,y2:Pe,className:"throughput-baseline"}),P.map((B,H)=>{let G=H*10+1,oe=x(B.completed),X=x(B.failed),q=oe>0&&X>0?at:0;return t("g",{children:[e("title",{children:`${Ne(B.at)}: ${B.completed} completed, ${B.failed} failed`}),e("rect",{x:G-1,y:0,width:10,height:Pe,className:"throughput-hit"}),oe>0&&e("rect",{x:G,y:Pe-oe,width:8,height:oe,rx:1,className:"bar-completed"}),X>0&&e("rect",{x:G,y:Pe-oe-q-X,width:8,height:X,rx:1,className:"bar-failed"})]},B.at)})]}),P.length>0&&t("figcaption",{className:"throughput-axis","aria-hidden":"true",children:[e("span",{children:Ne(P[0].at)}),e("span",{children:Ne(P.at(-1).at)})]}),t("table",{className:"visually-hidden",children:[e("caption",{children:v}),e("thead",{children:t("tr",{children:[e("th",{scope:"col",children:"Minute"}),e("th",{scope:"col",children:"Completed"}),e("th",{scope:"col",children:"Failed"})]})}),e("tbody",{children:P.map((B)=>t("tr",{children:[e("th",{scope:"row",children:Ne(B.at)}),e("td",{children:B.completed}),e("td",{children:B.failed})]},B.at))})]})]})}function lt(g){let v=g.filter((N)=>N.sweeps!==void 0);if(v.length===0)return null;if(v.some((N)=>N.sweeps===!0))return null;let P=v.length<g.length;return{message:P?`No live worker on this queue reports that it runs housekeeping, and some are too old to say — so there may be nobody doing it. ${"Jobs still run — delayed jobs are promoted and stalled ones recovered — but expired results, repeat series and stale queue state are not swept."}`:`No live worker on this queue runs housekeeping. ${"Jobs still run — delayed jobs are promoted and stalled ones recovered — but expired results, repeat series and stale queue state are not swept."}`,uncertain:P}}var jt=25;function ut({queue:g}){let v=i(),P=le("workers");fn(g,y("queues.read"));let N=Oe(),C=a({queryKey:ee.workers(g),queryFn:({signal:H})=>As(v,g,H),refetchInterval:P});if(C.isPending)return e(p,{label:"Loading workers",showLabel:!0});if(C.isError)return e(D,{error:C.error,title:"Could not load workers",onRetry:()=>void C.refetch()});let x=C.data.items;if(x.length===0)return e(d,{title:"No live workers",description:"No worker has reported for this queue recently."});let Q=x.some((H)=>H.host!==void 0||H.pid!==void 0),B=lt(x);return t(n,{children:[B&&e("p",{className:"workers-sweep-note",role:"note","data-testid":"sweep-warning","data-uncertain":B.uncertain?"true":"false",children:B.message}),e(Dt,{workers:x,label:`Workers of ${g}`,showQueue:!1,showHost:Q,linkKeys:N,pageSize:jt})]})}var Ht=["completed","failed","dead","waiting","delayed","waiting-children"],Gt=["failed","dead","completed"],Jt=[{value:"minutes",label:"minutes"},{value:"hours",label:"hours"},{value:"days",label:"days"}];function mt({queue:g,paused:v}){let P=i(),N=re(),[C,x]=r(null),Q=()=>x(null),B=ce(g),H=O({mutationFn:()=>ms(P,g),successMessage:`Paused ${g}`,errorTitle:`Could not pause ${g}`,invalidate:B}),G=O({mutationFn:()=>fs(P,g),successMessage:`Resumed ${g}`,errorTitle:`Could not resume ${g}`,invalidate:B}),oe=v===!1&&N("queues.pause"),X=v===!0&&N("queues.resume"),q=N("queues.drain"),se=N("queues.clean"),j=N("jobs.retryAll");if(!oe&&!X&&!q&&!se&&!j)return null;return t("div",{className:"queue-actions",role:"group","aria-label":"Queue actions",children:[oe&&e(s,{onClick:()=>H.mutate(),disabled:H.isPending,"aria-busy":H.isPending||void 0,children:"Pause"}),X&&e(s,{variant:"primary",onClick:()=>G.mutate(),disabled:G.isPending,"aria-busy":G.isPending||void 0,children:"Resume"}),j&&e(s,{onClick:()=>x("retryAll"),children:"Retry all…"}),se&&e(s,{variant:"danger",onClick:()=>x("clean"),children:"Clean…"}),q&&e(s,{variant:"danger",onClick:()=>x("drain"),children:"Drain…"}),C==="drain"&&e(Vt,{queue:g,onClose:Q}),C==="clean"&&e(Yt,{queue:g,onClose:Q}),C==="retryAll"&&e(zt,{queue:g,onClose:Q})]})}function Vt({queue:g,onClose:v}){let P=i(),[N,C]=r(!1),x=O({mutationFn:(Q)=>ps(P,g,Q),successMessage:(Q)=>`Drained ${h(Q.count,"job")} from ${g}`,invalidate:ce(g),toastErrors:!1});return e(k,{open:!0,onClose:v,title:`Drain ${g}?`,description:"Removes every waiting job (and, if chosen, every delayed one). Running jobs are not touched. This cannot be undone.",variant:"danger",confirmLabel:"Drain",pendingLabel:"Draining…",confirmText:g,onConfirm:()=>x.mutateAsync(N),children:e(w,{checked:N,onChange:C,label:"Also remove delayed jobs"})})}function Yt({queue:g,onClose:v}){let P=i(),{limits:N}=m(),C=te(),[x,Q]=r("completed"),[B,H]=r(1),[G,oe]=r("days"),[X,q]=r(N.defaultClean),se=O({mutationFn:(he)=>hs(P,g,he),onSuccess:(he)=>{C.success(`Cleaned ${h(he.count,"job")} from ${g}`,{description:he.ids.length>0?`Removed: ${mn(he.ids)}`:void 0})},invalidate:ce(g),toastErrors:!1}),j=Lo(B,G),ue=X!==void 0&&Number.isInteger(X)&&X>=1&&X<=N.maxClean,pe=(he)=>se.fieldErrors[he];return t(k,{open:!0,onClose:v,title:`Clean ${g}?`,description:"Removes jobs in one state that are older than the age you give, up to the limit. This cannot be undone.",variant:"danger",confirmLabel:"Clean",pendingLabel:"Cleaning…",confirmText:g,confirmDisabled:j===void 0||!ue,onConfirm:()=>se.mutateAsync({state:x,olderThan:j??0,limit:X}),children:[e(l,{label:"State",error:pe("state"),children:e(S,{options:Ht.map((he)=>({value:he,label:z[he]})),value:x,onChange:Q})}),t("div",{className:"field-row",children:[e(l,{label:"Older than",error:pe("olderThan"),required:!0,children:e(V,{value:B,onChange:H,min:0,step:"any"})}),e(l,{label:"Unit",children:e(S,{options:Jt,value:G,onChange:oe})})]}),e(l,{label:"Limit",hint:`At most ${o(N.maxClean)}.`,error:pe("limit")??(ue?void 0:`Between 1 and ${o(N.maxClean)}.`),children:e(V,{value:X,onChange:q,min:1,max:N.maxClean})})]})}function zt({queue:g,onClose:v}){let P=i(),{limits:N}=m(),C=te(),[x,Q]=r("failed"),[B,H]=r(""),[G,oe]=r(""),[X,q]=r(N.maxRetryAll),[se,j]=r(!0),ue=O({mutationFn:(J)=>gs(P,g,J),onSuccess:(J)=>{C.success(`Retried ${h(J.count,"job")} in ${g}`,{description:J.ids.length===0?void 0:`${mn(J.ids)}${J.truncated?` (ids of the first ${N.maxRetryAllIds} only)`:""}`})},invalidate:ce(g),toastErrors:!1}),pe=X!==void 0&&Number.isInteger(X)&&X>=1&&X<=N.maxRetryAll,he=b(ue.error)&&ue.error.code==="OPERATION_IN_PROGRESS",be=(J)=>ue.fieldErrors[J];return t(k,{open:!0,onClose:v,title:`Retry every ${z[x].toLowerCase()} job in ${g}?`,description:"Moves matching jobs back to waiting. It runs to completion before answering, which can take a while on a large queue.",confirmLabel:"Retry all",pendingLabel:"Retrying… this can take a while",confirmText:g,confirmDisabled:!pe,onConfirm:()=>ue.mutateAsync({state:x,name:B.trim()||void 0,reason:G.trim()||void 0,limit:X,resetAttempts:se}),children:[e(l,{label:"State",error:be("state"),children:e(S,{options:Gt.map((J)=>({value:J,label:z[J]})),value:x,onChange:Q})}),e(l,{label:"Job name",hint:"Optional: only jobs with exactly this name.",error:be("name"),children:e(M,{value:B,onChange:H,maxLength:200})}),e(l,{label:"Failure reason contains",hint:"Optional: a plain substring of “ErrorName: message”.",error:be("reason"),children:e(M,{value:G,onChange:oe,maxLength:500})}),e(l,{label:"Limit",hint:`At most ${o(N.maxRetryAll)}.`,error:be("limit")??(pe?void 0:`Between 1 and ${o(N.maxRetryAll)}.`),children:e(V,{value:X,onChange:q,min:1,max:N.maxRetryAll})}),e(w,{checked:se,onChange:j,label:"Reset attempts"}),he&&e("p",{className:"notice",role:"note","data-testid":"retry-all-in-progress",children:"A retry-all is already running on this queue. Only one runs per queue at a time; wait for it to finish, then try again."})]})}function Xt(){let{queue:g=""}=de(),v=i(),P=m(),N=y("queues.read"),C=y("jobs.list"),x=y("jobs.add"),Q=y("repeatables.list"),B=gt(),H=le("detail"),G=le("counts");Cs(g,{enabled:N&&B,jobs:C,repeatables:Q});let[oe,X]=r(!1),q=T(null),[se,j]=r(!1),ue=T(null),pe=a({queryKey:ee.detail(g),queryFn:({signal:Se})=>ds(v,g,Se),refetchInterval:H,enabled:N}),he=a({queryKey:ee.counts(g),queryFn:({signal:Se})=>cs(v,g,Se),refetchInterval:G,enabled:N});if(N&&pe.isError&&!pe.data)return e("div",{className:"screen",children:e(L,{headingLevel:1,error:pe.error,title:`Could not load the queue ${g}`,onRetry:()=>void pe.refetch()})});let be=ts(P,x),J=be&&P.features.addFlow,Re=he.data?Z.reduce((Se,ye)=>Se+he.data[ye],0):pe.data?.total;return t("div",{className:"screen queue-screen","data-testid":"queue-screen",children:[t("nav",{"aria-label":"Breadcrumb",className:"breadcrumb",children:[e(E,{to:"/queues",children:"Queues"}),e("span",{"aria-hidden":"true",children:" / "})]}),t("header",{className:"queue-header",children:[t("div",{className:"queue-heading",children:[e("h1",{className:"screen-title",children:g}),pe.data?.paused&&e(f,{tone:"warning",children:"Paused"}),Re!==void 0&&t("span",{className:"queue-total","data-testid":"queue-total",children:[o(Re)," jobs"]}),he.dataUpdatedAt>0&&t("span",{className:"muted queue-updated",children:["Updated ",e(A,{value:he.dataUpdatedAt})]})]}),t("div",{className:"queue-toolbar",children:[be&&e(s,{ref:q,variant:"primary",onClick:()=>X(!0),children:"Add job"}),J&&e(s,{ref:ue,onClick:()=>j(!0),children:"Add flow"}),e(mt,{queue:g,paused:pe.data?.paused})]})]}),be&&e(wn,{queue:g,open:oe,onClose:()=>{X(!1),q.current?.focus()}}),J&&se&&e(Zr,{queue:g,open:!0,onClose:()=>{j(!1),ue.current?.focus()}}),C?e(R,{title:"Jobs",children:e(bo,{queue:g,counts:he.data})}):e(d,{title:"Jobs hidden",description:"You may not list this queue's jobs."}),e(eo,{queue:g,limits:pe.data?pe.data.limits:void 0,detailLoading:N&&pe.isPending})]})}function eo({queue:g,limits:v,detailLoading:P}){let N=m(),C=re(),x=y("workers.list"),Q=y("queues.read"),B=y("metrics.read"),H=y("repeatables.list"),G=Ee(),oe=i(),X=a({queryKey:ke.status(oe,g),queryFn:({signal:pe})=>$n(oe,g,pe),enabled:Q,retry:!1}),q=[];if(N.features.limits&&(v!==void 0||P))q.push({value:"limits",label:"Limits",render:()=>v===void 0?e(p,{label:"Loading limits",showLabel:!0}):e(Fe,{queue:g,limits:v,editable:C("queues.limits")})});if(G.shown)q.push({value:"job-defaults",label:"Job defaults",render:()=>e(xs,{queue:g,canEdit:G.canEdit,canApply:G.canApply})});if(N.features.workers&&x)q.push({value:"workers",label:"Workers",render:()=>e(ut,{queue:g})});if(N.features.demand&&Q)q.push({value:"demand",label:"Demand",render:()=>e(Le,{queue:g})});if(Q&&(X.isError||(X.data??null)!==null))q.push({value:"summon",label:"Summon",render:()=>e(nt,{queue:g})});if(N.features.throughput&&B)q.push({value:"throughput",label:"Throughput",render:()=>e(st,{queue:g})});if(H)q.push({value:"repeatables",label:"Repeatables",render:()=>e(_e,{queue:g,canRemove:C("repeatables.remove"),canDisable:C("repeatables.disable"),canEnable:C("repeatables.enable")})});let se=q[0]?.value??"limits",j=Ye("panel",q,se);if(q.length===0)return null;let ue=q.find((pe)=>pe.value===j)??q[0];return e(R,{title:"Details",className:"queue-panels",children:e(Rt,{tabs:q.map(({value:pe,label:he})=>({value:pe,label:he})),param:"panel",defaultValue:se,label:"Queue details",children:ue.render()})})}function to(){let g=i(),{limits:v}=m(),P=y("queues.list"),[N,C]=W(),x=c(),Q=N.get("search")??"",B=Me(Q),H=we(me(N,"limit",Math.min(v.defaultPageSize,v.maxQueues)),v.maxQueues),G=me(N,"offset",0),oe={search:B,offset:G,limit:H},X=le("list");Ls(P);let q=a({queryKey:ee.page(oe),queryFn:({signal:se})=>ls(g,oe,se),refetchInterval:X,placeholderData:Y,enabled:P});if(!P)return t("div",{className:"screen",children:[e("h1",{className:"screen-title",children:"Queues"}),e(d,{title:"Nothing to show",description:"You may not list queues on this API."})]});return t("div",{className:"screen","data-testid":"queues-list",children:[e("h1",{className:"screen-title",children:"Queues"}),e(R,{title:"Queues",actions:t("div",{className:"search",children:[e("label",{htmlFor:x,className:"visually-hidden",children:"Search queues by name"}),e("input",{id:x,type:"search",className:"input",placeholder:"Search queues",...ve,value:Q,maxLength:200,onChange:(se)=>C({search:se.target.value,offset:null})})]}),children:q.isPending?e(p,{label:"Loading queues",showLabel:!0}):q.isError?e(L,{error:q.error,title:"Could not load queues",onRetry:()=>void q.refetch()}):q.data.items.length===0&&G===0?B?e(d,{title:"No queues match",description:`No queue name contains “${B}”.`}):e(d,{title:"No queues yet",description:"Queues appear here once a job is added to one."}):t(n,{children:[e(zr,{items:q.data.items,sparklines:!1}),e(ne,{label:"Queue pages",offset:G,limit:H,total:q.data.page.total,itemCount:q.data.items.length,hasMore:q.data.page.hasMore,maxPageSize:v.maxQueues,onChange:(se)=>C({offset:se.offset>0?String(se.offset):null,limit:String(se.limit)})})]})})]})}export{Xt as QueueScreen,to as QueuesListScreen};
2
+
3
+ //# debugId=5378BD3EF15812B464756E2164756E21
4
+ //# sourceMappingURL=index-d5r5n0mm.js.map