@btst/stack 2.3.0 → 2.5.0

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 (208) hide show
  1. package/dist/packages/stack/src/client/components/compose.cjs +1 -2
  2. package/dist/packages/stack/src/client/components/compose.mjs +1 -2
  3. package/dist/packages/stack/src/plugins/ai-chat/api/page-tools.cjs +71 -0
  4. package/dist/packages/stack/src/plugins/ai-chat/api/page-tools.mjs +68 -0
  5. package/dist/packages/stack/src/plugins/ai-chat/api/plugin.cjs +87 -54
  6. package/dist/packages/stack/src/plugins/ai-chat/api/plugin.mjs +87 -54
  7. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-input.cjs +2 -2
  8. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-input.mjs +2 -2
  9. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.cjs +89 -22
  10. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.mjs +90 -23
  11. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.cjs +110 -33
  12. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.mjs +112 -35
  13. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-sidebar.cjs +1 -1
  14. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-sidebar.mjs +1 -1
  15. package/dist/packages/stack/src/plugins/ai-chat/client/plugin.cjs +14 -21
  16. package/dist/packages/stack/src/plugins/ai-chat/client/plugin.mjs +15 -22
  17. package/dist/packages/stack/src/plugins/ai-chat/schemas.cjs +17 -1
  18. package/dist/packages/stack/src/plugins/ai-chat/schemas.mjs +17 -1
  19. package/dist/packages/stack/src/plugins/blog/api/plugin.cjs +28 -45
  20. package/dist/packages/stack/src/plugins/blog/api/plugin.mjs +22 -39
  21. package/dist/packages/stack/src/plugins/blog/client/components/forms/post-forms.cjs +15 -2
  22. package/dist/packages/stack/src/plugins/blog/client/components/forms/post-forms.mjs +16 -3
  23. package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.internal.cjs +24 -1
  24. package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.internal.mjs +24 -1
  25. package/dist/packages/stack/src/plugins/blog/client/components/pages/fill-blog-form-handler.cjs +26 -0
  26. package/dist/packages/stack/src/plugins/blog/client/components/pages/fill-blog-form-handler.mjs +24 -0
  27. package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.internal.cjs +30 -1
  28. package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.internal.mjs +30 -1
  29. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.cjs +18 -0
  30. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.mjs +18 -0
  31. package/dist/packages/stack/src/plugins/blog/client/plugin.cjs +23 -27
  32. package/dist/packages/stack/src/plugins/blog/client/plugin.mjs +24 -28
  33. package/dist/packages/stack/src/plugins/cms/api/mutations.cjs +48 -0
  34. package/dist/packages/stack/src/plugins/cms/api/mutations.mjs +46 -0
  35. package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +21 -18
  36. package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +21 -18
  37. package/dist/packages/stack/src/plugins/cms/client/plugin.cjs +11 -15
  38. package/dist/packages/stack/src/plugins/cms/client/plugin.mjs +12 -16
  39. package/dist/packages/stack/src/plugins/form-builder/api/plugin.cjs +58 -62
  40. package/dist/packages/stack/src/plugins/form-builder/api/plugin.mjs +58 -62
  41. package/dist/packages/stack/src/plugins/form-builder/client/plugin.cjs +12 -12
  42. package/dist/packages/stack/src/plugins/form-builder/client/plugin.mjs +13 -13
  43. package/dist/packages/stack/src/plugins/kanban/api/mutations.cjs +91 -0
  44. package/dist/packages/stack/src/plugins/kanban/api/mutations.mjs +87 -0
  45. package/dist/packages/stack/src/plugins/kanban/api/plugin.cjs +92 -118
  46. package/dist/packages/stack/src/plugins/kanban/api/plugin.mjs +89 -115
  47. package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-hooks.cjs +7 -3
  48. package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-hooks.mjs +7 -3
  49. package/dist/packages/stack/src/plugins/kanban/client/plugin.cjs +22 -29
  50. package/dist/packages/stack/src/plugins/kanban/client/plugin.mjs +23 -30
  51. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.cjs +89 -0
  52. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.mjs +89 -0
  53. package/dist/packages/stack/src/plugins/ui-builder/client/plugin.cjs +8 -8
  54. package/dist/packages/stack/src/plugins/ui-builder/client/plugin.mjs +9 -9
  55. package/dist/packages/stack/src/plugins/utils.cjs +42 -0
  56. package/dist/packages/stack/src/plugins/utils.mjs +41 -1
  57. package/dist/plugins/ai-chat/api/index.d.cts +1 -1
  58. package/dist/plugins/ai-chat/api/index.d.mts +1 -1
  59. package/dist/plugins/ai-chat/api/index.d.ts +1 -1
  60. package/dist/plugins/ai-chat/client/components/index.d.cts +1 -1
  61. package/dist/plugins/ai-chat/client/components/index.d.mts +1 -1
  62. package/dist/plugins/ai-chat/client/components/index.d.ts +1 -1
  63. package/dist/plugins/ai-chat/client/context/page-ai-context.cjs +92 -0
  64. package/dist/plugins/ai-chat/client/context/page-ai-context.d.cts +84 -0
  65. package/dist/plugins/ai-chat/client/context/page-ai-context.d.mts +84 -0
  66. package/dist/plugins/ai-chat/client/context/page-ai-context.d.ts +84 -0
  67. package/dist/plugins/ai-chat/client/context/page-ai-context.mjs +88 -0
  68. package/dist/plugins/ai-chat/client/hooks/index.d.cts +1 -1
  69. package/dist/plugins/ai-chat/client/hooks/index.d.mts +1 -1
  70. package/dist/plugins/ai-chat/client/hooks/index.d.ts +1 -1
  71. package/dist/plugins/ai-chat/client/index.d.cts +10 -10
  72. package/dist/plugins/ai-chat/client/index.d.mts +10 -10
  73. package/dist/plugins/ai-chat/client/index.d.ts +10 -10
  74. package/dist/plugins/ai-chat/query-keys.d.cts +1 -1
  75. package/dist/plugins/ai-chat/query-keys.d.mts +1 -1
  76. package/dist/plugins/ai-chat/query-keys.d.ts +1 -1
  77. package/dist/plugins/blog/api/index.d.cts +2 -2
  78. package/dist/plugins/blog/api/index.d.mts +2 -2
  79. package/dist/plugins/blog/api/index.d.ts +2 -2
  80. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  81. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  82. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  83. package/dist/plugins/blog/client/index.d.cts +13 -13
  84. package/dist/plugins/blog/client/index.d.mts +13 -13
  85. package/dist/plugins/blog/client/index.d.ts +13 -13
  86. package/dist/plugins/blog/query-keys.d.cts +2 -2
  87. package/dist/plugins/blog/query-keys.d.mts +2 -2
  88. package/dist/plugins/blog/query-keys.d.ts +2 -2
  89. package/dist/plugins/client/index.cjs +1 -0
  90. package/dist/plugins/client/index.d.cts +8 -1
  91. package/dist/plugins/client/index.d.mts +8 -1
  92. package/dist/plugins/client/index.d.ts +8 -1
  93. package/dist/plugins/client/index.mjs +1 -1
  94. package/dist/plugins/cms/api/index.cjs +2 -0
  95. package/dist/plugins/cms/api/index.d.cts +2 -2
  96. package/dist/plugins/cms/api/index.d.mts +2 -2
  97. package/dist/plugins/cms/api/index.d.ts +2 -2
  98. package/dist/plugins/cms/api/index.mjs +1 -0
  99. package/dist/plugins/cms/client/hooks/index.d.cts +1 -1
  100. package/dist/plugins/cms/client/hooks/index.d.mts +1 -1
  101. package/dist/plugins/cms/client/hooks/index.d.ts +1 -1
  102. package/dist/plugins/cms/client/index.d.cts +6 -6
  103. package/dist/plugins/cms/client/index.d.mts +6 -6
  104. package/dist/plugins/cms/client/index.d.ts +6 -6
  105. package/dist/plugins/cms/query-keys.d.cts +2 -2
  106. package/dist/plugins/cms/query-keys.d.mts +2 -2
  107. package/dist/plugins/cms/query-keys.d.ts +2 -2
  108. package/dist/plugins/form-builder/api/index.d.cts +2 -2
  109. package/dist/plugins/form-builder/api/index.d.mts +2 -2
  110. package/dist/plugins/form-builder/api/index.d.ts +2 -2
  111. package/dist/plugins/form-builder/client/components/index.d.cts +1 -1
  112. package/dist/plugins/form-builder/client/components/index.d.mts +1 -1
  113. package/dist/plugins/form-builder/client/components/index.d.ts +1 -1
  114. package/dist/plugins/form-builder/client/hooks/index.d.cts +1 -1
  115. package/dist/plugins/form-builder/client/hooks/index.d.mts +1 -1
  116. package/dist/plugins/form-builder/client/hooks/index.d.ts +1 -1
  117. package/dist/plugins/form-builder/client/index.d.cts +6 -6
  118. package/dist/plugins/form-builder/client/index.d.mts +6 -6
  119. package/dist/plugins/form-builder/client/index.d.ts +6 -6
  120. package/dist/plugins/form-builder/query-keys.d.cts +2 -2
  121. package/dist/plugins/form-builder/query-keys.d.mts +2 -2
  122. package/dist/plugins/form-builder/query-keys.d.ts +2 -2
  123. package/dist/plugins/kanban/api/index.cjs +4 -0
  124. package/dist/plugins/kanban/api/index.d.cts +1 -1
  125. package/dist/plugins/kanban/api/index.d.mts +1 -1
  126. package/dist/plugins/kanban/api/index.d.ts +1 -1
  127. package/dist/plugins/kanban/api/index.mjs +1 -0
  128. package/dist/plugins/kanban/client/index.d.cts +12 -12
  129. package/dist/plugins/kanban/client/index.d.mts +12 -12
  130. package/dist/plugins/kanban/client/index.d.ts +12 -12
  131. package/dist/plugins/kanban/query-keys.d.cts +1 -1
  132. package/dist/plugins/kanban/query-keys.d.mts +1 -1
  133. package/dist/plugins/kanban/query-keys.d.ts +1 -1
  134. package/dist/plugins/ui-builder/client/hooks/index.d.cts +1 -1
  135. package/dist/plugins/ui-builder/client/hooks/index.d.mts +1 -1
  136. package/dist/plugins/ui-builder/client/hooks/index.d.ts +1 -1
  137. package/dist/plugins/ui-builder/client/index.d.cts +3 -3
  138. package/dist/plugins/ui-builder/client/index.d.mts +3 -3
  139. package/dist/plugins/ui-builder/client/index.d.ts +3 -3
  140. package/dist/plugins/ui-builder/index.d.cts +2 -2
  141. package/dist/plugins/ui-builder/index.d.mts +2 -2
  142. package/dist/plugins/ui-builder/index.d.ts +2 -2
  143. package/dist/shared/{stack.C-WUPMT6.d.cts → stack.B2xZTSiO.d.cts} +4 -4
  144. package/dist/shared/{stack.B1EeBt1b.d.ts → stack.B58oHdqm.d.mts} +33 -3
  145. package/dist/shared/{stack.CVDTkMoO.d.mts → stack.B8QD11QU.d.cts} +7 -7
  146. package/dist/shared/{stack.CVDTkMoO.d.cts → stack.B8QD11QU.d.mts} +7 -7
  147. package/dist/shared/{stack.CVDTkMoO.d.ts → stack.B8QD11QU.d.ts} +7 -7
  148. package/dist/shared/{stack.CIP6QS9l.d.ts → stack.BDVEpue1.d.ts} +1 -1
  149. package/dist/shared/{stack.C5dtIncc.d.mts → stack.BTvbxZvw.d.cts} +1 -1
  150. package/dist/shared/{stack.DaOcgmrM.d.ts → stack.BV9hnvu4.d.cts} +31 -7
  151. package/dist/shared/{stack.DaOcgmrM.d.cts → stack.BV9hnvu4.d.mts} +31 -7
  152. package/dist/shared/{stack.DaOcgmrM.d.mts → stack.BV9hnvu4.d.ts} +31 -7
  153. package/dist/shared/{stack.DdI5W6MB.d.mts → stack.BozPgbrZ.d.cts} +19 -19
  154. package/dist/shared/{stack.DdI5W6MB.d.ts → stack.BozPgbrZ.d.mts} +19 -19
  155. package/dist/shared/{stack.DdI5W6MB.d.cts → stack.BozPgbrZ.d.ts} +19 -19
  156. package/dist/shared/{stack.CP68pFEH.d.mts → stack.C9Mg2Q46.d.cts} +33 -3
  157. package/dist/shared/{stack.BeSm90va.d.ts → stack.CTDVxbrA.d.ts} +72 -14
  158. package/dist/shared/{stack.C-Ptrz8s.d.ts → stack.Cj_zKww4.d.ts} +4 -4
  159. package/dist/shared/{stack.TIBF2AOx.d.ts → stack.CxaFNQCV.d.mts} +89 -34
  160. package/dist/shared/{stack.CMh_EdxW.d.cts → stack.D-b5zbPm.d.cts} +72 -14
  161. package/dist/shared/{stack.Dw0Ly2TM.d.cts → stack.DTtmJPQO.d.mts} +1 -1
  162. package/dist/shared/{stack.BKfolAyK.d.ts → stack.DXnclTG7.d.ts} +11 -11
  163. package/dist/shared/{stack.snB1EDP7.d.cts → stack.DaZM10cp.d.cts} +11 -11
  164. package/dist/shared/{stack.Dg09R0oB.d.mts → stack.FVWf2JhZ.d.mts} +72 -14
  165. package/dist/shared/{stack.BIXEI6v_.d.mts → stack.cfCkioTe.d.mts} +11 -11
  166. package/dist/shared/{stack.6fUOjLs9.d.mts → stack.dH7u-TJH.d.mts} +4 -4
  167. package/dist/shared/{stack.BpolpQpf.d.cts → stack.j75TpKh2.d.ts} +89 -34
  168. package/dist/shared/{stack.rTy7-wQU.d.mts → stack.n1_i1p2B.d.cts} +89 -34
  169. package/dist/shared/{stack.IdtKDRka.d.cts → stack.sO33ZDhK.d.ts} +33 -3
  170. package/package.json +14 -1
  171. package/src/client/components/compose.tsx +7 -4
  172. package/src/plugins/ai-chat/api/page-tools.ts +111 -0
  173. package/src/plugins/ai-chat/api/plugin.ts +228 -72
  174. package/src/plugins/ai-chat/client/components/chat-input.tsx +2 -2
  175. package/src/plugins/ai-chat/client/components/chat-interface.tsx +154 -58
  176. package/src/plugins/ai-chat/client/components/chat-layout.tsx +166 -32
  177. package/src/plugins/ai-chat/client/components/chat-sidebar.tsx +1 -1
  178. package/src/plugins/ai-chat/client/context/page-ai-context.tsx +240 -0
  179. package/src/plugins/ai-chat/client/plugin.tsx +23 -31
  180. package/src/plugins/ai-chat/schemas.ts +16 -0
  181. package/src/plugins/blog/api/plugin.ts +31 -47
  182. package/src/plugins/blog/client/components/forms/post-forms.tsx +29 -2
  183. package/src/plugins/blog/client/components/pages/edit-post-page.internal.tsx +28 -0
  184. package/src/plugins/blog/client/components/pages/fill-blog-form-handler.ts +38 -0
  185. package/src/plugins/blog/client/components/pages/new-post-page.internal.tsx +33 -1
  186. package/src/plugins/blog/client/components/pages/post-page.internal.tsx +20 -0
  187. package/src/plugins/blog/client/plugin.tsx +36 -39
  188. package/src/plugins/client/index.ts +5 -1
  189. package/src/plugins/cms/api/index.ts +4 -0
  190. package/src/plugins/cms/api/mutations.ts +84 -0
  191. package/src/plugins/cms/api/plugin.ts +23 -17
  192. package/src/plugins/cms/client/plugin.tsx +18 -21
  193. package/src/plugins/cms/types.ts +7 -7
  194. package/src/plugins/form-builder/api/plugin.ts +64 -64
  195. package/src/plugins/form-builder/client/plugin.tsx +19 -18
  196. package/src/plugins/form-builder/types.ts +19 -24
  197. package/src/plugins/kanban/api/index.ts +6 -0
  198. package/src/plugins/kanban/api/mutations.ts +169 -0
  199. package/src/plugins/kanban/api/plugin.ts +123 -136
  200. package/src/plugins/kanban/client/hooks/kanban-hooks.tsx +4 -0
  201. package/src/plugins/kanban/client/plugin.tsx +35 -41
  202. package/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.tsx +132 -0
  203. package/src/plugins/ui-builder/client/plugin.tsx +11 -10
  204. package/src/plugins/ui-builder/types.ts +4 -4
  205. package/src/plugins/utils.ts +92 -1
  206. package/dist/shared/{stack.CBON0dWL.d.mts → stack.BQmuNl5p.d.cts} +2 -2
  207. package/dist/shared/{stack.CBON0dWL.d.ts → stack.BQmuNl5p.d.mts} +2 -2
  208. package/dist/shared/{stack.CBON0dWL.d.cts → stack.BQmuNl5p.d.ts} +2 -2
