@kedem/okdb 1.0.1 → 1.0.4

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 (142) hide show
  1. package/bin/okdb.js +1 -1
  2. package/keys/public.pem +3 -0
  3. package/okdb.js +1 -1
  4. package/package.json +2 -2
  5. package/public/admin-app.js +1 -1
  6. package/public/components/admin-route-host.ok.js +1 -1
  7. package/public/components/env-picker.ok.js +1 -1
  8. package/public/components/field-path-input.ok.js +1 -1
  9. package/public/components/field.html +154 -2
  10. package/public/components/geo-map.ok.js +1 -1
  11. package/public/components/ok-cron-input.ok.js +1 -1
  12. package/public/components/ok-dropzone.ok.js +1 -1
  13. package/public/components/ok-js-input.ok.js +1 -1
  14. package/public/components/ok-json-input.ok.js +1 -1
  15. package/public/components/ok-text-monaco-input.ok.js +1 -1
  16. package/public/components/theme-toggle.ok.js +1 -1
  17. package/public/index.html +29 -2
  18. package/public/layouts/_default.ok.html +1 -1
  19. package/public/layouts/okdb-cube.ok.html +1 -1
  20. package/public/login-app.js +1 -1
  21. package/public/login.html +205 -1
  22. package/public/modules/ok.js +1 -1
  23. package/public/sections/api/index.ok.html +1 -1
  24. package/public/sections/api/parts/api-endpoint.ok.js +1 -1
  25. package/public/sections/auth/index.ok.html +1 -1
  26. package/public/sections/auth/modals/create-token-modal.ok.js +1 -1
  27. package/public/sections/auth/modals/edit-token-modal.ok.js +1 -1
  28. package/public/sections/auth/modals/perm-info-popover.ok.js +1 -1
  29. package/public/sections/auth/parts/token-detail.ok.js +1 -1
  30. package/public/sections/auth/parts/token-list.ok.js +1 -1
  31. package/public/sections/db/index.ok.html +1 -1
  32. package/public/sections/db/modals/add-item-modal.ok.js +1 -1
  33. package/public/sections/db/modals/create-env-modal.ok.js +1 -1
  34. package/public/sections/db/modals/create-fts-modal.ok.js +1 -1
  35. package/public/sections/db/modals/create-index-modal.ok.js +1 -1
  36. package/public/sections/db/modals/create-pipeline-modal.ok.js +1 -1
  37. package/public/sections/db/modals/demo-env-modal.ok.js +1 -1
  38. package/public/sections/db/modals/remove-env-modal.ok.js +1 -1
  39. package/public/sections/db/modals/schema-edit-modal.ok.js +1 -1
  40. package/public/sections/db/parts/db-overview.ok.js +1 -1
  41. package/public/sections/db/parts/db-pipelines-panel.ok.js +1 -1
  42. package/public/sections/db/parts/edit-row.ok.js +1 -1
  43. package/public/sections/db/parts/tm-timeline.ok.js +1 -1
  44. package/public/sections/db/parts/type-changelog.ok.js +1 -1
  45. package/public/sections/db/parts/type-list.ok.js +1 -1
  46. package/public/sections/db/parts/type-pipelines-tab.ok.js +1 -1
  47. package/public/sections/db/parts/type-schema-tab.ok.js +1 -1
  48. package/public/sections/db/parts/type-viewer.ok.js +1 -1
  49. package/public/sections/db/parts/type-violations-tab.ok.js +1 -1
  50. package/public/sections/docs/index.ok.html +1 -1
  51. package/public/sections/embeddings/index.ok.html +1 -1
  52. package/public/sections/embeddings/modals/register-model-modal.ok.js +1 -1
  53. package/public/sections/embeddings/parts/embed-create-panel.ok.js +1 -1
  54. package/public/sections/embeddings/parts/embed-embedder-panel.ok.js +1 -1
  55. package/public/sections/embeddings/parts/embed-engine-declaration-editor.ok.js +1 -1
  56. package/public/sections/embeddings/parts/embed-engine-list.ok.js +1 -1
  57. package/public/sections/embeddings/parts/embed-indexer-panel.ok.js +1 -1
  58. package/public/sections/embeddings/parts/embed-lifecycle-bar.ok.js +1 -1
  59. package/public/sections/embeddings/parts/embed-model-catalog.ok.js +1 -1
  60. package/public/sections/embeddings/parts/embed-overview.ok.js +1 -1
  61. package/public/sections/embeddings/parts/embed-search-panel.ok.js +1 -1
  62. package/public/sections/embeddings/parts/embed-worker-panel.ok.js +1 -1
  63. package/public/sections/embeddings/parts/embeddings-engine-panel.ok.js +1 -1
  64. package/public/sections/embeddings/parts/engines-overview.ok.js +1 -1
  65. package/public/sections/embeddings/parts/pipeline-create-panel.ok.js +1 -1
  66. package/public/sections/engines/engine-panel-shared-style.js +1 -1
  67. package/public/sections/engines/engine-ui-utils.js +1 -1
  68. package/public/sections/engines/index.ok.html +1 -1
  69. package/public/sections/engines/modals/engine-modal.ok.js +1 -1
  70. package/public/sections/engines/parts/engine-declaration-editor.ok.js +1 -1
  71. package/public/sections/engines/parts/engine-lifecycle-actions.ok.js +1 -1
  72. package/public/sections/engines/parts/engine-panel-shell.ok.js +1 -1
  73. package/public/sections/engines/parts/engine-resource-card.ok.js +1 -1
  74. package/public/sections/engines/parts/engine-row-item.ok.js +1 -1
  75. package/public/sections/engines/parts/generic-engine-panel.ok.js +1 -1
  76. package/public/sections/files/index.ok.html +1 -1
  77. package/public/sections/files/parts/files-blob-status.ok.js +1 -1
  78. package/public/sections/files/parts/files-collection-list.ok.js +1 -1
  79. package/public/sections/files/parts/files-collection-panel.ok.js +1 -1
  80. package/public/sections/files/parts/files-file-list.ok.js +1 -1
  81. package/public/sections/files/parts/files-overview.ok.js +1 -1
  82. package/public/sections/files/parts/files-path-browser.ok.js +1 -1
  83. package/public/sections/files/parts/files-upload-panel.ok.js +1 -1
  84. package/public/sections/functions/index.ok.html +1 -1
  85. package/public/sections/functions/modals/function-modal.ok.js +1 -1
  86. package/public/sections/functions/parts/function-detail.ok.js +1 -1
  87. package/public/sections/functions/parts/function-logs.ok.js +1 -1
  88. package/public/sections/functions/parts/function-runner.ok.js +1 -1
  89. package/public/sections/functions/parts/functions-list.ok.js +1 -1
  90. package/public/sections/license/index.ok.html +1 -1
  91. package/public/sections/license/parts/license-panel.ok.js +1 -1
  92. package/public/sections/processors/index.ok.html +1 -1
  93. package/public/sections/queue/index.ok.html +1 -1
  94. package/public/sections/queue/modals/create-bucket-modal.ok.js +1 -1
  95. package/public/sections/queue/modals/edit-bucket-modal.ok.js +1 -1
  96. package/public/sections/queue/modals/edit-job-modal.ok.js +1 -1
  97. package/public/sections/queue/modals/enqueue-job-modal.ok.js +1 -1
  98. package/public/sections/queue/modals/job-type-modal.ok.js +1 -1
  99. package/public/sections/queue/parts/ok-job-form.ok.js +1 -1
  100. package/public/sections/queue/parts/queue-buckets.ok.js +1 -1
  101. package/public/sections/queue/parts/queue-job-types.ok.js +1 -1
  102. package/public/sections/queue/parts/queue-jobs.ok.js +1 -1
  103. package/public/sections/queue/parts/queue-types.ok.js +1 -1
  104. package/public/sections/queue/parts/queue-workers.ok.js +1 -1
  105. package/public/sections/sync/index.ok.html +1 -1
  106. package/public/sections/sync/parts/sync-node-detail.ok.js +1 -1
  107. package/public/sections/sync/parts/sync-retention.ok.js +1 -1
  108. package/public/sections/sync/parts/sync-topology.ok.js +1 -1
  109. package/public/sections/system/index.ok.html +1 -1
  110. package/public/sections/system/modals/data-ops-modal.ok.js +1 -1
  111. package/public/sections/system/parts/system-api-panel.ok.js +1 -1
  112. package/public/sections/system/parts/system-auth-panel.ok.js +1 -1
  113. package/public/sections/system/parts/system-log-history.ok.js +1 -1
  114. package/public/sections/system/parts/system-log.ok.js +1 -1
  115. package/public/sections/system/parts/system-status.ok.js +1 -1
  116. package/public/sections/system/parts/system-sync-panel.ok.js +1 -1
  117. package/public/sections/views/index.ok.html +1 -1
  118. package/public/sections/views/modals/create-view-modal.ok.js +1 -1
  119. package/public/sections/views/parts/view-detail.ok.js +1 -1
  120. package/public/sections/views/parts/views-list.ok.js +1 -1
  121. package/public/sections/views/parts/views-overview.ok.js +1 -1
  122. package/public/setup-app.js +1 -1
  123. package/public/setup.html +189 -1
  124. package/public/stores/auth-store.js +1 -1
  125. package/public/stores/events-store.js +1 -1
  126. package/public/stores/functions-store.js +1 -1
  127. package/public/stores/okdb-store.js +1 -1
  128. package/public/stores/theme-store.js +1 -1
  129. package/public/test-cron-input.html +53 -3
  130. package/public/test-json-input.html +182 -3
  131. package/public/util/permissions.js +1 -1
  132. package/public/util/sift-schema.js +1 -1
  133. package/public/vendor/okjs/ok.esm.min.js +1 -2
  134. package/public/view-models/type-query-vm.js +1 -1
  135. package/okdb-data/default/data.mdb +0 -0
  136. package/okdb-data/default/lock.mdb +0 -0
  137. package/okdb-data/~log/data.mdb +0 -0
  138. package/okdb-data/~log/lock.mdb +0 -0
  139. package/okdb-data/~system/data.mdb +0 -0
  140. package/okdb-data/~system/lock.mdb +0 -0
  141. package/public/images/logo.png +0 -0
  142. package/public/images/logo.svg +0 -333
