@stacksjs/defaults 0.70.354 → 0.70.356

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 (214) hide show
  1. package/ai/skills/stacks-browse/SKILL.md +17 -2
  2. package/ai/skills/stacks-browse/scripts/browse.ts +207 -6
  3. package/ai/skills/stacks-email/SKILL.md +73 -16
  4. package/app/Actions/ContactAction.ts +2 -2
  5. package/app/Actions/Dashboard/Analytics/EventAnalyticsAction.ts +2 -3
  6. package/app/Actions/Dashboard/Analytics/MarketingAnalyticsAction.ts +2 -3
  7. package/app/Actions/Dashboard/Analytics/SalesAnalyticsAction.ts +2 -3
  8. package/app/Actions/Dashboard/Analytics/WebAnalyticsAction.ts +9 -3
  9. package/app/Actions/Dashboard/Auth/MeAction.ts +12 -14
  10. package/app/Actions/Dashboard/Buddy/BuddyChatAction.ts +2 -3
  11. package/app/Actions/Dashboard/Ci/RepoRunJobsAction.ts +10 -15
  12. package/app/Actions/Dashboard/Ci/RepoRunsAction.ts +12 -14
  13. package/app/Actions/Dashboard/Ci/RunnerHistoryAction.ts +11 -10
  14. package/app/Actions/Dashboard/Ci/StatusAction.ts +2 -11
  15. package/app/Actions/Dashboard/Cloud/CloudIndexAction.ts +8 -1
  16. package/app/Actions/Dashboard/Cloud/ServerlessIndexAction.ts +15 -8
  17. package/app/Actions/Dashboard/Commerce/CommerceCategoriesAction.ts +2 -4
  18. package/app/Actions/Dashboard/Commerce/CommerceCouponsAction.ts +2 -4
  19. package/app/Actions/Dashboard/Commerce/CommerceCustomersAction.ts +2 -4
  20. package/app/Actions/Dashboard/Commerce/CommerceDashboardAction.ts +2 -4
  21. package/app/Actions/Dashboard/Commerce/CommerceDeliveryAction.ts +2 -4
  22. package/app/Actions/Dashboard/Commerce/CommerceGiftCardsAction.ts +2 -4
  23. package/app/Actions/Dashboard/Commerce/CommerceOrdersAction.ts +2 -4
  24. package/app/Actions/Dashboard/Commerce/CommercePaymentsAction.ts +2 -4
  25. package/app/Actions/Dashboard/Commerce/CommercePosAction.ts +2 -4
  26. package/app/Actions/Dashboard/Commerce/CommercePosCheckoutAction.ts +76 -61
  27. package/app/Actions/Dashboard/Commerce/CommercePrintDevicesAction.ts +2 -4
  28. package/app/Actions/Dashboard/Commerce/CommercePrintLogsAction.ts +4 -7
  29. package/app/Actions/Dashboard/Commerce/CommerceProductDetailAction.ts +4 -4
  30. package/app/Actions/Dashboard/Commerce/CommerceProductsAction.ts +2 -4
  31. package/app/Actions/Dashboard/Commerce/CommerceTaxesAction.ts +2 -4
  32. package/app/Actions/Dashboard/Commerce/DeliveryRouteIndexAction.ts +2 -4
  33. package/app/Actions/Dashboard/Commerce/DigitalDeliveryIndexAction.ts +2 -4
  34. package/app/Actions/Dashboard/Commerce/DriverIndexAction.ts +2 -4
  35. package/app/Actions/Dashboard/Commerce/LicenseKeyIndexAction.ts +2 -4
  36. package/app/Actions/Dashboard/Commerce/LicenseKeyOptionsAction.ts +2 -4
  37. package/app/Actions/Dashboard/Commerce/ManufacturerIndexAction.ts +2 -4
  38. package/app/Actions/Dashboard/Commerce/PaymentRefundAction.ts +1 -0
  39. package/app/Actions/Dashboard/Commerce/ProductUnitDefaultAction.ts +1 -0
  40. package/app/Actions/Dashboard/Commerce/ProductUnitIndexAction.ts +2 -4
  41. package/app/Actions/Dashboard/Commerce/ProductVariantIndexAction.ts +2 -4
  42. package/app/Actions/Dashboard/Commerce/ProductWaitlistIndexAction.ts +2 -4
  43. package/app/Actions/Dashboard/Commerce/RestaurantWaitlistIndexAction.ts +2 -4
  44. package/app/Actions/Dashboard/Commerce/ReviewIndexAction.ts +2 -4
  45. package/app/Actions/Dashboard/Commerce/ShippingMethodIndexAction.ts +2 -4
  46. package/app/Actions/Dashboard/Commerce/ShippingRateIndexAction.ts +2 -4
  47. package/app/Actions/Dashboard/Commerce/ShippingZoneIndexAction.ts +2 -4
  48. package/app/Actions/Dashboard/Commerce/TaxRateDefaultAction.ts +1 -0
  49. package/app/Actions/Dashboard/DashboardActivityAction.ts +2 -5
  50. package/app/Actions/Dashboard/Data/ActivityIndexAction.ts +2 -5
  51. package/app/Actions/Dashboard/Data/SubscriberIndexAction.ts +2 -5
  52. package/app/Actions/Dashboard/Data/TeamIndexAction.ts +2 -5
  53. package/app/Actions/Dashboard/Data/UserIndexAction.ts +2 -5
  54. package/app/Actions/Dashboard/Deployments/CreateDeployment.ts +26 -21
  55. package/app/Actions/Dashboard/Deployments/GetAverageDeploymentTime.ts +8 -2
  56. package/app/Actions/Dashboard/Deployments/GetDeployScript.ts +3 -6
  57. package/app/Actions/Dashboard/Deployments/GetDeployment.ts +5 -5
  58. package/app/Actions/Dashboard/Deployments/GetDeploymentCount.ts +7 -1
  59. package/app/Actions/Dashboard/Deployments/GetDeploymentLiveTerminalOutput.ts +10 -9
  60. package/app/Actions/Dashboard/Deployments/GetDeployments.ts +2 -4
  61. package/app/Actions/Dashboard/Deployments/GetRecentDeployments.ts +8 -2
  62. package/app/Actions/Dashboard/Deployments/UpdateDeployScript.ts +21 -7
  63. package/app/Actions/Dashboard/Email/CapturedMailIndexAction.ts +2 -4
  64. package/app/Actions/Dashboard/Email/CapturedMailShowAction.ts +3 -4
  65. package/app/Actions/Dashboard/Email/InboxActivityAction.ts +28 -16
  66. package/app/Actions/Dashboard/Email/InboxAttachmentDownloadAction.ts +45 -0
  67. package/app/Actions/Dashboard/Email/InboxDeleteAction.ts +11 -6
  68. package/app/Actions/Dashboard/Email/InboxIndexAction.ts +5 -7
  69. package/app/Actions/Dashboard/Email/InboxMarkReadAction.ts +11 -6
  70. package/app/Actions/Dashboard/Email/InboxMarkUnreadAction.ts +11 -6
  71. package/app/Actions/Dashboard/Email/InboxPreferenceShowAction.ts +10 -4
  72. package/app/Actions/Dashboard/Email/InboxPreferenceUpdateAction.ts +8 -9
  73. package/app/Actions/Dashboard/Email/InboxShowAction.ts +7 -7
  74. package/app/Actions/Dashboard/Email/InboxStatsAction.ts +5 -7
  75. package/app/Actions/Dashboard/Email/inbox-request.ts +33 -0
  76. package/app/Actions/Dashboard/Infrastructure/InsightsAction.ts +15 -14
  77. package/app/Actions/Dashboard/Infrastructure/LogIndexAction.ts +4 -11
  78. package/app/Actions/Dashboard/Infrastructure/ServerShowAction.ts +3 -6
  79. package/app/Actions/Dashboard/Jobs/JobIndexAction.ts +9 -18
  80. package/app/Actions/Dashboard/Jobs/JobRetryAction.ts +4 -3
  81. package/app/Actions/Dashboard/Jobs/JobShowAction.ts +16 -11
  82. package/app/Actions/Dashboard/Jobs/JobStatsAction.ts +2 -4
  83. package/app/Actions/Dashboard/Kanban/BoardDestroyAction.ts +7 -8
  84. package/app/Actions/Dashboard/Kanban/BoardShowAction.ts +11 -19
  85. package/app/Actions/Dashboard/Kanban/BoardStoreAction.ts +6 -6
  86. package/app/Actions/Dashboard/Kanban/BoardUpdateAction.ts +7 -8
  87. package/app/Actions/Dashboard/Kanban/BoardsIndexAction.ts +3 -5
  88. package/app/Actions/Dashboard/Kanban/BoardsReorderAction.ts +6 -6
  89. package/app/Actions/Dashboard/Kanban/CardAssigneesSyncAction.ts +13 -12
  90. package/app/Actions/Dashboard/Kanban/CardCommentDestroyAction.ts +12 -15
  91. package/app/Actions/Dashboard/Kanban/CardCommentStoreAction.ts +21 -46
  92. package/app/Actions/Dashboard/Kanban/CardCommentUpdateAction.ts +43 -0
  93. package/app/Actions/Dashboard/Kanban/CardDestroyAction.ts +7 -8
  94. package/app/Actions/Dashboard/Kanban/CardLabelsSyncAction.ts +8 -9
  95. package/app/Actions/Dashboard/Kanban/CardShowAction.ts +6 -7
  96. package/app/Actions/Dashboard/Kanban/CardStoreAction.ts +9 -9
  97. package/app/Actions/Dashboard/Kanban/CardUpdateAction.ts +7 -8
  98. package/app/Actions/Dashboard/Kanban/CardsReorderAction.ts +6 -6
  99. package/app/Actions/Dashboard/Kanban/ColumnDestroyAction.ts +7 -8
  100. package/app/Actions/Dashboard/Kanban/ColumnStoreAction.ts +6 -6
  101. package/app/Actions/Dashboard/Kanban/ColumnUpdateAction.ts +7 -8
  102. package/app/Actions/Dashboard/Kanban/ColumnsReorderAction.ts +6 -6
  103. package/app/Actions/Dashboard/Kanban/LabelDestroyAction.ts +6 -7
  104. package/app/Actions/Dashboard/Kanban/LabelStoreAction.ts +6 -6
  105. package/app/Actions/Dashboard/Kanban/LabelUpdateAction.ts +7 -8
  106. package/app/Actions/Dashboard/Kanban/UsersListAction.ts +3 -4
  107. package/app/Actions/Dashboard/Kanban/kanban-comment.ts +14 -0
  108. package/app/Actions/Dashboard/Kanban/kanban-model.test.ts +36 -0
  109. package/app/Actions/Dashboard/Kanban/kanban-model.ts +14 -7
  110. package/app/Actions/Dashboard/Kanban/kanban-response.ts +5 -0
  111. package/app/Actions/Dashboard/Library/CreateComponent.ts +15 -6
  112. package/app/Actions/Dashboard/Library/CreateFunction.ts +15 -6
  113. package/app/Actions/Dashboard/Library/DependencyIndexAction.ts +88 -77
  114. package/app/Actions/Dashboard/Library/GetAverageReleaseTime.ts +21 -15
  115. package/app/Actions/Dashboard/Library/GetComponents.ts +14 -7
  116. package/app/Actions/Dashboard/Library/GetDownloadCount.ts +14 -5
  117. package/app/Actions/Dashboard/Library/GetFunctions.ts +12 -5
  118. package/app/Actions/Dashboard/Library/GetReleaseCount.ts +14 -5
  119. package/app/Actions/Dashboard/Library/GetReleases.ts +17 -10
  120. package/app/Actions/Dashboard/Library/PackageIndexAction.ts +14 -7
  121. package/app/Actions/Dashboard/Models/ModelDestroyAction.ts +11 -8
  122. package/app/Actions/Dashboard/Models/ModelShowAction.ts +97 -192
  123. package/app/Actions/Dashboard/Models/ModelStoreAction.ts +14 -12
  124. package/app/Actions/Dashboard/Models/ModelUpdateAction.ts +18 -16
  125. package/app/Actions/Dashboard/Models/ModelsIndexAction.ts +50 -19
  126. package/app/Actions/Dashboard/Models/model-api.ts +27 -0
  127. package/app/Actions/Dashboard/Models/model-write.ts +22 -15
  128. package/app/Actions/Dashboard/Notifications/NotificationDeliveryHistoryAction.ts +49 -40
  129. package/app/Actions/Dashboard/Notifications/NotificationDeliveryIndexAction.ts +20 -10
  130. package/app/Actions/Dashboard/Notifications/NotificationDeliveryOverviewAction.ts +96 -84
  131. package/app/Actions/Dashboard/Notifications/NotificationDeliveryRetryAction.ts +34 -21
  132. package/app/Actions/Dashboard/Queries/QueryDashboardAction.ts +2 -4
  133. package/app/Actions/Dashboard/Queries/QueryShowAction.ts +3 -3
  134. package/app/Actions/Dashboard/Queue/QueueStatsAction.ts +2 -4
  135. package/app/Actions/Dashboard/Queue/QueueWorkersAction.ts +2 -4
  136. package/app/Actions/Dashboard/Rbac/PermissionDestroyAction.ts +6 -6
  137. package/app/Actions/Dashboard/Rbac/PermissionStoreAction.ts +5 -4
  138. package/app/Actions/Dashboard/Rbac/PermissionsIndexAction.ts +2 -3
  139. package/app/Actions/Dashboard/Rbac/RoleDestroyAction.ts +6 -6
  140. package/app/Actions/Dashboard/Rbac/RolePermissionsShowAction.ts +6 -6
  141. package/app/Actions/Dashboard/Rbac/RolePermissionsSyncAction.ts +6 -9
  142. package/app/Actions/Dashboard/Rbac/RoleStoreAction.ts +5 -4
  143. package/app/Actions/Dashboard/Rbac/RolesIndexAction.ts +4 -6
  144. package/app/Actions/Dashboard/Rbac/UserRolesShowAction.ts +6 -7
  145. package/app/Actions/Dashboard/Rbac/UserRolesSyncAction.ts +6 -13
  146. package/app/Actions/Dashboard/Rbac/UsersListAction.ts +2 -3
  147. package/app/Actions/Dashboard/Rbac/rbac-response.ts +10 -0
  148. package/app/Actions/Dashboard/Realtime/RealtimeStatsAction.ts +2 -4
  149. package/app/Actions/Dashboard/Search/GlobalSearchAction.ts +69 -55
  150. package/app/Actions/Dashboard/Teams/TeamInvitationDestroyAction.ts +34 -16
  151. package/app/Actions/Dashboard/Teams/TeamInvitationResendAction.ts +52 -29
  152. package/app/Actions/Dashboard/Teams/TeamInviteAction.ts +104 -73
  153. package/app/Actions/Dashboard/Teams/TeamMemberDestroyAction.ts +36 -17
  154. package/app/Actions/Dashboard/Teams/TeamMemberUpdateAction.ts +41 -28
  155. package/app/Actions/Dashboard/Teams/TeamPeopleIndexAction.ts +71 -58
  156. package/app/Actions/Dashboard/Teams/team-invitation-delivery.ts +29 -7
  157. package/app/Actions/Dashboard/Teams/team-member-count.ts +4 -2
  158. package/app/Actions/Dashboard/Teams/team-records.test.ts +2 -0
  159. package/app/Actions/Dashboard/Teams/team-records.ts +2 -0
  160. package/app/Actions/Dashboard/dashboard-request.ts +13 -0
  161. package/app/Actions/Dashboard/dashboard-response.ts +16 -0
  162. package/app/Actions/SendWelcomeEmail.ts +1 -1
  163. package/app/Actions/Teams/AcceptInvitationAction.ts +74 -51
  164. package/app/Actions/Teams/ShowInvitationAction.ts +21 -14
  165. package/app/Actions/Teams/team-response.ts +23 -0
  166. package/app/Mail/OrderConfirmation.ts +1 -1
  167. package/app/Mail/SubscriptionConfirmation.ts +1 -1
  168. package/app/Mail/TeamInvitation.ts +1 -1
  169. package/app/Models/CardComment.ts +3 -4
  170. package/app/Models/EmailIdempotency.ts +64 -0
  171. package/app/Models/EmailSuppression.ts +52 -0
  172. package/app/Models/EmailWebhookEvent.ts +51 -0
  173. package/app/Models/TeamInvitation.ts +13 -0
  174. package/functions/dashboard-api.ts +51 -10
  175. package/functions/data.ts +0 -1
  176. package/functions/inbox.test.ts +2 -0
  177. package/functions/inbox.ts +27 -2
  178. package/functions/jobs.ts +0 -1
  179. package/functions/public-application-url.test.ts +13 -0
  180. package/functions/public-application-url.ts +27 -0
  181. package/ide/vscode/package.json +1 -1
  182. package/package.json +1 -1
  183. package/resources/components/Dashboard/Auth/AccessTokens.stx +15 -0
  184. package/resources/components/Dashboard/Ci/CiDashboard.stx +24 -6
  185. package/resources/components/Dashboard/Commerce/CommerceTaxDialog.stx +1 -1
  186. package/resources/components/Dashboard/Commerce/ProductWaitlistDashboard.stx +8 -7
  187. package/resources/components/Dashboard/Commerce/RestaurantWaitlistDashboard.stx +8 -7
  188. package/resources/components/Dashboard/Content/BlogDashboard.stx +14 -2
  189. package/resources/components/Dashboard/Content/PostsDashboard.stx +7 -1
  190. package/resources/components/Dashboard/Data/DataRecords.stx +0 -4
  191. package/resources/components/Dashboard/Deployments/DeploymentDetail.stx +16 -2
  192. package/resources/components/Dashboard/Email/EmailNavigation.stx +5 -5
  193. package/resources/components/Dashboard/Email/EmailSidebar.stx +1 -1
  194. package/resources/components/Dashboard/Email/InboxAttachmentList.stx +53 -0
  195. package/resources/components/Dashboard/Email/InboxDashboard.stx +5 -4
  196. package/resources/components/Dashboard/Email/InboxMessageDetail.stx +6 -4
  197. package/resources/components/Dashboard/Jobs/JobDetail.stx +3 -2
  198. package/resources/components/Dashboard/Kanban/KanbanBoardDashboard.stx +63 -6
  199. package/resources/components/Dashboard/Kanban/KanbanBoardsDashboard.stx +3 -6
  200. package/resources/components/Dashboard/Management/PermissionsDashboard.stx +4 -4
  201. package/resources/components/Dashboard/Models/ModelRecordsDashboard.stx +7 -13
  202. package/resources/components/Dashboard/Models/ModelsOverview.stx +1 -1
  203. package/resources/components/Dashboard/NotFoundDashboard.stx +2 -5
  204. package/resources/components/Dashboard/Queries/QueryDashboard.stx +6 -2
  205. package/resources/components/Dashboard/Settings/AppearanceSettingsDashboard.stx +4 -4
  206. package/resources/components/Dashboard/UI/EmptyState.stx +2 -2
  207. package/resources/components/Dashboard/UI/PageLayout.stx +3 -3
  208. package/resources/functions/dashboard/sidebar.ts +4 -4
  209. package/routes/dashboard-api.ts +8 -20
  210. package/views/dashboard/.discovered-models.json +54 -0
  211. package/views/dashboard/layouts/default.stx +19 -16
  212. package/views/dashboard/stores/auth.ts +15 -14
  213. package/views/dashboard/stores/ci.ts +13 -7
  214. package/views/dashboard/stores/kanban.ts +48 -11