@@ -24,7 +24,13 @@ import {
24
24
  updateTaskSchema,
25
25
  } from "../schemas";
26
26
  import { getAllBoards, getBoardById } from "./getters";
27
+ import {
28
+ createKanbanTask,
29
+ findOrCreateKanbanBoard,
30
+ getKanbanColumnsByBoardId,
31
+ } from "./mutations";
27
32
  import { KANBAN_QUERY_KEYS } from "./query-key-defs";
33
+ import { runHookWithShim } from "../../utils";
28
34
  import { serializeBoard } from "./serializers";
29
35
  import type { QueryClient } from "@tanstack/react-query";
30
36
 
@@ -96,41 +102,41 @@ export interface KanbanApiContext<
96
102
  export interface KanbanBackendHooks {
97
103
  // ============ Board Hooks ============
98
104
  /**
99
- * Called before listing boards. Return false to deny access.
105
+ * Called before listing boards. Throw an error to deny access.
100
106
  */
101
107
  onBeforeListBoards?: (
102
108
  filter: z.infer<typeof BoardListQuerySchema>,
103
109
  context: KanbanApiContext,
104
- ) => Promise<boolean> | boolean;
110
+ ) => Promise<void> | void;
105
111
  /**
106
- * Called before creating a board. Return false to deny access.
112
+ * Called before creating a board. Throw an error to deny access.
107
113
  */