@@ -1 +1 @@
1
- const _0x408539=_0x115b;(function(_0x21a50e,_0xfce3d0){const _0x42ec45=_0x115b,_0x52ea29=_0x21a50e();while(!![]){try{const _0x2f26ce=parseInt(_0x42ec45(0x21e))/0x1+parseInt(_0x42ec45(0x1d5))/0x2*(parseInt(_0x42ec45(0x1b6))/0x3)+parseInt(_0x42ec45(0x1c5))/0x4*(-parseInt(_0x42ec45(0x1f3))/0x5)+parseInt(_0x42ec45(0x1f2))/0x6*(-parseInt(_0x42ec45(0x1ae))/0x7)+parseInt(_0x42ec45(0x1c3))/0x8+parseInt(_0x42ec45(0x1ca))/0x9*(-parseInt(_0x42ec45(0x214))/0xa)+-parseInt(_0x42ec45(0x20c))/0xb*(-parseInt(_0x42ec45(0x1ef))/0xc);if(_0x2f26ce===_0xfce3d0)break;else _0x52ea29['push'](_0x52ea29['shift']());}catch(_0x382f08){_0x52ea29['push'](_0x52ea29['shift']());}}}(_0x232a,0xa2454));import _0x12252c from'@toolkit/elements/ok-icon.ok.js';function _0x115b(_0x11c477,_0x2266b4){_0x11c477=_0x11c477-0x1aa;const _0x232a38=_0x232a();let _0x115b36=_0x232a38[_0x11c477];return _0x115b36;}import _0x41b321 from'@toolkit/core/ok-overlay.ok.js';import _0x574c9c from'../../../components/ok-cron-input.ok.js';import _0x19c597 from'../../../components/ok-json-input.ok.js';import _0x90bb3 from'../../../components/field-path-input.ok.js';function _0x232a(){const _0x5224bd=['pattern','apiBase','\x0a<div\x20style=\x22position:relative;\x20min-width:720px;\x20max-width:calc(100vw\x20-\x2080px);\x20display:flex;\x20flex-direction:column;\x20gap:12px;\x22>\x0a\x0a\x20\x20<if\x20:=\x22busy\x22>\x0a\x20\x20\x20\x20<ok-overlay>\x0a\x20\x20\x20\x20\x20\x20<div\x20class=\x22loader-center\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<ok-icon\x20name=\x22loader\x22\x20class=\x22spin\x22\x20style=\x22width:32px;height:32px;\x22></ok-icon>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</ok-overlay>\x0a\x20\x20</if>\x0a\x0a\x20\x20<!--\x20Row\x201:\x20type\x20+\x20priority\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label\x20style=\x22display:flex;\x20align-items:center;\x20gap:6px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20Type\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22typeIsRegistered\x20&&\x20typeStatus\x20===\x20\x27active\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22type-pill\x20type-pill--ok\x22>registered</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22typeStatus\x20===\x20\x27paused\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22type-pill\x20type-pill--warn\x22>paused</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22typeStatus\x20===\x20\x27draining\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22type-pill\x20type-pill--info\x22>draining</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20</label>\x0a\x20\x20\x20\x20\x20\x20<field-path-input\x0a\x20\x20\x20\x20\x20\x20\x20\x20:allow-custom=\x22true\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20:value=\x22type\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20:items=\x22typeItems\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20placeholder=\x22e.g.\x20email.send\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20@change=\x22onTypeSelected($event.detail)\x22\x0a\x20\x20\x20\x20\x20\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:120px;\x22>\x0a\x20\x20\x20\x20\x20\x20<label\x20style=\x22display:flex;\x20align-items:center;\x20gap:5px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20Priority\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22_fromType.priority\x22><span\x20class=\x22auto-badge\x22>auto</span></if>\x0a\x20\x20\x20\x20\x20\x20</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20placeholder=\x220\x22\x20:value=\x22priority\x22\x20@input=\x22_set(\x27priority\x27,\x20Number($el.value))\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x202:\x20tags\x20+\x20idempotency\x20key\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:10px;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Tags</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20placeholder=\x22comma\x20separated\x22\x20:value=\x22tags\x22\x20@input=\x22tags\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Idempotency\x20Key</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20placeholder=\x22optional\x20—\x20prevents\x20duplicate\x20jobs\x22\x20:value=\x22idempotencyKey\x22\x20@input=\x22idempotencyKey\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Buckets\x20-->\x0a\x20\x20<div\x20class=\x22field\x22>\x0a\x20\x20\x20\x20<label\x20style=\x22display:flex;\x20align-items:center;\x20gap:5px;\x22>\x0a\x20\x20\x20\x20\x20\x20Buckets\x20<span\x20style=\x22opacity:.4;\x20font-weight:400;\x20font-size:11px;\x22>(rate\x20limiting)</span>\x0a\x20\x20\x20\x20\x20\x20<if\x20:=\x22_fromType.buckets\x22><span\x20class=\x22auto-badge\x22>auto</span></if>\x0a\x20\x20\x20\x20</label>\x0a\x20\x20\x20\x20<each\x20:of=\x22buckets\x22\x20let:item=\x22b\x22\x20let:index=\x22i\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:6px;\x20align-items:center;\x20margin-bottom:4px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<select\x20style=\x22flex:1;\x22\x20@change=\x22onBucketSelect(i,\x20$el.value)\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22\x22>—\x20none\x20—</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20as=\x22repeat\x22\x20:of=\x22bucketOptions\x22\x20let:item=\x22opt\x22\x20:value=\x22opt.id\x22\x20:selected=\x22opt.id\x20===\x20b.id\x22>{{\x20opt.id\x20}}</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22__create__\x22>+\x20Create\x20bucket…</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</select>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<input\x20style=\x22width:72px;\x22\x20type=\x22number\x22\x20min=\x221\x22\x20placeholder=\x22tokens\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:value=\x22b.tokens\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20@input=\x22setBucketTokens(i,\x20$el.value)\x22\x20/>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22btn-ghost\x22\x20@click=\x22removeBucket(i)\x22\x20title=\x22Remove\x22>✕</button>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</each>\x0a\x20\x20\x20\x20<button\x20class=\x22btn-ghost\x20btn-ghost--sm\x22\x20@click=\x22addBucket()\x22\x20style=\x22margin-top:2px;\x22>+\x20Add\x20bucket</button>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x203:\x20when\x20+\x20max\x20tries\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>When</label>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:4px;\x20align-items:center;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20:class=\x22whenMode===\x27asap\x27\x20?\x20\x27mode-btn\x20mode-btn--on\x27\x20:\x20\x27mode-btn\x27\x22\x20@click=\x22whenMode=\x27asap\x27\x22>ASAP</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20:class=\x22whenMode===\x27datetime\x27\x20?\x20\x27mode-btn\x20mode-btn--on\x27\x20:\x20\x27mode-btn\x27\x22\x20@click=\x22whenMode=\x27datetime\x27\x22>Date</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20:class=\x22whenMode===\x27timestamp\x27\x20?\x20\x27mode-btn\x20mode-btn--on\x27\x20:\x20\x27mode-btn\x27\x22\x20@click=\x22whenMode=\x27timestamp\x27\x22>TS</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22whenMode\x20===\x20\x27datetime\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<input\x20type=\x22datetime-local\x22\x20style=\x22flex:1;\x22\x20:value=\x22whenDatetime\x22\x20@input=\x22whenDatetime\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22whenMode\x20===\x20\x27timestamp\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20style=\x22flex:1;\x22\x20placeholder=\x22ms\x20timestamp\x22\x20:value=\x22whenTs\x22\x20@input=\x22whenTs\x20=\x20$el.value\x20===\x20\x27\x27\x20?\x20null\x20:\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22whenMode\x20===\x20\x27asap\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22opacity:.5;\x20font-size:12px;\x20padding:0\x208px;\x22>Run\x20immediately</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:120px;\x22>\x0a\x20\x20\x20\x20\x20\x20<label\x20style=\x22display:flex;\x20align-items:center;\x20gap:5px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20Max\x20tries\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22_fromType.maxTries\x22><span\x20class=\x22auto-badge\x22>auto</span></if>\x0a\x20\x20\x20\x20\x20\x20</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20placeholder=\x22unlimited\x22\x20:value=\x22maxTries\x22\x20@input=\x22_set(\x27maxTries\x27,\x20$el.value\x20===\x20\x27\x27\x20?\x20null\x20:\x20Number($el.value))\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x204:\x20retry\x20delay\x20+\x20backoff\x20+\x20cron\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label\x20style=\x22display:flex;\x20align-items:center;\x20gap:5px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20Retry\x20delay\x20(ms)\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22_fromType.retryDelay\x22><span\x20class=\x22auto-badge\x22>auto</span></if>\x0a\x20\x20\x20\x20\x20\x20</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20placeholder=\x220\x22\x20:value=\x22retryDelay\x22\x20@input=\x22_set(\x27retryDelay\x27,\x20Number($el.value))\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:120px;\x22>\x0a\x20\x20\x20\x20\x20\x20<label\x20style=\x22display:flex;\x20align-items:center;\x20gap:5px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20Backoff\x20multiplier\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22_fromType.backoff\x22><span\x20class=\x22auto-badge\x22>auto</span></if>\x0a\x20\x20\x20\x20\x20\x20</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20step=\x220.1\x22\x20placeholder=\x221\x22\x20:value=\x22backoff\x22\x20@input=\x22_set(\x27backoff\x27,\x20Number($el.value))\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Cron</label>\x0a\x20\x20\x20\x20\x20\x20<ok-cron-input\x20:value=\x22cron\x22\x20placeholder=\x22optional\x22\x20@change=\x22cron\x20=\x20$event.detail\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x205:\x20retention\x20TTLs\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label\x20style=\x22display:flex;\x20align-items:center;\x20gap:5px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20Done\x20TTL\x20(ms)\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22_fromType.doneTtl\x22><span\x20class=\x22auto-badge\x22>auto</span></if>\x0a\x20\x20\x20\x20\x20\x20</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x20placeholder=\x220\x20=\x20delete\x20immediately,\x20blank\x20=\x20keep\x20forever\x22\x20:value=\x22doneTtl\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20@input=\x22_set(\x27doneTtl\x27,\x20$el.value\x20===\x20\x27\x27\x20?\x20null\x20:\x20Number($el.value))\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label\x20style=\x22display:flex;\x20align-items:center;\x20gap:5px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20Failed\x20TTL\x20(ms)\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22_fromType.failedTtl\x22><span\x20class=\x22auto-badge\x22>auto</span></if>\x0a\x20\x20\x20\x20\x20\x20</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x20placeholder=\x22blank\x20=\x20keep\x20forever\x22\x20:value=\x22failedTtl\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20@input=\x22_set(\x27failedTtl\x27,\x20$el.value\x20===\x20\x27\x27\x20?\x20null\x20:\x20Number($el.value))\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Payload\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:6px;\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20align-items:center;\x20gap:8px;\x22>\x0a\x20\x20\x20\x20\x20\x20<span\x20style=\x22opacity:.7;\x20font-size:12px;\x22>Payload</span>\x0a\x20\x20\x20\x20\x20\x20<if\x20:=\x22typeSchema\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22type-pill\x20type-pill--schema\x22>schema</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22payloadErrors.length\x20===\x200\x20&&\x20_payloadTouched\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22type-pill\x20type-pill--ok\x22\x20style=\x22font-size:10px;\x22>valid</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<ok-json-input\x0a\x20\x20\x20\x20\x20\x20mode=\x22textarea\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22height:220px;\x22\x0a\x20\x20\x20\x20\x20\x20:value=\x22payloadInit\x22\x0a\x20\x20\x20\x20\x20\x20@edit=\x22onPayloadChange($event.detail)\x22\x0a\x20\x20\x20\x20/>\x0a\x20\x20\x20\x20<if\x20:=\x22_payloadTouched\x20&&\x20payloadErrors.length\x20>\x200\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20class=\x22schema-errors\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<each\x20:of=\x22payloadErrors\x22\x20let:item=\x22err\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20class=\x22schema-error-row\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22schema-error-path\x22>{{\x20err.path\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22schema-error-msg\x22>{{\x20err.msg\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</each>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</if>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Footer\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:10px;\x20justify-content:flex-end;\x22>\x0a\x20\x20\x20\x20<button\x20@click=\x22cancel()\x22>Cancel</button>\x0a\x20\x20\x20\x20<button\x20@click=\x22submit()\x22\x20:disabled=\x22!type\x20||\x20busy\x22>Enqueue</button>\x0a\x20\x20</div>\x0a\x0a</div>\x0a','find','allOf','failed_ttl','data','70754xXtVCG','error','typeStatus','pattern\x20mismatch','_validatePayload','/queue/job-types','maxLength:\x20','/queue/enqueue/','type','split','minLength:\x20','open','idempotency_key','datetime','21kiwDFm','asap','trim','string','buckets','shared','entries','backoff_multiplier','132tIbxPb','enqueue\x20failed','priority','max_tries','json','_fromType','tokens','$ref','applyTypeDefaults','must\x20be\x20one\x20of\x20[','payload_schema','__create__','items','4502832snzwrE','Add\x20Token\x20Bucket','4rlUqvF','null','\x20|\x20','Failed\x20to\x20enqueue\x20job','maxTries','9pPftfF','retryDelay','.mode-btn{font-size:11px;padding:2px\x208px;opacity:.6;border-radius:4px}.mode-btn--on{opacity:1;background:color-mix(in\x20srgb,var(--okdb-accent)\x2013%,transparent);border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2033%,transparent);color:var(--okdb-accent)}.btn-ghost{background:0\x200;border:1px\x20solid\x20var(--okdb-app-border);border-radius:5px;color:inherit;cursor:pointer;opacity:.6;padding:2px\x208px;font-size:12px}.btn-ghost:hover{opacity:1;border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2033%,transparent)}.btn-ghost--sm{font-size:11px;padding:1px\x207px}.type-pill{font-size:10px;padding:1px\x206px;border-radius:999px;border:1px\x20solid;font-weight:400}.type-pill--ok{border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2033%,transparent);color:var(--okdb-accent)}.type-pill--warn{border-color:#f59e0b;color:#f59e0b}.type-pill--info{border-color:#3b82f6;color:#3b82f6}.type-pill--schema{border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2033%,transparent);color:var(--okdb-accent)}.auto-badge{font-size:9px;padding:1px\x205px;border-radius:999px;font-weight:600;letter-spacing:.04em;background:color-mix(in\x20srgb,var(--okdb-accent)\x2012%,transparent);border:1px\x20solid\x20color-mix(in\x20srgb,var(--okdb-accent)\x2025%,transparent);color:var(--okdb-accent);text-transform:uppercase}.schema-errors{display:flex;flex-direction:column;gap:3px;padding:8px\x2010px;border-radius:6px;border:1px\x20solid\x20color-mix(in\x20srgb,#ef4444\x2040%,transparent);background:color-mix(in\x20srgb,#ef4444\x206%,transparent);font-size:11px;max-height:120px;overflow-y:auto}.schema-error-row{display:flex;gap:8px;align-items:baseline}.schema-error-path{font-family:monospace;color:#f87171;flex-shrink:0}.schema-error-msg{color:#fca5a5;opacity:.8}','_syncTypeData','close','test','isArray','push','message','catch','POST','54358WDRdra','maxLength','active','length','typeIsRegistered','$attr','cron','join','object','getTime','modal','whenTs','<=\x20','busy','isInteger','expected\x20','properties','payloadErrors','retry_delay','result','_payloadTouched','getWhen','initialType','required','idempotencyKey','definition','5654700PonQuP','enqueue-job-modal','onDone','268146HPaLxj','3653835fgRufC','loadBuckets','status','keys','payload','failedTtl','whenMode','map','some','minLength','_revalidatePayload','typeItems','done_ttl','doneTtl','>=\x20','tags','minimum','enum','registeredTypes','stringify','bucketOptions','all','max','Payload\x20does\x20not\x20match\x20the\x20job\x20type\x20schema','toast','11bltGPi','backoff','maximum','(root)','number','/queue/buckets','typeSchema','$ok','7712090uKEUQN','filter','integer'];_0x232a=function(){return _0x5224bd;};return _0x232a();}import _0x3e5a36 from'./create-bucket-modal.ok.js';import _0xc45aec from'../../../stores/okdb-store.js';export default{'tag':_0x408539(0x1f0),'dependencies':[_0x574c9c,_0x19c597,_0x90bb3,_0x3e5a36,_0x41b321,_0x12252c],'attr':{'modal':{},'initialType':{},'onDone':{}},'context':_0x19084d=>({'busy':!0x1,'type':_0x19084d['$attr'][_0x408539(0x1eb)]??'','payloadInit':{},'payload':{},'priority':0x0,'maxTries':null,'retryDelay':0x0,'backoff':0x1,'tags':'','buckets':[{'id':'','tokens':0x1}],'cron':'','idempotencyKey':'','whenMode':_0x408539(0x1af),'whenDatetime':'','whenTs':null,'doneTtl':null,'failedTtl':null,'bucketOptions':[],'registeredTypes':[],'typeItems':[],'typeIsRegistered':!0x1,'typeStatus':null,'typeSchema':null,'_fromType':{},'payloadErrors':[],'_payloadTouched':!0x1,'_syncTypeData'(){const _0x4494a9=_0x408539,_0x4e0c9b=this[_0x4494a9(0x205)][_0x4494a9(0x21a)](_0x466310=>_0x466310['id']===this['type'])??null;this[_0x4494a9(0x1d9)]=!!_0x4e0c9b,this[_0x4494a9(0x220)]=_0x4e0c9b?.[_0x4494a9(0x1f5)]??null,this[_0x4494a9(0x212)]=_0x4e0c9b?.[_0x4494a9(0x1c0)]??null,this[_0x4494a9(0x1fe)]=this['registeredTypes'][_0x4494a9(0x1fa)](_0x464f9b=>({'path':_0x464f9b['id'],'types':_0x464f9b[_0x4494a9(0x1f5)]&&_0x4494a9(0x1d7)!==_0x464f9b['status']?[_0x464f9b[_0x4494a9(0x1f5)]]:[],'hint':null}));},'_set'(_0x453926,_0x2a03ac){const _0x483dd1=_0x408539;if(this[_0x453926]=_0x2a03ac,this['_fromType'][_0x453926]){const _0xf64684={...this[_0x483dd1(0x1bb)]};delete _0xf64684[_0x453926],this['_fromType']=_0xf64684;}},async 'loadBuckets'(){const _0x40611b=_0x408539,[_0x91f236,_0x69ac0]=await Promise[_0x40611b(0x208)]([fetch(_0xc45aec['apiBase']+_0x40611b(0x211))[_0x40611b(0x1d3)](()=>null),fetch(_0xc45aec[_0x40611b(0x218)]+_0x40611b(0x223))[_0x40611b(0x1d3)](()=>null)]);if(_0x91f236?.['ok']){const _0x4cbe23=await _0x91f236[_0x40611b(0x1ba)]()['catch'](()=>({}));this[_0x40611b(0x207)]=_0x4cbe23?.[_0x40611b(0x1e8)]??_0x4cbe23?.['data']??[];}if(_0x69ac0?.['ok']){const _0x4393df=await _0x69ac0['json']()['catch'](()=>({}));this['registeredTypes']=_0x4393df?.[_0x40611b(0x1e8)]??_0x4393df?.[_0x40611b(0x21d)]??[];}this[_0x40611b(0x1cd)]();},'applyTypeDefaults'(_0xe499a2){const _0x153b64=_0x408539,_0x44dac9=this['registeredTypes'][_0x153b64(0x21a)](_0x182683=>_0x182683['id']===_0xe499a2);if(!_0x44dac9)return!0x1;const _0x20f82e=_0x44dac9['defaults']??{},_0x2f2a75={};return null!=_0x20f82e[_0x153b64(0x1b9)]&&(this[_0x153b64(0x1c9)]=_0x20f82e['max_tries'],_0x2f2a75[_0x153b64(0x1c9)]=!0x0),null!=_0x20f82e[_0x153b64(0x1e7)]&&(this['retryDelay']=_0x20f82e[_0x153b64(0x1e7)],_0x2f2a75[_0x153b64(0x1cb)]=!0x0),null!=_0x20f82e[_0x153b64(0x1b5)]&&(this[_0x153b64(0x20d)]=_0x20f82e['backoff_multiplier'],_0x2f2a75[_0x153b64(0x20d)]=!0x0),null!=_0x20f82e[_0x153b64(0x1b8)]&&(this[_0x153b64(0x1b8)]=_0x20f82e['priority'],_0x2f2a75[_0x153b64(0x1b8)]=!0x0),null!=_0x20f82e[_0x153b64(0x1ff)]&&(this['doneTtl']=_0x20f82e[_0x153b64(0x1ff)],_0x2f2a75[_0x153b64(0x200)]=!0x0),null!=_0x20f82e[_0x153b64(0x21c)]&&(this[_0x153b64(0x1f8)]=_0x20f82e['failed_ttl'],_0x2f2a75['failedTtl']=!0x0),Array['isArray'](_0x20f82e[_0x153b64(0x1b2)])&&_0x20f82e[_0x153b64(0x1b2)][_0x153b64(0x1d8)]&&(this['buckets']=_0x20f82e[_0x153b64(0x1b2)][_0x153b64(0x1fa)](_0x1d5862=>({'id':_0x1d5862['id'],'tokens':_0x1d5862[_0x153b64(0x1bc)]??0x1})),_0x2f2a75[_0x153b64(0x1b2)]=!0x0),this[_0x153b64(0x1bb)]=_0x2f2a75,!0x0;},'onTypeSelected'(_0x5f536a){const _0x501593=_0x408539;null!=_0x5f536a&&(this[_0x501593(0x226)]=_0x5f536a,this[_0x501593(0x1cd)](),this[_0x501593(0x1be)](_0x5f536a)||_0x5f536a||(this[_0x501593(0x1bb)]={}),this[_0x501593(0x1fd)]());},async 'onBucketSelect'(_0x30f5e5,_0x1c2c44){const _0x123d26=_0x408539;if(this[_0x123d26(0x1bb)][_0x123d26(0x1b2)]&&(this[_0x123d26(0x1bb)]={...this[_0x123d26(0x1bb)],'buckets':!0x1}),_0x123d26(0x1c1)===_0x1c2c44){const _0x40cc4f=this;await _0x19084d[_0x123d26(0x213)][_0x123d26(0x1b3)][_0x123d26(0x1df)][_0x123d26(0x1ab)]({'owner':_0x19084d,'title':_0x123d26(0x1c4),'content':'<create-bucket-modal\x20:modal=\x22modal\x22\x20:on-done=\x22onDone\x22\x20/>','context':_0x365e91=>({'modal':_0x365e91,'onDone':async _0x17d92c=>{const _0x4715ff=_0x123d26;await _0x40cc4f[_0x4715ff(0x1f4)](),_0x40cc4f[_0x4715ff(0x1b2)]=_0x40cc4f[_0x4715ff(0x1b2)][_0x4715ff(0x1fa)]((_0x34b07c,_0x46cae9)=>_0x46cae9===_0x30f5e5?{..._0x34b07c,'id':_0x17d92c}:_0x34b07c);}})});}else this[_0x123d26(0x1b2)]=this[_0x123d26(0x1b2)][_0x123d26(0x1fa)]((_0x4b0668,_0xe04980)=>_0xe04980===_0x30f5e5?{..._0x4b0668,'id':_0x1c2c44}:_0x4b0668);},'setBucketTokens'(_0x155451,_0x2a4765){const _0x129b40=_0x408539;this[_0x129b40(0x1bb)][_0x129b40(0x1b2)]&&(this['_fromType']={...this[_0x129b40(0x1bb)],'buckets':!0x1});const _0x49a41e=''===_0x2a4765?0x1:Math[_0x129b40(0x209)](0x1,parseInt(_0x2a4765)||0x1);this[_0x129b40(0x1b2)]=this[_0x129b40(0x1b2)][_0x129b40(0x1fa)]((_0x3bd06b,_0x14ea06)=>_0x14ea06===_0x155451?{..._0x3bd06b,'tokens':_0x49a41e}:_0x3bd06b);},'addBucket'(){const _0x4e2424=_0x408539;this['_fromType'][_0x4e2424(0x1b2)]&&(this['_fromType']={...this[_0x4e2424(0x1bb)],'buckets':!0x1}),this[_0x4e2424(0x1b2)]=[...this[_0x4e2424(0x1b2)],{'id':'','tokens':0x1}];},'removeBucket'(_0x5ddfcb){const _0x5e0e95=_0x408539;this[_0x5e0e95(0x1bb)][_0x5e0e95(0x1b2)]&&(this[_0x5e0e95(0x1bb)]={...this[_0x5e0e95(0x1bb)],'buckets':!0x1}),this[_0x5e0e95(0x1b2)]=this[_0x5e0e95(0x1b2)]['filter']((_0x36bf2c,_0x1d23da)=>_0x1d23da!==_0x5ddfcb);},'onPayloadChange'(_0x59d0e8){const _0x52c0bb=_0x408539;this['payload']=_0x59d0e8,this[_0x52c0bb(0x1e9)]=!0x0,this['_revalidatePayload']();},'_revalidatePayload'(){const _0x121c9d=_0x408539,_0x4a5bf1=this[_0x121c9d(0x212)];this['payloadErrors']=_0x4a5bf1?.[_0x121c9d(0x1ee)]?this[_0x121c9d(0x222)](this[_0x121c9d(0x1f7)],_0x4a5bf1[_0x121c9d(0x1ee)]):[];},'_validatePayload'(_0x457966,_0x5b5593){const _0x55fdac=[],_0x32b2fd=(_0x5359aa,_0x5eec9f,_0x296cd1)=>{const _0x5d5bbe=_0x115b;if(_0x5eec9f&&'object'==typeof _0x5eec9f&&!_0x5eec9f[_0x5d5bbe(0x1bd)]){if(_0x5eec9f[_0x5d5bbe(0x226)]){const _0x49244d=Array[_0x5d5bbe(0x1d0)](_0x5eec9f[_0x5d5bbe(0x226)])?_0x5eec9f['type']:[_0x5eec9f[_0x5d5bbe(0x226)]],_0x6c4b79=null===_0x5359aa?_0x5d5bbe(0x1c6):Array[_0x5d5bbe(0x1d0)](_0x5359aa)?'array':typeof _0x5359aa;if(!_0x49244d[_0x5d5bbe(0x1fb)](_0x48b98a=>_0x48b98a===_0x6c4b79||_0x5d5bbe(0x216)===_0x48b98a&&'number'===_0x6c4b79&&Number[_0x5d5bbe(0x1e3)](_0x5359aa)))return void _0x55fdac[_0x5d5bbe(0x1d1)]({'path':_0x296cd1||_0x5d5bbe(0x20f),'msg':_0x5d5bbe(0x1e4)+_0x49244d[_0x5d5bbe(0x1dc)](_0x5d5bbe(0x1c7))+',\x20got\x20'+_0x6c4b79});}if(_0x5eec9f[_0x5d5bbe(0x204)]&&!_0x5eec9f[_0x5d5bbe(0x204)][_0x5d5bbe(0x1fb)](_0x36763c=>JSON[_0x5d5bbe(0x206)](_0x36763c)===JSON[_0x5d5bbe(0x206)](_0x5359aa))&&_0x55fdac[_0x5d5bbe(0x1d1)]({'path':_0x296cd1||_0x5d5bbe(0x20f),'msg':_0x5d5bbe(0x1bf)+_0x5eec9f[_0x5d5bbe(0x204)][_0x5d5bbe(0x1fa)](_0xda9e8b=>JSON['stringify'](_0xda9e8b))['join'](',\x20')+']'}),_0x5d5bbe(0x1b1)==typeof _0x5359aa&&(null!=_0x5eec9f[_0x5d5bbe(0x1fc)]&&_0x5359aa[_0x5d5bbe(0x1d8)]<_0x5eec9f[_0x5d5bbe(0x1fc)]&&_0x55fdac[_0x5d5bbe(0x1d1)]({'path':_0x296cd1||_0x5d5bbe(0x20f),'msg':_0x5d5bbe(0x1aa)+_0x5eec9f[_0x5d5bbe(0x1fc)]}),null!=_0x5eec9f[_0x5d5bbe(0x1d6)]&&_0x5359aa[_0x5d5bbe(0x1d8)]>_0x5eec9f['maxLength']&&_0x55fdac[_0x5d5bbe(0x1d1)]({'path':_0x296cd1||_0x5d5bbe(0x20f),'msg':_0x5d5bbe(0x224)+_0x5eec9f[_0x5d5bbe(0x1d6)]}),_0x5eec9f['pattern']))try{new RegExp(_0x5eec9f[_0x5d5bbe(0x217)])[_0x5d5bbe(0x1cf)](_0x5359aa)||_0x55fdac[_0x5d5bbe(0x1d1)]({'path':_0x296cd1||'(root)','msg':_0x5d5bbe(0x221)});}catch{}if(_0x5d5bbe(0x210)==typeof _0x5359aa&&(null!=_0x5eec9f[_0x5d5bbe(0x203)]&&_0x5359aa<_0x5eec9f['minimum']&&_0x55fdac[_0x5d5bbe(0x1d1)]({'path':_0x296cd1||'(root)','msg':_0x5d5bbe(0x201)+_0x5eec9f[_0x5d5bbe(0x203)]}),null!=_0x5eec9f[_0x5d5bbe(0x20e)]&&_0x5359aa>_0x5eec9f[_0x5d5bbe(0x20e)]&&_0x55fdac[_0x5d5bbe(0x1d1)]({'path':_0x296cd1||_0x5d5bbe(0x20f),'msg':_0x5d5bbe(0x1e1)+_0x5eec9f[_0x5d5bbe(0x20e)]})),_0x5d5bbe(0x1dd)==typeof _0x5359aa&&null!==_0x5359aa&&!Array['isArray'](_0x5359aa)){for(const _0x1da1c5 of _0x5eec9f[_0x5d5bbe(0x1ec)]??[])_0x1da1c5 in _0x5359aa||_0x55fdac[_0x5d5bbe(0x1d1)]({'path':_0x296cd1?_0x296cd1+'.'+_0x1da1c5:_0x1da1c5,'msg':_0x5d5bbe(0x1ec)});if(_0x5eec9f[_0x5d5bbe(0x1e5)]){for(const [_0x4c3f03,_0x277c9d]of Object[_0x5d5bbe(0x1b4)](_0x5eec9f[_0x5d5bbe(0x1e5)]))_0x4c3f03 in _0x5359aa&&_0x32b2fd(_0x5359aa[_0x4c3f03],_0x277c9d,_0x296cd1?_0x296cd1+'.'+_0x4c3f03:_0x4c3f03);if(!0x1===_0x5eec9f['additionalProperties']){for(const _0x5c0c1d of Object[_0x5d5bbe(0x1f6)](_0x5359aa))_0x5c0c1d in _0x5eec9f[_0x5d5bbe(0x1e5)]||_0x55fdac[_0x5d5bbe(0x1d1)]({'path':_0x296cd1?_0x296cd1+'.'+_0x5c0c1d:_0x5c0c1d,'msg':'additional\x20property\x20not\x20allowed'});}}}if(Array['isArray'](_0x5359aa)&&_0x5eec9f[_0x5d5bbe(0x1c2)]&&_0x5359aa['forEach']((_0x518ecf,_0x18ed26)=>_0x32b2fd(_0x518ecf,_0x5eec9f[_0x5d5bbe(0x1c2)],(_0x296cd1||'')+'['+_0x18ed26+']')),_0x5eec9f[_0x5d5bbe(0x21b)]){for(const _0x23fcff of _0x5eec9f[_0x5d5bbe(0x21b)])_0x32b2fd(_0x5359aa,_0x23fcff,_0x296cd1);}}};return _0x32b2fd(_0x457966,_0x5b5593,''),_0x55fdac;},'getWhen'(){const _0x45f271=_0x408539;if('asap'===this[_0x45f271(0x1f9)])return null;if(_0x45f271(0x1ad)===this[_0x45f271(0x1f9)]){const _0x49ccfc=new Date(this['whenDatetime']);return isNaN(_0x49ccfc['getTime']())?null:_0x49ccfc[_0x45f271(0x1de)]();}return this[_0x45f271(0x1e0)];},'_parseTags'(_0x4ca4a1){const _0x404fa4=_0x408539;if(!_0x4ca4a1)return;const _0x3b2083=String(_0x4ca4a1)[_0x404fa4(0x227)](',')[_0x404fa4(0x1fa)](_0x55f665=>_0x55f665[_0x404fa4(0x1b0)]())[_0x404fa4(0x215)](Boolean);return _0x3b2083[_0x404fa4(0x1d8)]?_0x3b2083:void 0x0;},async 'submit'(){const _0x4ac7c5=_0x408539;if(!this[_0x4ac7c5(0x226)])return void _0x19084d[_0x4ac7c5(0x213)][_0x4ac7c5(0x1b3)]?.[_0x4ac7c5(0x20b)]?.[_0x4ac7c5(0x21f)]?.('Please\x20enter\x20a\x20job\x20type');if(this['typeSchema']?.[_0x4ac7c5(0x1ee)]){const _0x2b6a63=this[_0x4ac7c5(0x222)](this['payload'],this['typeSchema'][_0x4ac7c5(0x1ee)]);if(_0x2b6a63[_0x4ac7c5(0x1d8)])return this[_0x4ac7c5(0x1e9)]=!0x0,this[_0x4ac7c5(0x1e6)]=_0x2b6a63,void _0x19084d[_0x4ac7c5(0x213)][_0x4ac7c5(0x1b3)]?.[_0x4ac7c5(0x20b)]?.[_0x4ac7c5(0x21f)]?.(_0x4ac7c5(0x20a));}const _0x16d66d=this[_0x4ac7c5(0x1ea)](),_0x242165=String(this['cron']||'')[_0x4ac7c5(0x1b0)]()||null,_0x1ac272=this['buckets'][_0x4ac7c5(0x215)](_0x46dd19=>_0x46dd19['id'])[_0x4ac7c5(0x1fa)](_0x46b008=>({'id':_0x46b008['id'],'tokens':_0x46b008[_0x4ac7c5(0x1bc)]||0x1})),_0x2c2509=this['_parseTags'](this[_0x4ac7c5(0x202)]),_0x29b2d1={};null!=_0x16d66d&&(_0x29b2d1['when']=_0x16d66d),this[_0x4ac7c5(0x1b8)]&&(_0x29b2d1[_0x4ac7c5(0x1b8)]=Number(this[_0x4ac7c5(0x1b8)])),null!==this[_0x4ac7c5(0x1c9)]&&''!==this['maxTries']&&(_0x29b2d1['max_tries']=Number(this[_0x4ac7c5(0x1c9)])),this[_0x4ac7c5(0x1cb)]&&(_0x29b2d1[_0x4ac7c5(0x1e7)]=Number(this[_0x4ac7c5(0x1cb)])),0x1!==this[_0x4ac7c5(0x20d)]&&(_0x29b2d1[_0x4ac7c5(0x1b5)]=Number(this[_0x4ac7c5(0x20d)])),_0x2c2509&&(_0x29b2d1[_0x4ac7c5(0x202)]=_0x2c2509),_0x1ac272[_0x4ac7c5(0x1d8)]&&(_0x29b2d1[_0x4ac7c5(0x1b2)]=_0x1ac272),_0x242165&&(_0x29b2d1[_0x4ac7c5(0x1db)]=_0x242165),this[_0x4ac7c5(0x1ed)]['trim']()&&(_0x29b2d1[_0x4ac7c5(0x1ac)]=this[_0x4ac7c5(0x1ed)][_0x4ac7c5(0x1b0)]()),null!==this[_0x4ac7c5(0x200)]&&''!==this['doneTtl']&&(_0x29b2d1[_0x4ac7c5(0x1ff)]=Number(this[_0x4ac7c5(0x200)])),null!==this[_0x4ac7c5(0x1f8)]&&''!==this[_0x4ac7c5(0x1f8)]&&(_0x29b2d1['failed_ttl']=Number(this[_0x4ac7c5(0x1f8)])),this['busy']=!0x0;try{const _0x74037f=await fetch(_0xc45aec[_0x4ac7c5(0x218)]+_0x4ac7c5(0x225)+encodeURIComponent(this[_0x4ac7c5(0x226)]),{'method':_0x4ac7c5(0x1d4),'headers':{'Content-Type':'application/json'},'body':JSON[_0x4ac7c5(0x206)]({'payload':this[_0x4ac7c5(0x1f7)],'options':_0x29b2d1})});if(!_0x74037f['ok']){const _0x630da=await _0x74037f[_0x4ac7c5(0x1ba)]()[_0x4ac7c5(0x1d3)](()=>({}));throw new Error(_0x630da?.[_0x4ac7c5(0x21f)]?.[_0x4ac7c5(0x1d2)]||_0x630da?.[_0x4ac7c5(0x1d2)]||_0x4ac7c5(0x1b7));}await _0x19084d[_0x4ac7c5(0x1da)][_0x4ac7c5(0x1df)]['close'](),await _0x19084d['$attr'][_0x4ac7c5(0x1f1)]?.();}catch(_0x5a33b0){_0x19084d[_0x4ac7c5(0x213)][_0x4ac7c5(0x1b3)]?.['toast']?.[_0x4ac7c5(0x21f)]?.(_0x5a33b0[_0x4ac7c5(0x1d2)]||_0x4ac7c5(0x1c8));}finally{this[_0x4ac7c5(0x1e2)]=!0x1;}},'cancel'(){const _0x3808ee=_0x408539;_0x19084d[_0x3808ee(0x1da)][_0x3808ee(0x1df)][_0x3808ee(0x1ce)]();}}),async 'mount'(_0x2273ae){const _0x3fcaa1=_0x408539;await this[_0x3fcaa1(0x1f4)](),this['type']&&this[_0x3fcaa1(0x1be)](this[_0x3fcaa1(0x226)]);},'template':_0x408539(0x219),'style':_0x408539(0x1cc)};
1
+ import e from"@toolkit/elements/ok-icon.ok.js";import t from"@toolkit/core/ok-overlay.ok.js";import n from"../../../components/ok-cron-input.ok.js";import a from"../../../components/ok-json-input.ok.js";import i from"../../../components/field-path-input.ok.js";import s from"./create-bucket-modal.ok.js";import l from"../../../stores/okdb-store.js";export default{tag:"enqueue-job-modal",dependencies:[n,a,i,s,t,e],attr:{modal:{},initialType:{},onDone:{}},context:e=>({busy:!1,type:e.$attr.initialType??"",payloadInit:{},payload:{},priority:0,maxTries:null,retryDelay:0,backoff:1,tags:"",buckets:[{id:"",tokens:1}],cron:"",idempotencyKey:"",whenMode:"asap",whenDatetime:"",whenTs:null,doneTtl:null,failedTtl:null,bucketOptions:[],registeredTypes:[],typeItems:[],typeIsRegistered:!1,typeStatus:null,typeSchema:null,_fromType:{},payloadErrors:[],_payloadTouched:!1,_syncTypeData(){const e=this.registeredTypes.find(e=>e.id===this.type)??null;this.typeIsRegistered=!!e,this.typeStatus=e?.status??null,this.typeSchema=e?.payload_schema??null,this.typeItems=this.registeredTypes.map(e=>({path:e.id,types:e.status&&"active"!==e.status?[e.status]:[],hint:null}))},_set(e,t){if(this[e]=t,this._fromType[e]){const t={...this._fromType};delete t[e],this._fromType=t}},async loadBuckets(){const[e,t]=await Promise.all([fetch(l.apiBase+"/queue/buckets").catch(()=>null),fetch(l.apiBase+"/queue/job-types").catch(()=>null)]);if(e?.ok){const t=await e.json().catch(()=>({}));this.bucketOptions=t?.result??t?.data??[]}if(t?.ok){const e=await t.json().catch(()=>({}));this.registeredTypes=e?.result??e?.data??[]}this._syncTypeData()},applyTypeDefaults(e){const t=this.registeredTypes.find(t=>t.id===e);if(!t)return!1;const n=t.defaults??{},a={};return null!=n.max_tries&&(this.maxTries=n.max_tries,a.maxTries=!0),null!=n.retry_delay&&(this.retryDelay=n.retry_delay,a.retryDelay=!0),null!=n.backoff_multiplier&&(this.backoff=n.backoff_multiplier,a.backoff=!0),null!=n.priority&&(this.priority=n.priority,a.priority=!0),null!=n.done_ttl&&(this.doneTtl=n.done_ttl,a.doneTtl=!0),null!=n.failed_ttl&&(this.failedTtl=n.failed_ttl,a.failedTtl=!0),Array.isArray(n.buckets)&&n.buckets.length&&(this.buckets=n.buckets.map(e=>({id:e.id,tokens:e.tokens??1})),a.buckets=!0),this._fromType=a,!0},onTypeSelected(e){null!=e&&(this.type=e,this._syncTypeData(),this.applyTypeDefaults(e)||e||(this._fromType={}),this._revalidatePayload())},async onBucketSelect(t,n){if(this._fromType.buckets&&(this._fromType={...this._fromType,buckets:!1}),"__create__"===n){const n=this;await e.$ok.shared.modal.open({owner:e,title:"Add Token Bucket",content:'<create-bucket-modal :modal="modal" :on-done="onDone" />',context:e=>({modal:e,onDone:async e=>{await n.loadBuckets(),n.buckets=n.buckets.map((n,a)=>a===t?{...n,id:e}:n)}})})}else this.buckets=this.buckets.map((e,a)=>a===t?{...e,id:n}:e)},setBucketTokens(e,t){this._fromType.buckets&&(this._fromType={...this._fromType,buckets:!1});const n=""===t?1:Math.max(1,parseInt(t)||1);this.buckets=this.buckets.map((t,a)=>a===e?{...t,tokens:n}:t)},addBucket(){this._fromType.buckets&&(this._fromType={...this._fromType,buckets:!1}),this.buckets=[...this.buckets,{id:"",tokens:1}]},removeBucket(e){this._fromType.buckets&&(this._fromType={...this._fromType,buckets:!1}),this.buckets=this.buckets.filter((t,n)=>n!==e)},onPayloadChange(e){this.payload=e,this._payloadTouched=!0,this._revalidatePayload()},_revalidatePayload(){const e=this.typeSchema;this.payloadErrors=e?.definition?this._validatePayload(this.payload,e.definition):[]},_validatePayload(e,t){const n=[],a=(e,t,i)=>{if(t&&"object"==typeof t&&!t.$ref){if(t.type){const a=Array.isArray(t.type)?t.type:[t.type],s=null===e?"null":Array.isArray(e)?"array":typeof e;if(!a.some(t=>t===s||"integer"===t&&"number"===s&&Number.isInteger(e)))return void n.push({path:i||"(root)",msg:`expected ${a.join(" | ")}, got ${s}`})}if(t.enum&&!t.enum.some(t=>JSON.stringify(t)===JSON.stringify(e))&&n.push({path:i||"(root)",msg:`must be one of [${t.enum.map(e=>JSON.stringify(e)).join(", ")}]`}),"string"==typeof e&&(null!=t.minLength&&e.length<t.minLength&&n.push({path:i||"(root)",msg:`minLength: ${t.minLength}`}),null!=t.maxLength&&e.length>t.maxLength&&n.push({path:i||"(root)",msg:`maxLength: ${t.maxLength}`}),t.pattern))try{new RegExp(t.pattern).test(e)||n.push({path:i||"(root)",msg:"pattern mismatch"})}catch{}if("number"==typeof e&&(null!=t.minimum&&e<t.minimum&&n.push({path:i||"(root)",msg:`>= ${t.minimum}`}),null!=t.maximum&&e>t.maximum&&n.push({path:i||"(root)",msg:`<= ${t.maximum}`})),"object"==typeof e&&null!==e&&!Array.isArray(e)){for(const a of t.required??[])a in e||n.push({path:i?`${i}.${a}`:a,msg:"required"});if(t.properties){for(const[n,s]of Object.entries(t.properties))n in e&&a(e[n],s,i?`${i}.${n}`:n);if(!1===t.additionalProperties)for(const a of Object.keys(e))a in t.properties||n.push({path:i?`${i}.${a}`:a,msg:"additional property not allowed"})}}if(Array.isArray(e)&&t.items&&e.forEach((e,n)=>a(e,t.items,`${i||""}[${n}]`)),t.allOf)for(const n of t.allOf)a(e,n,i)}};return a(e,t,""),n},getWhen(){if("asap"===this.whenMode)return null;if("datetime"===this.whenMode){const e=new Date(this.whenDatetime);return isNaN(e.getTime())?null:e.getTime()}return this.whenTs},_parseTags(e){if(!e)return;const t=String(e).split(",").map(e=>e.trim()).filter(Boolean);return t.length?t:void 0},async submit(){if(!this.type)return void e.$ok.shared?.toast?.error?.("Please enter a job type");if(this.typeSchema?.definition){const t=this._validatePayload(this.payload,this.typeSchema.definition);if(t.length)return this._payloadTouched=!0,this.payloadErrors=t,void e.$ok.shared?.toast?.error?.("Payload does not match the job type schema")}const t=this.getWhen(),n=String(this.cron||"").trim()||null,a=this.buckets.filter(e=>e.id).map(e=>({id:e.id,tokens:e.tokens||1})),i=this._parseTags(this.tags),s={};null!=t&&(s.when=t),this.priority&&(s.priority=Number(this.priority)),null!==this.maxTries&&""!==this.maxTries&&(s.max_tries=Number(this.maxTries)),this.retryDelay&&(s.retry_delay=Number(this.retryDelay)),1!==this.backoff&&(s.backoff_multiplier=Number(this.backoff)),i&&(s.tags=i),a.length&&(s.buckets=a),n&&(s.cron=n),this.idempotencyKey.trim()&&(s.idempotency_key=this.idempotencyKey.trim()),null!==this.doneTtl&&""!==this.doneTtl&&(s.done_ttl=Number(this.doneTtl)),null!==this.failedTtl&&""!==this.failedTtl&&(s.failed_ttl=Number(this.failedTtl)),this.busy=!0;try{const t=await fetch(l.apiBase+"/queue/enqueue/"+encodeURIComponent(this.type),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({payload:this.payload,options:s})});if(!t.ok){const e=await t.json().catch(()=>({}));throw new Error(e?.error?.message||e?.message||"enqueue failed")}await e.$attr.modal.close(),await(e.$attr.onDone?.())}catch(t){e.$ok.shared?.toast?.error?.(t.message||"Failed to enqueue job")}finally{this.busy=!1}},cancel(){e.$attr.modal.close()}}),async mount(e){await this.loadBuckets(),this.type&&this.applyTypeDefaults(this.type)},template:'\n<div style="position:relative; min-width:720px; max-width:calc(100vw - 80px); display:flex; flex-direction:column; gap:12px;">\n\n <if :="busy">\n <ok-overlay>\n <div class="loader-center">\n <ok-icon name="loader" class="spin" style="width:32px;height:32px;"></ok-icon>\n </div>\n </ok-overlay>\n </if>\n\n \x3c!-- Row 1: type + priority --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;">\n <label style="display:flex; align-items:center; gap:6px;">\n Type\n <if :="typeIsRegistered && typeStatus === \'active\'">\n <span class="type-pill type-pill--ok">registered</span>\n </if>\n <if :="typeStatus === \'paused\'">\n <span class="type-pill type-pill--warn">paused</span>\n </if>\n <if :="typeStatus === \'draining\'">\n <span class="type-pill type-pill--info">draining</span>\n </if>\n </label>\n <field-path-input\n :allow-custom="true"\n :value="type"\n :items="typeItems"\n placeholder="e.g. email.send"\n @change="onTypeSelected($event.detail)"\n />\n </div>\n <div class="field" style="width:120px;">\n <label style="display:flex; align-items:center; gap:5px;">\n Priority\n <if :="_fromType.priority"><span class="auto-badge">auto</span></if>\n </label>\n <input type="number" placeholder="0" :value="priority" @input="_set(\'priority\', Number($el.value))" />\n </div>\n </div>\n\n \x3c!-- Row 2: tags + idempotency key --\x3e\n <div style="display:flex; gap:10px;">\n <div class="field" style="flex:1;"><label>Tags</label>\n <input placeholder="comma separated" :value="tags" @input="tags = $el.value" />\n </div>\n <div class="field" style="flex:1;"><label>Idempotency Key</label>\n <input placeholder="optional — prevents duplicate jobs" :value="idempotencyKey" @input="idempotencyKey = $el.value" />\n </div>\n </div>\n\n \x3c!-- Buckets --\x3e\n <div class="field">\n <label style="display:flex; align-items:center; gap:5px;">\n Buckets <span style="opacity:.4; font-weight:400; font-size:11px;">(rate limiting)</span>\n <if :="_fromType.buckets"><span class="auto-badge">auto</span></if>\n </label>\n <each :of="buckets" let:item="b" let:index="i">\n <div style="display:flex; gap:6px; align-items:center; margin-bottom:4px;">\n <select style="flex:1;" @change="onBucketSelect(i, $el.value)">\n <option value="">— none —</option>\n <option as="repeat" :of="bucketOptions" let:item="opt" :value="opt.id" :selected="opt.id === b.id">{{ opt.id }}</option>\n <option value="__create__">+ Create bucket…</option>\n </select>\n <input style="width:72px;" type="number" min="1" placeholder="tokens"\n :value="b.tokens"\n @input="setBucketTokens(i, $el.value)" />\n <button class="btn-ghost" @click="removeBucket(i)" title="Remove">✕</button>\n </div>\n </each>\n <button class="btn-ghost btn-ghost--sm" @click="addBucket()" style="margin-top:2px;">+ Add bucket</button>\n </div>\n\n \x3c!-- Row 3: when + max tries --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;">\n <label>When</label>\n <div style="display:flex; gap:4px; align-items:center;">\n <button :class="whenMode===\'asap\' ? \'mode-btn mode-btn--on\' : \'mode-btn\'" @click="whenMode=\'asap\'">ASAP</button>\n <button :class="whenMode===\'datetime\' ? \'mode-btn mode-btn--on\' : \'mode-btn\'" @click="whenMode=\'datetime\'">Date</button>\n <button :class="whenMode===\'timestamp\' ? \'mode-btn mode-btn--on\' : \'mode-btn\'" @click="whenMode=\'timestamp\'">TS</button>\n <if :="whenMode === \'datetime\'">\n <input type="datetime-local" style="flex:1;" :value="whenDatetime" @input="whenDatetime = $el.value" />\n </if>\n <if :="whenMode === \'timestamp\'">\n <input type="number" style="flex:1;" placeholder="ms timestamp" :value="whenTs" @input="whenTs = $el.value === \'\' ? null : Number($el.value)" />\n </if>\n <if :="whenMode === \'asap\'">\n <span style="opacity:.5; font-size:12px; padding:0 8px;">Run immediately</span>\n </if>\n </div>\n </div>\n <div class="field" style="width:120px;">\n <label style="display:flex; align-items:center; gap:5px;">\n Max tries\n <if :="_fromType.maxTries"><span class="auto-badge">auto</span></if>\n </label>\n <input type="number" placeholder="unlimited" :value="maxTries" @input="_set(\'maxTries\', $el.value === \'\' ? null : Number($el.value))" />\n </div>\n </div>\n\n \x3c!-- Row 4: retry delay + backoff + cron --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;">\n <label style="display:flex; align-items:center; gap:5px;">\n Retry delay (ms)\n <if :="_fromType.retryDelay"><span class="auto-badge">auto</span></if>\n </label>\n <input type="number" placeholder="0" :value="retryDelay" @input="_set(\'retryDelay\', Number($el.value))" />\n </div>\n <div class="field" style="width:120px;">\n <label style="display:flex; align-items:center; gap:5px;">\n Backoff multiplier\n <if :="_fromType.backoff"><span class="auto-badge">auto</span></if>\n </label>\n <input type="number" step="0.1" placeholder="1" :value="backoff" @input="_set(\'backoff\', Number($el.value))" />\n </div>\n <div class="field" style="flex:1;"><label>Cron</label>\n <ok-cron-input :value="cron" placeholder="optional" @change="cron = $event.detail" />\n </div>\n </div>\n\n \x3c!-- Row 5: retention TTLs --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;">\n <label style="display:flex; align-items:center; gap:5px;">\n Done TTL (ms)\n <if :="_fromType.doneTtl"><span class="auto-badge">auto</span></if>\n </label>\n <input type="number" min="0" placeholder="0 = delete immediately, blank = keep forever" :value="doneTtl"\n @input="_set(\'doneTtl\', $el.value === \'\' ? null : Number($el.value))" />\n </div>\n <div class="field" style="flex:1;">\n <label style="display:flex; align-items:center; gap:5px;">\n Failed TTL (ms)\n <if :="_fromType.failedTtl"><span class="auto-badge">auto</span></if>\n </label>\n <input type="number" min="0" placeholder="blank = keep forever" :value="failedTtl"\n @input="_set(\'failedTtl\', $el.value === \'\' ? null : Number($el.value))" />\n </div>\n </div>\n\n \x3c!-- Payload --\x3e\n <div style="display:flex; flex-direction:column; gap:6px;">\n <div style="display:flex; align-items:center; gap:8px;">\n <span style="opacity:.7; font-size:12px;">Payload</span>\n <if :="typeSchema">\n <span class="type-pill type-pill--schema">schema</span>\n <if :="payloadErrors.length === 0 && _payloadTouched">\n <span class="type-pill type-pill--ok" style="font-size:10px;">valid</span>\n </if>\n </if>\n </div>\n <ok-json-input\n mode="textarea"\n style="height:220px;"\n :value="payloadInit"\n @edit="onPayloadChange($event.detail)"\n />\n <if :="_payloadTouched && payloadErrors.length > 0">\n <div class="schema-errors">\n <each :of="payloadErrors" let:item="err">\n <div class="schema-error-row">\n <span class="schema-error-path">{{ err.path }}</span>\n <span class="schema-error-msg">{{ err.msg }}</span>\n </div>\n </each>\n </div>\n </if>\n </div>\n\n \x3c!-- Footer --\x3e\n <div style="display:flex; gap:10px; justify-content:flex-end;">\n <button @click="cancel()">Cancel</button>\n <button @click="submit()" :disabled="!type || busy">Enqueue</button>\n </div>\n\n</div>\n',style:".mode-btn{font-size:11px;padding:2px 8px;opacity:.6;border-radius:4px}.mode-btn--on{opacity:1;background:color-mix(in srgb,var(--okdb-accent) 13%,transparent);border-color:color-mix(in srgb,var(--okdb-accent) 33%,transparent);color:var(--okdb-accent)}.btn-ghost{background:0 0;border:1px solid var(--okdb-app-border);border-radius:5px;color:inherit;cursor:pointer;opacity:.6;padding:2px 8px;font-size:12px}.btn-ghost:hover{opacity:1;border-color:color-mix(in srgb,var(--okdb-accent) 33%,transparent)}.btn-ghost--sm{font-size:11px;padding:1px 7px}.type-pill{font-size:10px;padding:1px 6px;border-radius:999px;border:1px solid;font-weight:400}.type-pill--ok{border-color:color-mix(in srgb,var(--okdb-accent) 33%,transparent);color:var(--okdb-accent)}.type-pill--warn{border-color:#f59e0b;color:#f59e0b}.type-pill--info{border-color:#3b82f6;color:#3b82f6}.type-pill--schema{border-color:color-mix(in srgb,var(--okdb-accent) 33%,transparent);color:var(--okdb-accent)}.auto-badge{font-size:9px;padding:1px 5px;border-radius:999px;font-weight:600;letter-spacing:.04em;background:color-mix(in srgb,var(--okdb-accent) 12%,transparent);border:1px solid color-mix(in srgb,var(--okdb-accent) 25%,transparent);color:var(--okdb-accent);text-transform:uppercase}.schema-errors{display:flex;flex-direction:column;gap:3px;padding:8px 10px;border-radius:6px;border:1px solid color-mix(in srgb,#ef4444 40%,transparent);background:color-mix(in srgb,#ef4444 6%,transparent);font-size:11px;max-height:120px;overflow-y:auto}.schema-error-row{display:flex;gap:8px;align-items:baseline}.schema-error-path{font-family:monospace;color:#f87171;flex-shrink:0}.schema-error-msg{color:#fca5a5;opacity:.8}"};
@@ -1 +1 @@
1
- const _0x3b08a1=_0x3277;(function(_0x5857fb,_0x460640){const _0xf69f2a=_0x3277,_0x52ef18=_0x5857fb();while(!![]){try{const _0x49af78=parseInt(_0xf69f2a(0xbf))/0x1+parseInt(_0xf69f2a(0xb3))/0x2+parseInt(_0xf69f2a(0xd0))/0x3+parseInt(_0xf69f2a(0xcc))/0x4*(parseInt(_0xf69f2a(0xc9))/0x5)+-parseInt(_0xf69f2a(0xa3))/0x6*(-parseInt(_0xf69f2a(0xba))/0x7)+parseInt(_0xf69f2a(0xb5))/0x8+-parseInt(_0xf69f2a(0xbd))/0x9;if(_0x49af78===_0x460640)break;else _0x52ef18['push'](_0x52ef18['shift']());}catch(_0x253778){_0x52ef18['push'](_0x52ef18['shift']());}}}(_0x4b2c,0x279d2));import _0x9a8aaf from'@toolkit/core/ok-overlay.ok.js';import _0x3a06b2 from'@toolkit/elements/ok-icon.ok.js';function _0x3277(_0x4b425e,_0x48ebac){_0x4b425e=_0x4b425e-0xa1;const _0x4b2c64=_0x4b2c();let _0x327766=_0x4b2c64[_0x4b425e];return _0x327766;}function _0x4b2c(){const _0x2578ca=['defBackoff','max','defaults','job-type-modal','/queue/job-type','onDone','keys','Failed\x20to\x20save\x20job\x20type','open','map','<create-bucket-modal\x20:modal=\x22modal\x22\x20:on-done=\x22onDone\x22\x20/>','defTtl','isEdit','/schema','toast','defRetryDelay','failed_ttl','backoff_multiplier','Add\x20Token\x20Bucket','max_tries','enforce','busy','__create__','Save\x20the\x20job\x20type\x20first\x20before\x20editing\x20its\x20schema','$ok','449646qvcbhP','status','defMaxTries','data','length','payload_schema','apiBase','.btn-ghost{background:0\x200;border:1px\x20solid\x20var(--okdb-app-border);border-radius:5px;color:inherit;cursor:pointer;opacity:.6;padding:2px\x208px;font-size:12px}.btn-ghost:hover{opacity:1;border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2033%,transparent)}.btn-ghost--sm{font-size:11px;padding:1px\x207px}','error','max_concurrency','retry_delay','priority','application/json','parseOptInt','close','/queue/job-type/','98972wQYUoj','shared','2233112MtVWAR','Job\x20type\x20name\x20is\x20required','isArray','result','filter','21fbNChz','/queue/buckets','trim','7073019oJSFNx','active','179707GQvZRL','defBuckets','tokens','defFailedTtl','defDoneTtl','<schema-edit-modal\x20:modal=\x22modal\x22\x20:type=\x22type\x22\x20:schema=\x22schema\x22\x20:save-url=\x22saveUrl\x22\x20:samples-url=\x22samplesUrl\x22\x20:hide-enforce=\x22true\x22\x20:on-done=\x22onDone\x22\x20/>','Payload\x20Schema\x20·\x20','/sample-payloads','bucketOptions','$attr','85XsnJNj','defPriority','stringify','4104iyJxox','modal','jobType','buckets','592656forcnX','\x0a<div\x20style=\x22position:relative;\x20min-width:600px;\x20max-width:calc(100vw\x20-\x2080px);\x20display:flex;\x20flex-direction:column;\x20gap:14px;\x22>\x0a\x0a\x20\x20<if\x20:=\x22busy\x22>\x0a\x20\x20\x20\x20<ok-overlay>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;align-items:center;justify-content:center;height:100%;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<ok-icon\x20name=\x22loader\x22\x20class=\x22spin\x22\x20style=\x22width:28px;height:28px;\x22></ok-icon>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</ok-overlay>\x0a\x20\x20</if>\x0a\x0a\x20\x20<!--\x20name\x20+\x20status\x20+\x20concurrency\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Type\x20name</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20placeholder=\x22e.g.\x20email.send\x22\x20:value=\x22id\x22\x20@input=\x22id\x20=\x20$el.value\x22\x20:disabled=\x22isEdit\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:150px;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Status</label>\x0a\x20\x20\x20\x20\x20\x20<select\x20:value=\x22status\x22\x20@change=\x22status\x20=\x20$el.value\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22active\x22>Active</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22paused\x22>Paused</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22draining\x22>Draining</option>\x0a\x20\x20\x20\x20\x20\x20</select>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:140px;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Max\x20concurrency</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x221\x22\x20placeholder=\x22unlimited\x22\x20:value=\x22maxConcurrency\x22\x20@input=\x22maxConcurrency\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<div\x20style=\x22height:1px;\x20background:var(--okdb-app-border);\x22></div>\x0a\x20\x20<div\x20style=\x22font-size:11px;\x20font-weight:600;\x20text-transform:uppercase;\x20letter-spacing:.06em;\x20opacity:.5;\x22>Defaults\x20(applied\x20when\x20enqueueing\x20without\x20explicit\x20values)</div>\x0a\x0a\x20\x20<!--\x20retry\x20defaults\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Max\x20tries</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x221\x22\x20placeholder=\x22queue\x20default\x22\x20:value=\x22defMaxTries\x22\x20@input=\x22defMaxTries\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Retry\x20delay\x20(ms)</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x20placeholder=\x220\x22\x20:value=\x22defRetryDelay\x22\x20@input=\x22defRetryDelay\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Backoff\x20multiplier</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x221\x22\x20step=\x220.1\x22\x20placeholder=\x221\x22\x20:value=\x22defBackoff\x22\x20@input=\x22defBackoff\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:110px;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Priority</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20placeholder=\x220\x22\x20:value=\x22defPriority\x22\x20@input=\x22defPriority\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20TTLs\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Claim\x20TTL\x20(ms)</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x20placeholder=\x22queue\x20default\x22\x20:value=\x22defTtl\x22\x20@input=\x22defTtl\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Done\x20TTL\x20(ms)</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x20placeholder=\x22keep\x20forever\x22\x20:value=\x22defDoneTtl\x22\x20@input=\x22defDoneTtl\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Failed\x20TTL\x20(ms)</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x20placeholder=\x22keep\x20forever\x22\x20:value=\x22defFailedTtl\x22\x20@input=\x22defFailedTtl\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20default\x20buckets\x20-->\x0a\x20\x20<div\x20class=\x22field\x22>\x0a\x20\x20\x20\x20<label>Default\x20buckets\x20<span\x20style=\x22opacity:.4;\x20font-weight:400;\x20font-size:11px;\x22>(rate\x20limiting)</span></label>\x0a\x20\x20\x20\x20<each\x20:of=\x22defBuckets\x22\x20let:item=\x22b\x22\x20let:index=\x22i\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:6px;\x20align-items:center;\x20margin-bottom:4px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<select\x20style=\x22flex:1;\x22\x20@change=\x22onBucketSelect(i,\x20$el.value)\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22\x22>—\x20none\x20—</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20as=\x22repeat\x22\x20:of=\x22bucketOptions\x22\x20let:item=\x22opt\x22\x20:value=\x22opt.id\x22\x20:selected=\x22opt.id\x20===\x20b.id\x22>{{\x20opt.id\x20}}</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22__create__\x22>+\x20Create\x20bucket…</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</select>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<input\x20style=\x22width:72px;\x22\x20type=\x22number\x22\x20min=\x221\x22\x20placeholder=\x22tokens\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:value=\x22b.tokens\x22\x20@input=\x22setBucketTokens(i,\x20$el.value)\x22\x20/>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22btn-ghost\x22\x20@click=\x22removeBucket(i)\x22\x20title=\x22Remove\x22>✕</button>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</each>\x0a\x20\x20\x20\x20<button\x20class=\x22btn-ghost\x20btn-ghost--sm\x22\x20@click=\x22addBucket()\x22>+\x20Add\x20bucket</button>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20schema\x20row\x20(only\x20shown\x20in\x20edit\x20mode\x20since\x20we\x20need\x20the\x20type\x20to\x20exist\x20first)\x20-->\x0a\x20\x20<if\x20:=\x22isEdit\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20align-items:center;\x20gap:10px;\x20padding:8px\x2012px;\x20border:1px\x20solid\x20var(--okdb-app-border);\x20border-radius:6px;\x20background:var(--okdb-app-bg-deep);\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22flex:1;\x20font-size:12px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22font-weight:600;\x22>Payload\x20Schema</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22hasSchema\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22margin-left:8px;\x20font-size:10px;\x20padding:1px\x206px;\x20border-radius:999px;\x20border:1px\x20solid\x20color-mix(in\x20srgb,\x20var(--okdb-accent)\x2033%,\x20transparent);\x20color:var(--okdb-accent);\x22>defined</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22schemaEnforce\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22margin-left:4px;\x20font-size:10px;\x20padding:1px\x206px;\x20border-radius:999px;\x20border:1px\x20solid\x20#ef4444;\x20color:#ef4444;\x22>enforced</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22!hasSchema\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22margin-left:8px;\x20opacity:.4;\x20font-size:11px;\x22>none</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20<button\x20class=\x22btn-ghost\x22\x20@click=\x22openSchema()\x22>Edit\x20schema…</button>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</if>\x0a\x20\x20<if\x20:=\x22!isEdit\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22font-size:11px;\x20opacity:.45;\x20padding:0\x202px;\x22>Save\x20the\x20type\x20first,\x20then\x20edit\x20its\x20payload\x20schema.</div>\x0a\x20\x20</if>\x0a\x0a\x20\x20<!--\x20footer\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:10px;\x20justify-content:flex-end;\x22>\x0a\x20\x20\x20\x20<button\x20@click=\x22cancel()\x22>Cancel</button>\x0a\x20\x20\x20\x20<button\x20@click=\x22submit()\x22\x20:disabled=\x22!id.trim()\x20||\x20busy\x22>{{\x20isEdit\x20?\x20\x27Save\x27\x20:\x20\x27Register\x27\x20}}</button>\x0a\x20\x20</div>\x0a\x0a</div>\x0a','failed','init'];_0x4b2c=function(){return _0x2578ca;};return _0x4b2c();}import _0x26d47c from'./create-bucket-modal.ok.js';import _0x585a90 from'../../db/modals/schema-edit-modal.ok.js';import _0x534378 from'../../../stores/okdb-store.js';export default{'tag':_0x3b08a1(0xd7),'dependencies':[_0x9a8aaf,_0x3a06b2,_0x26d47c,_0x585a90],'attr':{'modal':{},'jobType':{},'bucketOptions':{},'onDone':{}},'context':_0x1376bf=>({'busy':!0x1,'id':'','status':'active','maxConcurrency':'','defMaxTries':'','defRetryDelay':'','defBackoff':'','defPriority':'','defTtl':'','defDoneTtl':'','defFailedTtl':'','defBuckets':[{'id':'','tokens':0x1}],'bucketOptions':[],get 'isEdit'(){const _0x4298d2=_0x3b08a1;return!!_0x1376bf[_0x4298d2(0xc8)][_0x4298d2(0xce)];},get 'hasSchema'(){const _0x125c6e=_0x3b08a1;return!!_0x1376bf['$attr'][_0x125c6e(0xce)]?.[_0x125c6e(0xa8)];},get 'schemaEnforce'(){const _0x481fc7=_0x3b08a1;return _0x1376bf['$attr'][_0x481fc7(0xce)]?.['payload_schema']?.[_0x481fc7(0xe8)]??!0x1;},'init'(){const _0x2afa30=_0x3b08a1;this[_0x2afa30(0xc7)]=Array['isArray'](_0x1376bf[_0x2afa30(0xc8)]['bucketOptions'])?_0x1376bf[_0x2afa30(0xc8)]['bucketOptions']:[];const _0x497e32=_0x1376bf['$attr']['jobType'];if(!_0x497e32)return;this['id']=_0x497e32['id']??'',this[_0x2afa30(0xa4)]=_0x497e32[_0x2afa30(0xa4)]??_0x2afa30(0xbe),this['maxConcurrency']=null!=_0x497e32[_0x2afa30(0xac)]?String(_0x497e32[_0x2afa30(0xac)]):'';const _0x4c2a00=_0x497e32[_0x2afa30(0xd6)]??{};this[_0x2afa30(0xa5)]=null!=_0x4c2a00['max_tries']?String(_0x4c2a00[_0x2afa30(0xe7)]):'',this[_0x2afa30(0xe3)]=null!=_0x4c2a00[_0x2afa30(0xad)]?String(_0x4c2a00[_0x2afa30(0xad)]):'',this[_0x2afa30(0xd4)]=null!=_0x4c2a00[_0x2afa30(0xe5)]?String(_0x4c2a00[_0x2afa30(0xe5)]):'',this[_0x2afa30(0xca)]=null!=_0x4c2a00[_0x2afa30(0xae)]?String(_0x4c2a00['priority']):'',this[_0x2afa30(0xdf)]=null!=_0x4c2a00['ttl']?String(_0x4c2a00['ttl']):'',this[_0x2afa30(0xc3)]=null!=_0x4c2a00['done_ttl']?String(_0x4c2a00['done_ttl']):'',this[_0x2afa30(0xc2)]=null!=_0x4c2a00[_0x2afa30(0xe4)]?String(_0x4c2a00[_0x2afa30(0xe4)]):'',this['defBuckets']=Array[_0x2afa30(0xb7)](_0x4c2a00[_0x2afa30(0xcf)])&&_0x4c2a00[_0x2afa30(0xcf)]['length']?_0x4c2a00['buckets']['map'](_0x4d887c=>({'id':_0x4d887c['id'],'tokens':_0x4d887c[_0x2afa30(0xc1)]??0x1})):[{'id':'','tokens':0x1}];},'parseOptInt'(_0x36d49c){const _0x2b92c6=parseInt(_0x36d49c);return isNaN(_0x2b92c6)?null:_0x2b92c6;},'parseOptFloat'(_0x1b08f3){const _0x50bdb7=parseFloat(_0x1b08f3);return isNaN(_0x50bdb7)?null:_0x50bdb7;},async 'onBucketSelect'(_0x4f654b,_0x4d3a23){const _0x112f28=_0x3b08a1;if(_0x112f28(0xea)===_0x4d3a23){const _0x1897a5=this;await _0x1376bf['$ok'][_0x112f28(0xb4)]['modal'][_0x112f28(0xdc)]({'owner':_0x1376bf,'title':_0x112f28(0xe6),'content':_0x112f28(0xde),'context':_0x516003=>({'modal':_0x516003,'onDone':async _0x88e22f=>{const _0xb5e14c=_0x112f28;try{const _0x3d5d51=await fetch(_0x534378[_0xb5e14c(0xa9)]+_0xb5e14c(0xbb)),_0x4832a6=await _0x3d5d51['json']();_0x1897a5[_0xb5e14c(0xc7)]=_0x4832a6?.[_0xb5e14c(0xb8)]??_0x4832a6?.[_0xb5e14c(0xa6)]??[];}catch{}_0x1897a5[_0xb5e14c(0xc0)]=_0x1897a5['defBuckets']['map']((_0x39cad9,_0x11325c)=>_0x11325c===_0x4f654b?{..._0x39cad9,'id':_0x88e22f}:_0x39cad9);}})});}else this['defBuckets']=this['defBuckets'][_0x112f28(0xdd)]((_0x404a95,_0x5a4912)=>_0x5a4912===_0x4f654b?{..._0x404a95,'id':_0x4d3a23}:_0x404a95);},'setBucketTokens'(_0x320304,_0x30eda5){const _0x2e4e5f=_0x3b08a1,_0x1e4292=''===_0x30eda5?0x1:Math[_0x2e4e5f(0xd5)](0x1,parseInt(_0x30eda5)||0x1);this['defBuckets']=this[_0x2e4e5f(0xc0)][_0x2e4e5f(0xdd)]((_0x52be39,_0x4af66d)=>_0x4af66d===_0x320304?{..._0x52be39,'tokens':_0x1e4292}:_0x52be39);},'addBucket'(){const _0x2be052=_0x3b08a1;this[_0x2be052(0xc0)]=[...this[_0x2be052(0xc0)],{'id':'','tokens':0x1}];},'removeBucket'(_0x450853){const _0x7296ee=_0x3b08a1;this['defBuckets']=this[_0x7296ee(0xc0)][_0x7296ee(0xb9)]((_0x475aca,_0xc06252)=>_0xc06252!==_0x450853);},'openSchema'(){const _0xc09163=_0x3b08a1,_0x25f4f8=this['id'][_0xc09163(0xbc)]()||(_0x1376bf[_0xc09163(0xc8)][_0xc09163(0xce)]?.['id']??'');if(!_0x25f4f8)return void _0x1376bf[_0xc09163(0xa2)][_0xc09163(0xb4)]?.['toast']?.[_0xc09163(0xab)]?.(_0xc09163(0xa1));const _0x5c3105=_0x534378['apiBase']+'/queue/job-type/'+encodeURIComponent(_0x25f4f8)+_0xc09163(0xe1),_0x184454=_0x534378[_0xc09163(0xa9)]+_0xc09163(0xb2)+encodeURIComponent(_0x25f4f8)+_0xc09163(0xc6);_0x1376bf[_0xc09163(0xa2)][_0xc09163(0xb4)]['modal']['open']({'owner':_0x1376bf,'title':_0xc09163(0xc5)+_0x25f4f8,'canDrag':!0x0,'content':_0xc09163(0xc4),'context':_0x21a066=>({'modal':_0x21a066,'type':_0x25f4f8,'schema':_0x1376bf[_0xc09163(0xc8)]['jobType']?.[_0xc09163(0xa8)]??null,'saveUrl':_0x5c3105,'samplesUrl':_0x184454,'onDone':()=>_0x1376bf[_0xc09163(0xc8)][_0xc09163(0xd9)]?.()})});},async 'submit'(){const _0x2d8dba=_0x3b08a1,_0x485020=this['id'][_0x2d8dba(0xbc)]();if(!_0x485020)return void _0x1376bf['$ok'][_0x2d8dba(0xb4)]?.[_0x2d8dba(0xe2)]?.[_0x2d8dba(0xab)]?.(_0x2d8dba(0xb6));const _0x91e08b=this[_0x2d8dba(0xc0)][_0x2d8dba(0xb9)](_0x5731e4=>_0x5731e4['id'])[_0x2d8dba(0xdd)](_0x361f4f=>({'id':_0x361f4f['id'],'tokens':_0x361f4f['tokens']||0x1})),_0x5d884b={'status':this['status']},_0xfe5ea2=this[_0x2d8dba(0xb0)](this['maxConcurrency']);null!=_0xfe5ea2&&(_0x5d884b[_0x2d8dba(0xac)]=_0xfe5ea2);const _0xb62589={},_0x1eab87=this[_0x2d8dba(0xb0)](this[_0x2d8dba(0xa5)]);null!=_0x1eab87&&(_0xb62589[_0x2d8dba(0xe7)]=_0x1eab87);const _0x2b7b38=this[_0x2d8dba(0xb0)](this[_0x2d8dba(0xe3)]);null!=_0x2b7b38&&(_0xb62589[_0x2d8dba(0xad)]=_0x2b7b38);const _0x2e0607=this['parseOptFloat'](this[_0x2d8dba(0xd4)]);null!=_0x2e0607&&(_0xb62589[_0x2d8dba(0xe5)]=_0x2e0607);const _0x262fdc=this[_0x2d8dba(0xb0)](this[_0x2d8dba(0xca)]);null!=_0x262fdc&&(_0xb62589[_0x2d8dba(0xae)]=_0x262fdc);const _0xa3690=this[_0x2d8dba(0xb0)](this[_0x2d8dba(0xdf)]);null!=_0xa3690&&(_0xb62589['ttl']=_0xa3690);const _0x42af0a=this[_0x2d8dba(0xb0)](this[_0x2d8dba(0xc3)]);null!=_0x42af0a&&(_0xb62589['done_ttl']=_0x42af0a);const _0x553d25=this['parseOptInt'](this[_0x2d8dba(0xc2)]);null!=_0x553d25&&(_0xb62589['failed_ttl']=_0x553d25),_0x91e08b['length']&&(_0xb62589[_0x2d8dba(0xcf)]=_0x91e08b),Object[_0x2d8dba(0xda)](_0xb62589)[_0x2d8dba(0xa7)]&&(_0x5d884b[_0x2d8dba(0xd6)]=_0xb62589),this[_0x2d8dba(0xe9)]=!0x0;try{const _0x5023a0=this[_0x2d8dba(0xe0)]?_0x534378[_0x2d8dba(0xa9)]+_0x2d8dba(0xb2)+encodeURIComponent(_0x485020):_0x534378[_0x2d8dba(0xa9)]+_0x2d8dba(0xd8),_0x584117=this['isEdit']?'PUT':'POST',_0x293b5f=this['isEdit']?_0x5d884b:{'id':_0x485020,..._0x5d884b};if(!(await fetch(_0x5023a0,{'method':_0x584117,'headers':{'Content-Type':_0x2d8dba(0xaf)},'body':JSON[_0x2d8dba(0xcb)](_0x293b5f)}))['ok'])throw new Error(_0x2d8dba(0xd2));await _0x1376bf[_0x2d8dba(0xc8)][_0x2d8dba(0xcd)][_0x2d8dba(0xb1)](),await _0x1376bf['$attr'][_0x2d8dba(0xd9)]?.();}catch{_0x1376bf[_0x2d8dba(0xa2)]['shared']?.[_0x2d8dba(0xe2)]?.[_0x2d8dba(0xab)]?.(_0x2d8dba(0xdb));}finally{this[_0x2d8dba(0xe9)]=!0x1;}},'cancel'(){const _0x53f957=_0x3b08a1;_0x1376bf['$attr'][_0x53f957(0xcd)]['close']();}}),'mount'(){const _0x56f69f=_0x3b08a1;this[_0x56f69f(0xd3)]();},'template':_0x3b08a1(0xd1),'style':_0x3b08a1(0xaa)};
1
+ import e from"@toolkit/core/ok-overlay.ok.js";import t from"@toolkit/elements/ok-icon.ok.js";import n from"./create-bucket-modal.ok.js";import i from"../../db/modals/schema-edit-modal.ok.js";import a from"../../../stores/okdb-store.js";export default{tag:"job-type-modal",dependencies:[e,t,n,i],attr:{modal:{},jobType:{},bucketOptions:{},onDone:{}},context:e=>({busy:!1,id:"",status:"active",maxConcurrency:"",defMaxTries:"",defRetryDelay:"",defBackoff:"",defPriority:"",defTtl:"",defDoneTtl:"",defFailedTtl:"",defBuckets:[{id:"",tokens:1}],bucketOptions:[],get isEdit(){return!!e.$attr.jobType},get hasSchema(){return!!e.$attr.jobType?.payload_schema},get schemaEnforce(){return e.$attr.jobType?.payload_schema?.enforce??!1},init(){this.bucketOptions=Array.isArray(e.$attr.bucketOptions)?e.$attr.bucketOptions:[];const t=e.$attr.jobType;if(!t)return;this.id=t.id??"",this.status=t.status??"active",this.maxConcurrency=null!=t.max_concurrency?String(t.max_concurrency):"";const n=t.defaults??{};this.defMaxTries=null!=n.max_tries?String(n.max_tries):"",this.defRetryDelay=null!=n.retry_delay?String(n.retry_delay):"",this.defBackoff=null!=n.backoff_multiplier?String(n.backoff_multiplier):"",this.defPriority=null!=n.priority?String(n.priority):"",this.defTtl=null!=n.ttl?String(n.ttl):"",this.defDoneTtl=null!=n.done_ttl?String(n.done_ttl):"",this.defFailedTtl=null!=n.failed_ttl?String(n.failed_ttl):"",this.defBuckets=Array.isArray(n.buckets)&&n.buckets.length?n.buckets.map(e=>({id:e.id,tokens:e.tokens??1})):[{id:"",tokens:1}]},parseOptInt(e){const t=parseInt(e);return isNaN(t)?null:t},parseOptFloat(e){const t=parseFloat(e);return isNaN(t)?null:t},async onBucketSelect(t,n){if("__create__"===n){const n=this;await e.$ok.shared.modal.open({owner:e,title:"Add Token Bucket",content:'<create-bucket-modal :modal="modal" :on-done="onDone" />',context:e=>({modal:e,onDone:async e=>{try{const e=await fetch(a.apiBase+"/queue/buckets"),t=await e.json();n.bucketOptions=t?.result??t?.data??[]}catch{}n.defBuckets=n.defBuckets.map((n,i)=>i===t?{...n,id:e}:n)}})})}else this.defBuckets=this.defBuckets.map((e,i)=>i===t?{...e,id:n}:e)},setBucketTokens(e,t){const n=""===t?1:Math.max(1,parseInt(t)||1);this.defBuckets=this.defBuckets.map((t,i)=>i===e?{...t,tokens:n}:t)},addBucket(){this.defBuckets=[...this.defBuckets,{id:"",tokens:1}]},removeBucket(e){this.defBuckets=this.defBuckets.filter((t,n)=>n!==e)},openSchema(){const t=this.id.trim()||(e.$attr.jobType?.id??"");if(!t)return void e.$ok.shared?.toast?.error?.("Save the job type first before editing its schema");const n=a.apiBase+"/queue/job-type/"+encodeURIComponent(t)+"/schema",i=a.apiBase+"/queue/job-type/"+encodeURIComponent(t)+"/sample-payloads";e.$ok.shared.modal.open({owner:e,title:`Payload Schema · ${t}`,canDrag:!0,content:'<schema-edit-modal :modal="modal" :type="type" :schema="schema" :save-url="saveUrl" :samples-url="samplesUrl" :hide-enforce="true" :on-done="onDone" />',context:a=>({modal:a,type:t,schema:e.$attr.jobType?.payload_schema??null,saveUrl:n,samplesUrl:i,onDone:()=>e.$attr.onDone?.()})})},async submit(){const t=this.id.trim();if(!t)return void e.$ok.shared?.toast?.error?.("Job type name is required");const n=this.defBuckets.filter(e=>e.id).map(e=>({id:e.id,tokens:e.tokens||1})),i={status:this.status},l=this.parseOptInt(this.maxConcurrency);null!=l&&(i.max_concurrency=l);const s={},o=this.parseOptInt(this.defMaxTries);null!=o&&(s.max_tries=o);const d=this.parseOptInt(this.defRetryDelay);null!=d&&(s.retry_delay=d);const r=this.parseOptFloat(this.defBackoff);null!=r&&(s.backoff_multiplier=r);const p=this.parseOptInt(this.defPriority);null!=p&&(s.priority=p);const c=this.parseOptInt(this.defTtl);null!=c&&(s.ttl=c);const u=this.parseOptInt(this.defDoneTtl);null!=u&&(s.done_ttl=u);const f=this.parseOptInt(this.defFailedTtl);null!=f&&(s.failed_ttl=f),n.length&&(s.buckets=n),Object.keys(s).length&&(i.defaults=s),this.busy=!0;try{const n=this.isEdit?a.apiBase+"/queue/job-type/"+encodeURIComponent(t):a.apiBase+"/queue/job-type",l=this.isEdit?"PUT":"POST",s=this.isEdit?i:{id:t,...i};if(!(await fetch(n,{method:l,headers:{"Content-Type":"application/json"},body:JSON.stringify(s)})).ok)throw new Error("failed");await e.$attr.modal.close(),await(e.$attr.onDone?.())}catch{e.$ok.shared?.toast?.error?.("Failed to save job type")}finally{this.busy=!1}},cancel(){e.$attr.modal.close()}}),mount(){this.init()},template:'\n<div style="position:relative; min-width:600px; max-width:calc(100vw - 80px); display:flex; flex-direction:column; gap:14px;">\n\n <if :="busy">\n <ok-overlay>\n <div style="display:flex;align-items:center;justify-content:center;height:100%;">\n <ok-icon name="loader" class="spin" style="width:28px;height:28px;"></ok-icon>\n </div>\n </ok-overlay>\n </if>\n\n \x3c!-- name + status + concurrency --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;">\n <label>Type name</label>\n <input placeholder="e.g. email.send" :value="id" @input="id = $el.value" :disabled="isEdit" />\n </div>\n <div class="field" style="width:150px;">\n <label>Status</label>\n <select :value="status" @change="status = $el.value">\n <option value="active">Active</option>\n <option value="paused">Paused</option>\n <option value="draining">Draining</option>\n </select>\n </div>\n <div class="field" style="width:140px;">\n <label>Max concurrency</label>\n <input type="number" min="1" placeholder="unlimited" :value="maxConcurrency" @input="maxConcurrency = $el.value" />\n </div>\n </div>\n\n <div style="height:1px; background:var(--okdb-app-border);"></div>\n <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.06em; opacity:.5;">Defaults (applied when enqueueing without explicit values)</div>\n\n \x3c!-- retry defaults --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;">\n <label>Max tries</label>\n <input type="number" min="1" placeholder="queue default" :value="defMaxTries" @input="defMaxTries = $el.value" />\n </div>\n <div class="field" style="flex:1;">\n <label>Retry delay (ms)</label>\n <input type="number" min="0" placeholder="0" :value="defRetryDelay" @input="defRetryDelay = $el.value" />\n </div>\n <div class="field" style="flex:1;">\n <label>Backoff multiplier</label>\n <input type="number" min="1" step="0.1" placeholder="1" :value="defBackoff" @input="defBackoff = $el.value" />\n </div>\n <div class="field" style="width:110px;">\n <label>Priority</label>\n <input type="number" placeholder="0" :value="defPriority" @input="defPriority = $el.value" />\n </div>\n </div>\n\n \x3c!-- TTLs --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;">\n <label>Claim TTL (ms)</label>\n <input type="number" min="0" placeholder="queue default" :value="defTtl" @input="defTtl = $el.value" />\n </div>\n <div class="field" style="flex:1;">\n <label>Done TTL (ms)</label>\n <input type="number" min="0" placeholder="keep forever" :value="defDoneTtl" @input="defDoneTtl = $el.value" />\n </div>\n <div class="field" style="flex:1;">\n <label>Failed TTL (ms)</label>\n <input type="number" min="0" placeholder="keep forever" :value="defFailedTtl" @input="defFailedTtl = $el.value" />\n </div>\n </div>\n\n \x3c!-- default buckets --\x3e\n <div class="field">\n <label>Default buckets <span style="opacity:.4; font-weight:400; font-size:11px;">(rate limiting)</span></label>\n <each :of="defBuckets" let:item="b" let:index="i">\n <div style="display:flex; gap:6px; align-items:center; margin-bottom:4px;">\n <select style="flex:1;" @change="onBucketSelect(i, $el.value)">\n <option value="">— none —</option>\n <option as="repeat" :of="bucketOptions" let:item="opt" :value="opt.id" :selected="opt.id === b.id">{{ opt.id }}</option>\n <option value="__create__">+ Create bucket…</option>\n </select>\n <input style="width:72px;" type="number" min="1" placeholder="tokens"\n :value="b.tokens" @input="setBucketTokens(i, $el.value)" />\n <button class="btn-ghost" @click="removeBucket(i)" title="Remove">✕</button>\n </div>\n </each>\n <button class="btn-ghost btn-ghost--sm" @click="addBucket()">+ Add bucket</button>\n </div>\n\n \x3c!-- schema row (only shown in edit mode since we need the type to exist first) --\x3e\n <if :="isEdit">\n <div style="display:flex; align-items:center; gap:10px; padding:8px 12px; border:1px solid var(--okdb-app-border); border-radius:6px; background:var(--okdb-app-bg-deep);">\n <div style="flex:1; font-size:12px;">\n <span style="font-weight:600;">Payload Schema</span>\n <if :="hasSchema">\n <span style="margin-left:8px; font-size:10px; padding:1px 6px; border-radius:999px; border:1px solid color-mix(in srgb, var(--okdb-accent) 33%, transparent); color:var(--okdb-accent);">defined</span>\n <if :="schemaEnforce">\n <span style="margin-left:4px; font-size:10px; padding:1px 6px; border-radius:999px; border:1px solid #ef4444; color:#ef4444;">enforced</span>\n </if>\n </if>\n <if :="!hasSchema">\n <span style="margin-left:8px; opacity:.4; font-size:11px;">none</span>\n </if>\n </div>\n <button class="btn-ghost" @click="openSchema()">Edit schema…</button>\n </div>\n </if>\n <if :="!isEdit">\n <div style="font-size:11px; opacity:.45; padding:0 2px;">Save the type first, then edit its payload schema.</div>\n </if>\n\n \x3c!-- footer --\x3e\n <div style="display:flex; gap:10px; justify-content:flex-end;">\n <button @click="cancel()">Cancel</button>\n <button @click="submit()" :disabled="!id.trim() || busy">{{ isEdit ? \'Save\' : \'Register\' }}</button>\n </div>\n\n</div>\n',style:".btn-ghost{background:0 0;border:1px solid var(--okdb-app-border);border-radius:5px;color:inherit;cursor:pointer;opacity:.6;padding:2px 8px;font-size:12px}.btn-ghost:hover{opacity:1;border-color:color-mix(in srgb,var(--okdb-accent) 33%,transparent)}.btn-ghost--sm{font-size:11px;padding:1px 7px}"};
@@ -1 +1 @@
1
- const _0x4c3a2c=_0x522b;(function(_0x5c36ec,_0x274104){const _0x50f99a=_0x522b,_0x4e0c07=_0x5c36ec();while(!![]){try{const _0x2fd2ae=-parseInt(_0x50f99a(0x110))/0x1+-parseInt(_0x50f99a(0x118))/0x2+parseInt(_0x50f99a(0x113))/0x3*(-parseInt(_0x50f99a(0xf2))/0x4)+parseInt(_0x50f99a(0x100))/0x5*(-parseInt(_0x50f99a(0x11d))/0x6)+parseInt(_0x50f99a(0x115))/0x7+-parseInt(_0x50f99a(0xf5))/0x8*(parseInt(_0x50f99a(0x12a))/0x9)+parseInt(_0x50f99a(0xef))/0xa*(parseInt(_0x50f99a(0x121))/0xb);if(_0x2fd2ae===_0x274104)break;else _0x4e0c07['push'](_0x4e0c07['shift']());}catch(_0x5397a9){_0x4e0c07['push'](_0x4e0c07['shift']());}}}(_0x2148,0xcd016));import _0x19c300 from'../../../components/ok-cron-input.ok.js';function _0x2148(){const _0x3ea9da=['form-change','open','dispatchEvent','when','ok-job-form','initialType','_populateFromJob','now','asap','/queue/buckets','backoff_multiplier','_emitChange','__create__','payloadInit','apiBase','973742QFBEYd','$ok','tags','24WJcjLu','filter','4868360BhbaFW','buckets','.mode-btn{font-size:11px;padding:2px\x208px;opacity:.6;border-radius:4px}.mode-btn--on{opacity:1;background:color-mix(in\x20srgb,var(--okdb-accent)\x2013%,transparent);border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2033%,transparent);color:var(--okdb-accent)}.btn-ghost{background:0\x200;border:1px\x20solid\x20var(--okdb-app-border);border-radius:5px;color:inherit;cursor:pointer;opacity:.6;padding:2px\x208px;font-size:12px}.btn-ghost:hover{opacity:1;border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2033%,transparent)}.btn-ghost--sm{font-size:11px;padding:1px\x207px}','548038zRzzQQ','bucketOptions','loadBuckets','getWhen','data','99714nqVxma','whenMode','toISOString','idempotency_key','105248viwePr','$attr','priority','whenTs','length','getTime','slice','type','whenDatetime','9TRlemt','bucket_tokens','maxTries','job','bucket','payload','5990eXjjZr','trim','isArray','768748Uvfops','\x0a<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:10px;\x22>\x0a\x0a\x20\x20<!--\x20Row\x201:\x20type\x20+\x20priority\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Type</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20placeholder=\x22e.g.\x20email.send\x22\x20:value=\x22type\x22\x20@input=\x22type\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:120px;\x22><label>Priority</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20placeholder=\x220\x22\x20:value=\x22priority\x22\x20@input=\x22priority\x20=\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x202:\x20tags\x20+\x20idempotency\x20key\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Tags</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20placeholder=\x22comma\x20separated\x22\x20:value=\x22tags\x22\x20@input=\x22tags\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>Idempotency\x20Key</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22text\x22\x20placeholder=\x22optional\x20—\x20prevents\x20duplicate\x20jobs\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:value=\x22idempotencyKey\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20@input=\x22idempotencyKey\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Buckets\x20-->\x0a\x20\x20<div\x20class=\x22field\x22>\x0a\x20\x20\x20\x20<label>Buckets\x20<span\x20style=\x22opacity:.4;\x20font-weight:400;\x20font-size:11px;\x22>(rate\x20limiting)</span></label>\x0a\x20\x20\x20\x20<each\x20:of=\x22buckets\x22\x20let:item=\x22b\x22\x20let:index=\x22i\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:6px;\x20align-items:center;\x20margin-bottom:4px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<select\x20style=\x22flex:1;\x22\x20@change=\x22onBucketSelect(i,\x20$el.value)\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22\x22>—\x20none\x20—</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20as=\x22repeat\x22\x20:of=\x22bucketOptions\x22\x20let:item=\x22opt\x22\x20:value=\x22opt.id\x22\x20:selected=\x22opt.id\x20===\x20b.id\x22>{{\x20opt.id\x20}}</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22__create__\x22>+\x20Create\x20bucket…</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</select>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<input\x20style=\x22width:72px;\x22\x20type=\x22number\x22\x20min=\x221\x22\x20placeholder=\x22tokens\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:value=\x22b.tokens\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20@input=\x22setBucketTokens(i,\x20$el.value)\x22\x20/>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22btn-ghost\x22\x20@click=\x22removeBucket(i)\x22\x20title=\x22Remove\x22>✕</button>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</each>\x0a\x20\x20\x20\x20<button\x20class=\x22btn-ghost\x20btn-ghost--sm\x22\x20@click=\x22addBucket()\x22\x20style=\x22margin-top:2px;\x22>+\x20Add\x20bucket</button>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x203:\x20when\x20+\x20max\x20tries\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>When</label>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:4px;\x20align-items:center;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20:class=\x22whenMode===\x27asap\x27\x20?\x20\x27mode-btn\x20mode-btn--on\x27\x20:\x20\x27mode-btn\x27\x22\x20@click=\x22whenMode=\x27asap\x27\x22>ASAP</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20:class=\x22whenMode===\x27datetime\x27\x20?\x20\x27mode-btn\x20mode-btn--on\x27\x20:\x20\x27mode-btn\x27\x22\x20@click=\x22whenMode=\x27datetime\x27\x22>Date</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20:class=\x22whenMode===\x27timestamp\x27\x20?\x20\x27mode-btn\x20mode-btn--on\x27\x20:\x20\x27mode-btn\x27\x22\x20@click=\x22whenMode=\x27timestamp\x27\x22>TS</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22whenMode\x20===\x20\x27datetime\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<input\x20type=\x22datetime-local\x22\x20style=\x22flex:1;\x22\x20:value=\x22whenDatetime\x22\x20@input=\x22whenDatetime\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22whenMode\x20===\x20\x27timestamp\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20style=\x22flex:1;\x22\x20placeholder=\x22ms\x20timestamp\x22\x20:value=\x22whenTs\x22\x20@input=\x22whenTs\x20=\x20$el.value\x20===\x20\x27\x27\x20?\x20null\x20:\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22whenMode\x20===\x20\x27asap\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22opacity:.5;\x20font-size:12px;\x20padding:0\x208px;\x22>Run\x20immediately</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:120px;\x22><label>Max\x20tries</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20placeholder=\x22unlimited\x22\x20:value=\x22maxTries\x22\x20@input=\x22maxTries\x20=\x20$el.value\x20===\x20\x27\x27\x20?\x20null\x20:\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x204:\x20retry\x20delay\x20+\x20backoff\x20+\x20cron\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Retry\x20delay\x20(ms)</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20placeholder=\x220\x22\x20:value=\x22retryDelay\x22\x20@input=\x22retryDelay\x20=\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:120px;\x22><label>Backoff\x20multiplier</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20step=\x220.1\x22\x20placeholder=\x221\x22\x20:value=\x22backoff\x22\x20@input=\x22backoff\x20=\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Cron</label>\x0a\x20\x20\x20\x20\x20\x20<ok-cron-input\x20:value=\x22cron\x22\x20placeholder=\x22optional\x22\x20@change=\x22cron\x20=\x20$event.detail\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Payload\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:6px;\x22>\x0a\x20\x20\x20\x20<span\x20style=\x22opacity:.7;\x20font-size:12px;\x22>Payload</span>\x0a\x20\x20\x20\x20<ok-json-input\x0a\x20\x20\x20\x20\x20\x20mode=\x22textarea\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22height:260px;\x22\x0a\x20\x20\x20\x20\x20\x20:value=\x22payloadInit\x22\x0a\x20\x20\x20\x20\x20\x20@edit=\x22payload\x20=\x20$event.detail\x22\x0a\x20\x20\x20\x20/>\x0a\x20\x20</div>\x0a\x0a</div>\x0a','idempotencyKey','11644912srmWES','retry_delay','join','modal','tokens','cron','backoff','datetime','map','retryDelay','shared','405yfGVrc'];_0x2148=function(){return _0x3ea9da;};return _0x2148();}import _0x4a1e03 from'../../../components/ok-json-input.ok.js';import _0x565a0b from'../modals/create-bucket-modal.ok.js';function _0x522b(_0x416b0a,_0x340a4b){_0x416b0a=_0x416b0a-0xeb;const _0x2148b5=_0x2148();let _0x522ba0=_0x2148b5[_0x416b0a];return _0x522ba0;}import _0x2093e4 from'../../../stores/okdb-store.js';export default{'tag':_0x4c3a2c(0x105),'dependencies':[_0x19c300,_0x4a1e03,_0x565a0b],'attr':{'job':{},'initialType':{}},'context':_0x751829=>({'type':'','payloadInit':{},'payload':{},'when':null,'whenMode':'asap','whenDatetime':'','whenTs':null,'priority':0x0,'maxTries':null,'retryDelay':0x0,'backoff':0x1,'tags':'','buckets':[],'cron':'','idempotencyKey':'','bucketOptions':[],async 'loadBuckets'(){const _0x5d1320=_0x4c3a2c;try{const _0x4b3875=await fetch(_0x2093e4[_0x5d1320(0x10f)]+_0x5d1320(0x10a)),_0x1b0871=await _0x4b3875['json']();this[_0x5d1320(0x119)]=_0x1b0871?.[_0x5d1320(0x11c)]??[];}catch{this[_0x5d1320(0x119)]=[];}},async 'onBucketSelect'(_0x254899,_0x13da33){const _0x2a8b1d=_0x4c3a2c;if(_0x2a8b1d(0x10d)===_0x13da33){const _0x4f0eb5=this;await _0x751829[_0x2a8b1d(0x111)][_0x2a8b1d(0xff)][_0x2a8b1d(0xf8)][_0x2a8b1d(0x102)]({'owner':_0x751829,'title':'Add\x20Token\x20Bucket','content':'<create-bucket-modal\x20:modal=\x22modal\x22\x20:on-done=\x22onDone\x22\x20/>','context':_0xa442d3=>({'modal':_0xa442d3,'onDone':async _0x1b915e=>{const _0x55a3cb=_0x2a8b1d;await _0x4f0eb5[_0x55a3cb(0x11a)](),_0x4f0eb5[_0x55a3cb(0x116)]=_0x4f0eb5['buckets'][_0x55a3cb(0xfd)]((_0x2964b5,_0x4c46b7)=>_0x4c46b7===_0x254899?{..._0x2964b5,'id':_0x1b915e}:_0x2964b5);}})});}else this[_0x2a8b1d(0x116)]=this[_0x2a8b1d(0x116)][_0x2a8b1d(0xfd)]((_0x3beb73,_0x4ad712)=>_0x4ad712===_0x254899?{..._0x3beb73,'id':_0x13da33}:_0x3beb73);},'setBucketTokens'(_0x13cb90,_0x45f511){const _0x4a2f48=_0x4c3a2c,_0x2f0a8d=''===_0x45f511?0x1:Math['max'](0x1,parseInt(_0x45f511)||0x1);this[_0x4a2f48(0x116)]=this[_0x4a2f48(0x116)][_0x4a2f48(0xfd)]((_0x645b27,_0x4e20a3)=>_0x4e20a3===_0x13cb90?{..._0x645b27,'tokens':_0x2f0a8d}:_0x645b27);},'addBucket'(){const _0x2cf01c=_0x4c3a2c;this[_0x2cf01c(0x116)]=[...this[_0x2cf01c(0x116)],{'id':'','tokens':0x1}];},'removeBucket'(_0x560e8e){const _0x123205=_0x4c3a2c;this[_0x123205(0x116)]=this[_0x123205(0x116)][_0x123205(0x114)]((_0x2677d1,_0x34db8d)=>_0x34db8d!==_0x560e8e);},'getWhen'(){const _0x153230=_0x4c3a2c;if(_0x153230(0x109)===this['whenMode'])return null;if(_0x153230(0xfc)===this['whenMode']){const _0x55d48c=new Date(this['whenDatetime']);return isNaN(_0x55d48c[_0x153230(0x126)]())?null:_0x55d48c[_0x153230(0x126)]();}return this[_0x153230(0x124)];},'_parseTags'(_0x3202dc){const _0x27405f=_0x4c3a2c;if(!_0x3202dc)return;const _0x17ba96=String(_0x3202dc)['split'](',')[_0x27405f(0xfd)](_0x43a0ec=>_0x43a0ec['trim']())[_0x27405f(0x114)](Boolean);return _0x17ba96[_0x27405f(0x125)]?_0x17ba96:void 0x0;},'_emitChange'(){const _0x289c55=_0x4c3a2c,_0x128441=this[_0x289c55(0x11b)](),_0x50e58e=String(this[_0x289c55(0xfa)]||'')[_0x289c55(0xf0)]();_0x751829['$el'][_0x289c55(0x103)](new CustomEvent(_0x289c55(0x101),{'bubbles':!0x1,'detail':{'type':String(this['type']||'')[_0x289c55(0xf0)](),'payload':this[_0x289c55(0xee)],'priority':Number(this[_0x289c55(0x123)])||0x0,'retry_delay':Number(this['retryDelay'])||0x0,'backoff_multiplier':Number(this[_0x289c55(0xfb)])||0x1,'max_tries':null!==this[_0x289c55(0xeb)]&&''!==this['maxTries']?Number(this[_0x289c55(0xeb)]):null,'buckets':this[_0x289c55(0x116)][_0x289c55(0x114)](_0x19c36f=>_0x19c36f['id'])[_0x289c55(0xfd)](_0xa18a95=>({'id':_0xa18a95['id'],'tokens':_0xa18a95[_0x289c55(0xf9)]||0x1})),'when':_0x128441,'cron':_0x50e58e||null,'tags':this['_parseTags'](this['tags']),'idempotency_key':this[_0x289c55(0xf4)]||null}}));},'_populateFromJob'(_0xd4b1ef){const _0x57e41c=_0x4c3a2c;if(!_0xd4b1ef)return this[_0x57e41c(0x128)]='',this[_0x57e41c(0x10e)]={},this[_0x57e41c(0xee)]={},this[_0x57e41c(0x104)]=null,this[_0x57e41c(0x11e)]='asap',this['whenDatetime']='',this[_0x57e41c(0x124)]=null,this[_0x57e41c(0x123)]=0x0,this['maxTries']=null,this[_0x57e41c(0xfe)]=0x0,this[_0x57e41c(0xfb)]=0x1,this[_0x57e41c(0x112)]='',this[_0x57e41c(0x116)]=[{'id':'','tokens':0x1}],this[_0x57e41c(0xfa)]='',this[_0x57e41c(0xf4)]='',void this[_0x57e41c(0x10c)]();this[_0x57e41c(0x128)]=_0xd4b1ef?.[_0x57e41c(0x128)]??'',this[_0x57e41c(0x10e)]=_0xd4b1ef?.[_0x57e41c(0xee)]??{},this[_0x57e41c(0xee)]=_0xd4b1ef?.[_0x57e41c(0xee)]??{},this[_0x57e41c(0x104)]=_0xd4b1ef?.[_0x57e41c(0x104)]??null,this[_0x57e41c(0x123)]=_0xd4b1ef?.[_0x57e41c(0x123)]??0x0,this[_0x57e41c(0xeb)]=_0xd4b1ef?.['max_tries']??null,this[_0x57e41c(0xfe)]=_0xd4b1ef?.[_0x57e41c(0xf6)]??0x0,this[_0x57e41c(0xfb)]=_0xd4b1ef?.[_0x57e41c(0x10b)]??0x1,this[_0x57e41c(0x112)]=Array[_0x57e41c(0xf1)](_0xd4b1ef?.['tags'])?_0xd4b1ef[_0x57e41c(0x112)][_0x57e41c(0xf7)](',\x20'):'',this[_0x57e41c(0xfa)]=_0xd4b1ef?.['cron']??'',this['idempotencyKey']=_0xd4b1ef?.[_0x57e41c(0x120)]||'',_0xd4b1ef?.[_0x57e41c(0x104)]&&_0xd4b1ef[_0x57e41c(0x104)]>Date[_0x57e41c(0x108)]()-0x3e8?(this[_0x57e41c(0x11e)]='timestamp',this['whenDatetime']=new Date(_0xd4b1ef['when'])[_0x57e41c(0x11f)]()[_0x57e41c(0x127)](0x0,0x10),this[_0x57e41c(0x124)]=_0xd4b1ef[_0x57e41c(0x104)]):(this['whenMode']=_0x57e41c(0x109),this[_0x57e41c(0x129)]='',this[_0x57e41c(0x124)]=null),Array[_0x57e41c(0xf1)](_0xd4b1ef?.['buckets'])&&_0xd4b1ef[_0x57e41c(0x116)][_0x57e41c(0x125)]>0x0?this[_0x57e41c(0x116)]=_0xd4b1ef[_0x57e41c(0x116)][_0x57e41c(0xfd)](_0x2c1b4a=>({'id':_0x2c1b4a['id'],'tokens':_0x2c1b4a['tokens']??0x1})):this[_0x57e41c(0x116)]=_0xd4b1ef?.[_0x57e41c(0xed)]?[{'id':_0xd4b1ef[_0x57e41c(0xed)],'tokens':_0xd4b1ef[_0x57e41c(0x12b)]??0x1}]:[],this[_0x57e41c(0x10c)]();}}),async 'mount'(_0x3eb8f2){const _0x35b3c6=_0x4c3a2c;await this['loadBuckets'](),this['_populateFromJob'](_0x3eb8f2['$attr'][_0x35b3c6(0xec)]??null),_0x3eb8f2[_0x35b3c6(0x122)][_0x35b3c6(0x106)]&&(this[_0x35b3c6(0x128)]=_0x3eb8f2[_0x35b3c6(0x122)][_0x35b3c6(0x106)]),setTimeout(()=>this[_0x35b3c6(0x10c)](),0x0);},'watch':{'job'(_0x35906b){const _0x58bcd1=_0x4c3a2c;this[_0x58bcd1(0x107)](_0x35906b);},'type'(){const _0x501695=_0x4c3a2c;this[_0x501695(0x10c)]();},'payload'(){this['_emitChange']();},'priority'(){const _0x74d88c=_0x4c3a2c;this[_0x74d88c(0x10c)]();},'retryDelay'(){const _0x14cdd0=_0x4c3a2c;this[_0x14cdd0(0x10c)]();},'backoff'(){const _0x13780a=_0x4c3a2c;this[_0x13780a(0x10c)]();},'maxTries'(){this['_emitChange']();},'buckets'(){this['_emitChange']();},'whenMode'(){this['_emitChange']();},'whenDatetime'(){const _0x2797dc=_0x4c3a2c;this[_0x2797dc(0x10c)]();},'whenTs'(){const _0x309213=_0x4c3a2c;this[_0x309213(0x10c)]();},'cron'(){const _0x4c6c32=_0x4c3a2c;this[_0x4c6c32(0x10c)]();},'tags'(){const _0x426003=_0x4c3a2c;this[_0x426003(0x10c)]();},'idempotencyKey'(){this['_emitChange']();}},'template':_0x4c3a2c(0xf3),'style':_0x4c3a2c(0x117)};
1
+ import e from"../../../components/ok-cron-input.ok.js";import t from"../../../components/ok-json-input.ok.js";import n from"../modals/create-bucket-modal.ok.js";import i from"../../../stores/okdb-store.js";export default{tag:"ok-job-form",dependencies:[e,t,n],attr:{job:{},initialType:{}},context:e=>({type:"",payloadInit:{},payload:{},when:null,whenMode:"asap",whenDatetime:"",whenTs:null,priority:0,maxTries:null,retryDelay:0,backoff:1,tags:"",buckets:[],cron:"",idempotencyKey:"",bucketOptions:[],async loadBuckets(){try{const e=await fetch(i.apiBase+"/queue/buckets"),t=await e.json();this.bucketOptions=t?.data??[]}catch{this.bucketOptions=[]}},async onBucketSelect(t,n){if("__create__"===n){const n=this;await e.$ok.shared.modal.open({owner:e,title:"Add Token Bucket",content:'<create-bucket-modal :modal="modal" :on-done="onDone" />',context:e=>({modal:e,onDone:async e=>{await n.loadBuckets(),n.buckets=n.buckets.map((n,i)=>i===t?{...n,id:e}:n)}})})}else this.buckets=this.buckets.map((e,i)=>i===t?{...e,id:n}:e)},setBucketTokens(e,t){const n=""===t?1:Math.max(1,parseInt(t)||1);this.buckets=this.buckets.map((t,i)=>i===e?{...t,tokens:n}:t)},addBucket(){this.buckets=[...this.buckets,{id:"",tokens:1}]},removeBucket(e){this.buckets=this.buckets.filter((t,n)=>n!==e)},getWhen(){if("asap"===this.whenMode)return null;if("datetime"===this.whenMode){const e=new Date(this.whenDatetime);return isNaN(e.getTime())?null:e.getTime()}return this.whenTs},_parseTags(e){if(!e)return;const t=String(e).split(",").map(e=>e.trim()).filter(Boolean);return t.length?t:void 0},_emitChange(){const t=this.getWhen(),n=String(this.cron||"").trim();e.$el.dispatchEvent(new CustomEvent("form-change",{bubbles:!1,detail:{type:String(this.type||"").trim(),payload:this.payload,priority:Number(this.priority)||0,retry_delay:Number(this.retryDelay)||0,backoff_multiplier:Number(this.backoff)||1,max_tries:null!==this.maxTries&&""!==this.maxTries?Number(this.maxTries):null,buckets:this.buckets.filter(e=>e.id).map(e=>({id:e.id,tokens:e.tokens||1})),when:t,cron:n||null,tags:this._parseTags(this.tags),idempotency_key:this.idempotencyKey||null}}))},_populateFromJob(e){if(!e)return this.type="",this.payloadInit={},this.payload={},this.when=null,this.whenMode="asap",this.whenDatetime="",this.whenTs=null,this.priority=0,this.maxTries=null,this.retryDelay=0,this.backoff=1,this.tags="",this.buckets=[{id:"",tokens:1}],this.cron="",this.idempotencyKey="",void this._emitChange();this.type=e?.type??"",this.payloadInit=e?.payload??{},this.payload=e?.payload??{},this.when=e?.when??null,this.priority=e?.priority??0,this.maxTries=e?.max_tries??null,this.retryDelay=e?.retry_delay??0,this.backoff=e?.backoff_multiplier??1,this.tags=Array.isArray(e?.tags)?e.tags.join(", "):"",this.cron=e?.cron??"",this.idempotencyKey=e?.idempotency_key||"",e?.when&&e.when>Date.now()-1e3?(this.whenMode="timestamp",this.whenDatetime=new Date(e.when).toISOString().slice(0,16),this.whenTs=e.when):(this.whenMode="asap",this.whenDatetime="",this.whenTs=null),Array.isArray(e?.buckets)&&e.buckets.length>0?this.buckets=e.buckets.map(e=>({id:e.id,tokens:e.tokens??1})):this.buckets=e?.bucket?[{id:e.bucket,tokens:e.bucket_tokens??1}]:[],this._emitChange()}}),async mount(e){await this.loadBuckets(),this._populateFromJob(e.$attr.job??null),e.$attr.initialType&&(this.type=e.$attr.initialType),setTimeout(()=>this._emitChange(),0)},watch:{job(e){this._populateFromJob(e)},type(){this._emitChange()},payload(){this._emitChange()},priority(){this._emitChange()},retryDelay(){this._emitChange()},backoff(){this._emitChange()},maxTries(){this._emitChange()},buckets(){this._emitChange()},whenMode(){this._emitChange()},whenDatetime(){this._emitChange()},whenTs(){this._emitChange()},cron(){this._emitChange()},tags(){this._emitChange()},idempotencyKey(){this._emitChange()}},template:'\n<div style="display:flex; flex-direction:column; gap:10px;">\n\n \x3c!-- Row 1: type + priority --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;"><label>Type</label>\n <input placeholder="e.g. email.send" :value="type" @input="type = $el.value" />\n </div>\n <div class="field" style="width:120px;"><label>Priority</label>\n <input type="number" placeholder="0" :value="priority" @input="priority = Number($el.value)" />\n </div>\n </div>\n\n \x3c!-- Row 2: tags + idempotency key --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;"><label>Tags</label>\n <input placeholder="comma separated" :value="tags" @input="tags = $el.value" />\n </div>\n <div class="field" style="flex:1;">\n <label>Idempotency Key</label>\n <input type="text" placeholder="optional — prevents duplicate jobs"\n :value="idempotencyKey"\n @input="idempotencyKey = $el.value" />\n </div>\n </div>\n\n \x3c!-- Buckets --\x3e\n <div class="field">\n <label>Buckets <span style="opacity:.4; font-weight:400; font-size:11px;">(rate limiting)</span></label>\n <each :of="buckets" let:item="b" let:index="i">\n <div style="display:flex; gap:6px; align-items:center; margin-bottom:4px;">\n <select style="flex:1;" @change="onBucketSelect(i, $el.value)">\n <option value="">— none —</option>\n <option as="repeat" :of="bucketOptions" let:item="opt" :value="opt.id" :selected="opt.id === b.id">{{ opt.id }}</option>\n <option value="__create__">+ Create bucket…</option>\n </select>\n <input style="width:72px;" type="number" min="1" placeholder="tokens"\n :value="b.tokens"\n @input="setBucketTokens(i, $el.value)" />\n <button class="btn-ghost" @click="removeBucket(i)" title="Remove">✕</button>\n </div>\n </each>\n <button class="btn-ghost btn-ghost--sm" @click="addBucket()" style="margin-top:2px;">+ Add bucket</button>\n </div>\n\n \x3c!-- Row 3: when + max tries --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;">\n <label>When</label>\n <div style="display:flex; gap:4px; align-items:center;">\n <button :class="whenMode===\'asap\' ? \'mode-btn mode-btn--on\' : \'mode-btn\'" @click="whenMode=\'asap\'">ASAP</button>\n <button :class="whenMode===\'datetime\' ? \'mode-btn mode-btn--on\' : \'mode-btn\'" @click="whenMode=\'datetime\'">Date</button>\n <button :class="whenMode===\'timestamp\' ? \'mode-btn mode-btn--on\' : \'mode-btn\'" @click="whenMode=\'timestamp\'">TS</button>\n <if :="whenMode === \'datetime\'">\n <input type="datetime-local" style="flex:1;" :value="whenDatetime" @input="whenDatetime = $el.value" />\n </if>\n <if :="whenMode === \'timestamp\'">\n <input type="number" style="flex:1;" placeholder="ms timestamp" :value="whenTs" @input="whenTs = $el.value === \'\' ? null : Number($el.value)" />\n </if>\n <if :="whenMode === \'asap\'">\n <span style="opacity:.5; font-size:12px; padding:0 8px;">Run immediately</span>\n </if>\n </div>\n </div>\n <div class="field" style="width:120px;"><label>Max tries</label>\n <input type="number" placeholder="unlimited" :value="maxTries" @input="maxTries = $el.value === \'\' ? null : Number($el.value)" />\n </div>\n </div>\n\n \x3c!-- Row 4: retry delay + backoff + cron --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;"><label>Retry delay (ms)</label>\n <input type="number" placeholder="0" :value="retryDelay" @input="retryDelay = Number($el.value)" />\n </div>\n <div class="field" style="width:120px;"><label>Backoff multiplier</label>\n <input type="number" step="0.1" placeholder="1" :value="backoff" @input="backoff = Number($el.value)" />\n </div>\n <div class="field" style="flex:1;"><label>Cron</label>\n <ok-cron-input :value="cron" placeholder="optional" @change="cron = $event.detail" />\n </div>\n </div>\n\n \x3c!-- Payload --\x3e\n <div style="display:flex; flex-direction:column; gap:6px;">\n <span style="opacity:.7; font-size:12px;">Payload</span>\n <ok-json-input\n mode="textarea"\n style="height:260px;"\n :value="payloadInit"\n @edit="payload = $event.detail"\n />\n </div>\n\n</div>\n',style:".mode-btn{font-size:11px;padding:2px 8px;opacity:.6;border-radius:4px}.mode-btn--on{opacity:1;background:color-mix(in srgb,var(--okdb-accent) 13%,transparent);border-color:color-mix(in srgb,var(--okdb-accent) 33%,transparent);color:var(--okdb-accent)}.btn-ghost{background:0 0;border:1px solid var(--okdb-app-border);border-radius:5px;color:inherit;cursor:pointer;opacity:.6;padding:2px 8px;font-size:12px}.btn-ghost:hover{opacity:1;border-color:color-mix(in srgb,var(--okdb-accent) 33%,transparent)}.btn-ghost--sm{font-size:11px;padding:1px 7px}"};
@@ -1 +1 @@
1
- const _0x2e8eec=_0x4d13;(function(_0x510311,_0x59869f){const _0x235d7f=_0x4d13,_0x3c6792=_0x510311();while(!![]){try{const _0x3010ed=parseInt(_0x235d7f(0x1ac))/0x1*(-parseInt(_0x235d7f(0x1bd))/0x2)+parseInt(_0x235d7f(0x1bb))/0x3+-parseInt(_0x235d7f(0x1c8))/0x4*(-parseInt(_0x235d7f(0x1d0))/0x5)+-parseInt(_0x235d7f(0x1b7))/0x6*(-parseInt(_0x235d7f(0x1b8))/0x7)+parseInt(_0x235d7f(0x1bf))/0x8*(-parseInt(_0x235d7f(0x1d2))/0x9)+parseInt(_0x235d7f(0x1b9))/0xa+-parseInt(_0x235d7f(0x1bc))/0xb;if(_0x3010ed===_0x59869f)break;else _0x3c6792['push'](_0x3c6792['shift']());}catch(_0x727ea8){_0x3c6792['push'](_0x3c6792['shift']());}}}(_0xeaa5,0xbaa6f));import _0xcc7af5 from'../modals/edit-bucket-modal.ok.js';import _0x55b54f from'../../../stores/okdb-store.js';function _0x4d13(_0x101bdb,_0x4c7956){_0x101bdb=_0x101bdb-0x19e;const _0xeaa52a=_0xeaa5();let _0x4d13bd=_0xeaa52a[_0x101bdb];return _0x4d13bd;}export default{'tag':_0x2e8eec(0x1a4),'dependencies':[_0xcc7af5],'context':_0x5f48d4=>({'busy':!0x1,'buckets':[],'error':null,async 'load'(){const _0x44f72b=_0x2e8eec;this[_0x44f72b(0x19f)]=!0x0,this[_0x44f72b(0x1ab)]=null;try{const _0x314f3a=await fetch(_0x55b54f[_0x44f72b(0x1aa)]+_0x44f72b(0x1a7)),_0xcf05d1=await _0x314f3a[_0x44f72b(0x1b4)]();if(!_0x314f3a['ok'])throw new Error(_0xcf05d1?.[_0x44f72b(0x1ab)]?.['message']||_0x44f72b(0x1a8));this[_0x44f72b(0x1c7)]=_0xcf05d1[_0x44f72b(0x1a2)]??[];}catch(_0x104b59){this[_0x44f72b(0x1ab)]=_0x104b59?.[_0x44f72b(0x1ce)]||String(_0x104b59),this[_0x44f72b(0x1c7)]=[];}finally{this[_0x44f72b(0x19f)]=!0x1;}},async 'openCreate'(){const _0x3550ca=_0x2e8eec;await _0x5f48d4[_0x3550ca(0x1be)]['shared'][_0x3550ca(0x1c4)][_0x3550ca(0x1c2)]({'owner':_0x5f48d4,'title':'Add\x20Token\x20Bucket','content':_0x3550ca(0x1a9),'context':_0x5b8756=>({'modal':_0x5b8756,'editMode':!0x1,'initialForm':{},'onDone':async()=>{const _0x53556a=_0x3550ca;await this[_0x53556a(0x1a6)]();}})});},async 'openEdit'(_0x38f351){const _0x514bf3=_0x2e8eec;await _0x5f48d4[_0x514bf3(0x1be)][_0x514bf3(0x1d4)][_0x514bf3(0x1c4)]['open']({'owner':_0x5f48d4,'title':_0x514bf3(0x1b0),'content':'<edit-bucket-modal\x20:modal=\x22modal\x22\x20:edit-mode=\x22editMode\x22\x20:initial-form=\x22initialForm\x22\x20:on-done=\x22onDone\x22\x20/>','context':_0x3c3624=>({'modal':_0x3c3624,'editMode':!0x0,'initialForm':{'id':_0x38f351['id'],'capacity':_0x38f351['capacity'],'tokens':_0x38f351[_0x514bf3(0x1ad)],'refill_amount':_0x38f351[_0x514bf3(0x1cc)]??0x0,'refill_every':_0x38f351[_0x514bf3(0x1b1)]??0x0},'onDone':async()=>{await this['load']();}})});},async 'remove'(_0x2ba5a3){const _0x226eca=_0x2e8eec;if(await _0x5f48d4[_0x226eca(0x1be)][_0x226eca(0x1d4)][_0x226eca(0x1c4)][_0x226eca(0x1ba)](_0x226eca(0x1cb)+_0x2ba5a3['id']+'</b>\x22?'))try{if(!(await fetch(_0x55b54f[_0x226eca(0x1aa)]+_0x226eca(0x1c6)+encodeURIComponent(_0x2ba5a3['id']),{'method':'POST'}))['ok'])throw new Error(_0x226eca(0x1cf));_0x5f48d4[_0x226eca(0x1be)][_0x226eca(0x1d4)]?.[_0x226eca(0x1b2)]?.['info']?.('Deleted\x20\x22'+_0x2ba5a3['id']+'\x22'),await this[_0x226eca(0x1a6)]();}catch(_0x2decbe){_0x5f48d4[_0x226eca(0x1be)][_0x226eca(0x1d4)]?.['toast']?.[_0x226eca(0x1ab)]?.(_0x2decbe?.['message']||'Delete\x20failed');}},async 'refill'(_0x2e1f35){const _0x19005b=_0x2e8eec;try{if(!(await fetch(_0x55b54f[_0x19005b(0x1aa)]+_0x19005b(0x1a3)+encodeURIComponent(_0x2e1f35['id']),{'method':_0x19005b(0x1c1),'headers':{'Content-Type':_0x19005b(0x1c0)},'body':JSON[_0x19005b(0x1c3)]({'tokens':_0x2e1f35[_0x19005b(0x1b6)]})}))['ok'])throw new Error(_0x19005b(0x1cd));_0x5f48d4['$ok'][_0x19005b(0x1d4)]?.[_0x19005b(0x1b2)]?.[_0x19005b(0x1b3)]?.(_0x19005b(0x1b5)+_0x2e1f35['id']+'\x22'),await this[_0x19005b(0x1a6)]();}catch(_0x3aed6d){_0x5f48d4[_0x19005b(0x1be)][_0x19005b(0x1d4)]?.[_0x19005b(0x1b2)]?.['error']?.(_0x3aed6d?.[_0x19005b(0x1ce)]||_0x19005b(0x1cd));}},'tokenPct':_0x3cc8de=>_0x3cc8de[_0x2e8eec(0x1b6)]?Math[_0x2e8eec(0x1ca)](0x64,Math['round'](_0x3cc8de[_0x2e8eec(0x1ad)]/_0x3cc8de[_0x2e8eec(0x1b6)]*0x64)):0x0,'pctColor':_0x1c98bd=>_0x1c98bd>0x3c?'var(--okdb-success)':_0x1c98bd>0x19?_0x2e8eec(0x1d1):_0x2e8eec(0x1ae),'refillLabel'(_0x56fc2d){const _0x2eeccf=_0x2e8eec;if(!_0x56fc2d['refill_every']||!_0x56fc2d[_0x2eeccf(0x1cc)])return _0x2eeccf(0x1d3);const _0x3dab06=_0x56fc2d[_0x2eeccf(0x1b1)];let _0xbceb69;return _0xbceb69=_0x3dab06>=0x36ee80?(_0x3dab06/0x36ee80)[_0x2eeccf(0x1a1)](0x1)+'h':_0x3dab06>=0xea60?Math[_0x2eeccf(0x1a0)](_0x3dab06/0xea60)+'m':Math[_0x2eeccf(0x1a0)](_0x3dab06/0x3e8)+'s','+'+_0x56fc2d[_0x2eeccf(0x1cc)]+'\x20/\x20'+_0xbceb69;}}),async 'mount'(_0x316a55){const _0x29e46a=_0x2e8eec;await this[_0x29e46a(0x1a6)](),_0x316a55[_0x29e46a(0x19e)](()=>_0x316a55['$ok'][_0x29e46a(0x1af)][_0x29e46a(0x1c9)][_0x29e46a(0x1c5)],()=>this[_0x29e46a(0x1a6)]());},'template':_0x2e8eec(0x1a5)};function _0xeaa5(){const _0x5eb4b8=['tokens','var(--okdb-danger)','stores','Edit\x20Bucket','refill_every','toast','success','json','Refilled\x20\x22','capacity','6YFbTAF','8155483EcZUEY','14954100ZbTnVo','confirm','2035197fXrzzg','22988361DRDePh','2TYJhiB','$ok','6756976SECesD','application/json','POST','open','stringify','modal','selectedEnv','/queue/bucket/remove/','buckets','648fDxtPf','okdb','min','Delete\x20bucket\x20\x22<b>','refill_amount','Refill\x20failed','message','Delete\x20failed','35250DjTrbC','var(--okdb-warning)','9XrgEhO','No\x20refill','shared','$watch','busy','round','toFixed','data','/queue/bucket/update/','queue-buckets','\x0a<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:16px;\x20height:100%;\x20min-height:0;\x22>\x0a\x0a\x20\x20<!--\x20Toolbar\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:center;\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22font-weight:600;\x20font-size:16px;\x22>Token\x20Buckets</div>\x0a\x20\x20\x20\x20<div\x20style=\x22margin-left:auto;\x20display:flex;\x20gap:8px;\x22>\x0a\x20\x20\x20\x20\x20\x20<button\x20@click=\x22load()\x22\x20:disabled=\x22busy\x22>↻\x20Refresh</button>\x0a\x20\x20\x20\x20\x20\x20<button\x20@click=\x22openCreate()\x22\x20style=\x22background:color-mix(in\x20srgb,\x20var(--okdb-accent)\x2013%,\x20transparent);\x20border-color:var(--okdb-accent);\x20color:var(--okdb-accent);\x22>+\x20Add\x20Bucket</button>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<if\x20:=\x22error\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22color:var(--okdb-danger);\x20font-size:13px;\x20padding:10px\x2012px;\x20border:1px\x20solid\x20var(--okdb-danger);\x20border-radius:8px;\x22>{{error}}</div>\x0a\x20\x20</if>\x0a\x0a\x20\x20<!--\x20Empty\x20state\x20-->\x0a\x20\x20<if\x20:=\x22!busy\x20&&\x20buckets.length\x20===\x200\x20&&\x20!error\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22opacity:.55;\x20font-size:13px;\x20line-height:1.8;\x20padding:16px\x200;\x22>\x0a\x20\x20\x20\x20\x20\x20No\x20token\x20buckets\x20defined.<br>\x0a\x20\x20\x20\x20\x20\x20Token\x20buckets\x20let\x20you\x20rate-limit\x20queue\x20job\x20processing\x20per\x20bucket\x20ID.\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</if>\x0a\x0a\x20\x20<!--\x20Bucket\x20cards\x20-->\x0a\x20\x20<div\x20style=\x22display:grid;\x20grid-template-columns:repeat(auto-fill,minmax(280px,1fr));\x20gap:12px;\x20overflow:auto;\x20min-height:0;\x22>\x0a\x20\x20\x20\x20<for\x20:=\x22buckets\x22\x20let:b>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20background:#1e2326;\x0a\x20\x20\x20\x20\x20\x20\x20\x20border:1px\x20solid\x20var(--okdb-app-border);\x0a\x20\x20\x20\x20\x20\x20\x20\x20border-radius:12px;\x0a\x20\x20\x20\x20\x20\x20\x20\x20padding:14px\x2016px;\x0a\x20\x20\x20\x20\x20\x20\x20\x20display:flex;\x0a\x20\x20\x20\x20\x20\x20\x20\x20flex-direction:column;\x0a\x20\x20\x20\x20\x20\x20\x20\x20gap:10px;\x0a\x20\x20\x20\x20\x20\x20\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<!--\x20Header\x20-->\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20align-items:center;\x20gap:8px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-weight:600;\x20font-family:monospace;\x20font-size:13px;\x20flex:1;\x20overflow:hidden;\x20text-overflow:ellipsis;\x20white-space:nowrap;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20{{b.id}}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20@click=\x22openEdit(b)\x22\x20style=\x22font-size:11px;\x20padding:3px\x208px;\x20opacity:.8;\x22>Edit</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20@click=\x22remove(b)\x22\x20style=\x22font-size:11px;\x20padding:3px\x208px;\x20color:var(--okdb-danger);\x20opacity:.8;\x22>✕</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20<!--\x20Token\x20bar\x20-->\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20justify-content:space-between;\x20font-size:12px;\x20margin-bottom:4px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22opacity:.7;\x22>Tokens</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20:style=\x22\x27color:\x27\x20+\x20pctColor(tokenPct(b))\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20{{b.tokens}}\x20/\x20{{b.capacity}}\x20({{tokenPct(b)}}%)\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22height:6px;\x20background:var(--okdb-app-border);\x20border-radius:999px;\x20overflow:hidden;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:style=\x22\x27height:100%;\x20border-radius:999px;\x20width:\x27\x20+\x20tokenPct(b)\x20+\x20\x27%;\x20background:\x27\x20+\x20pctColor(tokenPct(b))\x20+\x20\x27;\x20transition:width\x200.3s;\x27\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20></div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20<!--\x20Stats\x20row\x20-->\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:12px;\x20font-size:12px;\x20opacity:.75;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>Refill:\x20<b>{{refillLabel(b)}}</b></span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20<!--\x20Actions\x20-->\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:6px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20@click=\x22refill(b)\x22\x20style=\x22font-size:11px;\x20flex:1;\x22\x20:disabled=\x22b.tokens\x20>=\x20b.capacity\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Refill\x20to\x20Full\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</for>\x0a\x20\x20</div>\x0a\x0a</div>\x0a','load','/queue/buckets','Failed\x20to\x20load\x20buckets','<edit-bucket-modal\x20:modal=\x22modal\x22\x20:edit-mode=\x22editMode\x22\x20:initial-form=\x22initialForm\x22\x20:on-done=\x22onDone\x22\x20/>','apiBase','error','781978CvHWkf'];_0xeaa5=function(){return _0x5eb4b8;};return _0xeaa5();}
1
+ import e from"../modals/edit-bucket-modal.ok.js";import t from"../../../stores/okdb-store.js";export default{tag:"queue-buckets",dependencies:[e],context:e=>({busy:!1,buckets:[],error:null,async load(){this.busy=!0,this.error=null;try{const e=await fetch(`${t.apiBase}/queue/buckets`),o=await e.json();if(!e.ok)throw new Error(o?.error?.message||"Failed to load buckets");this.buckets=o.data??[]}catch(e){this.error=e?.message||String(e),this.buckets=[]}finally{this.busy=!1}},async openCreate(){await e.$ok.shared.modal.open({owner:e,title:"Add Token Bucket",content:'<edit-bucket-modal :modal="modal" :edit-mode="editMode" :initial-form="initialForm" :on-done="onDone" />',context:e=>({modal:e,editMode:!1,initialForm:{},onDone:async()=>{await this.load()}})})},async openEdit(t){await e.$ok.shared.modal.open({owner:e,title:"Edit Bucket",content:'<edit-bucket-modal :modal="modal" :edit-mode="editMode" :initial-form="initialForm" :on-done="onDone" />',context:e=>({modal:e,editMode:!0,initialForm:{id:t.id,capacity:t.capacity,tokens:t.tokens,refill_amount:t.refill_amount??0,refill_every:t.refill_every??0},onDone:async()=>{await this.load()}})})},async remove(o){if(await e.$ok.shared.modal.confirm(`Delete bucket "<b>${o.id}</b>"?`))try{if(!(await fetch(`${t.apiBase}/queue/bucket/remove/${encodeURIComponent(o.id)}`,{method:"POST"})).ok)throw new Error("Delete failed");e.$ok.shared?.toast?.info?.(`Deleted "${o.id}"`),await this.load()}catch(t){e.$ok.shared?.toast?.error?.(t?.message||"Delete failed")}},async refill(o){try{if(!(await fetch(`${t.apiBase}/queue/bucket/update/${encodeURIComponent(o.id)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tokens:o.capacity})})).ok)throw new Error("Refill failed");e.$ok.shared?.toast?.success?.(`Refilled "${o.id}"`),await this.load()}catch(t){e.$ok.shared?.toast?.error?.(t?.message||"Refill failed")}},tokenPct:e=>e.capacity?Math.min(100,Math.round(e.tokens/e.capacity*100)):0,pctColor:e=>e>60?"var(--okdb-success)":e>25?"var(--okdb-warning)":"var(--okdb-danger)",refillLabel(e){if(!e.refill_every||!e.refill_amount)return"No refill";const t=e.refill_every;let o;return o=t>=36e5?`${(t/36e5).toFixed(1)}h`:t>=6e4?`${Math.round(t/6e4)}m`:`${Math.round(t/1e3)}s`,`+${e.refill_amount} / ${o}`}}),async mount(e){await this.load(),e.$watch(()=>e.$ok.stores.okdb.selectedEnv,()=>this.load())},template:'\n<div style="display:flex; flex-direction:column; gap:16px; height:100%; min-height:0;">\n\n \x3c!-- Toolbar --\x3e\n <div style="display:flex; gap:8px; align-items:center;">\n <div style="font-weight:600; font-size:16px;">Token Buckets</div>\n <div style="margin-left:auto; display:flex; gap:8px;">\n <button @click="load()" :disabled="busy">↻ Refresh</button>\n <button @click="openCreate()" style="background:color-mix(in srgb, var(--okdb-accent) 13%, transparent); border-color:var(--okdb-accent); color:var(--okdb-accent);">+ Add Bucket</button>\n </div>\n </div>\n\n <if :="error">\n <div style="color:var(--okdb-danger); font-size:13px; padding:10px 12px; border:1px solid var(--okdb-danger); border-radius:8px;">{{error}}</div>\n </if>\n\n \x3c!-- Empty state --\x3e\n <if :="!busy && buckets.length === 0 && !error">\n <div style="opacity:.55; font-size:13px; line-height:1.8; padding:16px 0;">\n No token buckets defined.<br>\n Token buckets let you rate-limit queue job processing per bucket ID.\n </div>\n </if>\n\n \x3c!-- Bucket cards --\x3e\n <div style="display:grid; grid-template-columns:repeat(auto-fill,minmax(280px,1fr)); gap:12px; overflow:auto; min-height:0;">\n <for :="buckets" let:b>\n <div style="\n background:#1e2326;\n border:1px solid var(--okdb-app-border);\n border-radius:12px;\n padding:14px 16px;\n display:flex;\n flex-direction:column;\n gap:10px;\n ">\n \x3c!-- Header --\x3e\n <div style="display:flex; align-items:center; gap:8px;">\n <div style="font-weight:600; font-family:monospace; font-size:13px; flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">\n {{b.id}}\n </div>\n <button @click="openEdit(b)" style="font-size:11px; padding:3px 8px; opacity:.8;">Edit</button>\n <button @click="remove(b)" style="font-size:11px; padding:3px 8px; color:var(--okdb-danger); opacity:.8;">✕</button>\n </div>\n\n \x3c!-- Token bar --\x3e\n <div>\n <div style="display:flex; justify-content:space-between; font-size:12px; margin-bottom:4px;">\n <span style="opacity:.7;">Tokens</span>\n <span :style="\'color:\' + pctColor(tokenPct(b))">\n {{b.tokens}} / {{b.capacity}} ({{tokenPct(b)}}%)\n </span>\n </div>\n <div style="height:6px; background:var(--okdb-app-border); border-radius:999px; overflow:hidden;">\n <div\n :style="\'height:100%; border-radius:999px; width:\' + tokenPct(b) + \'%; background:\' + pctColor(tokenPct(b)) + \'; transition:width 0.3s;\'"\n ></div>\n </div>\n </div>\n\n \x3c!-- Stats row --\x3e\n <div style="display:flex; gap:12px; font-size:12px; opacity:.75;">\n <span>Refill: <b>{{refillLabel(b)}}</b></span>\n </div>\n\n \x3c!-- Actions --\x3e\n <div style="display:flex; gap:6px;">\n <button @click="refill(b)" style="font-size:11px; flex:1;" :disabled="b.tokens >= b.capacity">\n Refill to Full\n </button>\n </div>\n </div>\n </for>\n </div>\n\n</div>\n'};
@@ -1 +1 @@
1
- const _0x307a37=_0x3823;function _0x3823(_0x442079,_0x534a39){_0x442079=_0x442079-0x16b;const _0xb5f0da=_0xb5f0();let _0x3823bf=_0xb5f0da[_0x442079];return _0x3823bf;}(function(_0x427218,_0x41ae30){const _0x3e69aa=_0x3823,_0x10f7fc=_0x427218();while(!![]){try{const _0x8107c7=parseInt(_0x3e69aa(0x177))/0x1+parseInt(_0x3e69aa(0x19a))/0x2+parseInt(_0x3e69aa(0x192))/0x3+parseInt(_0x3e69aa(0x1a1))/0x4+parseInt(_0x3e69aa(0x183))/0x5+parseInt(_0x3e69aa(0x1a2))/0x6+-parseInt(_0x3e69aa(0x175))/0x7;if(_0x8107c7===_0x41ae30)break;else _0x10f7fc['push'](_0x10f7fc['shift']());}catch(_0x520baa){_0x10f7fc['push'](_0x10f7fc['shift']());}}}(_0xb5f0,0x2097c));import _0x42dcb3 from'../../../stores/okdb-store.js';import _0x3b83c0 from'../modals/job-type-modal.ok.js';import _0x497e42 from'../modals/enqueue-job-modal.ok.js';const n={'active':'Active','paused':_0x307a37(0x191),'draining':_0x307a37(0x188)},a={'active':_0x307a37(0x16f),'paused':'color-mix(in\x20srgb,\x20#f59e0b\x2080%,\x20transparent)','draining':_0x307a37(0x16b)};export default{'tag':_0x307a37(0x182),'dependencies':[_0x3b83c0,_0x497e42],'context':_0x2dbf0=>({'busy':!0x1,'loadError':null,'types':[],'bucketOptions':[],async 'load'(){const _0x31afda=_0x307a37;this['busy']=!0x0,this[_0x31afda(0x194)]=null;try{const [_0x4ceaa4,_0x2de1b0]=await Promise['all']([fetch(_0x42dcb3[_0x31afda(0x18c)]+_0x31afda(0x19e)),fetch(_0x42dcb3[_0x31afda(0x18c)]+'/queue/buckets')['catch'](()=>null)]);if(!_0x4ceaa4['ok']){const _0x2dabc6=await _0x4ceaa4['json']()['catch'](()=>({}));throw new Error(_0x2dabc6?.[_0x31afda(0x1a3)]?.[_0x31afda(0x1a0)]??_0x2dabc6?.[_0x31afda(0x1a0)]??_0x31afda(0x17f)+_0x4ceaa4[_0x31afda(0x178)]);}const _0x12f796=await _0x4ceaa4[_0x31afda(0x199)]();this[_0x31afda(0x1a4)]=_0x12f796?.[_0x31afda(0x185)]??_0x12f796?.[_0x31afda(0x16e)]??[];const _0x2b3298=_0x2de1b0?.['ok']?await _0x2de1b0[_0x31afda(0x199)]()['catch'](()=>({})):{};this[_0x31afda(0x181)]=_0x2b3298?.[_0x31afda(0x185)]??_0x2b3298?.[_0x31afda(0x16e)]??[];}catch(_0x597a04){console[_0x31afda(0x1a3)](_0x31afda(0x17e),_0x597a04),this['loadError']=_0x597a04['message']||_0x31afda(0x176),this[_0x31afda(0x1a4)]=[];}finally{this[_0x31afda(0x19b)]=!0x1;}},'statusLabel':_0x302b3c=>n[_0x302b3c]??_0x302b3c,'statusColor':_0x3aa542=>a[_0x3aa542]??_0x307a37(0x179),'formatDefaults'(_0xe1c1ac){const _0x3cf4f1=_0x307a37,_0x4e89e9=_0xe1c1ac[_0x3cf4f1(0x19d)]??{},_0x570707=[];return null!=_0x4e89e9[_0x3cf4f1(0x187)]&&_0x570707[_0x3cf4f1(0x17b)](_0x3cf4f1(0x172)+_0x4e89e9['max_tries']),null!=_0x4e89e9[_0x3cf4f1(0x195)]&&_0x570707[_0x3cf4f1(0x17b)]('delay:'+_0x4e89e9[_0x3cf4f1(0x195)]+'ms'),null!=_0x4e89e9['priority']&&_0x570707[_0x3cf4f1(0x17b)](_0x3cf4f1(0x193)+_0x4e89e9[_0x3cf4f1(0x171)]),null!=_0x4e89e9[_0x3cf4f1(0x174)]&&_0x570707['push'](_0x3cf4f1(0x170)+_0x4e89e9[_0x3cf4f1(0x174)]+'ms'),null!=_0xe1c1ac[_0x3cf4f1(0x19f)]&&_0x570707['push'](_0x3cf4f1(0x19c)+_0xe1c1ac[_0x3cf4f1(0x19f)]),_0x570707[_0x3cf4f1(0x16c)]('\x20\x20·\x20\x20')||'—';},'openCreate'(){const _0x1fa5fb=_0x307a37,_0x47eed1=this;_0x2dbf0[_0x1fa5fb(0x18b)][_0x1fa5fb(0x18f)][_0x1fa5fb(0x17d)][_0x1fa5fb(0x198)]({'owner':_0x2dbf0,'title':'Register\x20Job\x20Type','content':_0x1fa5fb(0x186),'context':_0x37e80b=>({'modal':_0x37e80b,'bucketOptions':_0x47eed1[_0x1fa5fb(0x181)],'onDone':()=>_0x47eed1[_0x1fa5fb(0x16d)]()})});},'openEdit'(_0x465b7a){const _0x4d42e6=_0x307a37;_0x2dbf0[_0x4d42e6(0x18b)][_0x4d42e6(0x18f)][_0x4d42e6(0x17d)]['open']({'owner':_0x2dbf0,'title':'Edit:\x20'+_0x465b7a['id'],'content':'<job-type-modal\x20:modal=\x22modal\x22\x20:job-type=\x22jobType\x22\x20:bucket-options=\x22bucketOptions\x22\x20:on-done=\x22onDone\x22\x20/>','context':_0xdd1be3=>({'modal':_0xdd1be3,'jobType':_0x465b7a,'bucketOptions':this[_0x4d42e6(0x181)],'onDone':()=>this[_0x4d42e6(0x16d)]()})});},async 'setStatus'(_0x5f5dce,_0x385c04){const _0x22fec9=_0x307a37;try{if(!(await fetch(_0x42dcb3['apiBase']+_0x22fec9(0x17a)+encodeURIComponent(_0x5f5dce['id'])+'/'+_0x385c04,{'method':_0x22fec9(0x18a)}))['ok'])throw new Error(_0x22fec9(0x196));await this[_0x22fec9(0x16d)]();}catch{_0x2dbf0[_0x22fec9(0x18b)][_0x22fec9(0x18f)]?.[_0x22fec9(0x18d)]?.[_0x22fec9(0x1a3)]?.(_0x22fec9(0x189)+_0x385c04+_0x22fec9(0x17c));}},'openEnqueue'(_0x53d91a){const _0x2bd215=_0x307a37;_0x2dbf0[_0x2bd215(0x18b)]['shared'][_0x2bd215(0x17d)]['open']({'owner':_0x2dbf0,'title':_0x2bd215(0x197),'content':_0x2bd215(0x173),'context':_0x1771ac=>({'modal':_0x1771ac,'initialType':_0x53d91a['id'],'onDone':()=>_0x2dbf0[_0x2bd215(0x18b)][_0x2bd215(0x18f)]?.[_0x2bd215(0x18d)]?.['success']?.(_0x2bd215(0x184))})});},async 'remove'(_0x4103cf){const _0x17cd0b=_0x307a37;if(await _0x2dbf0[_0x17cd0b(0x18b)][_0x17cd0b(0x18f)]['modal'][_0x17cd0b(0x180)](_0x17cd0b(0x190)+_0x4103cf['id']+'\x22?\x20Existing\x20jobs\x20are\x20unaffected.'))try{if(!(await fetch(_0x42dcb3['apiBase']+_0x17cd0b(0x17a)+encodeURIComponent(_0x4103cf['id']),{'method':'DELETE'}))['ok'])throw new Error(_0x17cd0b(0x196));await this['load']();}catch{_0x2dbf0[_0x17cd0b(0x18b)]['shared']?.[_0x17cd0b(0x18d)]?.['error']?.('Failed\x20to\x20remove\x20job\x20type');}}}),async 'mount'(_0x2d86ec){const _0x518bf3=_0x307a37;await this['load'](),_0x2d86ec[_0x518bf3(0x18e)](()=>_0x42dcb3['selectedEnv'],()=>{const _0x596223=_0x518bf3;this[_0x596223(0x1a4)]=[],this['load']();});},'template':'\x0a<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:16px;\x20padding:16px\x200;\x22>\x0a\x0a\x20\x20<!--\x20header\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20align-items:center;\x20gap:10px;\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22font-size:14px;\x20font-weight:600;\x20flex:1;\x22>Registered\x20Job\x20Types</div>\x0a\x20\x20\x20\x20<button\x20@click=\x22load\x22\x20:disabled=\x22busy\x22\x20style=\x22opacity:.6;\x20font-size:11px;\x20padding:2px\x208px;\x22>↻</button>\x0a\x20\x20\x20\x20<button\x20@click=\x22openCreate()\x22\x20style=\x22font-size:12px;\x20padding:4px\x2012px;\x22>+\x20Register\x20type</button>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20error\x20state\x20-->\x0a\x20\x20<if\x20:=\x22loadError\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22color:#ef4444;\x20font-size:12px;\x20padding:8px\x2012px;\x20border:1px\x20solid\x20#ef4444;\x20border-radius:6px;\x20background:color-mix(in\x20srgb,\x20#ef4444\x208%,\x20transparent);\x22>\x0a\x20\x20\x20\x20\x20\x20Failed\x20to\x20load\x20job\x20types:\x20{{\x20loadError\x20}}\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</if>\x0a\x0a\x20\x20<!--\x20empty\x20state\x20-->\x0a\x20\x20<if\x20:=\x22!busy\x20&&\x20!loadError\x20&&\x20types.length\x20===\x200\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22opacity:.45;\x20font-size:13px;\x20line-height:1.8;\x20padding:32px\x200;\x20text-align:center;\x22>\x0a\x20\x20\x20\x20\x20\x20No\x20registered\x20job\x20types.<br>\x0a\x20\x20\x20\x20\x20\x20<span\x20style=\x22font-size:12px;\x22>Register\x20a\x20type\x20to\x20set\x20defaults,\x20control\x20concurrency,\x20and\x20pause/resume\x20claiming.</span>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</if>\x0a\x0a\x20\x20<!--\x20types\x20list\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:8px;\x22>\x0a\x20\x20\x20\x20<each\x20:of=\x22types\x22\x20let:item=\x22jt\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20class=\x22jt-card\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20align-items:center;\x20gap:10px;\x20flex-wrap:wrap;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22font-size:13px;\x20font-weight:600;\x20flex:1;\x20min-width:0;\x20overflow:hidden;\x20text-overflow:ellipsis;\x20white-space:nowrap;\x22>{{\x20jt.id\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22status-badge\x22\x20:style=\x22\x27border-color:\x27\x20+\x20statusColor(jt.status)\x20+\x20\x27;\x20color:\x27\x20+\x20statusColor(jt.status)\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20{{\x20statusLabel(jt.status)\x20}}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22jt.payload_schema\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22status-badge\x22\x20style=\x22border-color:var(--okdb-accent);\x20color:var(--okdb-accent);\x22>schema</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22jt.payload_schema.enforce\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22status-badge\x22\x20style=\x22border-color:#ef4444;\x20color:#ef4444;\x22>enforced</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<!--\x20lifecycle\x20actions\x20-->\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22jt.status\x20!==\x20\x27paused\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22act-btn\x22\x20@click=\x22setStatus(jt,\x20\x27pause\x27)\x22\x20title=\x22Pause\x20—\x20stop\x20workers\x20from\x20claiming\x20new\x20jobs\x22>⏸\x20Pause</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22jt.status\x20===\x20\x27paused\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22act-btn\x20act-btn--accent\x22\x20@click=\x22setStatus(jt,\x20\x27resume\x27)\x22\x20title=\x22Resume\x20normal\x20claiming\x22>▶\x20Resume</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22jt.status\x20!==\x20\x27draining\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22act-btn\x22\x20@click=\x22setStatus(jt,\x20\x27drain\x27)\x22\x20title=\x22Drain\x20—\x20stop\x20new\x20enqueues,\x20let\x20queue\x20empty\x22>⬇\x20Drain</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22jt.status\x20===\x20\x27draining\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22act-btn\x20act-btn--accent\x22\x20@click=\x22setStatus(jt,\x20\x27resume\x27)\x22>▶\x20Resume</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22act-btn\x20act-btn--enqueue\x22\x20@click=\x22openEnqueue(jt)\x22>+\x20Enqueue</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22act-btn\x22\x20@click=\x22openEdit(jt)\x22>Edit</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22act-btn\x20act-btn--danger\x22\x20@click=\x22remove(jt)\x22>Remove</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:11px;\x20opacity:.55;\x20margin-top:4px;\x20font-family:monospace;\x22>{{\x20formatDefaults(jt)\x20}}</div>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</each>\x0a\x20\x20</div>\x0a\x0a</div>\x0a','style':'.jt-card{border:1px\x20solid\x20var(--okdb-app-border);border-radius:8px;padding:10px\x2014px;background:var(--okdb-app-surface)}.status-badge{font-size:10px;padding:2px\x208px;border-radius:999px;border:1px\x20solid;font-weight:600;letter-spacing:.04em}.act-btn{font-size:11px;padding:2px\x208px;border-radius:5px;border:1px\x20solid\x20var(--okdb-app-border);background:0\x200;cursor:pointer;color:var(--okdb-app-fg-muted)}.act-btn:hover{border-color:var(--okdb-app-fg-muted);color:var(--okdb-app-fg)}.act-btn--accent,.act-btn--enqueue{border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2040%,transparent);color:var(--okdb-accent)}.act-btn--danger:hover{border-color:#ef4444;color:#ef4444}.act-btn--enqueue{border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2030%,transparent)}.act-btn--enqueue:hover{border-color:var(--okdb-accent)}'};function _0xb5f0(){const _0xb15547=['error','types','color-mix(in\x20srgb,\x20#3b82f6\x2080%,\x20transparent)','join','load','data','color-mix(in\x20srgb,\x20#22c55e\x2080%,\x20transparent)','ttl:','priority','tries:','<enqueue-job-modal\x20:modal=\x22modal\x22\x20:initial-type=\x22initialType\x22\x20:on-done=\x22onDone\x22\x20/>','ttl','4781847FKcZzH','Failed\x20to\x20load','217632FQEcaY','status','var(--okdb-app-fg-muted)','/queue/job-type/','push','\x20job\x20type','modal','queue-job-types\x20load\x20error','HTTP\x20','confirm','bucketOptions','queue-job-types','950445hxINEA','Job\x20enqueued','result','<job-type-modal\x20:modal=\x22modal\x22\x20:job-type=\x22null\x22\x20:bucket-options=\x22bucketOptions\x22\x20:on-done=\x22onDone\x22\x20/>','max_tries','Draining','Failed\x20to\x20','POST','$ok','apiBase','toast','$watch','shared','Remove\x20job\x20type\x20\x22','Paused','352443REojcy','pri:','loadError','retry_delay','failed','Enqueue\x20Job','open','json','147482sbbTTO','busy','concurrency:','defaults','/queue/job-types','max_concurrency','message','826468XoRSeo','66366OIdBLe'];_0xb5f0=function(){return _0xb15547;};return _0xb5f0();}
1
+ import t from"../../../stores/okdb-store.js";import e from"../modals/job-type-modal.ok.js";import o from"../modals/enqueue-job-modal.ok.js";const n={active:"Active",paused:"Paused",draining:"Draining"},a={active:"color-mix(in srgb, #22c55e 80%, transparent)",paused:"color-mix(in srgb, #f59e0b 80%, transparent)",draining:"color-mix(in srgb, #3b82f6 80%, transparent)"};export default{tag:"queue-job-types",dependencies:[e,o],context:e=>({busy:!1,loadError:null,types:[],bucketOptions:[],async load(){this.busy=!0,this.loadError=null;try{const[e,o]=await Promise.all([fetch(t.apiBase+"/queue/job-types"),fetch(t.apiBase+"/queue/buckets").catch(()=>null)]);if(!e.ok){const t=await e.json().catch(()=>({}));throw new Error(t?.error?.message??t?.message??`HTTP ${e.status}`)}const n=await e.json();this.types=n?.result??n?.data??[];const a=o?.ok?await o.json().catch(()=>({})):{};this.bucketOptions=a?.result??a?.data??[]}catch(t){console.error("queue-job-types load error",t),this.loadError=t.message||"Failed to load",this.types=[]}finally{this.busy=!1}},statusLabel:t=>n[t]??t,statusColor:t=>a[t]??"var(--okdb-app-fg-muted)",formatDefaults(t){const e=t.defaults??{},o=[];return null!=e.max_tries&&o.push(`tries:${e.max_tries}`),null!=e.retry_delay&&o.push(`delay:${e.retry_delay}ms`),null!=e.priority&&o.push(`pri:${e.priority}`),null!=e.ttl&&o.push(`ttl:${e.ttl}ms`),null!=t.max_concurrency&&o.push(`concurrency:${t.max_concurrency}`),o.join(" · ")||"—"},openCreate(){const t=this;e.$ok.shared.modal.open({owner:e,title:"Register Job Type",content:'<job-type-modal :modal="modal" :job-type="null" :bucket-options="bucketOptions" :on-done="onDone" />',context:e=>({modal:e,bucketOptions:t.bucketOptions,onDone:()=>t.load()})})},openEdit(t){e.$ok.shared.modal.open({owner:e,title:`Edit: ${t.id}`,content:'<job-type-modal :modal="modal" :job-type="jobType" :bucket-options="bucketOptions" :on-done="onDone" />',context:e=>({modal:e,jobType:t,bucketOptions:this.bucketOptions,onDone:()=>this.load()})})},async setStatus(o,n){try{if(!(await fetch(t.apiBase+"/queue/job-type/"+encodeURIComponent(o.id)+"/"+n,{method:"POST"})).ok)throw new Error("failed");await this.load()}catch{e.$ok.shared?.toast?.error?.(`Failed to ${n} job type`)}},openEnqueue(t){e.$ok.shared.modal.open({owner:e,title:"Enqueue Job",content:'<enqueue-job-modal :modal="modal" :initial-type="initialType" :on-done="onDone" />',context:o=>({modal:o,initialType:t.id,onDone:()=>e.$ok.shared?.toast?.success?.("Job enqueued")})})},async remove(o){if(await e.$ok.shared.modal.confirm(`Remove job type "${o.id}"? Existing jobs are unaffected.`))try{if(!(await fetch(t.apiBase+"/queue/job-type/"+encodeURIComponent(o.id),{method:"DELETE"})).ok)throw new Error("failed");await this.load()}catch{e.$ok.shared?.toast?.error?.("Failed to remove job type")}}}),async mount(e){await this.load(),e.$watch(()=>t.selectedEnv,()=>{this.types=[],this.load()})},template:'\n<div style="display:flex; flex-direction:column; gap:16px; padding:16px 0;">\n\n \x3c!-- header --\x3e\n <div style="display:flex; align-items:center; gap:10px;">\n <div style="font-size:14px; font-weight:600; flex:1;">Registered Job Types</div>\n <button @click="load" :disabled="busy" style="opacity:.6; font-size:11px; padding:2px 8px;">↻</button>\n <button @click="openCreate()" style="font-size:12px; padding:4px 12px;">+ Register type</button>\n </div>\n\n \x3c!-- error state --\x3e\n <if :="loadError">\n <div style="color:#ef4444; font-size:12px; padding:8px 12px; border:1px solid #ef4444; border-radius:6px; background:color-mix(in srgb, #ef4444 8%, transparent);">\n Failed to load job types: {{ loadError }}\n </div>\n </if>\n\n \x3c!-- empty state --\x3e\n <if :="!busy && !loadError && types.length === 0">\n <div style="opacity:.45; font-size:13px; line-height:1.8; padding:32px 0; text-align:center;">\n No registered job types.<br>\n <span style="font-size:12px;">Register a type to set defaults, control concurrency, and pause/resume claiming.</span>\n </div>\n </if>\n\n \x3c!-- types list --\x3e\n <div style="display:flex; flex-direction:column; gap:8px;">\n <each :of="types" let:item="jt">\n <div class="jt-card">\n <div style="display:flex; align-items:center; gap:10px; flex-wrap:wrap;">\n <span style="font-size:13px; font-weight:600; flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ jt.id }}</span>\n <span class="status-badge" :style="\'border-color:\' + statusColor(jt.status) + \'; color:\' + statusColor(jt.status)">\n {{ statusLabel(jt.status) }}\n </span>\n <if :="jt.payload_schema">\n <span class="status-badge" style="border-color:var(--okdb-accent); color:var(--okdb-accent);">schema</span>\n <if :="jt.payload_schema.enforce">\n <span class="status-badge" style="border-color:#ef4444; color:#ef4444;">enforced</span>\n </if>\n </if>\n \x3c!-- lifecycle actions --\x3e\n <if :="jt.status !== \'paused\'">\n <button class="act-btn" @click="setStatus(jt, \'pause\')" title="Pause — stop workers from claiming new jobs">⏸ Pause</button>\n </if>\n <if :="jt.status === \'paused\'">\n <button class="act-btn act-btn--accent" @click="setStatus(jt, \'resume\')" title="Resume normal claiming">▶ Resume</button>\n </if>\n <if :="jt.status !== \'draining\'">\n <button class="act-btn" @click="setStatus(jt, \'drain\')" title="Drain — stop new enqueues, let queue empty">⬇ Drain</button>\n </if>\n <if :="jt.status === \'draining\'">\n <button class="act-btn act-btn--accent" @click="setStatus(jt, \'resume\')">▶ Resume</button>\n </if>\n <button class="act-btn act-btn--enqueue" @click="openEnqueue(jt)">+ Enqueue</button>\n <button class="act-btn" @click="openEdit(jt)">Edit</button>\n <button class="act-btn act-btn--danger" @click="remove(jt)">Remove</button>\n </div>\n <div style="font-size:11px; opacity:.55; margin-top:4px; font-family:monospace;">{{ formatDefaults(jt) }}</div>\n </div>\n </each>\n </div>\n\n</div>\n',style:".jt-card{border:1px solid var(--okdb-app-border);border-radius:8px;padding:10px 14px;background:var(--okdb-app-surface)}.status-badge{font-size:10px;padding:2px 8px;border-radius:999px;border:1px solid;font-weight:600;letter-spacing:.04em}.act-btn{font-size:11px;padding:2px 8px;border-radius:5px;border:1px solid var(--okdb-app-border);background:0 0;cursor:pointer;color:var(--okdb-app-fg-muted)}.act-btn:hover{border-color:var(--okdb-app-fg-muted);color:var(--okdb-app-fg)}.act-btn--accent,.act-btn--enqueue{border-color:color-mix(in srgb,var(--okdb-accent) 40%,transparent);color:var(--okdb-accent)}.act-btn--danger:hover{border-color:#ef4444;color:#ef4444}.act-btn--enqueue{border-color:color-mix(in srgb,var(--okdb-accent) 30%,transparent)}.act-btn--enqueue:hover{border-color:var(--okdb-accent)}"};
@@ -1 +1 @@
1
- const _0x5013c1=_0x3ae3;(function(_0x57c268,_0x1fd176){const _0x54017a=_0x3ae3,_0x5edff8=_0x57c268();while(!![]){try{const _0x5bbbdb=-parseInt(_0x54017a(0xd4))/0x1*(-parseInt(_0x54017a(0xbe))/0x2)+-parseInt(_0x54017a(0x111))/0x3*(parseInt(_0x54017a(0xdf))/0x4)+-parseInt(_0x54017a(0xf2))/0x5+-parseInt(_0x54017a(0xdc))/0x6+parseInt(_0x54017a(0x123))/0x7+parseInt(_0x54017a(0xa7))/0x8+parseInt(_0x54017a(0xe5))/0x9;if(_0x5bbbdb===_0x1fd176)break;else _0x5edff8['push'](_0x5edff8['shift']());}catch(_0x4adf19){_0x5edff8['push'](_0x5edff8['shift']());}}}(_0xe319,0x87fa2));import _0x5a46e5 from'@toolkit/elements/ok-icon.ok.js';import _0x1309db from'@toolkit/elements/ok-table.ok.js';import _0x488880 from'@toolkit/core/ok-overlay.ok.js';function _0x3ae3(_0x1372c2,_0x51ed58){_0x1372c2=_0x1372c2-0xa1;const _0xe3196e=_0xe319();let _0x3ae39d=_0xe3196e[_0x1372c2];return _0x3ae39d;}function _0xe319(){const _0x37de54=['total','Clear\x20all\x20\x22done\x22\x20jobs','bulkBusy','in\x20a\x20moment','h\x20ago','_nextCursor','var(--okdb-success)','indexOf','cursor','_busy','resetPaging','busy','function','modal','Edit\x20Job\x20','isArray','byType','sortMode','queue:failed','cancel\x20failed','/remove','confirm','52wkfQHb','POST','Delete\x20failed','/queue/job/','Clear\x20failed\x20jobs\x20error:\x20','\x20stuck\x20jobs','values','/queue/clearDone','application/json','items','push','queue:removed','toast','now','_scheduleLiveReload','$watch','<edit-job-modal\x20:modal=\x22modal\x22\x20:job-id=\x22jobId\x22\x20:on-done=\x22onDone\x22\x20/>','Cancel\x20failed','\x20done\x20jobs','open','set','/cancel','35942duwqRt','Cancel\x20job\x20<code>','/queue/stats','sort','hasNext','Cancelled\x20','pageSize','hasPrev','3537168MDHYle','info','_unsubs','12StNkYi','abs','Delete\x20all\x20\x22failed\x22\x20jobs','Delete\x20all\x20stuck\x20(expired-claim)\x20running\x20jobs','$dispatch','/queue/retryFailed','5656023hHybRA','$ok','byStatus','_applyFilterAndReload','_cursors','Deleted\x20','asc',';\x20border-color:','Retry\x20failed:\x20','slice','deleted','Cleared\x20','load','1513060LPocRH','queue:enqueued','Clear\x20stuck\x20failed:\x20','Clear\x20done\x20failed:\x20','apiBase','$attr','result','round','Bulk\x20retry\x20failed:\x20','loadError','stats','retry\x20failed','</code>?','type','Retry\x20job\x20<code>','Delete\x20job\x20<code>','data','stores','Retried\x20','HTTP\x20','refresh','success','when','json','all','in\x20','direction','?<br><br>Stuck\x20jobs\x20are\x20running\x20jobs\x20whose\x20TTL\x20has\x20expired.\x20They\x20will\x20be\x20permanently\x20deleted,\x20not\x20re-queued.','_applySort','var(--okdb-danger)','message','996516gEMcwk','/queue/clearFailed','error','\x20for\x20type\x20\x22<b>','shared','d\x20ago','color:','loadStats','stringify','.pill{display:inline-flex;align-items:center;gap:5px;border:1px\x20solid\x20var(--okdb-app-border);background:var(--okdb-app-surface);padding:4px\x2010px;border-radius:999px;font-size:12px;cursor:pointer;color:var(--okdb-app-fg-muted);transition:border-color\x20.12s,color\x20.12s}.pill:hover{border-color:#3b4754;color:var(--okdb-app-fg)}.pill--on{box-shadow:0\x200\x200\x201px\x20rgba(255,255,255,.07)\x20inset}.pill--p.pill--on{color:var(--okdb-warning);border-color:var(--okdb-warning)}.pill--r.pill--on{color:var(--okdb-info);border-color:var(--okdb-info)}.pill--f.pill--on{color:var(--okdb-danger);border-color:var(--okdb-danger)}.pill--d.pill--on{color:var(--okdb-success);border-color:var(--okdb-success)}.pill-count{font-size:11px;opacity:.65;font-variant-numeric:tabular-nums}.status-badge{display:inline-block;padding:2px\x208px;border-radius:999px;font-size:11px;border:1px\x20solid;white-space:nowrap}','</b>\x22','_liveTimer','length','?<br><br>This\x20cannot\x20be\x20undone.','selectedEnv','queue-jobs','status','_pageIdx','1979166lFFtKV','pendingUpdates','_scheduleStats','/queue/reconcile','_statsTimer','queue:reconciled','failed','\x20failed\x20jobs','/queue/jobs?','query','retried','4799528VhQgdX'];_0xe319=function(){return _0x37de54;};return _0xe319();}import _0x46c9d5 from'../modals/edit-job-modal.ok.js';import _0x44b236 from'../modals/enqueue-job-modal.ok.js';import _0x3d44de from'../../../stores/events-store.js';import _0x3ebfbd from'../../../stores/okdb-store.js';export default{'tag':_0x5013c1(0x120),'dependencies':[_0x5a46e5,_0x1309db,_0x488880,_0x44b236,_0x46c9d5],'attr':{'type':{}},'context':_0x4b5458=>({'query':function({apiBase:_0x3b7d08,pageSize:_0x46d758=0x32}){const _0x2a2866=_0x5013c1;return{'busy':!0x1,'values':[],'total':0x0,'hasNext':!0x1,'hasPrev':!0x1,'pageSize':_0x46d758,'loadError':null,'type':null,'status':null,'sort':_0x2a2866(0x108),'direction':_0x2a2866(0xeb),'_cursors':[null],'_pageIdx':0x0,'_nextCursor':null,'resetPaging'(){const _0x377358=_0x2a2866;this[_0x377358(0xe9)]=[null],this['_pageIdx']=0x0,this['_nextCursor']=null,this[_0x377358(0xd8)]=!0x1,this[_0x377358(0xdb)]=!0x1;},async 'load'(){const _0x4af9f5=_0x2a2866,_0x21c499=_0x4af9f5(0xb4)==typeof _0x3b7d08?_0x3b7d08():_0x3b7d08,_0x2d8178=this[_0x4af9f5(0xe9)][this['_pageIdx']];this['busy']=!0x0,this['loadError']=null;try{const _0x2bc91a=new URLSearchParams({'limit':String(this[_0x4af9f5(0xda)]),'sort':this['sort']??_0x4af9f5(0x108),'direction':this[_0x4af9f5(0x10c)]??'asc'});this[_0x4af9f5(0xff)]&&_0x2bc91a['set'](_0x4af9f5(0xff),this['type']),this['status']&&_0x2bc91a['set'](_0x4af9f5(0x121),this[_0x4af9f5(0x121)]),null!=_0x2d8178&&_0x2bc91a[_0x4af9f5(0xd2)](_0x4af9f5(0xb0),JSON[_0x4af9f5(0x119)](_0x2d8178));const _0x3cead8=await fetch(_0x21c499+_0x4af9f5(0xa4)+_0x2bc91a),_0x27cfdb=await _0x3cead8[_0x4af9f5(0x109)]();if(!_0x3cead8['ok'])throw new Error(_0x27cfdb?.[_0x4af9f5(0x113)]?.['message']||_0x4af9f5(0x105)+_0x3cead8[_0x4af9f5(0x121)]);const _0x248a69=_0x27cfdb[_0x4af9f5(0xf8)]??_0x27cfdb[_0x4af9f5(0x102)]??_0x27cfdb;this[_0x4af9f5(0xc4)]=Array[_0x4af9f5(0xb7)](_0x248a69[_0x4af9f5(0xc7)])?_0x248a69[_0x4af9f5(0xc7)]:Array[_0x4af9f5(0xb7)](_0x248a69)?_0x248a69:[],this[_0x4af9f5(0xad)]=_0x248a69[_0x4af9f5(0xb0)]??null,this[_0x4af9f5(0xd8)]=null!=this['_nextCursor'],this[_0x4af9f5(0xdb)]=this[_0x4af9f5(0x122)]>0x0;}catch(_0x23fa8b){this[_0x4af9f5(0xfb)]=_0x23fa8b?.[_0x4af9f5(0x110)]||String(_0x23fa8b),this['values']=[];}finally{this[_0x4af9f5(0xb3)]=!0x1;}},async 'nextPage'(){const _0x2f6c42=_0x2a2866;this[_0x2f6c42(0xd8)]&&!this[_0x2f6c42(0xb3)]&&(this['_pageIdx']+0x1>=this[_0x2f6c42(0xe9)][_0x2f6c42(0x11d)]&&this['_cursors'][_0x2f6c42(0xc8)](this[_0x2f6c42(0xad)]),this[_0x2f6c42(0x122)]++,await this[_0x2f6c42(0xf1)]());},async 'prevPage'(){const _0x3d32be=_0x2a2866;this[_0x3d32be(0xdb)]&&!this[_0x3d32be(0xb3)]&&(this[_0x3d32be(0x122)]--,await this[_0x3d32be(0xf1)]());}};}({'apiBase':()=>_0x3ebfbd[_0x5013c1(0xf6)],'pageSize':0x32}),'type':null,'status':null,'sortMode':'when','direction':'asc','stats':null,'_statsTimer':null,async 'loadStats'(){const _0x4bea28=_0x5013c1;try{const _0x1b6366=await fetch(_0x3ebfbd[_0x4bea28(0xf6)]+_0x4bea28(0xd6));if(_0x1b6366['ok']){const _0x409843=await _0x1b6366[_0x4bea28(0x109)]();this[_0x4bea28(0xfc)]=_0x409843['result']??_0x409843['data']??null;}}catch{}},'_scheduleStats'(){const _0x27fcc4=_0x5013c1;clearTimeout(this[_0x27fcc4(0x127)]),this[_0x27fcc4(0x127)]=setTimeout(()=>this[_0x27fcc4(0x118)](),0x190);},'statusCount'(_0x165ede){const _0x36f972=_0x5013c1;if(!this[_0x36f972(0xfc)])return'';const _0x457258=this[_0x36f972(0xff)];return _0x165ede?_0x457258?this[_0x36f972(0xfc)][_0x36f972(0xb8)]?.[_0x457258]?.[_0x36f972(0xe7)]?.[_0x165ede]??0x0:this[_0x36f972(0xfc)]['byStatus']?.[_0x165ede]??0x0:_0x457258?this[_0x36f972(0xfc)][_0x36f972(0xb8)]?.[_0x457258]?.[_0x36f972(0xa8)]??0x0:this[_0x36f972(0xfc)][_0x36f972(0xa8)]??0x0;},'applyTypeAttr'(){const _0xe33325=_0x5013c1;var _0x4ed2fa;this[_0xe33325(0xff)]=null==(_0x4ed2fa=_0x4b5458[_0xe33325(0xf7)][_0xe33325(0xff)])||''===_0x4ed2fa?null:String(_0x4ed2fa),this[_0xe33325(0xe8)]();},'setStatus'(_0x32c5d2){const _0x14bc44=_0x5013c1;this[_0x14bc44(0x121)]=null==_0x32c5d2||''===_0x32c5d2?null:_0x32c5d2,this['_applyFilterAndReload']();},'setSort'(_0x27476b){const _0x1740e1=_0x5013c1;this['sortMode']=_0x27476b,this[_0x1740e1(0x10e)](),this[_0x1740e1(0xa5)][_0x1740e1(0xb2)](),this[_0x1740e1(0xa5)][_0x1740e1(0xf1)]();},'toggleDirection'(){const _0x46edf1=_0x5013c1;this[_0x46edf1(0x10c)]=_0x46edf1(0xeb)===this[_0x46edf1(0x10c)]?'desc':_0x46edf1(0xeb),this[_0x46edf1(0x10e)](),this[_0x46edf1(0xa5)][_0x46edf1(0xb2)](),this[_0x46edf1(0xa5)][_0x46edf1(0xf1)]();},'_applySort'(){const _0x126b0c=_0x5013c1;this[_0x126b0c(0xa5)][_0x126b0c(0xd7)]=this[_0x126b0c(0xb9)],this[_0x126b0c(0xa5)]['direction']=this[_0x126b0c(0x10c)];},'_applyFilterAndReload'(){const _0x5d7519=_0x5013c1;this[_0x5d7519(0xa5)][_0x5d7519(0xff)]=this['type'],this['query'][_0x5d7519(0x121)]=this[_0x5d7519(0x121)],this[_0x5d7519(0x10e)](),this[_0x5d7519(0xa5)][_0x5d7519(0xb2)](),this[_0x5d7519(0xa5)]['load']();},async 'refresh'(){const _0x13f519=_0x5013c1;clearTimeout(this[_0x13f519(0x11c)]),this[_0x13f519(0x124)]=0x0;try{await fetch(_0x3ebfbd[_0x13f519(0xf6)]+_0x13f519(0x126),{'method':'POST'});}catch{}await this['query'][_0x13f519(0xf1)](),await this[_0x13f519(0x118)](),_0x4b5458[_0x13f519(0xe3)](_0x13f519(0x106));},async 'rebuildStats'(){const _0x1fcd8c=_0x5013c1;try{await fetch(_0x3ebfbd[_0x1fcd8c(0xf6)]+'/queue/stats/rebuild',{'method':_0x1fcd8c(0xbf)}),_0x4b5458['$ok'][_0x1fcd8c(0x115)]?.[_0x1fcd8c(0xca)]?.['success']?.('Stats\x20rebuilt'),await this[_0x1fcd8c(0x118)](),_0x4b5458[_0x1fcd8c(0xe3)](_0x1fcd8c(0x106)),await this[_0x1fcd8c(0xa5)][_0x1fcd8c(0xf1)]();}catch{_0x4b5458[_0x1fcd8c(0xe6)]['shared']?.['toast']?.[_0x1fcd8c(0x113)]?.('Stats\x20rebuild\x20failed');}},async 'openEnqueue'(){const _0x4329f7=_0x5013c1,_0x5381bb=this;await _0x4b5458['$ok'][_0x4329f7(0x115)][_0x4329f7(0xb5)][_0x4329f7(0xd1)]({'owner':_0x4b5458,'title':'Enqueue\x20Job','content':'<enqueue-job-modal\x20:modal=\x22modal\x22\x20:initial-type=\x22initialType\x22\x20:on-done=\x22onDone\x22\x20/>','context':_0xedb295=>({'modal':_0xedb295,'initialType':_0x5381bb[_0x4329f7(0xff)]||'','onDone':async()=>{const _0x28e2bf=_0x4329f7;_0x4b5458[_0x28e2bf(0xe6)]['shared']?.[_0x28e2bf(0xca)]?.[_0x28e2bf(0x107)]?.('Job\x20enqueued'),await _0x5381bb['refresh']();}})});},async 'openEditJob'(_0x3a83ab){const _0x8a56dc=_0x5013c1,_0x4abd44=this;await _0x4b5458[_0x8a56dc(0xe6)]['shared']['modal'][_0x8a56dc(0xd1)]({'owner':_0x4b5458,'title':_0x8a56dc(0xb6)+_0x3a83ab['id'][_0x8a56dc(0xee)](0x0,0x8),'content':_0x8a56dc(0xce),'context':_0x2140f4=>({'modal':_0x2140f4,'jobId':_0x3a83ab['id'],'onDone':async()=>{const _0x29e3e6=_0x8a56dc;await _0x4abd44[_0x29e3e6(0x106)]();}})});},async 'deleteJob'(_0x498ad1){const _0x2520f9=_0x5013c1;if(await _0x4b5458[_0x2520f9(0xe6)][_0x2520f9(0x115)][_0x2520f9(0xb5)]['confirm'](_0x2520f9(0x101)+_0x498ad1['id'][_0x2520f9(0xee)](0x0,0x8)+_0x2520f9(0xfe))){_0x498ad1[_0x2520f9(0xb1)]=!0x0;try{if(!(await fetch(_0x3ebfbd[_0x2520f9(0xf6)]+_0x2520f9(0xc1)+_0x498ad1['id']+_0x2520f9(0xbc),{'method':'POST'}))['ok'])throw new Error('remove\x20failed');const _0x3592ef=this[_0x2520f9(0xa5)][_0x2520f9(0xc4)][_0x2520f9(0xaf)](_0x498ad1);_0x3592ef>=0x0&&this[_0x2520f9(0xa5)][_0x2520f9(0xc4)]['splice'](_0x3592ef,0x1),_0x4b5458['$ok'][_0x2520f9(0x115)][_0x2520f9(0xca)]?.['info'](_0x2520f9(0xea)+_0x498ad1['id'][_0x2520f9(0xee)](0x0,0x8)),_0x4b5458[_0x2520f9(0xe3)](_0x2520f9(0x106)),this[_0x2520f9(0x125)]();}catch{_0x4b5458[_0x2520f9(0xe6)]['shared'][_0x2520f9(0xca)]?.[_0x2520f9(0x113)](_0x2520f9(0xc0));}finally{_0x498ad1['_busy']=!0x1;}}},async 'cancelJob'(_0x5bd670){const _0x46615d=_0x5013c1;if(await _0x4b5458[_0x46615d(0xe6)][_0x46615d(0x115)]['modal'][_0x46615d(0xbd)](_0x46615d(0xd5)+_0x5bd670['id'][_0x46615d(0xee)](0x0,0x8)+_0x46615d(0xfe))){_0x5bd670[_0x46615d(0xb1)]=!0x0;try{if(!(await fetch(_0x3ebfbd[_0x46615d(0xf6)]+_0x46615d(0xc1)+_0x5bd670['id']+_0x46615d(0xd3),{'method':_0x46615d(0xbf)}))['ok'])throw new Error(_0x46615d(0xbb));_0x4b5458[_0x46615d(0xe6)][_0x46615d(0x115)][_0x46615d(0xca)]?.[_0x46615d(0xdd)](_0x46615d(0xd9)+_0x5bd670['id']['slice'](0x0,0x8)),await this[_0x46615d(0x106)]();}catch{_0x4b5458[_0x46615d(0xe6)][_0x46615d(0x115)]['toast']?.['error'](_0x46615d(0xcf));}finally{_0x5bd670[_0x46615d(0xb1)]=!0x1;}}},async 'retryJob'(_0x2f99ed){const _0x15aecb=_0x5013c1;if(await _0x4b5458[_0x15aecb(0xe6)][_0x15aecb(0x115)][_0x15aecb(0xb5)][_0x15aecb(0xbd)](_0x15aecb(0x100)+_0x2f99ed['id'][_0x15aecb(0xee)](0x0,0x8)+_0x15aecb(0xfe))){_0x2f99ed[_0x15aecb(0xb1)]=!0x0;try{const _0x5435ce=await fetch(_0x3ebfbd[_0x15aecb(0xf6)]+'/queue/job/'+_0x2f99ed['id']+'/retry',{'method':_0x15aecb(0xbf)}),_0x4fe620=await _0x5435ce[_0x15aecb(0x109)]();if(!_0x5435ce['ok'])throw new Error(_0x4fe620?.['error']?.['message']||_0x15aecb(0xfd));_0x4b5458[_0x15aecb(0xe6)]['shared'][_0x15aecb(0xca)]?.[_0x15aecb(0x107)](_0x15aecb(0x104)+_0x2f99ed['id']['slice'](0x0,0x8)),await this[_0x15aecb(0x106)]();}catch(_0x5849dc){_0x4b5458[_0x15aecb(0xe6)][_0x15aecb(0x115)][_0x15aecb(0xca)]?.['error'](_0x15aecb(0xed)+(_0x5849dc?.[_0x15aecb(0x110)]||_0x5849dc));}finally{_0x2f99ed[_0x15aecb(0xb1)]=!0x1;}}},'bulkBusy':!0x1,async 'retryAllFailed'(){const _0x2eee39=_0x5013c1;if(await _0x4b5458[_0x2eee39(0xe6)]['shared']['modal'][_0x2eee39(0xbd)]('Retry\x20all\x20failed\x20jobs'+(this[_0x2eee39(0xff)]?'\x20for\x20type\x20\x22<b>'+this[_0x2eee39(0xff)]+_0x2eee39(0x11b):'')+'?')){this[_0x2eee39(0xaa)]=!0x0;try{const _0x5c08f6={'limit':0x1388};this[_0x2eee39(0xff)]&&(_0x5c08f6[_0x2eee39(0xff)]=this['type']);const _0x1c5ae6=await fetch(_0x3ebfbd[_0x2eee39(0xf6)]+_0x2eee39(0xe4),{'method':_0x2eee39(0xbf),'headers':{'Content-Type':'application/json'},'body':JSON['stringify'](_0x5c08f6)}),_0x400553=await _0x1c5ae6[_0x2eee39(0x109)]();if(!_0x1c5ae6['ok'])throw new Error(_0x400553?.[_0x2eee39(0x113)]?.[_0x2eee39(0x110)]||_0x2eee39(0xa2));_0x4b5458[_0x2eee39(0xe6)][_0x2eee39(0x115)]?.[_0x2eee39(0xca)]?.[_0x2eee39(0x107)]?.(_0x2eee39(0x104)+_0x400553[_0x2eee39(0x102)][_0x2eee39(0xa6)]+_0x2eee39(0xa3)),await this[_0x2eee39(0x106)]();}catch(_0x2fc06e){_0x4b5458[_0x2eee39(0xe6)][_0x2eee39(0x115)]?.[_0x2eee39(0xca)]?.[_0x2eee39(0x113)]?.(_0x2eee39(0xfa)+(_0x2fc06e?.[_0x2eee39(0x110)]||_0x2fc06e));}finally{this['bulkBusy']=!0x1;}}},async 'deleteAllDone'(){const _0x17c1fa=_0x5013c1;if(await _0x4b5458[_0x17c1fa(0xe6)]['shared'][_0x17c1fa(0xb5)]['confirm'](_0x17c1fa(0xa9)+(this[_0x17c1fa(0xff)]?_0x17c1fa(0x114)+this[_0x17c1fa(0xff)]+_0x17c1fa(0x11b):'')+_0x17c1fa(0x11e))){this[_0x17c1fa(0xaa)]=!0x0;try{const _0x2087bf={'limit':0xc350};this[_0x17c1fa(0xff)]&&(_0x2087bf['type']=this[_0x17c1fa(0xff)]);const _0x5ba5f1=await fetch(_0x3ebfbd[_0x17c1fa(0xf6)]+_0x17c1fa(0xc5),{'method':'POST','headers':{'Content-Type':_0x17c1fa(0xc6)},'body':JSON[_0x17c1fa(0x119)](_0x2087bf)}),_0x509b90=await _0x5ba5f1[_0x17c1fa(0x109)]();if(!_0x5ba5f1['ok'])throw new Error(_0x509b90?.[_0x17c1fa(0x113)]?.[_0x17c1fa(0x110)]||'failed');_0x4b5458[_0x17c1fa(0xe6)][_0x17c1fa(0x115)]?.[_0x17c1fa(0xca)]?.[_0x17c1fa(0xdd)]?.(_0x17c1fa(0xf0)+_0x509b90[_0x17c1fa(0x102)][_0x17c1fa(0xef)]+_0x17c1fa(0xd0)),await this[_0x17c1fa(0x106)]();}catch(_0x20ca6c){_0x4b5458[_0x17c1fa(0xe6)][_0x17c1fa(0x115)]?.[_0x17c1fa(0xca)]?.[_0x17c1fa(0x113)]?.(_0x17c1fa(0xf5)+(_0x20ca6c?.[_0x17c1fa(0x110)]||_0x20ca6c));}finally{this[_0x17c1fa(0xaa)]=!0x1;}}},async 'deleteAllFailed'(){const _0x3e31d0=_0x5013c1;if(await _0x4b5458[_0x3e31d0(0xe6)][_0x3e31d0(0x115)][_0x3e31d0(0xb5)]['confirm'](_0x3e31d0(0xe1)+(this[_0x3e31d0(0xff)]?_0x3e31d0(0x114)+this[_0x3e31d0(0xff)]+_0x3e31d0(0x11b):'')+'?<br><br>This\x20cannot\x20be\x20undone.')){this[_0x3e31d0(0xaa)]=!0x0;try{const _0x80ef83={'limit':0xc350};this[_0x3e31d0(0xff)]&&(_0x80ef83[_0x3e31d0(0xff)]=this[_0x3e31d0(0xff)]);const _0x3f21d2=await fetch(_0x3ebfbd[_0x3e31d0(0xf6)]+_0x3e31d0(0x112),{'method':_0x3e31d0(0xbf),'headers':{'Content-Type':'application/json'},'body':JSON['stringify'](_0x80ef83)}),_0x15a26d=await _0x3f21d2['json']();if(!_0x3f21d2['ok'])throw new Error(_0x15a26d?.[_0x3e31d0(0x113)]?.[_0x3e31d0(0x110)]||_0x3e31d0(0xa2));_0x4b5458[_0x3e31d0(0xe6)][_0x3e31d0(0x115)]?.['toast']?.[_0x3e31d0(0xdd)]?.('Cleared\x20'+_0x15a26d['data'][_0x3e31d0(0xef)]+_0x3e31d0(0xa3)),await this['refresh']();}catch(_0x491b1e){_0x4b5458[_0x3e31d0(0xe6)][_0x3e31d0(0x115)]?.[_0x3e31d0(0xca)]?.[_0x3e31d0(0x113)]?.(_0x3e31d0(0xc2)+(_0x491b1e?.[_0x3e31d0(0x110)]||_0x491b1e));}finally{this[_0x3e31d0(0xaa)]=!0x1;}}},async 'clearStuck'(){const _0x4bd46f=_0x5013c1;if(await _0x4b5458[_0x4bd46f(0xe6)][_0x4bd46f(0x115)]['modal'][_0x4bd46f(0xbd)](_0x4bd46f(0xe2)+(this['type']?'\x20for\x20type\x20\x22<b>'+this[_0x4bd46f(0xff)]+'</b>\x22':'')+_0x4bd46f(0x10d))){this[_0x4bd46f(0xaa)]=!0x0;try{const _0x3a32db={'limit':0xc350};this[_0x4bd46f(0xff)]&&(_0x3a32db['type']=this[_0x4bd46f(0xff)]);const _0x1cbf31=await fetch(_0x3ebfbd['apiBase']+'/queue/clearStuck',{'method':'POST','headers':{'Content-Type':'application/json'},'body':JSON[_0x4bd46f(0x119)](_0x3a32db)}),_0x24745e=await _0x1cbf31[_0x4bd46f(0x109)]();if(!_0x1cbf31['ok'])throw new Error(_0x24745e?.[_0x4bd46f(0x113)]?.['message']||_0x4bd46f(0xa2));_0x4b5458[_0x4bd46f(0xe6)]['shared']?.[_0x4bd46f(0xca)]?.[_0x4bd46f(0xdd)]?.(_0x4bd46f(0xf0)+_0x24745e[_0x4bd46f(0x102)]['deleted']+_0x4bd46f(0xc3)),await this[_0x4bd46f(0x106)]();}catch(_0x4dbabb){_0x4b5458[_0x4bd46f(0xe6)]['shared']?.[_0x4bd46f(0xca)]?.[_0x4bd46f(0x113)]?.(_0x4bd46f(0xf4)+(_0x4dbabb?.[_0x4bd46f(0x110)]||_0x4dbabb));}finally{this[_0x4bd46f(0xaa)]=!0x1;}}},'isStatusOn'(_0x33c682){const _0x499bab=_0x5013c1;return(this[_0x499bab(0x121)]??null)===(_0x33c682??null);},'statusBadgeStyle'(_0x44186f){const _0x20563d=_0x5013c1,_0x1d314a={'pending':'var(--okdb-warning)','running':'var(--okdb-info)','done':_0x20563d(0xae),'failed':_0x20563d(0x10f)}[_0x44186f]||'#9ca3af';return _0x20563d(0x117)+_0x1d314a+_0x20563d(0xec)+_0x1d314a+';';},'formatWhen'(_0x12ccde){const _0x5a29a6=_0x5013c1;if(!_0x12ccde)return'—';const _0xb2369c=_0x12ccde-Date[_0x5a29a6(0xcb)](),_0x49a7a2=Math[_0x5a29a6(0xe0)](_0xb2369c),_0x402ee9=Math[_0x5a29a6(0xf9)](_0x49a7a2/0x3e8);if(_0x402ee9<0x5)return _0xb2369c<=0x0?_0x5a29a6(0xcb):_0x5a29a6(0xab);if(_0x402ee9<0x3c)return _0xb2369c<0x0?_0x402ee9+'s\x20ago':_0x5a29a6(0x10b)+_0x402ee9+'s';const _0x551521=Math[_0x5a29a6(0xf9)](_0x402ee9/0x3c);if(_0x551521<0x3c)return _0xb2369c<0x0?_0x551521+'m\x20ago':'in\x20'+_0x551521+'m';const _0x33d55b=Math['round'](_0x551521/0x3c);if(_0x33d55b<0x18)return _0xb2369c<0x0?_0x33d55b+_0x5a29a6(0xac):_0x5a29a6(0x10b)+_0x33d55b+'h';const _0x5798b1=Math[_0x5a29a6(0xf9)](_0x33d55b/0x18);return _0xb2369c<0x0?_0x5798b1+_0x5a29a6(0x116):_0x5a29a6(0x10b)+_0x5798b1+'d';},'shortId':_0x372b00=>(_0x372b00||'')[_0x5013c1(0xee)](0x0,0x8),'_unsubs':[],'_liveTimer':null,'pendingUpdates':0x0,'_scheduleLiveReload'(){const _0x29437b=_0x5013c1;this[_0x29437b(0x124)]++,clearTimeout(this[_0x29437b(0x11c)]),this[_0x29437b(0x11c)]=setTimeout(async()=>{const _0x3435cb=_0x29437b;await this['query'][_0x3435cb(0xf1)](),await this['loadStats'](),_0x4b5458[_0x3435cb(0xe3)](_0x3435cb(0x106)),this[_0x3435cb(0x124)]=0x0;},0xbb8);}}),async 'init'(_0x79138a){const _0x345b8d=_0x5013c1;this['applyTypeAttr'](),_0x79138a[_0x345b8d(0xcd)](()=>_0x79138a[_0x345b8d(0xf7)][_0x345b8d(0xff)],()=>this['applyTypeAttr']()),_0x79138a[_0x345b8d(0xcd)](()=>_0x79138a[_0x345b8d(0xe6)][_0x345b8d(0x103)]['okdb'][_0x345b8d(0x11f)],()=>{const _0x1573f3=_0x345b8d;this['stats']=null,this[_0x1573f3(0xa5)][_0x1573f3(0xb2)](),Promise[_0x1573f3(0x10a)]([this[_0x1573f3(0xa5)][_0x1573f3(0xf1)](),this[_0x1573f3(0x118)]()]);}),await Promise[_0x345b8d(0x10a)]([this['query'][_0x345b8d(0xf1)](),this[_0x345b8d(0x118)]()]);},'mount'(){const _0x2f8bfc=_0x5013c1,_0x2bce0d=()=>this[_0x2f8bfc(0xcc)]();this[_0x2f8bfc(0xde)]=[_0x3d44de['on'](_0x2f8bfc(0xf3),_0x2bce0d),_0x3d44de['on']('queue:done',_0x2bce0d),_0x3d44de['on'](_0x2f8bfc(0xba),_0x2bce0d),_0x3d44de['on']('queue:retry',_0x2bce0d),_0x3d44de['on'](_0x2f8bfc(0xc9),_0x2bce0d),_0x3d44de['on'](_0x2f8bfc(0xa1),_0x2bce0d)];},'unmount'(){const _0x11b592=_0x5013c1;clearTimeout(this[_0x11b592(0x11c)]),clearTimeout(this['_statsTimer']);for(const _0x278bd9 of this[_0x11b592(0xde)])_0x278bd9();this[_0x11b592(0xde)]=[];},'template':'\x0a<div\x20style=\x22display:flex;\x20flex-direction:column;\x20height:100%;\x20min-height:0;\x20gap:10px;\x22>\x0a\x0a\x20\x20<!--\x20──\x20Row\x201:\x20Status\x20filters\x20+\x20Enqueue\x20──\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:6px;\x20align-items:center;\x20flex-wrap:wrap;\x22>\x0a\x20\x20\x20\x20<button\x20@click=\x22setStatus(null)\x22\x20\x20\x20\x20\x20\x20:class=\x22isStatusOn(null)\x20\x20\x20\x20\x20\x20?\x20\x27pill\x20pill--on\x27\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:\x20\x27pill\x27\x22>All<span\x20class=\x22pill-count\x22>{{\x20statusCount(null)\x20}}</span></button>\x0a\x20\x20\x20\x20<button\x20@click=\x22setStatus(\x27pending\x27)\x22\x20:class=\x22isStatusOn(\x27pending\x27)\x20?\x20\x27pill\x20pill--on\x20pill--p\x27\x20\x20\x20:\x20\x27pill\x20pill--p\x27\x22>Pending<span\x20class=\x22pill-count\x22>{{\x20statusCount(\x27pending\x27)\x20}}</span></button>\x0a\x20\x20\x20\x20<button\x20@click=\x22setStatus(\x27running\x27)\x22\x20:class=\x22isStatusOn(\x27running\x27)\x20?\x20\x27pill\x20pill--on\x20pill--r\x27\x20\x20\x20:\x20\x27pill\x20pill--r\x27\x22>Running<span\x20class=\x22pill-count\x22>{{\x20statusCount(\x27running\x27)\x20}}</span></button>\x0a\x20\x20\x20\x20<button\x20@click=\x22setStatus(\x27failed\x27)\x22\x20\x20:class=\x22isStatusOn(\x27failed\x27)\x20\x20?\x20\x27pill\x20pill--on\x20pill--f\x27\x20\x20\x20:\x20\x27pill\x20pill--f\x27\x22>Failed<span\x20\x20class=\x22pill-count\x22>{{\x20statusCount(\x27failed\x27)\x20}}</span></button>\x0a\x20\x20\x20\x20<button\x20@click=\x22setStatus(\x27done\x27)\x22\x20\x20\x20\x20:class=\x22isStatusOn(\x27done\x27)\x20\x20\x20\x20?\x20\x27pill\x20pill--on\x20pill--d\x27\x20\x20\x20:\x20\x27pill\x20pill--d\x27\x22>Done<span\x20\x20\x20\x20class=\x22pill-count\x22>{{\x20statusCount(\x27done\x27)\x20}}</span></button>\x0a\x0a\x20\x20\x20\x20<div\x20style=\x22flex:1;\x22></div>\x0a\x0a\x20\x20\x20\x20<select\x20@change=\x22setSort($el.value)\x22\x20:value=\x22sortMode\x22\x20style=\x22font-size:12px;\x22>\x0a\x20\x20\x20\x20\x20\x20<option\x20value=\x22when\x22>Sort:\x20When</option>\x0a\x20\x20\x20\x20\x20\x20<option\x20value=\x22priority\x22>Sort:\x20Priority</option>\x0a\x20\x20\x20\x20</select>\x0a\x20\x20\x20\x20<button\x20@click=\x22toggleDirection\x22\x20style=\x22font-size:12px;\x20min-width:32px;\x22\x20title=\x22Toggle\x20sort\x20direction\x22>\x0a\x20\x20\x20\x20\x20\x20{{\x20direction\x20===\x20\x27asc\x27\x20?\x20\x27▲\x27\x20:\x20\x27▼\x27\x20}}\x0a\x20\x20\x20\x20</button>\x0a\x0a\x20\x20\x20\x20<button\x20@click=\x22openEnqueue\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22background:color-mix(in\x20srgb,\x20var(--okdb-accent)\x2013%,\x20transparent);\x20border-color:color-mix(in\x20srgb,\x20var(--okdb-accent)\x2033%,\x20transparent);\x20color:var(--okdb-accent);\x20font-weight:600;\x20padding:4px\x2014px;\x20font-size:12px;\x20white-space:nowrap;\x22>\x0a\x20\x20\x20\x20\x20\x20+\x20Enqueue\x0a\x20\x20\x20\x20</button>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20──\x20Row\x202:\x20Bulk\x20actions\x20──\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:6px;\x20align-items:center;\x20flex-wrap:wrap;\x20padding:5px\x200;\x20border-top:1px\x20solid\x20var(--okdb-app-surface-2);\x20border-bottom:1px\x20solid\x20var(--okdb-app-surface-2);\x22>\x0a\x20\x20\x20\x20<button\x20@click=\x22retryAllFailed()\x22\x20:disabled=\x22bulkBusy\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22font-size:12px;\x20color:var(--okdb-warning);\x20border-color:color-mix(in\x20srgb,\x20var(--okdb-warning)\x2020%,\x20transparent);\x20background:transparent;\x20padding:3px\x2010px;\x22\x0a\x20\x20\x20\x20\x20\x20title=\x22Reset\x20all\x20failed\x20jobs\x20back\x20to\x20pending\x22>\x0a\x20\x20\x20\x20\x20\x20↺\x20Retry\x20Failed\x0a\x20\x20\x20\x20</button>\x0a\x20\x20\x20\x20<div\x20style=\x22width:1px;\x20height:16px;\x20background:var(--okdb-app-border);\x22></div>\x0a\x20\x20\x20\x20<button\x20@click=\x22deleteAllDone()\x22\x20:disabled=\x22bulkBusy\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22font-size:12px;\x20color:var(--okdb-success);\x20border-color:color-mix(in\x20srgb,\x20var(--okdb-success)\x2020%,\x20transparent);\x20background:transparent;\x20padding:3px\x2010px;\x22\x0a\x20\x20\x20\x20\x20\x20title=\x22Delete\x20all\x20done\x20jobs\x20in\x20one\x20index\x20pass\x22>\x0a\x20\x20\x20\x20\x20\x20✕\x20Clear\x20Done\x0a\x20\x20\x20\x20</button>\x0a\x20\x20\x20\x20<button\x20@click=\x22deleteAllFailed()\x22\x20:disabled=\x22bulkBusy\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22font-size:12px;\x20color:var(--okdb-danger);\x20border-color:color-mix(in\x20srgb,\x20var(--okdb-danger)\x2020%,\x20transparent);\x20background:transparent;\x20padding:3px\x2010px;\x22\x0a\x20\x20\x20\x20\x20\x20title=\x22Permanently\x20delete\x20all\x20failed\x20jobs\x22>\x0a\x20\x20\x20\x20\x20\x20✕\x20Clear\x20Failed\x0a\x20\x20\x20\x20</button>\x0a\x20\x20\x20\x20<button\x20@click=\x22clearStuck()\x22\x20:disabled=\x22bulkBusy\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22font-size:12px;\x20color:var(--okdb-app-fg-muted);\x20border-color:color-mix(in\x20srgb,\x20var(--okdb-app-fg-muted)\x2020%,\x20transparent);\x20background:transparent;\x20padding:3px\x2010px;\x22\x0a\x20\x20\x20\x20\x20\x20title=\x22Delete\x20running\x20jobs\x20whose\x20claim\x20TTL\x20has\x20expired\x20(permanently,\x20not\x20re-queued)\x22>\x0a\x20\x20\x20\x20\x20\x20✕\x20Clear\x20Stuck\x0a\x20\x20\x20\x20</button>\x0a\x20\x20\x20\x20<div\x20style=\x22width:1px;\x20height:16px;\x20background:var(--okdb-app-border);\x22></div>\x0a\x20\x20\x20\x20<button\x20@click=\x22rebuildStats\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22font-size:11px;\x20opacity:.4;\x20background:transparent;\x20border-color:transparent;\x20padding:3px\x208px;\x22\x0a\x20\x20\x20\x20\x20\x20title=\x22Full\x20scan\x20rebuild\x20of\x20stats\x20counters\x22>\x0a\x20\x20\x20\x20\x20\x20Rebuild\x20Stats\x0a\x20\x20\x20\x20</button>\x0a\x20\x20\x20\x20<if\x20:=\x22pendingUpdates\x20>\x200\x22>\x0a\x20\x20\x20\x20\x20\x20<span\x20style=\x22font-size:11px;\x20color:var(--okdb-warning);\x20opacity:.8;\x20margin-left:4px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20⟳\x20{{\x20pendingUpdates\x20}}\x20pending\x0a\x20\x20\x20\x20\x20\x20</span>\x0a\x20\x20\x20\x20</if>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Table\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20position:relative;\x20flex:1;\x20min-height:0;\x22>\x0a\x20\x20\x20\x20<if\x20:=\x22query.busy\x22>\x0a\x20\x20\x20\x20\x20\x20<ok-overlay>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20class=\x22loader-center\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<ok-icon\x20name=\x22loader\x22\x20class=\x22spin\x22\x20style=\x22width:32px;height:32px;\x22></ok-icon>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20</ok-overlay>\x0a\x20\x20\x20\x20</if>\x0a\x0a\x20\x20\x20\x20<if\x20:=\x22query.loadError\x20&&\x20!query.busy\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22color:var(--okdb-danger);\x20padding:12px\x2014px;\x20border:1px\x20solid\x20color-mix(in\x20srgb,\x20var(--okdb-danger)\x2033%,\x20transparent);\x20border-radius:8px;\x20margin-bottom:8px;\x20font-size:13px;\x20display:flex;\x20align-items:center;\x20gap:10px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22flex:1;\x22>{{\x20query.loadError\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20@click=\x22query.load()\x22\x20style=\x22font-size:11px;\x20color:var(--okdb-danger);\x20border-color:color-mix(in\x20srgb,\x20var(--okdb-danger)\x2033%,\x20transparent);\x20padding:3px\x2010px;\x22>Retry</button>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</if>\x0a\x0a\x20\x20\x20\x20<ok-table\x20:data=\x22query.values\x22\x20no-scroll\x20style=\x22flex:1;\x20min-height:0;\x20overflow:auto;\x22>\x0a\x20\x20\x20\x20\x20\x20<columns>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<column\x20width=\x2280px\x22\x20no-resize>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<header>Status</header>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<cell\x20let:row>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22status-badge\x22\x20:style=\x22statusBadgeStyle(row.status)\x22>{{\x20row.status\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</cell>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</column>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<column\x20width=\x221fr\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<header>Type\x20+\x20Metadata</header>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<cell\x20let:row>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:2px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div>{{\x20row.type\x20}}</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:4px;\x20flex-wrap:wrap;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22row.tags\x20&&\x20row.tags.length\x20>\x200\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22display:inline-block;\x20font-size:11px;\x20padding:1px\x206px;\x20background:var(--okdb-app-bg-deep);\x20border-radius:3px;\x20opacity:.7;\x22>{{\x20row.tags.join(\x27,\x20\x27)\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22row.progress\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22display:inline-block;\x20font-size:11px;\x20padding:1px\x206px;\x20background:var(--okdb-app-bg-deep);\x20border-radius:3px;\x20opacity:.7;\x22>{{\x20row.progress\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22row.cron\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22display:inline-block;\x20font-size:11px;\x20padding:1px\x206px;\x20background:var(--okdb-app-bg-deep);\x20border-radius:3px;\x20opacity:.7;\x20color:var(--okdb-indigo);\x22>cron:\x20{{\x20row.cron\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22row.idempotency_key\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22display:inline-block;\x20font-size:11px;\x20padding:1px\x206px;\x20background:var(--okdb-app-bg-deep);\x20border-radius:3px;\x20opacity:.7;\x20color:#34d399;\x22>key:\x20{{\x20row.idempotency_key\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</cell>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</column>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<column\x20width=\x22130px\x22\x20no-resize>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<header>When</header>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<cell\x20let:row>{{\x20formatWhen(row.when)\x20}}</cell>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</column>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<column\x20width=\x2270px\x22\x20no-resize>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<header>Tries</header>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<cell\x20let:row>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20:style=\x22row.tries\x20>=\x20row.max_tries\x20&&\x20row.max_tries\x20>\x200\x20?\x20\x27color:var(--okdb-danger);\x27\x20:\x20\x27\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20{{\x20row.tries\x20}}\x20/\x20{{\x20row.max_tries\x20}}\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</cell>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</column>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<column\x20width=\x22180px\x22\x20no-resize>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<header>Actions</header>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<cell\x20let:row>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20@click=\x22openEditJob(row)\x22\x20:disabled=\x22row._busy\x22\x20style=\x22font-size:11px;\x22>Edit</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20@click=\x22retryJob(row)\x22\x20\x20:disabled=\x22row._busy\x22\x20style=\x22font-size:11px;\x22>Retry</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22row.status\x20===\x20\x27pending\x27\x20||\x20row.status\x20===\x20\x27running\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20@click=\x22cancelJob(row)\x22\x20:disabled=\x22row._busy\x22\x20style=\x22font-size:11px;\x22>Cancel</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x20@click=\x22deleteJob(row)\x22\x20:disabled=\x22row._busy\x22\x20style=\x22color:#ff6c6b;\x20font-size:11px;\x22>Delete</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</cell>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</column>\x0a\x20\x20\x20\x20\x20\x20</columns>\x0a\x0a\x20\x20\x20\x20\x20\x20<expanded\x20let:row>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22padding:10px;\x20font-size:12px;\x20display:flex;\x20flex-direction:column;\x20gap:6px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22row.error_stack\x20||\x20row.status_message\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22row.status_message\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22white-space:pre-wrap;\x20opacity:.9;\x20margin-bottom:6px;\x22>{{\x20row.status_message\x20}}</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22row.error_stack\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<pre\x20style=\x22white-space:pre-wrap;\x20opacity:.85;\x20max-height:220px;\x20overflow:auto;\x20background:var(--okdb-app-bg-deep);\x20padding:8px;\x20border-radius:4px;\x22>{{\x20row.error_stack\x20}}</pre>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20flex-wrap:wrap;\x20gap:12px;\x20opacity:.8;\x20font-size:11px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>ID:\x20<b>{{\x20row.id\x20}}</b></span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>Status\x20code:\x20<b>{{\x20row.status_code\x20||\x20\x27—\x27\x20}}</b></span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>Max\x20tries:\x20<b>{{\x20row.max_tries\x20??\x20\x27—\x27\x20}}</b></span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>Retry\x20delay:\x20<b>{{\x20row.retry_delay\x20??\x200\x20}}ms</b></span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>Backoff:\x20<b>{{\x20row.backoff_multiplier\x20??\x201\x20}}</b></span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>Priority:\x20<b>{{\x20row.priority\x20??\x200\x20}}</b></span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20:=\x22row.bucket\x22\x20style=\x22display:inline;\x22>Bucket:\x20<b>{{\x20row.bucket\x20||\x20\x27—\x27\x20}}</b></span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>Claim\x20expires:\x20<b>{{\x20formatWhen(row.claim_expires)\x20}}</b></span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<details\x20style=\x22margin-top:6px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<summary\x20style=\x22cursor:pointer;\x20opacity:.8;\x20font-size:11px;\x22>Raw\x20job</summary>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<pre\x20style=\x22white-space:pre-wrap;\x20opacity:.85;\x20max-height:260px;\x20overflow:auto;\x20margin-top:4px;\x20background:var(--okdb-app-bg-deep);\x20padding:8px;\x20border-radius:4px;\x20font-size:10px;\x22>{{\x20row\x20}}</pre>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</details>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20</expanded>\x0a\x20\x20\x20\x20</ok-table>\x0a\x0a\x20\x20\x20\x20<!--\x20Pagination\x20-->\x0a\x20\x20\x20\x20<div\x20class=\x22ok-pager\x22>\x0a\x20\x20\x20\x20\x20\x20<button\x20@click=\x22query.prevPage()\x22\x20:disabled=\x22!query.hasPrev\x20||\x20query.busy\x22>‹\x20Prev</button>\x0a\x20\x20\x20\x20\x20\x20<span>{{\x20statusCount(status)\x20}}\x20total\x20·\x20{{\x20query.pageSize\x20}}/page</span>\x0a\x20\x20\x20\x20\x20\x20<button\x20@click=\x22query.nextPage()\x22\x20:disabled=\x22!query.hasNext\x20||\x20query.busy\x22>Next\x20›</button>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a</div>\x0a','style':_0x5013c1(0x11a)};
1
+ import e from"@toolkit/elements/ok-icon.ok.js";import t from"@toolkit/elements/ok-table.ok.js";import s from"@toolkit/core/ok-overlay.ok.js";import a from"../modals/edit-job-modal.ok.js";import o from"../modals/enqueue-job-modal.ok.js";import n from"../../../stores/events-store.js";import i from"../../../stores/okdb-store.js";export default{tag:"queue-jobs",dependencies:[e,t,s,o,a],attr:{type:{}},context:e=>({query:function({apiBase:e,pageSize:t=50}){return{busy:!1,values:[],total:0,hasNext:!1,hasPrev:!1,pageSize:t,loadError:null,type:null,status:null,sort:"when",direction:"asc",_cursors:[null],_pageIdx:0,_nextCursor:null,resetPaging(){this._cursors=[null],this._pageIdx=0,this._nextCursor=null,this.hasNext=!1,this.hasPrev=!1},async load(){const t="function"==typeof e?e():e,s=this._cursors[this._pageIdx];this.busy=!0,this.loadError=null;try{const e=new URLSearchParams({limit:String(this.pageSize),sort:this.sort??"when",direction:this.direction??"asc"});this.type&&e.set("type",this.type),this.status&&e.set("status",this.status),null!=s&&e.set("cursor",JSON.stringify(s));const a=await fetch(`${t}/queue/jobs?${e}`),o=await a.json();if(!a.ok)throw new Error(o?.error?.message||`HTTP ${a.status}`);const n=o.result??o.data??o;this.values=Array.isArray(n.items)?n.items:Array.isArray(n)?n:[],this._nextCursor=n.cursor??null,this.hasNext=null!=this._nextCursor,this.hasPrev=this._pageIdx>0}catch(e){this.loadError=e?.message||String(e),this.values=[]}finally{this.busy=!1}},async nextPage(){this.hasNext&&!this.busy&&(this._pageIdx+1>=this._cursors.length&&this._cursors.push(this._nextCursor),this._pageIdx++,await this.load())},async prevPage(){this.hasPrev&&!this.busy&&(this._pageIdx--,await this.load())}}}({apiBase:()=>i.apiBase,pageSize:50}),type:null,status:null,sortMode:"when",direction:"asc",stats:null,_statsTimer:null,async loadStats(){try{const e=await fetch(i.apiBase+"/queue/stats");if(e.ok){const t=await e.json();this.stats=t.result??t.data??null}}catch{}},_scheduleStats(){clearTimeout(this._statsTimer),this._statsTimer=setTimeout(()=>this.loadStats(),400)},statusCount(e){if(!this.stats)return"";const t=this.type;return e?t?this.stats.byType?.[t]?.byStatus?.[e]??0:this.stats.byStatus?.[e]??0:t?this.stats.byType?.[t]?.total??0:this.stats.total??0},applyTypeAttr(){var t;this.type=null==(t=e.$attr.type)||""===t?null:String(t),this._applyFilterAndReload()},setStatus(e){this.status=null==e||""===e?null:e,this._applyFilterAndReload()},setSort(e){this.sortMode=e,this._applySort(),this.query.resetPaging(),this.query.load()},toggleDirection(){this.direction="asc"===this.direction?"desc":"asc",this._applySort(),this.query.resetPaging(),this.query.load()},_applySort(){this.query.sort=this.sortMode,this.query.direction=this.direction},_applyFilterAndReload(){this.query.type=this.type,this.query.status=this.status,this._applySort(),this.query.resetPaging(),this.query.load()},async refresh(){clearTimeout(this._liveTimer),this.pendingUpdates=0;try{await fetch(i.apiBase+"/queue/reconcile",{method:"POST"})}catch{}await this.query.load(),await this.loadStats(),e.$dispatch("refresh")},async rebuildStats(){try{await fetch(i.apiBase+"/queue/stats/rebuild",{method:"POST"}),e.$ok.shared?.toast?.success?.("Stats rebuilt"),await this.loadStats(),e.$dispatch("refresh"),await this.query.load()}catch{e.$ok.shared?.toast?.error?.("Stats rebuild failed")}},async openEnqueue(){const t=this;await e.$ok.shared.modal.open({owner:e,title:"Enqueue Job",content:'<enqueue-job-modal :modal="modal" :initial-type="initialType" :on-done="onDone" />',context:s=>({modal:s,initialType:t.type||"",onDone:async()=>{e.$ok.shared?.toast?.success?.("Job enqueued"),await t.refresh()}})})},async openEditJob(t){const s=this;await e.$ok.shared.modal.open({owner:e,title:`Edit Job ${t.id.slice(0,8)}`,content:'<edit-job-modal :modal="modal" :job-id="jobId" :on-done="onDone" />',context:e=>({modal:e,jobId:t.id,onDone:async()=>{await s.refresh()}})})},async deleteJob(t){if(await e.$ok.shared.modal.confirm(`Delete job <code>${t.id.slice(0,8)}</code>?`)){t._busy=!0;try{if(!(await fetch(i.apiBase+"/queue/job/"+t.id+"/remove",{method:"POST"})).ok)throw new Error("remove failed");const s=this.query.values.indexOf(t);s>=0&&this.query.values.splice(s,1),e.$ok.shared.toast?.info("Deleted "+t.id.slice(0,8)),e.$dispatch("refresh"),this._scheduleStats()}catch{e.$ok.shared.toast?.error("Delete failed")}finally{t._busy=!1}}},async cancelJob(t){if(await e.$ok.shared.modal.confirm(`Cancel job <code>${t.id.slice(0,8)}</code>?`)){t._busy=!0;try{if(!(await fetch(i.apiBase+"/queue/job/"+t.id+"/cancel",{method:"POST"})).ok)throw new Error("cancel failed");e.$ok.shared.toast?.info("Cancelled "+t.id.slice(0,8)),await this.refresh()}catch{e.$ok.shared.toast?.error("Cancel failed")}finally{t._busy=!1}}},async retryJob(t){if(await e.$ok.shared.modal.confirm(`Retry job <code>${t.id.slice(0,8)}</code>?`)){t._busy=!0;try{const s=await fetch(i.apiBase+"/queue/job/"+t.id+"/retry",{method:"POST"}),a=await s.json();if(!s.ok)throw new Error(a?.error?.message||"retry failed");e.$ok.shared.toast?.success("Retried "+t.id.slice(0,8)),await this.refresh()}catch(t){e.$ok.shared.toast?.error("Retry failed: "+(t?.message||t))}finally{t._busy=!1}}},bulkBusy:!1,async retryAllFailed(){if(await e.$ok.shared.modal.confirm(`Retry all failed jobs${this.type?` for type "<b>${this.type}</b>"`:""}?`)){this.bulkBusy=!0;try{const t={limit:5e3};this.type&&(t.type=this.type);const s=await fetch(i.apiBase+"/queue/retryFailed",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),a=await s.json();if(!s.ok)throw new Error(a?.error?.message||"failed");e.$ok.shared?.toast?.success?.(`Retried ${a.data.retried} failed jobs`),await this.refresh()}catch(t){e.$ok.shared?.toast?.error?.("Bulk retry failed: "+(t?.message||t))}finally{this.bulkBusy=!1}}},async deleteAllDone(){if(await e.$ok.shared.modal.confirm(`Clear all "done" jobs${this.type?` for type "<b>${this.type}</b>"`:""}?<br><br>This cannot be undone.`)){this.bulkBusy=!0;try{const t={limit:5e4};this.type&&(t.type=this.type);const s=await fetch(i.apiBase+"/queue/clearDone",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),a=await s.json();if(!s.ok)throw new Error(a?.error?.message||"failed");e.$ok.shared?.toast?.info?.(`Cleared ${a.data.deleted} done jobs`),await this.refresh()}catch(t){e.$ok.shared?.toast?.error?.("Clear done failed: "+(t?.message||t))}finally{this.bulkBusy=!1}}},async deleteAllFailed(){if(await e.$ok.shared.modal.confirm(`Delete all "failed" jobs${this.type?` for type "<b>${this.type}</b>"`:""}?<br><br>This cannot be undone.`)){this.bulkBusy=!0;try{const t={limit:5e4};this.type&&(t.type=this.type);const s=await fetch(i.apiBase+"/queue/clearFailed",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),a=await s.json();if(!s.ok)throw new Error(a?.error?.message||"failed");e.$ok.shared?.toast?.info?.(`Cleared ${a.data.deleted} failed jobs`),await this.refresh()}catch(t){e.$ok.shared?.toast?.error?.("Clear failed jobs error: "+(t?.message||t))}finally{this.bulkBusy=!1}}},async clearStuck(){if(await e.$ok.shared.modal.confirm(`Delete all stuck (expired-claim) running jobs${this.type?` for type "<b>${this.type}</b>"`:""}?<br><br>Stuck jobs are running jobs whose TTL has expired. They will be permanently deleted, not re-queued.`)){this.bulkBusy=!0;try{const t={limit:5e4};this.type&&(t.type=this.type);const s=await fetch(i.apiBase+"/queue/clearStuck",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),a=await s.json();if(!s.ok)throw new Error(a?.error?.message||"failed");e.$ok.shared?.toast?.info?.(`Cleared ${a.data.deleted} stuck jobs`),await this.refresh()}catch(t){e.$ok.shared?.toast?.error?.("Clear stuck failed: "+(t?.message||t))}finally{this.bulkBusy=!1}}},isStatusOn(e){return(this.status??null)===(e??null)},statusBadgeStyle(e){const t={pending:"var(--okdb-warning)",running:"var(--okdb-info)",done:"var(--okdb-success)",failed:"var(--okdb-danger)"}[e]||"#9ca3af";return`color:${t}; border-color:${t};`},formatWhen(e){if(!e)return"—";const t=e-Date.now(),s=Math.abs(t),a=Math.round(s/1e3);if(a<5)return t<=0?"now":"in a moment";if(a<60)return t<0?`${a}s ago`:`in ${a}s`;const o=Math.round(a/60);if(o<60)return t<0?`${o}m ago`:`in ${o}m`;const n=Math.round(o/60);if(n<24)return t<0?`${n}h ago`:`in ${n}h`;const i=Math.round(n/24);return t<0?`${i}d ago`:`in ${i}d`},shortId:e=>(e||"").slice(0,8),_unsubs:[],_liveTimer:null,pendingUpdates:0,_scheduleLiveReload(){this.pendingUpdates++,clearTimeout(this._liveTimer),this._liveTimer=setTimeout(async()=>{await this.query.load(),await this.loadStats(),e.$dispatch("refresh"),this.pendingUpdates=0},3e3)}}),async init(e){this.applyTypeAttr(),e.$watch(()=>e.$attr.type,()=>this.applyTypeAttr()),e.$watch(()=>e.$ok.stores.okdb.selectedEnv,()=>{this.stats=null,this.query.resetPaging(),Promise.all([this.query.load(),this.loadStats()])}),await Promise.all([this.query.load(),this.loadStats()])},mount(){const e=()=>this._scheduleLiveReload();this._unsubs=[n.on("queue:enqueued",e),n.on("queue:done",e),n.on("queue:failed",e),n.on("queue:retry",e),n.on("queue:removed",e),n.on("queue:reconciled",e)]},unmount(){clearTimeout(this._liveTimer),clearTimeout(this._statsTimer);for(const e of this._unsubs)e();this._unsubs=[]},template:'\n<div style="display:flex; flex-direction:column; height:100%; min-height:0; gap:10px;">\n\n \x3c!-- ── Row 1: Status filters + Enqueue ── --\x3e\n <div style="display:flex; gap:6px; align-items:center; flex-wrap:wrap;">\n <button @click="setStatus(null)" :class="isStatusOn(null) ? \'pill pill--on\' : \'pill\'">All<span class="pill-count">{{ statusCount(null) }}</span></button>\n <button @click="setStatus(\'pending\')" :class="isStatusOn(\'pending\') ? \'pill pill--on pill--p\' : \'pill pill--p\'">Pending<span class="pill-count">{{ statusCount(\'pending\') }}</span></button>\n <button @click="setStatus(\'running\')" :class="isStatusOn(\'running\') ? \'pill pill--on pill--r\' : \'pill pill--r\'">Running<span class="pill-count">{{ statusCount(\'running\') }}</span></button>\n <button @click="setStatus(\'failed\')" :class="isStatusOn(\'failed\') ? \'pill pill--on pill--f\' : \'pill pill--f\'">Failed<span class="pill-count">{{ statusCount(\'failed\') }}</span></button>\n <button @click="setStatus(\'done\')" :class="isStatusOn(\'done\') ? \'pill pill--on pill--d\' : \'pill pill--d\'">Done<span class="pill-count">{{ statusCount(\'done\') }}</span></button>\n\n <div style="flex:1;"></div>\n\n <select @change="setSort($el.value)" :value="sortMode" style="font-size:12px;">\n <option value="when">Sort: When</option>\n <option value="priority">Sort: Priority</option>\n </select>\n <button @click="toggleDirection" style="font-size:12px; min-width:32px;" title="Toggle sort direction">\n {{ direction === \'asc\' ? \'▲\' : \'▼\' }}\n </button>\n\n <button @click="openEnqueue"\n style="background:color-mix(in srgb, var(--okdb-accent) 13%, transparent); border-color:color-mix(in srgb, var(--okdb-accent) 33%, transparent); color:var(--okdb-accent); font-weight:600; padding:4px 14px; font-size:12px; white-space:nowrap;">\n + Enqueue\n </button>\n </div>\n\n \x3c!-- ── Row 2: Bulk actions ── --\x3e\n <div style="display:flex; gap:6px; align-items:center; flex-wrap:wrap; padding:5px 0; border-top:1px solid var(--okdb-app-surface-2); border-bottom:1px solid var(--okdb-app-surface-2);">\n <button @click="retryAllFailed()" :disabled="bulkBusy"\n style="font-size:12px; color:var(--okdb-warning); border-color:color-mix(in srgb, var(--okdb-warning) 20%, transparent); background:transparent; padding:3px 10px;"\n title="Reset all failed jobs back to pending">\n ↺ Retry Failed\n </button>\n <div style="width:1px; height:16px; background:var(--okdb-app-border);"></div>\n <button @click="deleteAllDone()" :disabled="bulkBusy"\n style="font-size:12px; color:var(--okdb-success); border-color:color-mix(in srgb, var(--okdb-success) 20%, transparent); background:transparent; padding:3px 10px;"\n title="Delete all done jobs in one index pass">\n ✕ Clear Done\n </button>\n <button @click="deleteAllFailed()" :disabled="bulkBusy"\n style="font-size:12px; color:var(--okdb-danger); border-color:color-mix(in srgb, var(--okdb-danger) 20%, transparent); background:transparent; padding:3px 10px;"\n title="Permanently delete all failed jobs">\n ✕ Clear Failed\n </button>\n <button @click="clearStuck()" :disabled="bulkBusy"\n style="font-size:12px; color:var(--okdb-app-fg-muted); border-color:color-mix(in srgb, var(--okdb-app-fg-muted) 20%, transparent); background:transparent; padding:3px 10px;"\n title="Delete running jobs whose claim TTL has expired (permanently, not re-queued)">\n ✕ Clear Stuck\n </button>\n <div style="width:1px; height:16px; background:var(--okdb-app-border);"></div>\n <button @click="rebuildStats"\n style="font-size:11px; opacity:.4; background:transparent; border-color:transparent; padding:3px 8px;"\n title="Full scan rebuild of stats counters">\n Rebuild Stats\n </button>\n <if :="pendingUpdates > 0">\n <span style="font-size:11px; color:var(--okdb-warning); opacity:.8; margin-left:4px;">\n ⟳ {{ pendingUpdates }} pending\n </span>\n </if>\n </div>\n\n \x3c!-- Table --\x3e\n <div style="display:flex; flex-direction:column; position:relative; flex:1; min-height:0;">\n <if :="query.busy">\n <ok-overlay>\n <div class="loader-center">\n <ok-icon name="loader" class="spin" style="width:32px;height:32px;"></ok-icon>\n </div>\n </ok-overlay>\n </if>\n\n <if :="query.loadError && !query.busy">\n <div style="color:var(--okdb-danger); padding:12px 14px; border:1px solid color-mix(in srgb, var(--okdb-danger) 33%, transparent); border-radius:8px; margin-bottom:8px; font-size:13px; display:flex; align-items:center; gap:10px;">\n <span style="flex:1;">{{ query.loadError }}</span>\n <button @click="query.load()" style="font-size:11px; color:var(--okdb-danger); border-color:color-mix(in srgb, var(--okdb-danger) 33%, transparent); padding:3px 10px;">Retry</button>\n </div>\n </if>\n\n <ok-table :data="query.values" no-scroll style="flex:1; min-height:0; overflow:auto;">\n <columns>\n <column width="80px" no-resize>\n <header>Status</header>\n <cell let:row>\n <span class="status-badge" :style="statusBadgeStyle(row.status)">{{ row.status }}</span>\n </cell>\n </column>\n <column width="1fr">\n <header>Type + Metadata</header>\n <cell let:row>\n <div style="display:flex; flex-direction:column; gap:2px;">\n <div>{{ row.type }}</div>\n <div style="display:flex; gap:4px; flex-wrap:wrap;">\n <if :="row.tags && row.tags.length > 0">\n <span style="display:inline-block; font-size:11px; padding:1px 6px; background:var(--okdb-app-bg-deep); border-radius:3px; opacity:.7;">{{ row.tags.join(\', \') }}</span>\n </if>\n <if :="row.progress">\n <span style="display:inline-block; font-size:11px; padding:1px 6px; background:var(--okdb-app-bg-deep); border-radius:3px; opacity:.7;">{{ row.progress }}</span>\n </if>\n <if :="row.cron">\n <span style="display:inline-block; font-size:11px; padding:1px 6px; background:var(--okdb-app-bg-deep); border-radius:3px; opacity:.7; color:var(--okdb-indigo);">cron: {{ row.cron }}</span>\n </if>\n <if :="row.idempotency_key">\n <span style="display:inline-block; font-size:11px; padding:1px 6px; background:var(--okdb-app-bg-deep); border-radius:3px; opacity:.7; color:#34d399;">key: {{ row.idempotency_key }}</span>\n </if>\n </div>\n </div>\n </cell>\n </column>\n <column width="130px" no-resize>\n <header>When</header>\n <cell let:row>{{ formatWhen(row.when) }}</cell>\n </column>\n <column width="70px" no-resize>\n <header>Tries</header>\n <cell let:row>\n <span :style="row.tries >= row.max_tries && row.max_tries > 0 ? \'color:var(--okdb-danger);\' : \'\'">\n {{ row.tries }} / {{ row.max_tries }}\n </span>\n </cell>\n </column>\n <column width="180px" no-resize>\n <header>Actions</header>\n <cell let:row>\n <button @click="openEditJob(row)" :disabled="row._busy" style="font-size:11px;">Edit</button>\n <button @click="retryJob(row)" :disabled="row._busy" style="font-size:11px;">Retry</button>\n <if :="row.status === \'pending\' || row.status === \'running\'">\n <button @click="cancelJob(row)" :disabled="row._busy" style="font-size:11px;">Cancel</button>\n </if>\n <button @click="deleteJob(row)" :disabled="row._busy" style="color:#ff6c6b; font-size:11px;">Delete</button>\n </cell>\n </column>\n </columns>\n\n <expanded let:row>\n <div style="padding:10px; font-size:12px; display:flex; flex-direction:column; gap:6px;">\n <if :="row.error_stack || row.status_message">\n <div>\n <if :="row.status_message">\n <div style="white-space:pre-wrap; opacity:.9; margin-bottom:6px;">{{ row.status_message }}</div>\n </if>\n <if :="row.error_stack">\n <pre style="white-space:pre-wrap; opacity:.85; max-height:220px; overflow:auto; background:var(--okdb-app-bg-deep); padding:8px; border-radius:4px;">{{ row.error_stack }}</pre>\n </if>\n </div>\n </if>\n <div style="display:flex; flex-wrap:wrap; gap:12px; opacity:.8; font-size:11px;">\n <span>ID: <b>{{ row.id }}</b></span>\n <span>Status code: <b>{{ row.status_code || \'—\' }}</b></span>\n <span>Max tries: <b>{{ row.max_tries ?? \'—\' }}</b></span>\n <span>Retry delay: <b>{{ row.retry_delay ?? 0 }}ms</b></span>\n <span>Backoff: <b>{{ row.backoff_multiplier ?? 1 }}</b></span>\n <span>Priority: <b>{{ row.priority ?? 0 }}</b></span>\n <span :="row.bucket" style="display:inline;">Bucket: <b>{{ row.bucket || \'—\' }}</b></span>\n <span>Claim expires: <b>{{ formatWhen(row.claim_expires) }}</b></span>\n </div>\n <details style="margin-top:6px;">\n <summary style="cursor:pointer; opacity:.8; font-size:11px;">Raw job</summary>\n <pre style="white-space:pre-wrap; opacity:.85; max-height:260px; overflow:auto; margin-top:4px; background:var(--okdb-app-bg-deep); padding:8px; border-radius:4px; font-size:10px;">{{ row }}</pre>\n </details>\n </div>\n </expanded>\n </ok-table>\n\n \x3c!-- Pagination --\x3e\n <div class="ok-pager">\n <button @click="query.prevPage()" :disabled="!query.hasPrev || query.busy">‹ Prev</button>\n <span>{{ statusCount(status) }} total · {{ query.pageSize }}/page</span>\n <button @click="query.nextPage()" :disabled="!query.hasNext || query.busy">Next ›</button>\n </div>\n </div>\n\n</div>\n',style:".pill{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--okdb-app-border);background:var(--okdb-app-surface);padding:4px 10px;border-radius:999px;font-size:12px;cursor:pointer;color:var(--okdb-app-fg-muted);transition:border-color .12s,color .12s}.pill:hover{border-color:#3b4754;color:var(--okdb-app-fg)}.pill--on{box-shadow:0 0 0 1px rgba(255,255,255,.07) inset}.pill--p.pill--on{color:var(--okdb-warning);border-color:var(--okdb-warning)}.pill--r.pill--on{color:var(--okdb-info);border-color:var(--okdb-info)}.pill--f.pill--on{color:var(--okdb-danger);border-color:var(--okdb-danger)}.pill--d.pill--on{color:var(--okdb-success);border-color:var(--okdb-success)}.pill-count{font-size:11px;opacity:.65;font-variant-numeric:tabular-nums}.status-badge{display:inline-block;padding:2px 8px;border-radius:999px;font-size:11px;border:1px solid;white-space:nowrap}"};