@stacksjs/defaults 0.70.207 → 0.70.208

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 (408) hide show
  1. package/TODO.md +21 -0
  2. package/ai/AGENTS.md +79 -0
  3. package/ai/README.md +45 -0
  4. package/ai/claude/launch.json +12 -0
  5. package/ai/skills/stacks-actions/SKILL.md +128 -0
  6. package/ai/skills/stacks-ai/SKILL.md +276 -0
  7. package/ai/skills/stacks-alias/SKILL.md +94 -0
  8. package/ai/skills/stacks-analytics/SKILL.md +91 -0
  9. package/ai/skills/stacks-api/SKILL.md +738 -0
  10. package/ai/skills/stacks-arrays/SKILL.md +277 -0
  11. package/ai/skills/stacks-auth/SKILL.md +422 -0
  12. package/ai/skills/stacks-auto-imports/SKILL.md +138 -0
  13. package/ai/skills/stacks-brandkit/SKILL.md +811 -0
  14. package/ai/skills/stacks-browse/SKILL.md +127 -0
  15. package/ai/skills/stacks-browse/scripts/browse.ts +492 -0
  16. package/ai/skills/stacks-browser/SKILL.md +145 -0
  17. package/ai/skills/stacks-buddy/SKILL.md +676 -0
  18. package/ai/skills/stacks-build/SKILL.md +163 -0
  19. package/ai/skills/stacks-cache/SKILL.md +125 -0
  20. package/ai/skills/stacks-calendar/SKILL.md +113 -0
  21. package/ai/skills/stacks-chat/SKILL.md +371 -0
  22. package/ai/skills/stacks-cli/SKILL.md +112 -0
  23. package/ai/skills/stacks-cloud/SKILL.md +192 -0
  24. package/ai/skills/stacks-cms/SKILL.md +217 -0
  25. package/ai/skills/stacks-collections/SKILL.md +82 -0
  26. package/ai/skills/stacks-commerce/SKILL.md +114 -0
  27. package/ai/skills/stacks-composables/SKILL.md +161 -0
  28. package/ai/skills/stacks-config/SKILL.md +144 -0
  29. package/ai/skills/stacks-configuration/SKILL.md +154 -0
  30. package/ai/skills/stacks-cron/SKILL.md +123 -0
  31. package/ai/skills/stacks-crosswind/SKILL.md +190 -0
  32. package/ai/skills/stacks-dashboard/SKILL.md +173 -0
  33. package/ai/skills/stacks-database/SKILL.md +286 -0
  34. package/ai/skills/stacks-datetime/SKILL.md +269 -0
  35. package/ai/skills/stacks-dependencies/SKILL.md +111 -0
  36. package/ai/skills/stacks-deploy/SKILL.md +102 -0
  37. package/ai/skills/stacks-design-brutalist/SKILL.md +110 -0
  38. package/ai/skills/stacks-design-minimalist/SKILL.md +115 -0
  39. package/ai/skills/stacks-design-output/SKILL.md +55 -0
  40. package/ai/skills/stacks-design-soft/SKILL.md +156 -0
  41. package/ai/skills/stacks-design-taste/SKILL.md +1012 -0
  42. package/ai/skills/stacks-design-taste-codex/SKILL.md +294 -0
  43. package/ai/skills/stacks-desktop/SKILL.md +78 -0
  44. package/ai/skills/stacks-development/SKILL.md +298 -0
  45. package/ai/skills/stacks-dns/SKILL.md +149 -0
  46. package/ai/skills/stacks-docs/SKILL.md +109 -0
  47. package/ai/skills/stacks-email/SKILL.md +236 -0
  48. package/ai/skills/stacks-enums/SKILL.md +100 -0
  49. package/ai/skills/stacks-env/SKILL.md +181 -0
  50. package/ai/skills/stacks-error-handling/SKILL.md +137 -0
  51. package/ai/skills/stacks-events/SKILL.md +228 -0
  52. package/ai/skills/stacks-faker/SKILL.md +158 -0
  53. package/ai/skills/stacks-git/SKILL.md +158 -0
  54. package/ai/skills/stacks-guard/SKILL.md +98 -0
  55. package/ai/skills/stacks-health/SKILL.md +75 -0
  56. package/ai/skills/stacks-http/SKILL.md +316 -0
  57. package/ai/skills/stacks-i18n/SKILL.md +198 -0
  58. package/ai/skills/stacks-image-to-code/SKILL.md +629 -0
  59. package/ai/skills/stacks-imagegen-mobile/SKILL.md +1474 -0
  60. package/ai/skills/stacks-imagegen-web/SKILL.md +998 -0
  61. package/ai/skills/stacks-investigate/SKILL.md +119 -0
  62. package/ai/skills/stacks-jobs/SKILL.md +136 -0
  63. package/ai/skills/stacks-lint/SKILL.md +184 -0
  64. package/ai/skills/stacks-listeners/SKILL.md +99 -0
  65. package/ai/skills/stacks-logging/SKILL.md +78 -0
  66. package/ai/skills/stacks-mail/SKILL.md +120 -0
  67. package/ai/skills/stacks-middleware/SKILL.md +249 -0
  68. package/ai/skills/stacks-migrations/SKILL.md +105 -0
  69. package/ai/skills/stacks-models/SKILL.md +267 -0
  70. package/ai/skills/stacks-new-feature/SKILL.md +206 -0
  71. package/ai/skills/stacks-notifications/SKILL.md +210 -0
  72. package/ai/skills/stacks-objects/SKILL.md +205 -0
  73. package/ai/skills/stacks-office-hours/SKILL.md +134 -0
  74. package/ai/skills/stacks-orm/SKILL.md +342 -0
  75. package/ai/skills/stacks-path/SKILL.md +118 -0
  76. package/ai/skills/stacks-payments/SKILL.md +373 -0
  77. package/ai/skills/stacks-plan-review/SKILL.md +152 -0
  78. package/ai/skills/stacks-plugins/SKILL.md +149 -0
  79. package/ai/skills/stacks-push/SKILL.md +376 -0
  80. package/ai/skills/stacks-query-builder/SKILL.md +128 -0
  81. package/ai/skills/stacks-queue/SKILL.md +559 -0
  82. package/ai/skills/stacks-realtime/SKILL.md +442 -0
  83. package/ai/skills/stacks-redesign/SKILL.md +182 -0
  84. package/ai/skills/stacks-registry/SKILL.md +58 -0
  85. package/ai/skills/stacks-repl/SKILL.md +80 -0
  86. package/ai/skills/stacks-retro/SKILL.md +122 -0
  87. package/ai/skills/stacks-review/SKILL.md +135 -0
  88. package/ai/skills/stacks-router/SKILL.md +231 -0
  89. package/ai/skills/stacks-routes/SKILL.md +105 -0
  90. package/ai/skills/stacks-scaffolding/SKILL.md +162 -0
  91. package/ai/skills/stacks-scheduler/SKILL.md +363 -0
  92. package/ai/skills/stacks-search-engine/SKILL.md +112 -0
  93. package/ai/skills/stacks-security/SKILL.md +129 -0
  94. package/ai/skills/stacks-security-audit/SKILL.md +128 -0
  95. package/ai/skills/stacks-server/SKILL.md +458 -0
  96. package/ai/skills/stacks-shell/SKILL.md +62 -0
  97. package/ai/skills/stacks-slug/SKILL.md +87 -0
  98. package/ai/skills/stacks-sms/SKILL.md +385 -0
  99. package/ai/skills/stacks-socials/SKILL.md +303 -0
  100. package/ai/skills/stacks-storage/SKILL.md +576 -0
  101. package/ai/skills/stacks-strings/SKILL.md +416 -0
  102. package/ai/skills/stacks-stx/SKILL.md +176 -0
  103. package/ai/skills/stacks-technical-diagrams/LICENSE +22 -0
  104. package/ai/skills/stacks-technical-diagrams/SKILL.md +290 -0
  105. package/ai/skills/stacks-technical-diagrams/agents/openai.yaml +4 -0
  106. package/ai/skills/stacks-technical-diagrams/assets/template.html +1252 -0
  107. package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +289 -0
  108. package/ai/skills/stacks-technical-diagrams/bunfig.toml +1 -0
  109. package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +64 -0
  110. package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +88 -0
  111. package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +75 -0
  112. package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +70 -0
  113. package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +41 -0
  114. package/ai/skills/stacks-technical-diagrams/renderers/architecture/grid.mjs +62 -0
  115. package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +371 -0
  116. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +82 -0
  117. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +295 -0
  118. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +91 -0
  119. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +384 -0
  120. package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +84 -0
  121. package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +257 -0
  122. package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +42 -0
  123. package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +13 -0
  124. package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +230 -0
  125. package/ai/skills/stacks-technical-diagrams/renderers/shared/layout-report.mjs +40 -0
  126. package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +88 -0
  127. package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +40 -0
  128. package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +103 -0
  129. package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +481 -0
  130. package/ai/skills/stacks-technical-diagrams/schemas/README.md +68 -0
  131. package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +121 -0
  132. package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +43 -0
  133. package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +221 -0
  134. package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +241 -0
  135. package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +186 -0
  136. package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +332 -0
  137. package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +297 -0
  138. package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +28 -0
  139. package/ai/skills/stacks-testing/SKILL.md +129 -0
  140. package/ai/skills/stacks-tunnel/SKILL.md +125 -0
  141. package/ai/skills/stacks-types/SKILL.md +178 -0
  142. package/ai/skills/stacks-ui/SKILL.md +246 -0
  143. package/ai/skills/stacks-utils/SKILL.md +444 -0
  144. package/ai/skills/stacks-validation/SKILL.md +371 -0
  145. package/ai/skills/stacks-whois/SKILL.md +315 -0
  146. package/assets/world-map-data.ts +121 -0
  147. package/bootstrap.ts +114 -0
  148. package/docs/api-examples.md +46 -0
  149. package/docs/index.md +24 -0
  150. package/docs/markdown-examples.md +87 -0
  151. package/functions/analytics.ts +97 -0
  152. package/functions/api.ts +143 -0
  153. package/functions/auth.ts +208 -0
  154. package/functions/billing/payments.ts +149 -0
  155. package/functions/blog.ts +120 -0
  156. package/functions/cms/categorizables.ts +107 -0
  157. package/functions/cms/pages.ts +108 -0
  158. package/functions/cms/posts.ts +100 -0
  159. package/functions/cms/taggables.ts +102 -0
  160. package/functions/commerce/coupons.ts +128 -0
  161. package/functions/commerce/customers.ts +128 -0
  162. package/functions/commerce/gift-cards.ts +119 -0
  163. package/functions/commerce/orders.ts +112 -0
  164. package/functions/commerce/payments.ts +119 -0
  165. package/functions/commerce/products/categories.ts +123 -0
  166. package/functions/commerce/products/manufacturers.ts +123 -0
  167. package/functions/commerce/products/products.ts +123 -0
  168. package/functions/commerce/products/reviews.ts +123 -0
  169. package/functions/commerce/products/units.ts +123 -0
  170. package/functions/commerce/products/variants.ts +123 -0
  171. package/functions/commerce/shippings/delivery-routes.ts +123 -0
  172. package/functions/commerce/shippings/digital-deliveries.ts +119 -0
  173. package/functions/commerce/shippings/drivers.ts +123 -0
  174. package/functions/commerce/shippings/license-keys.ts +123 -0
  175. package/functions/commerce/shippings/shipping-methods.ts +123 -0
  176. package/functions/commerce/shippings/shipping-rates.ts +119 -0
  177. package/functions/commerce/shippings/shipping-zones.ts +119 -0
  178. package/functions/commerce/taxes.ts +119 -0
  179. package/functions/commerce/waitlist/products.ts +123 -0
  180. package/functions/commerce/waitlist/restaurants.ts +123 -0
  181. package/functions/craft.ts +107 -0
  182. package/functions/dashboard.ts +153 -0
  183. package/functions/inbox.ts +95 -0
  184. package/functions/jobs.ts +86 -0
  185. package/functions/monitoring/errors.ts +295 -0
  186. package/functions/queue.ts +111 -0
  187. package/functions/realtime/websockets.ts +96 -0
  188. package/functions/realtime.ts +52 -0
  189. package/functions/releases.ts +49 -0
  190. package/functions/search.ts +126 -0
  191. package/functions/toasts.ts +95 -0
  192. package/functions/uploads.ts +216 -0
  193. package/ide/.gitignore +4 -0
  194. package/ide/cursor/rules/auto-imports.mdc +574 -0
  195. package/ide/cursor/rules/code-style.mdc +12 -0
  196. package/ide/cursor/rules/documentation.mdc +9 -0
  197. package/ide/cursor/rules/error-handling-validation.mdc +11 -0
  198. package/ide/cursor/rules/key-conventions.mdc +11 -0
  199. package/ide/cursor/rules/project-structure.mdc +169 -0
  200. package/ide/cursor/rules/readme.mdc +318 -0
  201. package/ide/cursor/rules/syntax-formatting.mdc +9 -0
  202. package/ide/cursor/rules/testing.mdc +10 -0
  203. package/ide/cursor/rules/typescript.mdc +9 -0
  204. package/ide/cursor/rules/ui-styling.mdc +0 -0
  205. package/ide/dictionary.txt +397 -0
  206. package/ide/jetbrains/.fleet/settings.json +4 -0
  207. package/ide/jetbrains/.idea/.gitignore +5 -0
  208. package/ide/jetbrains/.idea/.name +1 -0
  209. package/ide/jetbrains/.idea/inspectionProfiles/Project_Default.xml +6 -0
  210. package/ide/jetbrains/.idea/jsLinters/eslint.xml +7 -0
  211. package/ide/jetbrains/.idea/modules.xml +8 -0
  212. package/ide/jetbrains/.idea/stacks.iml +22 -0
  213. package/ide/jetbrains/.idea/vcs.xml +6 -0
  214. package/ide/jetbrains/.idea/webResources.xml +14 -0
  215. package/ide/jetbrains/IntelliJ IDEA Global Settings +0 -0
  216. package/ide/jetbrains/installed.txt +4 -0
  217. package/ide/jetbrains/options/github-copilot.xml +5 -0
  218. package/ide/jetbrains/options/nodejs.xml +7 -0
  219. package/ide/jetbrains/options/templates.xml +124 -0
  220. package/ide/jetbrains/templates/JavaScript.xml +6 -0
  221. package/ide/jetbrains/templates/Stacks.xml +8 -0
  222. package/ide/vscode/.vscode/README.md +23 -0
  223. package/ide/vscode/.vscode/extensions.json +5 -0
  224. package/ide/vscode/.vscode/settings.json +176 -0
  225. package/ide/vscode/.vscode/stacks.code-snippets +181 -0
  226. package/ide/vscode/LICENSE.md +21 -0
  227. package/ide/vscode/README.md +36 -0
  228. package/ide/vscode/logo.png +0 -0
  229. package/ide/vscode/package.json +79 -0
  230. package/ide/zed/.zed/settings.json +139 -0
  231. package/lang/README.md +28 -0
  232. package/lang/de.yml +14 -0
  233. package/lang/en.yml +15 -0
  234. package/package.json +19 -3
  235. package/project/bootstrap +161 -0
  236. package/project/buddy +175 -0
  237. package/project/storage/framework/tsconfig.app.json +50 -0
  238. package/project/storage/framework/tsconfig.base.json +58 -0
  239. package/routes/core.ts +78 -0
  240. package/routes/dashboard-api.ts +233 -0
  241. package/routes/dashboard.ts +739 -0
  242. package/routes/email.ts +129 -0
  243. package/stores/[name].stx +16 -0
  244. package/stores/git.ts +71 -0
  245. package/stores/payment.ts +474 -0
  246. package/stores/queue.ts +43 -0
  247. package/stores/user.ts +29 -0
  248. package/stx-components-plugin.ts +25 -0
  249. package/styles/design-tokens.css +597 -0
  250. package/types/dashboard.ts +70 -0
  251. package/types/defaults.ts +438 -0
  252. package/vcs/github/CODE_OF_CONDUCT.md +83 -0
  253. package/vcs/github/CONTRIBUTING.md +187 -0
  254. package/vcs/github/EXPLANATIONS.md +41 -0
  255. package/vcs/github/FUNDING.yml +1 -0
  256. package/vcs/github/ISSUE_TEMPLATE/bug_report.yml +49 -0
  257. package/vcs/github/ISSUE_TEMPLATE/config.yml +10 -0
  258. package/vcs/github/ISSUE_TEMPLATE/feature_request.yml +44 -0
  259. package/vcs/github/SECURITY.md +70 -0
  260. package/vcs/github/renovate.json +5 -0
  261. package/vcs/github/stale.yml +14 -0
  262. package/vcs/github/workflows/README.md +16 -0
  263. package/vcs/github/workflows/ci.yml +114 -0
  264. package/vcs/github/workflows/export-size.yml +25 -0
  265. package/vcs/github/workflows/labeler.yml +13 -0
  266. package/vcs/github/workflows/release.yml +219 -0
  267. package/views/[...all].stx +8 -0
  268. package/views/about.md +3 -0
  269. package/views/auth/login.stx +1 -0
  270. package/views/auth/signup.stx +85 -0
  271. package/views/dashboard/.discovered-models.json +542 -0
  272. package/views/dashboard/AUDIT.md +904 -0
  273. package/views/dashboard/[...all].stx +89 -0
  274. package/views/dashboard/access-tokens/index.stx +177 -0
  275. package/views/dashboard/actions/index.stx +129 -0
  276. package/views/dashboard/analytics/blog/index.stx +356 -0
  277. package/views/dashboard/analytics/browsers/index.stx +263 -0
  278. package/views/dashboard/analytics/commerce/sales/index.stx +329 -0
  279. package/views/dashboard/analytics/commerce/web/index.stx +379 -0
  280. package/views/dashboard/analytics/countries/index.stx +289 -0
  281. package/views/dashboard/analytics/devices/index.stx +305 -0
  282. package/views/dashboard/analytics/events/index.stx +536 -0
  283. package/views/dashboard/analytics/index.stx +88 -0
  284. package/views/dashboard/analytics/marketing/index.stx +315 -0
  285. package/views/dashboard/analytics/pages/index.stx +271 -0
  286. package/views/dashboard/analytics/referrers/index.stx +319 -0
  287. package/views/dashboard/analytics/web/index.stx +356 -0
  288. package/views/dashboard/buddy/index.stx +198 -0
  289. package/views/dashboard/ci/index.stx +462 -0
  290. package/views/dashboard/cloud/index.stx +452 -0
  291. package/views/dashboard/commands/index.stx +295 -0
  292. package/views/dashboard/commerce/categories/index.stx +406 -0
  293. package/views/dashboard/commerce/coupons/index.stx +485 -0
  294. package/views/dashboard/commerce/customers/index.stx +410 -0
  295. package/views/dashboard/commerce/dashboard/index.stx +287 -0
  296. package/views/dashboard/commerce/delivery/delivery-routes.stx +305 -0
  297. package/views/dashboard/commerce/delivery/digital-delivery.stx +332 -0
  298. package/views/dashboard/commerce/delivery/drivers.stx +397 -0
  299. package/views/dashboard/commerce/delivery/index.stx +18 -0
  300. package/views/dashboard/commerce/delivery/license-keys.stx +380 -0
  301. package/views/dashboard/commerce/delivery/shipping-methods.stx +346 -0
  302. package/views/dashboard/commerce/delivery/shipping-rates.stx +338 -0
  303. package/views/dashboard/commerce/delivery/shipping-zones.stx +302 -0
  304. package/views/dashboard/commerce/delivery/tabs.ts +25 -0
  305. package/views/dashboard/commerce/gift-cards/index.stx +561 -0
  306. package/views/dashboard/commerce/manufacturers/index.stx +308 -0
  307. package/views/dashboard/commerce/orders/index.stx +247 -0
  308. package/views/dashboard/commerce/payments/index.stx +535 -0
  309. package/views/dashboard/commerce/pos/index.stx +653 -0
  310. package/views/dashboard/commerce/printers/devices/index.stx +273 -0
  311. package/views/dashboard/commerce/printers/receipts/index.stx +308 -0
  312. package/views/dashboard/commerce/products/detail.stx +563 -0
  313. package/views/dashboard/commerce/products/index.stx +370 -0
  314. package/views/dashboard/commerce/reviews/index.stx +656 -0
  315. package/views/dashboard/commerce/reviews/index.ts +0 -0
  316. package/views/dashboard/commerce/taxes/index.stx +377 -0
  317. package/views/dashboard/commerce/units/index.stx +317 -0
  318. package/views/dashboard/commerce/variants/index.stx +319 -0
  319. package/views/dashboard/commerce/waitlist/products.stx +942 -0
  320. package/views/dashboard/commerce/waitlist/restaurant.stx +1024 -0
  321. package/views/dashboard/components/index.stx +330 -0
  322. package/views/dashboard/composables/index.ts +19 -0
  323. package/views/dashboard/composables/useChart.ts +77 -0
  324. package/views/dashboard/composables/useNavigation.ts +99 -0
  325. package/views/dashboard/composables/useRole.ts +58 -0
  326. package/views/dashboard/composables/useTheme.ts +74 -0
  327. package/views/dashboard/content/authors/index.stx +171 -0
  328. package/views/dashboard/content/blog/index.stx +493 -0
  329. package/views/dashboard/content/categories/index.stx +169 -0
  330. package/views/dashboard/content/comments/index.stx +189 -0
  331. package/views/dashboard/content/dashboard.stx +383 -0
  332. package/views/dashboard/content/files/index.stx +706 -0
  333. package/views/dashboard/content/pages/index.stx +174 -0
  334. package/views/dashboard/content/posts/index.stx +269 -0
  335. package/views/dashboard/content/seo/index.stx +240 -0
  336. package/views/dashboard/content/tags/index.stx +167 -0
  337. package/views/dashboard/data/activity/index.stx +261 -0
  338. package/views/dashboard/data/dashboard/index.stx +116 -0
  339. package/views/dashboard/data/subscribers.stx +270 -0
  340. package/views/dashboard/data/teams.stx +275 -0
  341. package/views/dashboard/data/users.stx +279 -0
  342. package/views/dashboard/dependencies/index.stx +178 -0
  343. package/views/dashboard/deployments/[id].stx +234 -0
  344. package/views/dashboard/deployments/index.stx +9 -0
  345. package/views/dashboard/dns/index.stx +163 -0
  346. package/views/dashboard/environment/index.stx +283 -0
  347. package/views/dashboard/errors/index.stx +274 -0
  348. package/views/dashboard/forgot-password.stx +57 -0
  349. package/views/dashboard/functions/index.stx +159 -0
  350. package/views/dashboard/health/index.stx +248 -0
  351. package/views/dashboard/inbox/activity.stx +350 -0
  352. package/views/dashboard/inbox/index.stx +452 -0
  353. package/views/dashboard/inbox/settings.stx +290 -0
  354. package/views/dashboard/index.stx +252 -0
  355. package/views/dashboard/insights/index.stx +529 -0
  356. package/views/dashboard/jobs/[id].stx +430 -0
  357. package/views/dashboard/jobs/history.stx +222 -0
  358. package/views/dashboard/jobs/index.stx +214 -0
  359. package/views/dashboard/kanban/[id].stx +899 -0
  360. package/views/dashboard/kanban/index.stx +260 -0
  361. package/views/dashboard/layouts/default.stx +565 -0
  362. package/views/dashboard/layouts/guest.stx +10 -0
  363. package/views/dashboard/login.stx +48 -0
  364. package/views/dashboard/logs/index.stx +540 -0
  365. package/views/dashboard/mailboxes/index.stx +149 -0
  366. package/views/dashboard/management/permissions/index.stx +490 -0
  367. package/views/dashboard/marketing/campaigns/index.stx +604 -0
  368. package/views/dashboard/marketing/lists/index.stx +514 -0
  369. package/views/dashboard/marketing/reviews/index.stx +475 -0
  370. package/views/dashboard/marketing/social-posts/index.stx +560 -0
  371. package/views/dashboard/models/[model].stx +195 -0
  372. package/views/dashboard/models/index.stx +159 -0
  373. package/views/dashboard/monitoring/errors/index.stx +415 -0
  374. package/views/dashboard/notifications/dashboard.stx +383 -0
  375. package/views/dashboard/notifications/email.stx +336 -0
  376. package/views/dashboard/notifications/history.stx +373 -0
  377. package/views/dashboard/notifications/sms.stx +332 -0
  378. package/views/dashboard/packages/index.stx +195 -0
  379. package/views/dashboard/queries/[id].stx +381 -0
  380. package/views/dashboard/queries/history.stx +227 -0
  381. package/views/dashboard/queries/index.stx +335 -0
  382. package/views/dashboard/queries/slow.stx +318 -0
  383. package/views/dashboard/queue/index.stx +427 -0
  384. package/views/dashboard/realtime/index.stx +429 -0
  385. package/views/dashboard/register.stx +46 -0
  386. package/views/dashboard/releases/index.stx +389 -0
  387. package/views/dashboard/requests/index.stx +462 -0
  388. package/views/dashboard/serverless/index.stx +475 -0
  389. package/views/dashboard/servers/[id].stx +299 -0
  390. package/views/dashboard/servers/index.stx +483 -0
  391. package/views/dashboard/settings/billing.stx +50 -0
  392. package/views/dashboard/settings/index.stx +256 -0
  393. package/views/dashboard/settings/mail.stx +252 -0
  394. package/views/dashboard/stores/auth.ts +123 -0
  395. package/views/dashboard/stores/ci.ts +342 -0
  396. package/views/dashboard/stores/index.ts +129 -0
  397. package/views/dashboard/stores/kanban.ts +723 -0
  398. package/views/dashboard/stores/rbac.ts +350 -0
  399. package/views/dashboard/stx.config.ts +34 -0
  400. package/views/dashboard/teams/[id].stx +170 -0
  401. package/views/forum/category.stx +92 -0
  402. package/views/forum/index.stx +209 -0
  403. package/views/forum/new-topic.stx +113 -0
  404. package/views/forum/profile.stx +1 -0
  405. package/views/forum/topic.stx +143 -0
  406. package/views/hello-world/[name].stx +47 -0
  407. package/views/index.stx +21 -0
  408. package/views/system-tray/index.stx +147 -0