108
114
  onBeforeCreateBoard?: (
109
115
  data: z.infer<typeof createBoardSchema>,
110
116
  context: KanbanApiContext,
111
- ) => Promise<boolean> | boolean;
117
+ ) => Promise<void> | void;
112
118
  /**
113
- * Called before reading a single board. Return false to deny access.
119
+ * Called before reading a single board. Throw an error to deny access.
114
120
  */
115
121
  onBeforeReadBoard?: (
116
122
  boardId: string,
117
123
  context: KanbanApiContext,
118
- ) => Promise<boolean> | boolean;
124
+ ) => Promise<void> | void;
119
125
  /**
120
- * Called before updating a board. Return false to deny access.
126
+ * Called before updating a board. Throw an error to deny access.
121
127
  */
122
128
  onBeforeUpdateBoard?: (
123
129
  boardId: string,
124
130
  data: z.infer<typeof updateBoardSchema>,
125
131
  context: KanbanApiContext,
126
- ) => Promise<boolean> | boolean;
132
+ ) => Promise<void> | void;
127
133
  /**
128
- * Called before deleting a board. Return false to deny access.
134
+ * Called before deleting a board. Throw an error to deny access.
129
135
  */
130
136
  onBeforeDeleteBoard?: (
131
137
  boardId: string,
132
138
  context: KanbanApiContext,
133
- ) => Promise<boolean> | boolean;
139
+ ) => Promise<void> | void;
134
140
 