@@ -2,7 +2,7 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { emailSDK } from '@stacksjs/email'
4
4
  import { response } from '@stacksjs/router'
5
- import { defaultMailbox } from './mail-preference'
5
+ import { dashboardMailbox, inboxActionError } from './inbox-request'
6
6
 
7
7
  export default new Action({
8
8
  name: 'InboxMarkReadAction',
@@ -11,16 +11,21 @@ export default new Action({
11
11
  apiResponse: true,
12
12
 
13
13
  async handle(request: RequestInstance) {
14
- const mailbox = String(request.get('mailbox') || defaultMailbox())
15
14
  const messageId = String(request.get('messageId') || '')
16
15
 
17
16
  if (!messageId)
18
17
  return response.json({ message: 'messageId is required.' }, 422)
19
18
 
20
- const success = await emailSDK.markAsRead(mailbox, messageId)
21
- if (!success)
22
- return response.json({ message: 'Email not found or its read state could not be updated.' }, 404)
19
+ try {
20
+ const mailbox = dashboardMailbox(request)
21
+ const success = await emailSDK.markAsRead(mailbox, messageId)
22
+ if (!success)
23
+ return response.json({ message: 'Email not found or its read state could not be updated.' }, 404)
23
24
 
24
- return response.json({ success: true, mailbox, messageId })
25
+ return response.json({ success: true, mailbox, messageId })
26
+ }
27
+ catch (error) {
28
+ return inboxActionError(error, 'The email read state could not be updated.')
29
+ }
25
30
  },
26
31
  })
@@ -2,7 +2,7 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { emailSDK } from '@stacksjs/email'
4
4
  import { response } from '@stacksjs/router'
5
- import { defaultMailbox } from './mail-preference'
5
+ import { dashboardMailbox, inboxActionError } from './inbox-request'
6
6
 
7
7
  export default new Action({
8
8
  name: 'InboxMarkUnreadAction',
@@ -11,16 +11,21 @@ export default new Action({
11
11
  apiResponse: true,
12
12
 
13
13
  async handle(request: RequestInstance) {
14
- const mailbox = String(request.get('mailbox') || defaultMailbox())
15
14
  const messageId = String(request.get('messageId') || '')
16
15
 
17
16
  if (!messageId)
18
17
  return response.json({ message: 'messageId is required.' }, 422)
19
18
 
20
- const success = await emailSDK.markAsUnread(mailbox, messageId)
21
- if (!success)
22
- return response.json({ message: 'Email not found or its read state could not be updated.' }, 404)
19
+ try {
20
+ const mailbox = dashboardMailbox(request)
21
+ const success = await emailSDK.markAsUnread(mailbox, messageId)
22
+ if (!success)
23
+ return response.json({ message: 'Email not found or its read state could not be updated.' }, 404)
23
24
 
24
- return response.json({ success: true, mailbox, messageId })
25
+ return response.json({ success: true, mailbox, messageId })
26
+ }
27
+ catch (error) {
28
+ return inboxActionError(error, 'The email read state could not be updated.')
29
+ }
25
30
  },
26
31
  })
@@ -2,7 +2,8 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { MailPreference } from '@stacksjs/orm'
4
4
  import { response } from '@stacksjs/router'
5
- import { defaultMailbox, serializeMailPreference } from './mail-preference'
5
+ import { dashboardMailbox, inboxActionError } from './inbox-request'
6
+ import { serializeMailPreference } from './mail-preference'
6
7
 
7
8
  export default new Action({
8
9
  name: 'InboxPreferenceShowAction',
@@ -11,8 +12,13 @@ export default new Action({
11
12
  apiResponse: true,
12
13
 
13
14
  async handle(request: RequestInstance) {
14
- const mailbox = String(request.get('mailbox') || defaultMailbox()).trim().toLowerCase()
15
- const record = await MailPreference.where('mailbox', '=', mailbox).first()
16
- return response.json({ preference: serializeMailPreference(record, mailbox) })
15
+ try {
16
+ const mailbox = dashboardMailbox(request)
17
+ const record = await MailPreference.where('mailbox', '=', mailbox).first()
18
+ return response.json({ preference: serializeMailPreference(record, mailbox) })
19
+ }
20
+ catch (error) {
21
+ return inboxActionError(error, 'Mail settings could not be loaded.')
22
+ }
17
23
  },
18
24
  })
@@ -2,8 +2,8 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { MailPreference } from '@stacksjs/orm'
4
4
  import { response } from '@stacksjs/router'
5
+ import { dashboardMailbox, inboxActionError } from './inbox-request'
5
6
  import {
6
- defaultMailbox,
7
7
  type MailPreferenceInput,
8
8
  mailPreferenceAttributes,
9
9
  normalizeStringList,
@@ -132,14 +132,13 @@ export default new Action({
132
132
  apiResponse: true,
133
133
 
134
134
  async handle(request: RequestInstance) {
135
- const mailboxValue = request.get('mailbox')
136
- const mailbox = (mailboxValue === null || mailboxValue === undefined
137
- ? defaultMailbox()
138
- : typeof mailboxValue === 'string' ? mailboxValue : '')
139
- .trim()
140
- .toLowerCase()
141
- if (!/^[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+$/.test(mailbox) || mailbox.length > 320)
142
- return response.json({ message: 'The mail settings are invalid.', fields: { mailbox: 'Enter a valid mailbox.' } }, 422)
135
+ let mailbox: string
136
+ try {
137
+ mailbox = dashboardMailbox(request)
138
+ }
139
+ catch (error) {
140
+ return inboxActionError(error, 'Mail settings could not be updated.')
141
+ }
143
142
 
144
143
  const { fields, input } = inputFromRequest(request, mailbox)
145
144
  if (Object.keys(fields).length)
@@ -1,7 +1,8 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { emailSDK } from '@stacksjs/email'
3
4
  import { response } from '@stacksjs/router'
4
- import { defaultMailbox } from './mail-preference'
5
+ import { dashboardMailbox, inboxActionError } from './inbox-request'
5
6
  import { sanitizeInboxHtml } from './sanitize-inbox-html'
6
7
 
7
8
  export default new Action({
@@ -10,10 +11,10 @@ export default new Action({
10
11
  method: 'GET',
11
12
  apiResponse: true,
12
13
 
13
- async handle(request: any) {
14
+ async handle(request: RequestInstance) {
14
15
  try {
15
- const mailbox = request?.query?.mailbox || defaultMailbox()
16
- const messageId = request?.params?.id
16
+ const mailbox = dashboardMailbox(request)
17
+ const messageId = String(request.getParam('id') || '')
17
18
 
18
19
  if (!messageId)
19
20
  return response.json({ message: 'A message ID is required.' }, 422)
@@ -28,12 +29,11 @@ export default new Action({
28
29
  html: sanitizeInboxHtml(email.html ?? ''),
29
30
  text: email.text ?? '',
30
31
  metadata: email.metadata,
32
+ attachments: email.attachments,
31
33
  }
32
34
  }
33
35
  catch (err) {
34
- return response.json({
35
- message: err instanceof Error ? err.message : 'Email content could not be loaded.',
36
- }, 503)
36
+ return inboxActionError(err, 'Email content could not be loaded.')
37
37
  }
38
38
  },
39
39
  })
@@ -1,7 +1,7 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { emailSDK } from '@stacksjs/email'
3
- import { response } from '@stacksjs/router'
4
- import { defaultMailbox } from './mail-preference'
4
+ import { dashboardMailbox, inboxActionError } from './inbox-request'
5
5
 
6
6
  export default new Action({
7
7
  name: 'InboxStatsAction',
@@ -9,9 +9,9 @@ export default new Action({
9
9
  method: 'GET',
10
10
  apiResponse: true,
11
11
 
12
- async handle(request: any) {
12
+ async handle(request: RequestInstance) {
13
13
  try {
14
- const mailbox = request?.query?.mailbox || defaultMailbox()
14
+ const mailbox = dashboardMailbox(request)
15
15
  const stats = await emailSDK.getInboxStats(mailbox)
16
16
 
17
17
  return {
@@ -20,9 +20,7 @@ export default new Action({
20
20
  }
21
21
  }
22
22
  catch (err) {
23
- return response.json({
24
- message: err instanceof Error ? err.message : 'Inbox statistics could not be loaded.',
25
- }, 503)
23
+ return inboxActionError(err, 'Inbox statistics could not be loaded.')
26
24
  }
27
25
  },
28
26
  })
@@ -0,0 +1,33 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
2
+ import { config } from '@stacksjs/config'
3
+ import { inboxMailboxPath, InvalidInboxMailboxError } from '@stacksjs/email'
4
+ import { response } from '@stacksjs/router'
5
+ import { defaultMailbox } from './mail-preference'
6
+
7
+ function configuredMailDomain(): string {
8
+ const email = (config as any)?.email || {}
9
+ const fromAddress = String(email.from?.address || '').trim()
10
+ const fromDomain = fromAddress.includes('@') ? fromAddress.split('@').at(-1) : ''
11
+ return String(email.domain || fromDomain || 'stacksjs.com').trim().toLowerCase()
12
+ }
13
+
14
+ export function dashboardMailbox(request: RequestInstance): string {
15
+ const value = request.get('mailbox')
16
+ if (value !== null && value !== undefined && typeof value !== 'string')
17
+ throw new InvalidInboxMailboxError()
18
+
19
+ return inboxMailboxPath(value?.trim() || defaultMailbox(), configuredMailDomain()).address
20
+ }
21
+
22
+ export function inboxActionError(error: unknown, fallback: string): Response {
23
+ if (error instanceof InvalidInboxMailboxError) {
24
+ return response.json({
25
+ message: 'Enter a mailbox on the configured email domain.',
26
+ fields: { mailbox: 'Enter a mailbox on the configured email domain.' },
27
+ }, 422)
28
+ }
29
+
30
+ return response.json({
31
+ message: fallback,
32
+ }, 503)
33
+ }
@@ -6,6 +6,7 @@ import { cache } from '@stacksjs/cache'
6
6
  import { config } from '@stacksjs/config'
7
7
  import { db } from '@stacksjs/database'
8
8
  import { checkQueueHealth } from '@stacksjs/queue'
9
+ import { dashboardOperationalIssue } from '../dashboard-response'
9
10
  import {
10
11
  compactSql,
11
12
  countValue,
@@ -23,14 +24,14 @@ interface SourceResult<T> {
23
24
  error: string
24
25
  }
25
26
 
26
- async function inspectSource<T>(work: Promise<T>, fallback: T): Promise<SourceResult<T>> {
27
+ async function inspectSource<T>(work: Promise<T>, fallback: T, message: string, source: string): Promise<SourceResult<T>> {
27
28
  try {
28
29
  return { value: await work, error: '' }
29
30
  }
30
31
  catch (error) {
31
32
  return {
32
33
  value: fallback,
33
- error: error instanceof Error ? error.message : 'The source could not be inspected.',
34
+ error: dashboardOperationalIssue(error, message, `InsightsAction.${source}`),
34
35
  }
35
36
  }
36
37
  }
@@ -135,18 +136,18 @@ export default new Action({
135
136
  filesystem,
136
137
  databaseFile,
137
138
  ] = await Promise.all([
138
- inspectSource(requestSummaryQuery, undefined),
139
- inspectSource(requestSuccessQuery, undefined),
140
- inspectSource(slowRequestsQuery, []),
141
- inspectSource(querySummaryQuery, undefined),
142
- inspectSource(queryStatusesQuery, []),
143
- inspectSource(slowQueriesQuery, []),
144
- inspectSource(errorSummaryQuery, undefined),
145
- inspectSource(recentErrorsQuery, []),
146
- inspectSource(checkQueueHealth(), null),
147
- inspectSource(cache.getStats(), null),
148
- inspectSource(filesystemQuery, null),
149
- inspectSource(databaseFileQuery, null),
139
+ inspectSource(requestSummaryQuery, undefined, 'Request telemetry is unavailable.', 'requests.summary'),
140
+ inspectSource(requestSuccessQuery, undefined, 'Request telemetry is unavailable.', 'requests.success'),
141
+ inspectSource(slowRequestsQuery, [], 'Request telemetry is unavailable.', 'requests.slowest'),
142
+ inspectSource(querySummaryQuery, undefined, 'Query telemetry is unavailable.', 'queries.summary'),
143
+ inspectSource(queryStatusesQuery, [], 'Query telemetry is unavailable.', 'queries.statuses'),
144
+ inspectSource(slowQueriesQuery, [], 'Query telemetry is unavailable.', 'queries.slowest'),
145
+ inspectSource(errorSummaryQuery, undefined, 'Error telemetry is unavailable.', 'errors.summary'),
146
+ inspectSource(recentErrorsQuery, [], 'Error telemetry is unavailable.', 'errors.recent'),
147
+ inspectSource(checkQueueHealth(), null, 'Queue telemetry is unavailable.', 'queue'),
148
+ inspectSource(cache.getStats(), null, 'Cache telemetry is unavailable.', 'cache'),
149
+ inspectSource(filesystemQuery, null, 'Filesystem telemetry is unavailable.', 'filesystem'),
150
+ inspectSource(databaseFileQuery, null, 'Database file telemetry is unavailable.', 'database-file'),
150
151
  ])
151
152
 
152
153
  const requestTotal = countValue(requestSummary.value?.total)
@@ -1,15 +1,14 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { db } from '@stacksjs/database'
4
- import { request as routerRequest, response } from '@stacksjs/router'
4
+ import { dashboardRequestValue } from '../dashboard-request'
5
+ import { dashboardOperationalError } from '../dashboard-response'
5
6
  import { DASHBOARD_LOG_TYPES, normalizeDashboardLog, summarizeDashboardLogTypes } from './log-dashboard'
6
7
 
7
8
  const RANGES = ['1', '7', '30', '90', 'all'] as const
8
9
 
9
10
  function queryValue(request: RequestInstance, key: string): string {
10
- const query = ((routerRequest as any).query || {}) as Record<string, string | string[] | undefined>
11
- const value = query[key]
12
- return String((Array.isArray(value) ? value[0] : value) || request.get(key) || '').trim()
11
+ return dashboardRequestValue(request, key)
13
12
  }
14
13
 
15
14
  export default new Action({
@@ -102,13 +101,7 @@ export default new Action({
102
101
  }
103
102
  }
104
103
  catch (error) {
105
- return response.json({
106
- logs: [],
107
- summary: { total: 0, error: 0, warning: 0, info: 0, success: 0 },
108
- pagination: { page: 1, perPage: 25, total: 0, totalPages: 1 },
109
- options: { sources: [], projects: [], types: [...DASHBOARD_LOG_TYPES], ranges: [...RANGES] },
110
- error: error instanceof Error ? error.message : 'Logs could not be loaded.',
111
- }, 500)
104
+ return dashboardOperationalError(error, 'Logs could not be loaded.', 'LogIndexAction')
112
105
  }
113
106
  },
114
107
  })
@@ -1,3 +1,4 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { response } from '@stacksjs/router'
3
4
  import { tsCloud } from '~/config/cloud'
@@ -9,12 +10,8 @@ export default new Action({
9
10
  description: 'Returns one configured server or persisted deployment.',
10
11
  method: 'GET',
11
12
  apiResponse: true,
12
- async handle(request) {
13
- const identifier = String(
14
- (request as any)?.params?.id
15
- ?? (request as any)?.param?.('id')
16
- ?? '',
17
- )
13
+ async handle(request: RequestInstance) {
14
+ const identifier = request.getParam('id')
18
15
  if (!identifier)
19
16
  return response.json({ error: 'A server identifier is required.' }, 400)
20
17
 
@@ -1,26 +1,19 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { FailedJob, Job } from '@stacksjs/orm'
3
- import { request, response } from '@stacksjs/router'
4
+ import { dashboardOperationalError } from '../dashboard-response'
4
5
  import { matchesJobSearch, normalizeActiveJob, normalizeFailedJob } from './job-records'
5
6
 
6
7
  export default new Action({
7
8
  name: 'JobIndexAction',
8
9
  description: 'Returns a paginated list of jobs across the active queue and the failed_jobs table.',
9
10
  method: 'GET',
10
- async handle() {
11
- // bun-router populates `request.query` as a Record<string, string|string[]>.
12
- // Outside a request context (e.g. ad-hoc tests) it's undefined — fall back
13
- // to defaults so the action stays callable.
14
- const query = ((request as any).query || {}) as Record<string, string | string[] | undefined>
15
- const pick = (key: string) => {
16
- const v = query[key]
17
- return Array.isArray(v) ? v[0] : v
18
- }
19
- const page = Math.max(1, Number(pick('page')) || 1)
20
- const perPage = Math.min(200, Math.max(1, Number(pick('per_page')) || 25))
21
- const queueFilter = String(pick('queue') || '').trim()
22
- const statusFilter = String(pick('status') || '').trim().toLowerCase()
23
- const search = String(pick('search') || '').trim().toLowerCase()
11
+ async handle(request: RequestInstance) {
12
+ const page = Math.max(1, Number(request.get('page', 1)) || 1)
13
+ const perPage = Math.min(200, Math.max(1, Number(request.get('per_page', 25)) || 25))
14
+ const queueFilter = String(request.get('queue', '')).trim()
15
+ const statusFilter = String(request.get('status', '')).trim().toLowerCase()
16
+ const search = String(request.get('search', '')).trim().toLowerCase()
24
17
 
25
18
  try {
26
19
  const [activeJobs, failedJobs] = await Promise.all([
@@ -50,9 +43,7 @@ export default new Action({
50
43
  return { data, total, page, perPage, queues, queueConnected: true }
51
44
  }
52
45
  catch (error) {
53
- return response.json({
54
- message: error instanceof Error ? error.message : 'Job history could not be loaded.',
55
- }, 503)
46
+ return dashboardOperationalError(error, 'Job history could not be loaded.', 'JobIndexAction')
56
47
  }
57
48
  },
58
49
  })
@@ -1,5 +1,6 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
- import { request, response } from '@stacksjs/router'
3
+ import { response } from '@stacksjs/router'
3
4
  import { retryFailedJob } from '@stacksjs/queue'
4
5
  import { parseJobReference } from './job-records'
5
6
 
@@ -7,8 +8,8 @@ export default new Action({
7
8
  name: 'JobRetryAction',
8
9
  description: 'Retries a single failed job by its failed_jobs id.',
9
10
  method: 'POST',
10
- async handle() {
11
- const reference = String((request as any).params?.id || '')
11
+ async handle(request: RequestInstance) {
12
+ const reference = request.getParam('id')
12
13
  const parsed = parseJobReference(reference)
13
14
  const id = Number(parsed.id)
14
15
  if (parsed.source === 'job') {
@@ -1,29 +1,35 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { FailedJob, Job } from '@stacksjs/orm'
3
- import { request } from '@stacksjs/router'
4
+ import { response } from '@stacksjs/router'
5
+ import { dashboardOperationalError } from '../dashboard-response'
4
6
  import { normalizeActiveJob, normalizeFailedJob, parseJobReference } from './job-records'
5
7
 
6
8
  export default new Action({
7
9
  name: 'JobShowAction',
8
10
  description: 'Returns one native Job or FailedJob record for dashboard inspection.',
9
11
  method: 'GET',
10
- async handle() {
11
- const reference = String((request as any).params?.id || '')
12
+ async handle(request: RequestInstance) {
13
+ const reference = request.getParam('id')
12
14
  const parsed = parseJobReference(reference)
13
15
  const id = Number(parsed.id)
14
16
 
15
17
  if (!Number.isFinite(id) || id <= 0)
16
- return { job: null, error: 'Invalid job id.' }
18
+ return response.json({ message: 'Invalid job id.' }, 400)
17
19
 
18
20
  try {
19
21
  if (parsed.source === 'failed') {
20
22
  const record = await FailedJob.find(id)
21
- return { job: record ? normalizeFailedJob(record) : null }
23
+ return record
24
+ ? { job: normalizeFailedJob(record) }
25
+ : response.json({ message: 'Job not found.' }, 404)
22
26
  }
23
27
 
24
28
  if (parsed.source === 'job') {
25
29
  const record = await Job.find(id)
26
- return { job: record ? normalizeActiveJob(record) : null }
30
+ return record
31
+ ? { job: normalizeActiveJob(record) }
32
+ : response.json({ message: 'Job not found.' }, 404)
27
33
  }
28
34
 
29
35
  const activeRecord = await Job.find(id)
@@ -31,13 +37,12 @@ export default new Action({
31
37
  return { job: normalizeActiveJob(activeRecord) }
32
38
 
33
39
  const failedRecord = await FailedJob.find(id)
34
- return { job: failedRecord ? normalizeFailedJob(failedRecord) : null }
40
+ return failedRecord
41
+ ? { job: normalizeFailedJob(failedRecord) }
42
+ : response.json({ message: 'Job not found.' }, 404)
35
43
  }
36
44
  catch (error) {
37
- return {
38
- job: null,
39
- error: error instanceof Error ? error.message : 'Job could not be loaded.',
40
- }
45
+ return dashboardOperationalError(error, 'Job could not be loaded.', 'JobShowAction')
41
46
  }
42
47
  },
43
48
  })
@@ -1,7 +1,7 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { FailedJob, Job } from '@stacksjs/orm'
3
3
  import { getGlobalMetrics } from '@stacksjs/queue'
4
- import { response } from '@stacksjs/router'
4
+ import { dashboardOperationalError } from '../dashboard-response'
5
5
 
6
6
  export default new Action({
7
7
  name: 'JobStatsAction',
@@ -38,9 +38,7 @@ export default new Action({
38
38
  }
39
39
  }
40
40
  catch (error) {
41
- return response.json({
42
- message: error instanceof Error ? error.message : 'Job metrics could not be loaded.',
43
- }, 503)
41
+ return dashboardOperationalError(error, 'Job metrics could not be loaded.', 'JobStatsAction')
44
42
  }
45
43
  },
46
44
  })
@@ -1,9 +1,10 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { db } from '@stacksjs/database'
3
- import { kanbanError } from './kanban-response'
4
+ import { kanbanActionError, kanbanError } from './kanban-response'
4
5
 
5
6
  /**
6
- * `DELETE /api/dashboard/kanban/boards/:id` (stacksjs/stacks#1846 Phase 2).
7
+ * `DELETE /api/dashboard/kanban/boards/:id`.
7
8
  *
8
9
  * Hard delete with cascade: removes the board, its columns, its cards,
9
10
  * its labels, and the relevant pivot rows in `card_labels` and
@@ -23,9 +24,8 @@ export default new Action({
23
24
  description: 'Hard-deletes a board and all of its columns, cards, labels, and pivot rows.',
24
25
  method: 'DELETE',
25
26
  apiResponse: true,
26
- async handle(request) {
27
- const rawId = (request as any)?.params?.id ?? (request as any)?.param?.('id') ?? null
28
- const id = Number(rawId)
27
+ async handle(request: RequestInstance) {
28
+ const id = Number(request.getParam('id'))
29
29
  if (!Number.isFinite(id) || id <= 0) {
30
30
  return kanbanError('Invalid board id', 400)
31
31
  }
@@ -42,7 +42,7 @@ export default new Action({
42
42
  'DELETE FROM card_assignees WHERE card_id IN (SELECT id FROM cards WHERE board_id = ?)',
43
43
  [id],
44
44
  ).execute()
45
- // Card comments (Phase 3, migration 0000000112). Same
45
+ // Card comments use the same
46
46
  // card-scoped pattern as the pivots.
47
47
  await qb.unsafe(
48
48
  'DELETE FROM card_comments WHERE card_id IN (SELECT id FROM cards WHERE board_id = ?)',
@@ -60,8 +60,7 @@ export default new Action({
60
60
  return { deleted: true, id }
61
61
  }
62
62
  catch (err) {
63
- console.error('[dashboard/kanban] BoardDestroyAction failed:', err)
64
- return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
63
+ return kanbanActionError(err, 'BoardDestroyAction')
65
64
  }
66
65
  },
67
66
  })
@@ -1,7 +1,8 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { db } from '@stacksjs/database'
3
4
  import { modelBoolean } from './kanban-model'
4
- import { kanbanError } from './kanban-response'
5
+ import { kanbanActionError, kanbanError } from './kanban-response'
5
6
 
6
7
  interface BoardRow {
7
8
  id: number
@@ -56,8 +57,8 @@ interface LabelRow {
56
57
  *
57
58
  * Returns a single board with its columns + cards + label palette
58
59
  * nested in render order. Powers the kanban board page
59
- * (`/kanban/[id]`) Phase 2 hits this on mount, the drag handler
60
- * then mutates positions via the (Phase 2) reorder endpoint and
60
+ * (`/kanban/[id]`). The page loads this on mount, then its drag handler
61
+ * mutates positions through the reorder endpoint and
61
62
  * relies on optimistic updates for snappy UX.
62
63
  *
63
64
  * Query layout (3 queries, all indexed):
@@ -67,24 +68,16 @@ interface LabelRow {
67
68
  * `column_id, position`).
68
69
  * 4. All labels for the board.
69
70
  *
70
- * Card-label pivot reads land in Phase 3 alongside the card detail
71
- * modal the board view shows label chips inline, hydrated from a
72
- * single pivot query batched with the card fetch.
71
+ * Card-label pivots hydrate label chips inline through a single query
72
+ * batched with the card fetch.
73
73
  */
74
74
  export default new Action({
75
75
  name: 'Kanban Board Show',
76
76
  description: 'Returns a single kanban board with its columns + cards + label palette.',
77
77
  method: 'GET',
78
78
  apiResponse: true,
79
- async handle(request) {
80
- // Route param resolution: bun-router exposes `:id` via
81
- // `request.param('id')` / `request.params.id`. Both shapes appear
82
- // in this codebase depending on how the route was registered;
83
- // covering both keeps the action portable.
84
- const rawId = (request as any)?.params?.id
85
- ?? (request as any)?.param?.('id')
86
- ?? null
87
- const id = Number(rawId)
79
+ async handle(request: RequestInstance) {
80
+ const id = Number(request.getParam('id'))
88
81
  if (!Number.isFinite(id) || id <= 0) {
89
82
  return kanbanError('Invalid board id', 400)
90
83
  }
@@ -114,8 +107,8 @@ export default new Action({
114
107
  ).execute() as Promise<LabelRow[]>,
115
108
  ])
116
109
 
117
- // Pivot lookups for label + assignee chips on card previews
118
- // (stacksjs/stacks#1846 Phase 3). Two batched JOINs keyed off the
110
+ // Pivot lookups for label + assignee chips on card previews.
111
+ // Two batched JOINs keyed off the
119
112
  // board's card list — single round-trip per pivot, then group
120
113
  // client-side into `cardId → list` maps.
121
114
  const [cardLabelRows, cardAssigneeRows] = await Promise.all([
@@ -208,8 +201,7 @@ export default new Action({
208
201
  }
209
202
  }
210
203
  catch (err) {
211
- console.error('[dashboard/kanban] BoardShowAction failed:', err)
212
- return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
204
+ return kanbanActionError(err, 'BoardShowAction')
213
205
  }
214
206
  },
215
207
  })
@@ -1,7 +1,8 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { db } from '@stacksjs/database'
3
4
  import { Board } from '@stacksjs/orm'
4
- import { kanbanError } from './kanban-response'
5
+ import { kanbanActionError, kanbanError } from './kanban-response'
5
6
 
6
7
  interface BoardInput {
7
8
  name?: unknown
@@ -11,7 +12,7 @@ interface BoardInput {
11
12
  }
12
13
 
13
14
  /**
14
- * `POST /api/dashboard/kanban/boards` (stacksjs/stacks#1846 Phase 2).
15
+ * `POST /api/dashboard/kanban/boards`.
15
16
  *
16
17
  * Creates a new board with sensible defaults and appends it to the
17
18
  * end of the boards list (`position = max(position) + 1` so the new
@@ -28,8 +29,8 @@ export default new Action({
28
29
  description: 'Creates a new kanban board.',
29
30
  method: 'POST',
30
31
  apiResponse: true,
31
- async handle(request) {
32
- const body = (request as any).jsonBody as BoardInput | undefined ?? {}
32
+ async handle(request: RequestInstance<BoardInput>) {
33
+ const body = request.all()
33
34
 
34
35
  const name = typeof body.name === 'string' ? body.name.trim() : ''
35
36
  if (!name || name.length > 120) {
@@ -76,8 +77,7 @@ export default new Action({
76
77
  }
77
78
  }
78
79
  catch (err) {
79
- console.error('[dashboard/kanban] BoardStoreAction failed:', err)
80
- return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
80
+ return kanbanActionError(err, 'BoardStoreAction')
81
81
  }
82
82
  },
83
83
  })