@@ -0,0 +1,559 @@
1
+ ---
2
+ name: stacks-queue
3
+ description: Use when working with job queues in a Stacks application — creating jobs, dispatching, workers, batches, failed jobs, queue events, health checks, testing, Redis/database/sync drivers, rate limiting, or scheduled jobs. Covers @stacksjs/queue, config/queue.ts, and app/Jobs/.
4
+ license: MIT
5
+ compatibility: Bun >= 1.3.0, TypeScript
6
+ allowed-tools: Read Edit Write Bash Grep Glob
7
+ ---
8
+
9
+ # Stacks Queue System
10
+
11
+ ## Key Paths
12
+ - Queue package: `storage/framework/core/queue/src/`
13
+ - Configuration: `config/queue.ts`
14
+ - Application jobs: `app/Jobs/`
15
+ - Job model: `storage/framework/defaults/app/Models/Job.ts`
16
+ - Failed job model: `storage/framework/defaults/app/Models/FailedJob.ts`
17
+
18
+ ## Source Files
19
+ ```
20
+ queue/src/
21
+ ├── action.ts # Job class with dispatch/dispatchIf/dispatchAfter/dispatchNow
22
+ ├── job.ts # JobBuilder fluent API + job() helper + jobBatch() + runJob()
23
+ ├── discovery.ts # Job auto-discovery from app/Jobs/ via Bun.Glob
24
+ ├── scheduler.ts # Cron-based job scheduling with overlap prevention
25
+ ├── worker.ts # Queue worker (database polling & Redis processing)
26
+ ├── batch.ts # Job batching (PendingBatch, DispatchedBatch, Batch)
27
+ ├── events.ts # QueueEvents emitter, QueueMetrics, WorkerTracker
28
+ ├── health.ts # Queue health checks (checkQueueHealth, HTTP handler)
29
+ ├── notifications.ts # Failed job notifications (email/slack/discord/webhook)
30
+ ├── testing.ts # FakeQueue, QueueTester, runJob, expectJobToFail
31
+ ├── utils.ts # storeJob helper
32
+ ├── drivers/redis.ts # RedisQueue (bun-queue wrapper), StacksQueueManager
33
+ └── index.ts # Re-exports everything
34
+ ```
35
+
36
+ ## Job Class (action.ts)
37
+
38
+ ```typescript
39
+ export class Job {
40
+ name: string
41
+ description: string
42
+ action?: string | Function // action name or function
43
+ handle?: Function // direct handler function
44
+ queue?: string // queue name (default: 'default')
45
+ rate?: string // cron expression for scheduling
46
+ tries?: number // max retry attempts
47
+ timeout?: number // timeout in seconds
48
+ backoff?: number | number[] // backoff delay(s) in seconds
49
+ backoffConfig?: { strategy, initialDelay, factor, maxDelay, jitter }
50
+ enabled?: boolean
51
+
52
+ async dispatch(payload?): Promise<void>
53
+ async dispatchIf(condition: boolean, payload?): Promise<void>
54
+ async dispatchUnless(condition: boolean, payload?): Promise<void>
55
+ async dispatchAfter(delaySeconds: number, payload?): Promise<void>
56
+ async dispatchNow(payload?): Promise<void> // bypasses queue, runs immediately
57
+ }
58
+ ```
59
+
60
+ ### Dispatch Routing
61
+ 1. Checks `isFaked()` -- if testing, dispatches to FakeQueue
62
+ 2. `sync` driver: calls `dispatchNow()` (immediate execution)
63
+ 3. `redis` driver: creates `RedisQueue`, calls `queue.add()` with bun-queue options
64
+ 4. `database` driver: inserts into `jobs` table with JSON payload
65
+ 5. Fallback: `dispatchNow()`
66
+
67
+ ### dispatchNow() Execution Order
68
+ 1. If `handle` is a function: `await this.handle(payload)`
69
+ 2. If `action` is a string: `await runAction(this.action)` via `@stacksjs/actions`
70
+ 3. If `action` is a function: `await this.action()`
71
+ 4. Otherwise: throws error
72
+
73
+ ## JobBuilder Fluent API (job.ts)
74
+
75
+ ```typescript
76
+ import { job } from '@stacksjs/queue'
77
+
78
+ await job('SendWelcomeEmail', { email, name })
79
+ .onQueue('emails')
80
+ .delay(60) // seconds
81
+ .tries(5)
82
+ .timeout(30) // seconds
83
+ .backoff([10, 30, 60])
84
+ .withContext({ userId })
85
+ .dispatch()
86
+
87
+ // Conditional dispatch
88
+ await job('ProcessOrder', data).dispatchIf(isValid)
89
+ await job('ProcessOrder', data).dispatchUnless(isDuplicate)
90
+
91
+ // Immediate execution
92
+ await job('SendNotification', data).dispatchNow()
93
+ ```
94
+
95
+ The `job()` helper loads job modules from `app/Jobs/{name}.ts` via `runJob()`.
96
+
97
+ ### runJob(name, options)
98
+ Dynamically imports `app/Jobs/{name}.ts` and executes:
99
+ 1. `jobConfig.handle(payload)` if handle is a function
100
+ 2. `runAction(jobConfig.action)` if action is a string
101
+ 3. `jobConfig.action()` if action is a function
102
+ 4. `jobConfig(payload, context)` if default export is a function
103
+
104
+ ### jobBatch() Helper
105
+ ```typescript
106
+ import { jobBatch } from '@stacksjs/queue'
107
+ const batch = await jobBatch([SendEmail, ProcessOrder])
108
+ .name('Onboard User')
109
+ .allowFailures()
110
+ .then(async (b) => console.log('All done!'))
111
+ .dispatch()
112
+ ```
113
+
114
+ ## Job Batching (batch.ts)
115
+
116
+ ### PendingBatch (not yet dispatched)
117
+ ```typescript
118
+ const batch = Batch.create([
119
+ { job: SendEmail, payload: { to: 'a@b.com' } },
120
+ { job: ProcessImage, payload: { id: 1 } },
121
+ ])
122
+ .name('welcome-batch')
123
+ .onQueue('default')
124
+ .allowFailures() // continue processing on failure
125
+ .then(async (batch) => {}) // all succeeded (or allowFailures + all done)
126
+ .catch(async (batch, error) => {}) // first failure
127
+ .finally(async (batch) => {}) // always runs
128
+ .progress(async (batch) => {}) // each job completion
129
+
130
+ const dispatched = await batch.dispatch()
131
+ ```
132
+
133
+ ### DispatchedBatch (inspectable)
134
+ ```typescript
135
+ const b = await Batch.find(batchId) // returns DispatchedBatch | null
136
+ await b.progress() // 0-100 percentage
137
+ await b.pendingJobs() // count
138
+ await b.failedJobs() // count
139
+ await b.completedJobs() // total - pending
140
+ await b.failedJobIds() // string[]
141
+ await b.finished() // boolean (finished_at !== null)
142
+ await b.cancelled() // boolean
143
+ await b.hasFailures() // boolean
144
+ await b.cancel() // prevents remaining jobs, fires finally callbacks
145
+ await b.add([moreJobs]) // add jobs to running batch
146
+ await b.delete() // remove batch record + callbacks
147
+ await b.fresh() // get fresh BatchRecord from storage
148
+ ```
149
+
150
+ ### Batch Static Methods
151
+ ```typescript
152
+ Batch.create(jobs): PendingBatch
153
+ await Batch.find(id): DispatchedBatch | null
154
+ await Batch.all(): DispatchedBatch[]
155
+ await Batch.prune(olderThanHours = 24): number // delete finished batches
156
+ ```
157
+
158
+ ### Batch Storage
159
+ - **Database**: `job_batches` table with columns: `id`, `name`, `total_jobs`, `pending_jobs`, `failed_jobs`, `failed_job_ids` (JSON), `options` (JSON), `cancelled_at`, `created_at`, `finished_at`
160
+ - **Redis**: hash at `stacks:batch:{id}`, set index at `stacks:batches`
161
+ - Callbacks stored in in-memory `Map` (not serializable -- only work in dispatching process)
162
+
163
+ ### Batch Lifecycle
164
+ - On job completion: decrements `pending_jobs`, fires progress callbacks. When `pending_jobs === 0`: fires `then` + `finally` callbacks, emits `batch:completed`
165
+ - On job failure: increments `failed_jobs`, appends to `failed_job_ids`. If `allowFailures` is false: cancels batch immediately. Fires `catch` callbacks, emits `batch:failed`
166
+ - Worker checks `_batchId` in payload to skip cancelled batch jobs
167
+
168
+ ## Job Discovery (discovery.ts)
169
+
170
+ ```typescript
171
+ const jobs = await discoverJobs() // scans app/Jobs/**/*.{ts,js} via Bun.Glob
172
+ const job = getJob('SendWelcomeEmail') // by name from registry
173
+ const scheduled = getScheduledJobs() // jobs with rate/schedule
174
+ const all = getAllJobs()
175
+ await executeJob('SendWelcomeEmail', payload) // execute by name
176
+ ```
177
+
178
+ Skips `.test.`, `.spec.`, and `index` files. Supports both class-based (has `handle` on prototype) and function-based (object with `handle` method) jobs.
179
+
180
+ ### JobRegistry
181
+ Global singleton `jobRegistry`:
182
+ - `register(job)`, `get(name)`, `all()`, `byQueue(queue)`, `scheduled()`, `has(name)`, `clear()`
183
+
184
+ ### BackoffConfig
185
+ ```typescript
186
+ interface BackoffConfig {
187
+ strategy?: 'fixed' | 'exponential' | 'linear'
188
+ initialDelay?: number
189
+ factor?: number
190
+ maxDelay?: number
191
+ jitter?: { enabled?: boolean, factor?: number, minDelay?: number, maxDelay?: number }
192
+ }
193
+ ```
194
+
195
+ ## Job Scheduler (scheduler.ts)
196
+
197
+ Cron-based scheduling with 1-minute check interval.
198
+
199
+ ```typescript
200
+ await startScheduler({ checkInterval: 60000, timezone: 'UTC', preventOverlapping: true })
201
+ await stopScheduler()
202
+ isSchedulerRunning()
203
+ getSchedulerStatus() // { isRunning, jobCount, jobs: [{ name, schedule, lastRun, nextRun, isRunning }] }
204
+ getRegisteredJobs() // Map<string, ScheduledJobState>
205
+ await triggerJob('SendReport') // manually trigger a scheduled job
206
+ ```
207
+
208
+ ### Cron Support
209
+ - Standard 5-part cron: `minute hour day month dayOfWeek`
210
+ - 6-part cron (with seconds): seconds are stripped, runs at minute granularity
211
+ - Predefined: `@yearly`, `@monthly`, `@weekly`, `@daily`, `@hourly`, `@midnight`
212
+ - Every expressions: `Every.minute`, `Every.fiveMinutes`, `Every.hour`, `Every.day`, etc.
213
+ - Supports lists (`1,2,3`), ranges (`1-5`), steps (`*/5`, `1-5/2`), wildcards (`*`)
214
+
215
+ ### Overlap Prevention
216
+ - Global `preventOverlapping: true` (default) skips if job is still running
217
+ - Per-job `withoutOverlapping: true` on job config
218
+
219
+ ## Queue Worker (worker.ts)
220
+
221
+ ```typescript
222
+ await startProcessor('default', { concurrency: 5 }) // start worker
223
+ await stopProcessor() // graceful shutdown
224
+ await executeFailedJobs() // requeue all failed jobs
225
+ await retryFailedJob(id) // requeue specific failed job
226
+ getActiveJobCount() // currently processing count
227
+ isWorkerRunning() // boolean
228
+ ```
229
+
230
+ ### Database Worker Details
231
+ - Polls `jobs` table every **1 second** (`sleep(1000)`)
232
+ - Refreshes queue list every **10 seconds** via `getAllQueues()` (SELECT DISTINCT queue FROM jobs)
233
+ - **Atomic claim**: SELECT pending job + UPDATE with CAS pattern (`WHERE reserved_at IS NULL`)
234
+ - Increments `attempts`, sets `reserved_at` to current timestamp
235
+ - If `numUpdatedRows === 0`, another worker claimed it -- tries next
236
+ - On success: DELETE from `jobs`, emit `job:completed`
237
+ - On failure: compare `attempts` vs `maxAttempts` (from payload `options.tries`, default 1)
238
+ - **Exceeded**: move to `failed_jobs` table (uuid, connection, queue, payload, exception, failed_at), emit `job:failed`
239
+ - **Retrying**: release job by setting `reserved_at = null`, `available_at = now + backoff`, emit `job:retrying`
240
+ - Backoff: uses `options.backoff` from payload -- array (indexed by attempt), number (fixed), or 30s default
241
+ - Worker ID: `worker-{pid}-{timestamp}`
242
+ - Handles `SIGINT` / `SIGTERM` for graceful shutdown
243
+
244
+ ### Redis Worker Details
245
+ - Uses `RedisQueue.process(concurrency, handler)` from bun-queue
246
+ - Handler receives `BunJob` objects
247
+ - **Throws on failure** for bun-queue's built-in retry handling
248
+ - Keep-alive loop: polls `sleep(1000)` until `workerRunning === false`
249
+ - Closes queue on shutdown
250
+
251
+ ### Job Payload Format (database)
252
+ ```json
253
+ {
254
+ "jobName": "SendWelcomeEmail",
255
+ "payload": { "email": "user@example.com" },
256
+ "options": { "queue": "emails", "tries": 3, "timeout": 30, "backoff": [10, 30, 60] }
257
+ }
258
+ ```
259
+
260
+ ## Queue Events (events.ts)
261
+
262
+ ### QueueEvents Class
263
+ Custom event emitter (not mitt-based) with `Map<QueueEventType, Set<Handler>>` + wildcard Set.
264
+
265
+ ```typescript
266
+ const events = getQueueEvents() // global singleton
267
+ const unsub = events.on('job:failed', handler)
268
+ const unsub2 = events.onAny((event, payload) => { ... })
269
+ events.once('job:completed', handler)
270
+ await events.emit('job:completed', payload)
271
+ events.off('job:completed') // remove all handlers for event
272
+ events.removeAllListeners()
273
+ ```
274
+
275
+ ### Convenience Functions
276
+ ```typescript
277
+ const unsub = onQueueEvent('job:failed', (payload) => { ... })
278
+ const unsub2 = onQueueEvent('*', (event, payload) => { ... })
279
+ await emitQueueEvent('job:completed', { jobId, result, duration })
280
+ ```
281
+
282
+ ### Event Types
283
+ `'job:added'` | `'job:processing'` | `'job:completed'` | `'job:failed'` | `'job:retrying'` | `'job:stalled'` | `'job:progress'` | `'queue:paused'` | `'queue:resumed'` | `'queue:error'` | `'worker:started'` | `'worker:stopped'` | `'batch:added'` | `'batch:completed'` | `'batch:failed'`
284
+
285
+ ### QueueEventPayload
286
+ ```typescript
287
+ interface QueueEventPayload {
288
+ jobId?: string, queueName?: string, jobName?: string, data?: any,
289
+ result?: any, error?: Error, progress?: number, timestamp: number,
290
+ attemptsMade?: number, duration?: number
291
+ }
292
+ ```
293
+
294
+ ### Event-Aware Wrapper
295
+ ```typescript
296
+ const wrappedHandler = withEvents('emails', originalHandler)
297
+ // Automatically emits job:processing, job:completed, job:failed
298
+ ```
299
+
300
+ ### OnQueueEvent Decorator
301
+ ```typescript
302
+ class MyHandler {
303
+ @OnQueueEvent('job:failed')
304
+ handleFailed(payload: QueueEventPayload) { ... }
305
+ }
306
+ ```
307
+
308
+ ### QueueMetrics
309
+ ```typescript
310
+ const metrics = getGlobalMetrics() // global singleton
311
+ metrics.getThroughputPerMinute() // completions in last 60 seconds
312
+ metrics.getAverageProcessingTime() // avg duration in last 60 seconds
313
+ metrics.getMetrics() // { counts: {added,completed,failed,processing}, averageDuration, recentErrors, throughputPerMinute }
314
+ metrics.reset()
315
+ metrics.stop() // unsubscribe from events
316
+ ```
317
+ Tracks last 1000 completions and last 100 errors in sliding windows.
318
+
319
+ ### WorkerTracker
320
+ ```typescript
321
+ const tracker = getWorkerTracker() // global singleton (not lazy)
322
+ tracker.register(id, queue)
323
+ tracker.markActive(id) / tracker.markIdle(id)
324
+ tracker.recordCompletion(id) / tracker.recordFailure(id)
325
+ tracker.unregister(id) // marks as 'stopped'
326
+ tracker.getAll(): TrackedWorker[]
327
+ tracker.clear()
328
+ ```
329
+
330
+ ```typescript
331
+ interface TrackedWorker {
332
+ id: string, status: 'active' | 'idle' | 'stopped', queue: string,
333
+ processedCount: number, failedCount: number,
334
+ lastActivityAt: string, startedAt: string
335
+ }
336
+ ```
337
+
338
+ ## Queue Health (health.ts)
339
+
340
+ ```typescript
341
+ const result = await checkQueueHealth({
342
+ maxPendingWarning: 1000, maxPendingCritical: 5000,
343
+ maxFailedWarning: 10, maxFailedCritical: 100,
344
+ maxJobAgeWarning: 3600, maxJobAgeCritical: 86400, // seconds
345
+ maxErrorRateWarning: 0.1, maxErrorRateCritical: 0.5,
346
+ queues?: ['default', 'emails'] // filter to specific queues
347
+ })
348
+ // result: { status: 'healthy'|'degraded'|'unhealthy', queues, workers, metrics, alerts }
349
+ ```
350
+
351
+ - Queries `jobs` and `failed_jobs` tables directly
352
+ - Groups by queue name, calculates pending/processing/delayed/failed counts
353
+ - Oldest job age calculated from `created_at`
354
+ - Error rate = totalFailed / totalJobs
355
+ - Worker statuses from `getWorkerTracker().getAll()`
356
+ - Metrics from `getGlobalMetrics()`
357
+ - Returns real `TrackedWorker` data even if DB query fails
358
+
359
+ ```typescript
360
+ const healthy = await isQueueHealthy() // returns boolean
361
+ const handler = createHealthCheckHandler() // HTTP handler (200/207/503)
362
+ ```
363
+
364
+ ## Failed Job Notifications (notifications.ts)
365
+
366
+ ```typescript
367
+ configureFailedJobNotifications({
368
+ channels: ['email', 'slack', 'discord', 'webhook', 'log'],
369
+ email: { to: ['admin@example.com'], from?: string, subject?: string },
370
+ slack: { webhookUrl: '...', channel?: '#alerts', username?: 'Queue Monitor', iconEmoji?: ':warning:' },
371
+ discord: { webhookUrl: '...', username?: 'Queue Monitor', avatarUrl?: string },
372
+ webhook: { url: '...', headers?: Record<string,string>, secret?: 'hmac-key' },
373
+ rateLimit: 100, // max per hour
374
+ batch: true, // batch notifications
375
+ batchInterval: 60000, // ms (default 60s)
376
+ filter: (job) => job.queue !== 'low-priority',
377
+ })
378
+ ```
379
+
380
+ - **Email**: uses `@stacksjs/email` (`mail.send()`) with HTML table format
381
+ - **Slack**: sends Block Kit payload (header + sections, max 10 jobs per message)
382
+ - **Discord**: sends embeds (red color, max 10 per message)
383
+ - **Webhook**: JSON payload with HMAC SHA-256 signature in `X-Signature` header (via `crypto.subtle`)
384
+ - **Log**: writes to application log via `@stacksjs/logging`
385
+ - Rate limiter resets every **3,600,000ms** (1 hour)
386
+ - Batch: accumulates jobs, flushes on interval via `setTimeout`
387
+
388
+ ```typescript
389
+ const notifier = getFailedJobNotifier() // global singleton
390
+ await notifyJobFailed(jobInfo) // uses global notifier
391
+ ```
392
+
393
+ ## Queue Testing (testing.ts)
394
+
395
+ ```typescript
396
+ import { fake, restore, runJob as runTestJob, expectJobToFail } from '@stacksjs/queue'
397
+
398
+ const fakeQueue = fake() // sets global FakeQueue singleton
399
+
400
+ // Dispatch goes to fake queue (not real driver)
401
+ await job('SendEmail', data).dispatch()
402
+ await SendWelcomeEmail.dispatch(data)
403
+
404
+ // Assertions (throw on failure)
405
+ fakeQueue.assertDispatched('SendEmail')
406
+ fakeQueue.assertDispatched('SendEmail', (j) => j.data.to === 'test@example.com')
407
+ fakeQueue.assertNotDispatched('ProcessPayment')
408
+ fakeQueue.assertDispatchedTimes('SendEmail', 3)
409
+ fakeQueue.assertNothingDispatched()
410
+ fakeQueue.assertPushed('Reminder')
411
+ fakeQueue.assertPushedWithDelay('Reminder', 3600)
412
+ fakeQueue.assertPushedOn('emails', 'SendEmail')
413
+
414
+ // Run job synchronously for testing
415
+ const result = await runTestJob({ handle: async (data) => 'ok' }, testData)
416
+
417
+ // Assert job failure
418
+ const error = await expectJobToFail(BadJob, data, /expected error/)
419
+ // or: await expectJobToFail(BadJob, data, 'substring match')
420
+
421
+ restore() // clears FakeQueue singleton, re-enables real queue
422
+ ```
423
+
424
+ ### QueueTester Class
425
+ ```typescript
426
+ const tester = createQueueTester() // auto-calls fake()
427
+ tester.dispatch('Job', data).assertDispatched('Job').reset()
428
+ tester.cleanup() // calls restore()
429
+ ```
430
+
431
+ ### FakeQueue Internals
432
+ - `dispatchedJobs[]` -- recorded dispatches
433
+ - `pushedJobs[]` -- recorded delayed pushes
434
+ - `processedJobs[]` -- simulated processing results
435
+ - `failedJobs[]` -- simulated failures
436
+
437
+ ## Redis Driver (drivers/redis.ts)
438
+
439
+ Wraps `bun-queue`'s `Queue` class with Stacks-compatible API.
440
+
441
+ ```typescript
442
+ const queue = new RedisQueue<T>('emails', redisConfig)
443
+ await queue.add(data, options) // delay/attempts/priority/timeout/backoff
444
+ queue.process(concurrency, handler) // start processing (one-time call)
445
+ await queue.getJob(jobId)
446
+ await queue.getJobs('waiting' | 'active' | 'completed' | 'failed' | 'delayed')
447
+ await queue.getJobCounts() // { waiting, active, completed, failed, delayed }
448
+ await queue.removeJob(jobId)
449
+ await queue.pause() / queue.resume()
450
+ await queue.empty() / queue.close()
451
+ await queue.getMetrics()
452
+ await queue.ping() // health check
453
+ await queue.scheduleCron({ cron, data, tz?, name? })
454
+ await queue.unscheduleCron(jobId)
455
+ await queue.getDeadLetterJobs()
456
+ await queue.republishDeadLetterJob(jobId)
457
+ await queue.clearDeadLetterQueue()
458
+ await queue.bulkRemove(jobIds): number
459
+ await queue.getClusterInfo()
460
+ queue.isLeader(): boolean // for horizontal scaling
461
+ queue.getQueue(): BunQueue<T> // underlying bun-queue instance
462
+ queue.on(event, handler) // subscribe to bun-queue events
463
+ ```
464
+
465
+ ### StacksQueueManager
466
+ ```typescript
467
+ const manager = new StacksQueueManager(connectionConfigs)
468
+ manager.queue('emails') // get or create RedisQueue by name
469
+ manager.setDefaultConnection('high-priority')
470
+ await manager.closeAll()
471
+ ```
472
+
473
+ ### RedisQueue Events
474
+ `jobAdded`, `jobCompleted`, `jobFailed`, `jobProgress`, `jobActive`, `jobStalled`, `jobDelayed`, `ready`, `error`
475
+
476
+ ### bun-queue Re-exports
477
+ `batch`, `chain`, `dispatch`, `dispatchAfter`, `dispatchSync`, `getQueueManager`, `setQueueManager`, `QueueManager`
478
+
479
+ ## Creating a Job
480
+
481
+ ```typescript
482
+ // app/Jobs/SendWelcomeEmail.ts
483
+ import { Job } from '@stacksjs/queue'
484
+
485
+ export default new Job({
486
+ name: 'SendWelcomeEmail',
487
+ description: 'Send welcome email to new user',
488
+ queue: 'emails',
489
+ tries: 3,
490
+ backoff: [10, 30, 60],
491
+ timeout: 30,
492
+
493
+ async handle(payload: { email: string; name: string }) {
494
+ console.log(`Sending to ${payload.email}`)
495
+ return { sent: true }
496
+ }
497
+ })
498
+ ```
499
+
500
+ ### Dispatching
501
+ ```typescript
502
+ import SendWelcomeEmail from '~/app/Jobs/SendWelcomeEmail'
503
+
504
+ await SendWelcomeEmail.dispatch({ email, name })
505
+ await SendWelcomeEmail.dispatchIf(isNewUser, { email, name })
506
+ await SendWelcomeEmail.dispatchUnless(isDuplicate, { email, name })
507
+ await SendWelcomeEmail.dispatchAfter(60, { email, name }) // 60s delay
508
+ await SendWelcomeEmail.dispatchNow({ email, name }) // bypass queue
509
+ ```
510
+
511
+ ## config/queue.ts
512
+ ```typescript
513
+ {
514
+ default: 'sync', // 'sync' | 'database' | 'redis' | 'sqs' | 'memory'
515
+ connections: {
516
+ sync: { driver: 'sync' },
517
+ database: { driver: 'database', table: 'jobs', queue: 'default', retryAfter: 90 },
518
+ redis: {
519
+ driver: 'redis',
520
+ redis: { url, host: 'localhost', port: 6379, password, db: 0 },
521
+ prefix: 'stacks:queue',
522
+ concurrency: 5,
523
+ logLevel: 'info',
524
+ distributedLock: true,
525
+ stalledJobCheckInterval: 30000,
526
+ maxStalledJobRetries: 3,
527
+ limiter: { max: 100, duration: 1000 }, // if QUEUE_RATE_LIMIT_ENABLED
528
+ metrics: { enabled: false, collectInterval: 10000 },
529
+ defaultDeadLetterOptions: { enabled: true, maxRetries: 3, queueSuffix: '-dead-letter' },
530
+ horizontalScaling: { enabled, maxWorkersPerInstance, jobsPerWorker, leaderElection: { heartbeatInterval: 5000, leaderTimeout: 15000 } },
531
+ defaultJobOptions: { attempts: 3, removeOnComplete: true, removeOnFail: false, backoff: { type: 'exponential', delay: 1000 } }
532
+ },
533
+ sqs: { driver: 'sqs', key, secret, prefix, suffix, queue: 'default', region: 'us-east-1' },
534
+ memory: { driver: 'memory', maxSize: 10000 }
535
+ },
536
+ failed: { driver: 'database', table: 'failed_jobs', prefix: 'stacks:failed' },
537
+ batching: { driver: 'redis', prefix: 'stacks:batches' },
538
+ worker: { concurrency: 5, shutdownTimeout: 30000 }
539
+ }
540
+ ```
541
+
542
+ ## Gotchas
543
+ - Default driver is `sync` -- jobs run immediately in the same process (no background processing)
544
+ - Database driver polls every 1 second, refreshes queue list every 10 seconds
545
+ - Atomic claim prevents race conditions: SELECT + UPDATE WHERE reserved_at IS NULL (CAS pattern)
546
+ - Backoff supports: fixed number, array of delays (indexed by attempt), or BackoffConfig object with strategy
547
+ - Job batches store callbacks in-memory (`Map`) -- callbacks only work in the dispatching process, not across restarts
548
+ - Redis driver wraps `bun-queue` -- uses `bun-queue` Queue class for actual queue management
549
+ - Failed job notifications use HMAC SHA-256 (via Web Crypto API) for webhook signatures
550
+ - Health check queries `jobs` and `failed_jobs` tables directly for real counts
551
+ - Rate limiter for notifications resets hourly (3,600,000ms)
552
+ - Testing: `fake()` sets global singleton, `restore()` clears it -- affects ALL code in the process
553
+ - Queue events use a custom `QueueEvents` class (Map + Set), not mitt
554
+ - Metrics track last 1000 completions and 100 errors in sliding windows
555
+ - Worker ID format: `worker-{pid}-{timestamp}`
556
+ - The `QUEUE_DRIVER` env var controls which driver is used (reads via `@stacksjs/env`)
557
+ - `dispatchAfter()` on sync driver uses `setTimeout` to delay then executes immediately
558
+ - Failed jobs table: `uuid`, `connection` ('database'), `queue`, `payload`, `exception` (stack trace), `failed_at`
559
+ - `process.on('unhandledRejection')` and `process.on('uncaughtException')` are set at module load in worker.ts