135
141
  /**
136
142
  * Called after boards are listed successfully.
@@ -209,27 +215,27 @@ export interface KanbanBackendHooks {
209
215
 
210
216
  // ============ Column Hooks ============
211
217
  /**
212
- * Called before creating a column. Return false to deny access.
218
+ * Called before creating a column. Throw an error to deny access.
213
219
  */
214
220
  onBeforeCreateColumn?: (
215
221
  data: z.infer<typeof createColumnSchema>,
216
222
  context: KanbanApiContext,
217
- ) => Promise<boolean> | boolean;
223
+ ) => Promise<void> | void;
218
224
  /**
219
- * Called before updating a column. Return false to deny access.
225
+ * Called before updating a column. Throw an error to deny access.
220
226
  */
221
227
  onBeforeUpdateColumn?: (
222
228
  columnId: string,
223
229
  data: z.infer<typeof updateColumnSchema>,
224
230
  context: KanbanApiContext,
225
- ) => Promise<boolean> | boolean;
231
+ ) => Promise<void> | void;
226
232
  /**
227
- * Called before deleting a column. Return false to deny access.
233
+ * Called before deleting a column. Throw an error to deny access.
228
234
  */
229
235
  onBeforeDeleteColumn?: (
230
236
  columnId: string,
231
237
  context: KanbanApiContext,
232
- ) => Promise<boolean> | boolean;
238
+ ) => Promise<void> | void;
233
239
 
234
240
  /**
235
241
  * Called after a column is created successfully
@@ -255,27 +261,27 @@ export interface KanbanBackendHooks {
255
261
 
256
262
  // ============ Task Hooks ============
257
263
  /**
258
- * Called before creating a task. Return false to deny access.
264
+ * Called before creating a task. Throw an error to deny access.
259
265
  */
260
266
  onBeforeCreateTask?: (
261
267
  data: z.infer<typeof createTaskSchema>,
262
268
  context: KanbanApiContext,
263
- ) => Promise<boolean> | boolean;
269
+ ) => Promise<void> | void;
264
270
  /**
265
- * Called before updating a task. Return false to deny access.
271
+ * Called before updating a task. Throw an error to deny access.
266
272
  */
267
273
  onBeforeUpdateTask?: (
268
274
  taskId: string,
269
275
  data: z.infer<typeof updateTaskSchema>,
270
276
  context: KanbanApiContext,
271
- ) => Promise<boolean> | boolean;
277
+ ) => Promise<void> | void;
272
278
  /**
273
- * Called before deleting a task. Return false to deny access.
279
+ * Called before deleting a task. Throw an error to deny access.
274
280
  */
275
281
  onBeforeDeleteTask?: (
276
282
  taskId: string,
277
283
  context: KanbanApiContext,
278
- ) => Promise<boolean> | boolean;
284
+ ) => Promise<void> | void;
279
285
 
280
286
  /**
281
287
  * Called after a task is created successfully
@@ -317,6 +323,13 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
317
323
  getAllBoards(adapter, params),
318
324
  getBoardById: (id: string) => getBoardById(adapter, id),
319
325
  prefetchForRoute: createKanbanPrefetchForRoute(adapter),
326
+ // Mutations
327
+ createTask: (input: Parameters<typeof createKanbanTask>[1]) =>
328
+ createKanbanTask(adapter, input),
329
+ findOrCreateBoard: (slug: string, name: string, columnTitles: string[]) =>
330
+ findOrCreateKanbanBoard(adapter, slug, name, columnTitles),
331
+ getColumnsByBoardId: (boardId: string) =>
332
+ getKanbanColumnsByBoardId(adapter, boardId),
320
333
  }),
321
334
 
322
335
  routes: (adapter: Adapter) => {
@@ -334,12 +347,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
334
347
 
335
348
  try {
336
349
  if (hooks?.onBeforeListBoards) {
337
- const canList = await hooks.onBeforeListBoards(query, context);
338
- if (!canList) {
339
- throw ctx.error(403, {
340
- message: "Unauthorized: Cannot list boards",
341
- });
342
- }
350
+ await runHookWithShim(
351
+ () => hooks.onBeforeListBoards!(query, context),
352
+ ctx.error,
353
+ "Unauthorized: Cannot list boards",
354
+ );
343
355
  }
344
356
 
345
357
  const result = await getAllBoards(adapter, query);
@@ -369,12 +381,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
369
381
 
370
382
  try {
371
383
  if (hooks?.onBeforeReadBoard) {
372
- const canRead = await hooks.onBeforeReadBoard(params.id, context);
373
- if (!canRead) {
374
- throw ctx.error(403, {
375
- message: "Unauthorized: Cannot read board",
376
- });
377
- }
384
+ await runHookWithShim(
385
+ () => hooks.onBeforeReadBoard!(params.id, context),
386
+ ctx.error,
387
+ "Unauthorized: Cannot read board",
388
+ );
378
389
  }
379
390
 
380
391
  const result = await getBoardById(adapter, params.id);
@@ -411,15 +422,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
411
422
 
412
423
  try {
413
424
  if (hooks?.onBeforeCreateBoard) {
414
- const canCreate = await hooks.onBeforeCreateBoard(
415
- ctx.body,
416
- context,
425
+ await runHookWithShim(
426
+ () => hooks.onBeforeCreateBoard!(ctx.body, context),
427
+ ctx.error,
428
+ "Unauthorized: Cannot create board",
417
429
  );
418
- if (!canCreate) {
419
- throw ctx.error(403, {
420
- message: "Unauthorized: Cannot create board",
421
- });
422
- }
423
430
  }
424
431
 
425
432
  const { ...boardData } = ctx.body;
@@ -504,16 +511,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
504
511
 
505
512
  try {
506
513
  if (hooks?.onBeforeUpdateBoard) {
507
- const canUpdate = await hooks.onBeforeUpdateBoard(
508
- ctx.params.id,
509
- { ...ctx.body, id: ctx.params.id },
510
- context,
514
+ await runHookWithShim(
515
+ () =>
516
+ hooks.onBeforeUpdateBoard!(
517
+ ctx.params.id,
518
+ { ...ctx.body, id: ctx.params.id },
519
+ context,
520
+ ),
521
+ ctx.error,
522
+ "Unauthorized: Cannot update board",
511
523
  );
512
- if (!canUpdate) {
513
- throw ctx.error(403, {
514
- message: "Unauthorized: Cannot update board",
515
- });
516
- }
517
524
  }
518
525
 
519
526
  const { slug: rawSlug, ...restBoardData } = ctx.body;
@@ -583,15 +590,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
583
590
  }
584
591
 
585
592
  if (hooks?.onBeforeDeleteBoard) {
586
- const canDelete = await hooks.onBeforeDeleteBoard(
587
- ctx.params.id,
588
- context,
593
+ await runHookWithShim(
594
+ () => hooks.onBeforeDeleteBoard!(ctx.params.id, context),
595
+ ctx.error,
596
+ "Unauthorized: Cannot delete board",
589
597
  );
590
- if (!canDelete) {
591
- throw ctx.error(403, {
592
- message: "Unauthorized: Cannot delete board",
593
- });
594
- }
595
598
  }
596
599
 
597
600
  await adapter.delete<Board>({
@@ -629,15 +632,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
629
632
 
630
633
  try {
631
634
  if (hooks?.onBeforeCreateColumn) {
632
- const canCreate = await hooks.onBeforeCreateColumn(
633
- ctx.body,
634
- context,
635
+ await runHookWithShim(
636
+ () => hooks.onBeforeCreateColumn!(ctx.body, context),
637
+ ctx.error,
638
+ "Unauthorized: Cannot create column",
635
639
  );
636
- if (!canCreate) {
637
- throw ctx.error(403, {
638
- message: "Unauthorized: Cannot create column",
639
- });
640
- }
641
640
  }
642
641
 
643
642
  // Get existing columns to determine order
@@ -692,16 +691,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
692
691
 
693
692
  try {
694
693
  if (hooks?.onBeforeUpdateColumn) {
695
- const canUpdate = await hooks.onBeforeUpdateColumn(
696
- ctx.params.id,
697
- { ...ctx.body, id: ctx.params.id },
698
- context,
694
+ await runHookWithShim(
695
+ () =>
696
+ hooks.onBeforeUpdateColumn!(
697
+ ctx.params.id,
698
+ { ...ctx.body, id: ctx.params.id },
699
+ context,
700
+ ),
701
+ ctx.error,
702
+ "Unauthorized: Cannot update column",
699
703
  );
700
- if (!canUpdate) {
701
- throw ctx.error(403, {
702
- message: "Unauthorized: Cannot update column",
703
- });
704
- }
705
704
  }
706
705
 
707
706
  const updated = await adapter.update<Column>({
@@ -753,15 +752,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
753
752
  }
754
753
 
755
754
  if (hooks?.onBeforeDeleteColumn) {
756
- const canDelete = await hooks.onBeforeDeleteColumn(
757
- ctx.params.id,
758
- context,
755
+ await runHookWithShim(
756
+ () => hooks.onBeforeDeleteColumn!(ctx.params.id, context),
757
+ ctx.error,
758
+ "Unauthorized: Cannot delete column",
759
759
  );
760
- if (!canDelete) {
761
- throw ctx.error(403, {
762
- message: "Unauthorized: Cannot delete column",
763
- });
764
- }
765
760
  }
766
761
 
767
762
  await adapter.delete<Column>({
@@ -798,16 +793,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
798
793
  for (let i = 0; i < columnIds.length; i++) {
799
794
  const columnId = columnIds[i];
800
795
  if (!columnId) continue;
801
- const canUpdate = await hooks.onBeforeUpdateColumn(
802
- columnId,
803
- { id: columnId, order: i },
804
- context,
796
+ await runHookWithShim(
797
+ () =>
798
+ hooks.onBeforeUpdateColumn!(
799
+ columnId,
800
+ { id: columnId, order: i },
801
+ context,
802
+ ),
803
+ ctx.error,
804
+ "Unauthorized: Cannot reorder columns",
805
805
  );
806
- if (!canUpdate) {
807
- throw ctx.error(403, {
808
- message: "Unauthorized: Cannot reorder columns",
809
- });
810
- }
811
806
  }
812
807
  }
813
808
 
@@ -857,15 +852,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
857
852
 
858
853
  try {
859
854
  if (hooks?.onBeforeCreateTask) {
860
- const canCreate = await hooks.onBeforeCreateTask(
861
- ctx.body,
862
- context,
855
+ await runHookWithShim(
856
+ () => hooks.onBeforeCreateTask!(ctx.body, context),
857
+ ctx.error,
858
+ "Unauthorized: Cannot create task",
863
859
  );
864
- if (!canCreate) {
865
- throw ctx.error(403, {
866
- message: "Unauthorized: Cannot create task",
867
- });
868
- }
869
860
  }
870
861
 
871
862
  // Get existing tasks in column to determine order
@@ -927,16 +918,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
927
918
 
928
919
  try {
929
920
  if (hooks?.onBeforeUpdateTask) {
930
- const canUpdate = await hooks.onBeforeUpdateTask(
931
- ctx.params.id,
932
- { ...ctx.body, id: ctx.params.id },
933
- context,
921
+ await runHookWithShim(
922
+ () =>
923
+ hooks.onBeforeUpdateTask!(
924
+ ctx.params.id,
925
+ { ...ctx.body, id: ctx.params.id },
926
+ context,
927
+ ),
928
+ ctx.error,
929
+ "Unauthorized: Cannot update task",
934
930
  );
935
- if (!canUpdate) {
936
- throw ctx.error(403, {
937
- message: "Unauthorized: Cannot update task",
938
- });
939
- }
940
931
  }
941
932
 
942
933
  const updated = await adapter.update<Task>({
@@ -988,15 +979,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
988
979
  }
989
980
 
990
981
  if (hooks?.onBeforeDeleteTask) {
991
- const canDelete = await hooks.onBeforeDeleteTask(
992
- ctx.params.id,
993
- context,
982
+ await runHookWithShim(
983
+ () => hooks.onBeforeDeleteTask!(ctx.params.id, context),
984
+ ctx.error,
985
+ "Unauthorized: Cannot delete task",
994
986
  );
995
- if (!canDelete) {
996
- throw ctx.error(403, {
997
- message: "Unauthorized: Cannot delete task",
998
- });
999
- }
1000
987
  }
1001
988
 
1002
989
  await adapter.delete<Task>({
@@ -1040,16 +1027,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
1040
1027
 
1041
1028
  // Check authorization before moving task
1042
1029
  if (hooks?.onBeforeUpdateTask) {
1043
- const canUpdate = await hooks.onBeforeUpdateTask(
1044
- taskId,
1045
- { id: taskId, columnId: targetColumnId, order: targetOrder },
1046
- context,
1030
+ await runHookWithShim(
1031
+ () =>
1032
+ hooks.onBeforeUpdateTask!(
1033
+ taskId,
1034
+ { id: taskId, columnId: targetColumnId, order: targetOrder },
1035
+ context,
1036
+ ),
1037
+ ctx.error,
1038
+ "Unauthorized: Cannot move task",
1047
1039
  );
1048
- if (!canUpdate) {
1049
- throw ctx.error(403, {
1050
- message: "Unauthorized: Cannot move task",
1051
- });
1052
- }
1053
1040
  }
1054
1041
 
1055
1042
  // Update task with new column and order
@@ -1093,16 +1080,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
1093
1080
  for (let i = 0; i < taskIds.length; i++) {
1094
1081
  const taskId = taskIds[i];
1095
1082
  if (!taskId) continue;
1096
- const canUpdate = await hooks.onBeforeUpdateTask(
1097
- taskId,
1098
- { id: taskId, order: i },
1099
- context,
1083
+ await runHookWithShim(
1084
+ () =>
1085
+ hooks.onBeforeUpdateTask!(
1086
+ taskId,
1087
+ { id: taskId, order: i },
1088
+ context,
1089
+ ),
1090
+ ctx.error,
1091
+ "Unauthorized: Cannot reorder tasks",
1100
1092
  );
1101
- if (!canUpdate) {
1102
- throw ctx.error(403, {
1103
- message: "Unauthorized: Cannot reorder tasks",
1104
- });
1105
- }
1106
1093
  }
1107
1094
  }
1108
1095
 
@@ -85,6 +85,7 @@ export function useBoards(params?: {
85
85
  return useQuery({
86
86
  ...queries.boards.list(params),
87
87
  staleTime: 30_000,
88
+ refetchOnWindowFocus: true,
88
89
  });
89
90
  }
90
91
 
@@ -102,6 +103,7 @@ export function useSuspenseBoards(params?: {
102
103
  const result = useSuspenseQuery({
103
104
  ...queries.boards.list(params),
104
105
  staleTime: 30_000,
106
+ refetchOnWindowFocus: true,
105
107
  });
106
108
 
107
109
  if (result.error && !result.isFetching) {
@@ -121,6 +123,7 @@ export function useBoard(boardId: string) {
121
123
  return useQuery({
122
124
  ...queries.boards.detail(boardId),
123
125
  staleTime: 30_000,
126
+ refetchOnWindowFocus: true,
124
127
  enabled: !!boardId,
125
128
  });
126
129
  }
@@ -135,6 +138,7 @@ export function useSuspenseBoard(boardId: string) {
135
138
  const result = useSuspenseQuery({
136
139
  ...queries.boards.detail(boardId),
137
140
  staleTime: 30_000,
141
+ refetchOnWindowFocus: true,
138
142
  });
139
143
 
140
144
  if (result.error && !result.isFetching) {
@@ -2,6 +2,7 @@ import {
2
2
  defineClientPlugin,
3
3
  createApiClient,
4
4
  isConnectionError,
5
+ runClientHookWithShim,
5
6
  } from "@btst/stack/plugins/client";
6
7
  import { createRoute } from "@btst/yar";
7
8
  import type { QueryClient } from "@tanstack/react-query";
@@ -86,39 +87,39 @@ export interface KanbanClientConfig {
86
87
  */
87
88
  export interface KanbanClientHooks {
88
89
  /**
89
- * Called before loading boards list. Return false to cancel loading.
90
+ * Called before loading boards list. Throw an error to cancel loading.
90
91
  */
91
- beforeLoadBoards?: (context: LoaderContext) => Promise<boolean> | boolean;
92
+ beforeLoadBoards?: (context: LoaderContext) => Promise<void> | void;
92
93
  /**
93
- * Called after boards are loaded. Return false to cancel further processing.
94
+ * Called after boards are loaded. Throw an error to cancel further processing.
94
95
  */
95
96
  afterLoadBoards?: (
96
97
  boards: SerializedBoardWithColumns[] | null,
97
98
  context: LoaderContext,
98
- ) => Promise<boolean> | boolean;
99
+ ) => Promise<void> | void;
99
100
  /**
100
- * Called before loading a single board. Return false to cancel loading.
101
+ * Called before loading a single board. Throw an error to cancel loading.
101
102
  */
102
103
  beforeLoadBoard?: (
103
104
  boardId: string,
104
105
  context: LoaderContext,
105
- ) => Promise<boolean> | boolean;
106
+ ) => Promise<void> | void;
106
107
  /**
107
- * Called after a board is loaded. Return false to cancel further processing.
108
+ * Called after a board is loaded. Throw an error to cancel further processing.
108
109
  */
109
110
  afterLoadBoard?: (
110
111
  board: SerializedBoardWithColumns | null,
111
112
  boardId: string,
112
113
  context: LoaderContext,
113
- ) => Promise<boolean> | boolean;
114
+ ) => Promise<void> | void;
114
115
  /**
115
- * Called before loading the new board page. Return false to cancel.
116
+ * Called before loading the new board page. Throw an error to cancel.
116
117
  */
117
- beforeLoadNewBoard?: (context: LoaderContext) => Promise<boolean> | boolean;
118
+ beforeLoadNewBoard?: (context: LoaderContext) => Promise<void> | void;
118
119
  /**
119
- * Called after the new board page is loaded. Return false to cancel.
120
+ * Called after the new board page is loaded. Throw an error to cancel.
120
121
  */
121
- afterLoadNewBoard?: (context: LoaderContext) => Promise<boolean> | boolean;
122
+ afterLoadNewBoard?: (context: LoaderContext) => Promise<void> | void;
122
123
  /**
123
124
  * Called when a loading error occurs
124
125
  */
@@ -141,10 +142,10 @@ function createBoardsLoader(config: KanbanClientConfig) {
141
142
 
142
143
  try {
143
144
  if (hooks?.beforeLoadBoards) {
144
- const canLoad = await hooks.beforeLoadBoards(context);
145
- if (!canLoad) {
146
- throw new Error("Load prevented by beforeLoadBoards hook");
147
- }
145
+ await runClientHookWithShim(
146
+ () => hooks.beforeLoadBoards!(context),
147
+ "Load prevented by beforeLoadBoards hook",
148
+ );
148
149
  }
149
150
 
150
151
  const client = createApiClient<KanbanApiRouter>({
@@ -161,13 +162,10 @@ function createBoardsLoader(config: KanbanClientConfig) {
161
162
  const boards = queryClient.getQueryData<SerializedBoardWithColumns[]>(
162
163
  listQuery.queryKey,
163
164
  );
164
- const canContinue = await hooks.afterLoadBoards(
165
- boards || null,
166
- context,
165
+ await runClientHookWithShim(
166
+ () => hooks.afterLoadBoards!(boards || null, context),
167
+ "Load prevented by afterLoadBoards hook",
167
168
  );
168
- if (canContinue === false) {
169
- throw new Error("Load prevented by afterLoadBoards hook");
170
- }
171
169
  }
172
170
 
173
171
  const queryState = queryClient.getQueryState(listQuery.queryKey);
@@ -210,10 +208,10 @@ function createBoardLoader(boardId: string, config: KanbanClientConfig) {
210
208
 
211
209
  try {
212
210
  if (hooks?.beforeLoadBoard) {
213
- const canLoad = await hooks.beforeLoadBoard(boardId, context);
214
- if (!canLoad) {
215
- throw new Error("Load prevented by beforeLoadBoard hook");
216
- }
211
+ await runClientHookWithShim(
212
+ () => hooks.beforeLoadBoard!(boardId, context),
213
+ "Load prevented by beforeLoadBoard hook",
214
+ );
217
215
  }
218
216
 
219
217
  const client = createApiClient<KanbanApiRouter>({
@@ -229,14 +227,10 @@ function createBoardLoader(boardId: string, config: KanbanClientConfig) {
229
227
  const board = queryClient.getQueryData<SerializedBoardWithColumns>(
230
228
  boardQuery.queryKey,
231
229
  );
232
- const canContinue = await hooks.afterLoadBoard(
233
- board || null,
234
- boardId,
235
- context,
230
+ await runClientHookWithShim(
231
+ () => hooks.afterLoadBoard!(board || null, boardId, context),
232
+ "Load prevented by afterLoadBoard hook",
236
233
  );
237
- if (canContinue === false) {
238
- throw new Error("Load prevented by afterLoadBoard hook");
239
- }
240
234
  }
241
235
 
242
236
  const queryState = queryClient.getQueryState(boardQuery.queryKey);
@@ -278,17 +272,17 @@ function createNewBoardLoader(config: KanbanClientConfig) {
278
272
 
279
273
  try {
280
274
  if (hooks?.beforeLoadNewBoard) {
281
- const canLoad = await hooks.beforeLoadNewBoard(context);
282
- if (!canLoad) {
283
- throw new Error("Load prevented by beforeLoadNewBoard hook");
284
- }
275
+ await runClientHookWithShim(
276
+ () => hooks.beforeLoadNewBoard!(context),
277
+ "Load prevented by beforeLoadNewBoard hook",
278
+ );
285
279
  }
286
280
 
287
281
  if (hooks?.afterLoadNewBoard) {
288
- const canContinue = await hooks.afterLoadNewBoard(context);
289
- if (canContinue === false) {
290
- throw new Error("Load prevented by afterLoadNewBoard hook");
291
- }
282
+ await runClientHookWithShim(
283
+ () => hooks.afterLoadNewBoard!(context),
284
+ "Load prevented by afterLoadNewBoard hook",
285
+ );
292
286
  }
293
287
  } catch (error) {
294
288
  if (hooks?.onLoadError) {