@btst/stack 2.1.0 → 2.3.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 (229) hide show
  1. package/dist/api/index.cjs +9 -1
  2. package/dist/api/index.d.cts +4 -4
  3. package/dist/api/index.d.mts +4 -4
  4. package/dist/api/index.d.ts +4 -4
  5. package/dist/api/index.mjs +9 -1
  6. package/dist/client/index.d.cts +2 -2
  7. package/dist/client/index.d.mts +2 -2
  8. package/dist/client/index.d.ts +2 -2
  9. package/dist/index.d.cts +1 -1
  10. package/dist/index.d.mts +1 -1
  11. package/dist/index.d.ts +1 -1
  12. package/dist/packages/stack/src/plugins/ai-chat/api/getters.cjs +42 -0
  13. package/dist/packages/stack/src/plugins/ai-chat/api/getters.mjs +39 -0
  14. package/dist/packages/stack/src/plugins/ai-chat/api/plugin.cjs +5 -0
  15. package/dist/packages/stack/src/plugins/ai-chat/api/plugin.mjs +5 -0
  16. package/dist/packages/stack/src/plugins/blog/api/getters.cjs +131 -0
  17. package/dist/packages/stack/src/plugins/blog/api/getters.mjs +127 -0
  18. package/dist/packages/stack/src/plugins/blog/api/plugin.cjs +60 -107
  19. package/dist/packages/stack/src/plugins/blog/api/plugin.mjs +60 -107
  20. package/dist/packages/stack/src/plugins/blog/api/query-key-defs.cjs +18 -0
  21. package/dist/packages/stack/src/plugins/blog/api/query-key-defs.mjs +15 -0
  22. package/dist/packages/stack/src/plugins/blog/api/serializers.cjs +21 -0
  23. package/dist/packages/stack/src/plugins/blog/api/serializers.mjs +18 -0
  24. package/dist/packages/stack/src/plugins/blog/client/plugin.cjs +16 -1
  25. package/dist/packages/stack/src/plugins/blog/client/plugin.mjs +17 -2
  26. package/dist/packages/stack/src/plugins/cms/api/getters.cjs +156 -0
  27. package/dist/packages/stack/src/plugins/cms/api/getters.mjs +147 -0
  28. package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +624 -617
  29. package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +623 -616
  30. package/dist/packages/stack/src/plugins/cms/api/query-key-defs.cjs +29 -0
  31. package/dist/packages/stack/src/plugins/cms/api/query-key-defs.mjs +26 -0
  32. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.internal.cjs +1 -1
  33. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.internal.mjs +1 -1
  34. package/dist/packages/stack/src/plugins/cms/client/hooks/cms-hooks.cjs +6 -3
  35. package/dist/packages/stack/src/plugins/cms/client/hooks/cms-hooks.mjs +6 -3
  36. package/dist/packages/stack/src/plugins/cms/client/plugin.cjs +15 -0
  37. package/dist/packages/stack/src/plugins/cms/client/plugin.mjs +16 -1
  38. package/dist/packages/stack/src/plugins/form-builder/api/getters.cjs +120 -0
  39. package/dist/packages/stack/src/plugins/form-builder/api/getters.mjs +112 -0
  40. package/dist/packages/stack/src/plugins/form-builder/api/plugin.cjs +75 -86
  41. package/dist/packages/stack/src/plugins/form-builder/api/plugin.mjs +71 -82
  42. package/dist/packages/stack/src/plugins/form-builder/api/query-key-defs.cjs +37 -0
  43. package/dist/packages/stack/src/plugins/form-builder/api/query-key-defs.mjs +33 -0
  44. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.cjs +1 -1
  45. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.mjs +1 -1
  46. package/dist/packages/stack/src/plugins/form-builder/client/plugin.cjs +15 -0
  47. package/dist/packages/stack/src/plugins/form-builder/client/plugin.mjs +16 -1
  48. package/dist/packages/stack/src/plugins/kanban/api/getters.cjs +84 -0
  49. package/dist/packages/stack/src/plugins/kanban/api/getters.mjs +81 -0
  50. package/dist/packages/stack/src/plugins/kanban/api/plugin.cjs +37 -123
  51. package/dist/packages/stack/src/plugins/kanban/api/plugin.mjs +37 -123
  52. package/dist/packages/stack/src/plugins/kanban/api/query-key-defs.cjs +26 -0
  53. package/dist/packages/stack/src/plugins/kanban/api/query-key-defs.mjs +23 -0
  54. package/dist/packages/stack/src/plugins/kanban/api/serializers.cjs +30 -0
  55. package/dist/packages/stack/src/plugins/kanban/api/serializers.mjs +26 -0
  56. package/dist/packages/stack/src/plugins/kanban/client/plugin.cjs +11 -1
  57. package/dist/packages/stack/src/plugins/kanban/client/plugin.mjs +12 -2
  58. package/dist/packages/stack/src/plugins/utils.cjs +6 -0
  59. package/dist/packages/stack/src/plugins/utils.mjs +6 -1
  60. package/dist/plugins/ai-chat/api/index.cjs +3 -0
  61. package/dist/plugins/ai-chat/api/index.d.cts +27 -4
  62. package/dist/plugins/ai-chat/api/index.d.mts +27 -4
  63. package/dist/plugins/ai-chat/api/index.d.ts +27 -4
  64. package/dist/plugins/ai-chat/api/index.mjs +1 -0
  65. package/dist/plugins/ai-chat/client/hooks/index.d.cts +2 -2
  66. package/dist/plugins/ai-chat/client/hooks/index.d.mts +2 -2
  67. package/dist/plugins/ai-chat/client/hooks/index.d.ts +2 -2
  68. package/dist/plugins/ai-chat/query-keys.d.cts +9 -284
  69. package/dist/plugins/ai-chat/query-keys.d.mts +9 -284
  70. package/dist/plugins/ai-chat/query-keys.d.ts +9 -284
  71. package/dist/plugins/api/index.d.cts +4 -3
  72. package/dist/plugins/api/index.d.mts +4 -3
  73. package/dist/plugins/api/index.d.ts +4 -3
  74. package/dist/plugins/blog/api/index.cjs +9 -0
  75. package/dist/plugins/blog/api/index.d.cts +20 -4
  76. package/dist/plugins/blog/api/index.d.mts +20 -4
  77. package/dist/plugins/blog/api/index.d.ts +20 -4
  78. package/dist/plugins/blog/api/index.mjs +3 -0
  79. package/dist/plugins/blog/client/hooks/index.d.cts +5 -5
  80. package/dist/plugins/blog/client/hooks/index.d.mts +5 -5
  81. package/dist/plugins/blog/client/hooks/index.d.ts +5 -5
  82. package/dist/plugins/blog/client/index.d.cts +1 -1
  83. package/dist/plugins/blog/client/index.d.mts +1 -1
  84. package/dist/plugins/blog/client/index.d.ts +1 -1
  85. package/dist/plugins/blog/query-keys.cjs +13 -9
  86. package/dist/plugins/blog/query-keys.d.cts +8 -333
  87. package/dist/plugins/blog/query-keys.d.mts +8 -333
  88. package/dist/plugins/blog/query-keys.d.ts +8 -333
  89. package/dist/plugins/blog/query-keys.mjs +13 -9
  90. package/dist/plugins/client/index.cjs +1 -0
  91. package/dist/plugins/client/index.d.cts +10 -3
  92. package/dist/plugins/client/index.d.mts +10 -3
  93. package/dist/plugins/client/index.d.ts +10 -3
  94. package/dist/plugins/client/index.mjs +1 -1
  95. package/dist/plugins/cms/api/index.cjs +10 -0
  96. package/dist/plugins/cms/api/index.d.cts +7 -163
  97. package/dist/plugins/cms/api/index.d.mts +7 -163
  98. package/dist/plugins/cms/api/index.d.ts +7 -163
  99. package/dist/plugins/cms/api/index.mjs +2 -0
  100. package/dist/plugins/cms/client/hooks/index.d.cts +1 -1
  101. package/dist/plugins/cms/client/hooks/index.d.mts +1 -1
  102. package/dist/plugins/cms/client/hooks/index.d.ts +1 -1
  103. package/dist/plugins/cms/query-keys.cjs +2 -1
  104. package/dist/plugins/cms/query-keys.d.cts +6 -9
  105. package/dist/plugins/cms/query-keys.d.mts +6 -9
  106. package/dist/plugins/cms/query-keys.d.ts +6 -9
  107. package/dist/plugins/cms/query-keys.mjs +2 -1
  108. package/dist/plugins/form-builder/api/index.cjs +10 -0
  109. package/dist/plugins/form-builder/api/index.d.cts +7 -141
  110. package/dist/plugins/form-builder/api/index.d.mts +7 -141
  111. package/dist/plugins/form-builder/api/index.d.ts +7 -141
  112. package/dist/plugins/form-builder/api/index.mjs +2 -0
  113. package/dist/plugins/form-builder/client/components/index.d.cts +1 -1
  114. package/dist/plugins/form-builder/client/components/index.d.mts +1 -1
  115. package/dist/plugins/form-builder/client/components/index.d.ts +1 -1
  116. package/dist/plugins/form-builder/client/hooks/index.d.cts +1 -1
  117. package/dist/plugins/form-builder/client/hooks/index.d.mts +1 -1
  118. package/dist/plugins/form-builder/client/hooks/index.d.ts +1 -1
  119. package/dist/plugins/form-builder/query-keys.cjs +3 -2
  120. package/dist/plugins/form-builder/query-keys.d.cts +7 -6
  121. package/dist/plugins/form-builder/query-keys.d.mts +7 -6
  122. package/dist/plugins/form-builder/query-keys.d.ts +7 -6
  123. package/dist/plugins/form-builder/query-keys.mjs +3 -2
  124. package/dist/plugins/kanban/api/index.cjs +9 -0
  125. package/dist/plugins/kanban/api/index.d.cts +17 -395
  126. package/dist/plugins/kanban/api/index.d.mts +17 -395
  127. package/dist/plugins/kanban/api/index.d.ts +17 -395
  128. package/dist/plugins/kanban/api/index.mjs +3 -0
  129. package/dist/plugins/kanban/client/components/index.d.cts +1 -1
  130. package/dist/plugins/kanban/client/components/index.d.mts +1 -1
  131. package/dist/plugins/kanban/client/components/index.d.ts +1 -1
  132. package/dist/plugins/kanban/client/hooks/index.d.cts +1 -1
  133. package/dist/plugins/kanban/client/hooks/index.d.mts +1 -1
  134. package/dist/plugins/kanban/client/hooks/index.d.ts +1 -1
  135. package/dist/plugins/kanban/client/index.d.cts +1 -1
  136. package/dist/plugins/kanban/client/index.d.mts +1 -1
  137. package/dist/plugins/kanban/client/index.d.ts +1 -1
  138. package/dist/plugins/kanban/query-keys.cjs +6 -12
  139. package/dist/plugins/kanban/query-keys.d.cts +5 -16
  140. package/dist/plugins/kanban/query-keys.d.mts +5 -16
  141. package/dist/plugins/kanban/query-keys.d.ts +5 -16
  142. package/dist/plugins/kanban/query-keys.mjs +6 -12
  143. package/dist/plugins/open-api/api/index.d.cts +2 -2
  144. package/dist/plugins/open-api/api/index.d.mts +2 -2
  145. package/dist/plugins/open-api/api/index.d.ts +2 -2
  146. package/dist/plugins/route-docs/client/index.d.cts +1 -1
  147. package/dist/plugins/route-docs/client/index.d.mts +1 -1
  148. package/dist/plugins/route-docs/client/index.d.ts +1 -1
  149. package/dist/plugins/ui-builder/index.d.cts +1 -1
  150. package/dist/plugins/ui-builder/index.d.mts +1 -1
  151. package/dist/plugins/ui-builder/index.d.ts +1 -1
  152. package/dist/shared/{stack.BoA0xkJv.d.cts → stack.7n9Y_u7N.d.cts} +33 -7
  153. package/dist/shared/{stack.BoA0xkJv.d.mts → stack.7n9Y_u7N.d.mts} +33 -7
  154. package/dist/shared/{stack.BoA0xkJv.d.ts → stack.7n9Y_u7N.d.ts} +33 -7
  155. package/dist/shared/stack.B1EeBt1b.d.ts +297 -0
  156. package/dist/shared/stack.BIXEI6v_.d.mts +419 -0
  157. package/dist/shared/stack.BKfolAyK.d.ts +419 -0
  158. package/dist/shared/stack.BeSm90va.d.ts +289 -0
  159. package/dist/shared/stack.BpolpQpf.d.cts +445 -0
  160. package/dist/shared/stack.C5dtIncc.d.mts +293 -0
  161. package/dist/shared/stack.CIP6QS9l.d.ts +293 -0
  162. package/dist/shared/stack.CMh_EdxW.d.cts +289 -0
  163. package/dist/shared/stack.CP68pFEH.d.mts +297 -0
  164. package/dist/shared/{stack.BsXokfNh.d.mts → stack.CVDTkMoO.d.cts} +8 -2
  165. package/dist/shared/{stack.BsXokfNh.d.ts → stack.CVDTkMoO.d.mts} +8 -2
  166. package/dist/shared/{stack.BsXokfNh.d.cts → stack.CVDTkMoO.d.ts} +8 -2
  167. package/dist/shared/{stack.DKDMI-QO.d.mts → stack.DJaKVY7v.d.cts} +7 -1
  168. package/dist/shared/{stack.DKDMI-QO.d.ts → stack.DJaKVY7v.d.mts} +7 -1
  169. package/dist/shared/{stack.DKDMI-QO.d.cts → stack.DJaKVY7v.d.ts} +7 -1
  170. package/dist/shared/{stack.DzH_wcvr.d.mts → stack.DdI5W6MB.d.cts} +9 -3
  171. package/dist/shared/{stack.DzH_wcvr.d.ts → stack.DdI5W6MB.d.mts} +9 -3
  172. package/dist/shared/{stack.DzH_wcvr.d.cts → stack.DdI5W6MB.d.ts} +9 -3
  173. package/dist/shared/stack.Dg09R0oB.d.mts +289 -0
  174. package/dist/shared/stack.Dw0Ly2TM.d.cts +293 -0
  175. package/dist/shared/stack.IdtKDRka.d.cts +297 -0
  176. package/dist/shared/stack.TIBF2AOx.d.ts +445 -0
  177. package/dist/shared/stack.rTy7-wQU.d.mts +445 -0
  178. package/dist/shared/stack.snB1EDP7.d.cts +419 -0
  179. package/package.json +3 -3
  180. package/src/__tests__/stack-api.test.ts +118 -0
  181. package/src/api/index.ts +15 -1
  182. package/src/plugins/ai-chat/__tests__/getters.test.ts +109 -0
  183. package/src/plugins/ai-chat/api/getters.ts +71 -0
  184. package/src/plugins/ai-chat/api/index.ts +1 -0
  185. package/src/plugins/ai-chat/api/plugin.ts +8 -0
  186. package/src/plugins/api/index.ts +3 -1
  187. package/src/plugins/blog/__tests__/getters.test.ts +540 -0
  188. package/src/plugins/blog/api/getters.ts +243 -0
  189. package/src/plugins/blog/api/index.ts +9 -0
  190. package/src/plugins/blog/api/plugin.ts +98 -141
  191. package/src/plugins/blog/api/query-key-defs.ts +46 -0
  192. package/src/plugins/blog/api/serializers.ts +27 -0
  193. package/src/plugins/blog/client/plugin.tsx +21 -1
  194. package/src/plugins/blog/query-keys.ts +21 -20
  195. package/src/plugins/client/index.ts +1 -1
  196. package/src/plugins/cms/__tests__/getters.test.ts +206 -0
  197. package/src/plugins/cms/api/getters.ts +268 -0
  198. package/src/plugins/cms/api/index.ts +15 -1
  199. package/src/plugins/cms/api/plugin.ts +151 -150
  200. package/src/plugins/cms/api/query-key-defs.ts +53 -0
  201. package/src/plugins/cms/api/serializers.ts +12 -0
  202. package/src/plugins/cms/client/components/pages/content-editor-page.internal.tsx +1 -1
  203. package/src/plugins/cms/client/hooks/cms-hooks.tsx +3 -0
  204. package/src/plugins/cms/client/plugin.tsx +19 -0
  205. package/src/plugins/cms/query-keys.ts +2 -1
  206. package/src/plugins/cms/types.ts +1 -1
  207. package/src/plugins/form-builder/__tests__/getters.test.ts +159 -0
  208. package/src/plugins/form-builder/api/getters.ts +226 -0
  209. package/src/plugins/form-builder/api/index.ts +15 -1
  210. package/src/plugins/form-builder/api/plugin.ts +107 -109
  211. package/src/plugins/form-builder/api/query-key-defs.ts +79 -0
  212. package/src/plugins/form-builder/api/serializers.ts +12 -0
  213. package/src/plugins/form-builder/client/components/pages/submissions-page.internal.tsx +1 -1
  214. package/src/plugins/form-builder/client/plugin.tsx +19 -0
  215. package/src/plugins/form-builder/query-keys.ts +6 -2
  216. package/src/plugins/form-builder/types.ts +2 -2
  217. package/src/plugins/kanban/__tests__/getters.test.ts +172 -0
  218. package/src/plugins/kanban/api/getters.ts +149 -0
  219. package/src/plugins/kanban/api/index.ts +4 -0
  220. package/src/plugins/kanban/api/plugin.ts +65 -146
  221. package/src/plugins/kanban/api/query-key-defs.ts +54 -0
  222. package/src/plugins/kanban/api/serializers.ts +49 -0
  223. package/src/plugins/kanban/client/plugin.tsx +15 -1
  224. package/src/plugins/kanban/query-keys.ts +10 -14
  225. package/src/plugins/utils.ts +19 -0
  226. package/src/types.ts +44 -5
  227. package/dist/shared/{stack.CbuN2zVV.d.cts → stack.CBON0dWL.d.cts} +7 -7
  228. package/dist/shared/{stack.CbuN2zVV.d.mts → stack.CBON0dWL.d.mts} +7 -7
  229. package/dist/shared/{stack.CbuN2zVV.d.ts → stack.CBON0dWL.d.ts} +7 -7
@@ -4,10 +4,43 @@ const api = require('@btst/stack/plugins/api');
4
4
  const db = require('../db.cjs');
5
5
  const utils = require('../utils.cjs');
6
6
  const schemas = require('../schemas.cjs');
7
+ const getters = require('./getters.cjs');
8
+ const queryKeyDefs = require('./query-key-defs.cjs');
9
+ const serializers = require('./serializers.cjs');
7
10
 
11
+ function createKanbanPrefetchForRoute(adapter) {
12
+ return async function prefetchForRoute(key, qc, params) {
13
+ switch (key) {
14
+ case "boards": {
15
+ const result = await getters.getAllBoards(adapter, { limit: 50, offset: 0 });
16
+ qc.setQueryData(
17
+ queryKeyDefs.KANBAN_QUERY_KEYS.boardsList({}),
18
+ result.items.map(serializers.serializeBoard)
19
+ );
20
+ break;
21
+ }
22
+ case "board": {
23
+ const boardId = params?.boardId ?? "";
24
+ if (boardId) {
25
+ const board = await getters.getBoardById(adapter, boardId);
26
+ qc.setQueryData(
27
+ queryKeyDefs.KANBAN_QUERY_KEYS.boardDetail(boardId),
28
+ board ? serializers.serializeBoard(board) : null
29
+ );
30
+ }
31
+ break;
32
+ }
33
+ }
34
+ };
35
+ }
8
36
  const kanbanBackendPlugin = (hooks) => api.defineBackendPlugin({
9
37
  name: "kanban",
10
38
  dbPlugin: db.kanbanSchema,
39
+ api: (adapter) => ({
40
+ getAllBoards: (params) => getters.getAllBoards(adapter, params),
41
+ getBoardById: (id) => getters.getBoardById(adapter, id),
42
+ prefetchForRoute: createKanbanPrefetchForRoute(adapter)
43
+ }),
11
44
  routes: (adapter) => {
12
45
  const listBoards = api.createEndpoint(
13
46
  "/boards",
@@ -27,86 +60,9 @@ const kanbanBackendPlugin = (hooks) => api.defineBackendPlugin({
27
60
  });
28
61
  }
29
62
  }
30
- const whereConditions = [];
31
- if (query.slug) {
32
- whereConditions.push({
33
- field: "slug",
34
- value: query.slug,
35
- operator: "eq"
36
- });
37
- }
38
- if (query.ownerId) {
39
- whereConditions.push({
40
- field: "ownerId",
41
- value: query.ownerId,
42
- operator: "eq"
43
- });
44
- }
45
- if (query.organizationId) {
46
- whereConditions.push({
47
- field: "organizationId",
48
- value: query.organizationId,
49
- operator: "eq"
50
- });
51
- }
52
- const boards = await adapter.findMany({
53
- model: "kanbanBoard",
54
- limit: query.limit ?? 50,
55
- offset: query.offset ?? 0,
56
- where: whereConditions,
57
- sortBy: {
58
- field: "createdAt",
59
- direction: "desc"
60
- },
61
- join: {
62
- kanbanColumn: true
63
- }
64
- });
65
- const columnIds = [];
66
- for (const board of boards) {
67
- if (board.column) {
68
- for (const col of board.column) {
69
- columnIds.push(col.id);
70
- }
71
- }
72
- }
73
- const tasksByColumn = /* @__PURE__ */ new Map();
74
- if (columnIds.length > 0) {
75
- const taskQueries = columnIds.map(
76
- (columnId) => adapter.findMany({
77
- model: "kanbanTask",
78
- where: [
79
- {
80
- field: "columnId",
81
- value: columnId,
82
- operator: "eq"
83
- }
84
- ],
85
- sortBy: { field: "order", direction: "asc" }
86
- })
87
- );
88
- const taskResults = await Promise.all(taskQueries);
89
- for (let i = 0; i < columnIds.length; i++) {
90
- const columnId = columnIds[i];
91
- const tasks = taskResults[i];
92
- if (columnId && tasks) {
93
- tasksByColumn.set(columnId, tasks);
94
- }
95
- }
96
- }
97
- const result = boards.map((board) => {
98
- const columns = (board.column || []).sort((a, b) => a.order - b.order).map((col) => ({
99
- ...col,
100
- tasks: tasksByColumn.get(col.id) || []
101
- }));
102
- const { column: _, ...boardWithoutJoin } = board;
103
- return {
104
- ...boardWithoutJoin,
105
- columns
106
- };
107
- });
63
+ const result = await getters.getAllBoards(adapter, query);
108
64
  if (hooks?.onBoardsRead) {
109
- await hooks.onBoardsRead(result, query, context);
65
+ await hooks.onBoardsRead(result.items, query, context);
110
66
  }
111
67
  return result;
112
68
  } catch (error) {
@@ -134,52 +90,10 @@ const kanbanBackendPlugin = (hooks) => api.defineBackendPlugin({
134
90
  });
135
91
  }
136
92
  }
137
- const board = await adapter.findOne({
138
- model: "kanbanBoard",
139
- where: [
140
- { field: "id", value: params.id, operator: "eq" }
141
- ],
142
- join: {
143
- kanbanColumn: true
144
- }
145
- });
146
- if (!board) {
93
+ const result = await getters.getBoardById(adapter, params.id);
94
+ if (!result) {
147
95
  throw ctx.error(404, { message: "Board not found" });
148
96
  }
149
- const columnIds = (board.column || []).map((c) => c.id);
150
- const tasksByColumn = /* @__PURE__ */ new Map();
151
- if (columnIds.length > 0) {
152
- const taskQueries = columnIds.map(
153
- (columnId) => adapter.findMany({
154
- model: "kanbanTask",
155
- where: [
156
- {
157
- field: "columnId",
158
- value: columnId,
159
- operator: "eq"
160
- }
161
- ],
162
- sortBy: { field: "order", direction: "asc" }
163
- })
164
- );
165
- const taskResults = await Promise.all(taskQueries);
166
- for (let i = 0; i < columnIds.length; i++) {
167
- const columnId = columnIds[i];
168
- const tasks = taskResults[i];
169
- if (columnId && tasks) {
170
- tasksByColumn.set(columnId, tasks);
171
- }
172
- }
173
- }
174
- const columns = (board.column || []).sort((a, b) => a.order - b.order).map((col) => ({
175
- ...col,
176
- tasks: tasksByColumn.get(col.id) || []
177
- }));
178
- const { column: _, ...boardWithoutJoin } = board;
179
- const result = {
180
- ...boardWithoutJoin,
181
- columns
182
- };
183
97
  if (hooks?.onBoardRead) {
184
98
  await hooks.onBoardRead(result, context);
185
99
  }
@@ -2,10 +2,43 @@ import { defineBackendPlugin, createEndpoint } from '@btst/stack/plugins/api';
2
2
  import { kanbanSchema } from '../db.mjs';
3
3
  import { slugify } from '../utils.mjs';
4
4
  import { BoardListQuerySchema, createBoardSchema, updateBoardSchema, createColumnSchema, updateColumnSchema, reorderColumnsSchema, createTaskSchema, updateTaskSchema, moveTaskSchema, reorderTasksSchema } from '../schemas.mjs';
5
+ import { getBoardById, getAllBoards } from './getters.mjs';
6
+ import { KANBAN_QUERY_KEYS } from './query-key-defs.mjs';
7
+ import { serializeBoard } from './serializers.mjs';
5
8
 
9
+ function createKanbanPrefetchForRoute(adapter) {
10
+ return async function prefetchForRoute(key, qc, params) {
11
+ switch (key) {
12
+ case "boards": {
13
+ const result = await getAllBoards(adapter, { limit: 50, offset: 0 });
14
+ qc.setQueryData(
15
+ KANBAN_QUERY_KEYS.boardsList({}),
16
+ result.items.map(serializeBoard)
17
+ );
18
+ break;
19
+ }
20
+ case "board": {
21
+ const boardId = params?.boardId ?? "";
22
+ if (boardId) {
23
+ const board = await getBoardById(adapter, boardId);
24
+ qc.setQueryData(
25
+ KANBAN_QUERY_KEYS.boardDetail(boardId),
26
+ board ? serializeBoard(board) : null
27
+ );
28
+ }
29
+ break;
30
+ }
31
+ }
32
+ };
33
+ }
6
34
  const kanbanBackendPlugin = (hooks) => defineBackendPlugin({
7
35
  name: "kanban",
8
36
  dbPlugin: kanbanSchema,
37
+ api: (adapter) => ({
38
+ getAllBoards: (params) => getAllBoards(adapter, params),
39
+ getBoardById: (id) => getBoardById(adapter, id),
40
+ prefetchForRoute: createKanbanPrefetchForRoute(adapter)
41
+ }),
9
42
  routes: (adapter) => {
10
43
  const listBoards = createEndpoint(
11
44
  "/boards",
@@ -25,86 +58,9 @@ const kanbanBackendPlugin = (hooks) => defineBackendPlugin({
25
58
  });
26
59
  }
27
60
  }
28
- const whereConditions = [];
29
- if (query.slug) {
30
- whereConditions.push({
31
- field: "slug",
32
- value: query.slug,
33
- operator: "eq"
34
- });
35
- }
36
- if (query.ownerId) {
37
- whereConditions.push({
38
- field: "ownerId",
39
- value: query.ownerId,
40
- operator: "eq"
41
- });
42
- }
43
- if (query.organizationId) {
44
- whereConditions.push({
45
- field: "organizationId",
46
- value: query.organizationId,
47
- operator: "eq"
48
- });
49
- }
50
- const boards = await adapter.findMany({
51
- model: "kanbanBoard",
52
- limit: query.limit ?? 50,
53
- offset: query.offset ?? 0,
54
- where: whereConditions,
55
- sortBy: {
56
- field: "createdAt",
57
- direction: "desc"
58
- },
59
- join: {
60
- kanbanColumn: true
61
- }
62
- });
63
- const columnIds = [];
64
- for (const board of boards) {
65
- if (board.column) {
66
- for (const col of board.column) {
67
- columnIds.push(col.id);
68
- }
69
- }
70
- }
71
- const tasksByColumn = /* @__PURE__ */ new Map();
72
- if (columnIds.length > 0) {
73
- const taskQueries = columnIds.map(
74
- (columnId) => adapter.findMany({
75
- model: "kanbanTask",
76
- where: [
77
- {
78
- field: "columnId",
79
- value: columnId,
80
- operator: "eq"
81
- }
82
- ],
83
- sortBy: { field: "order", direction: "asc" }
84
- })
85
- );
86
- const taskResults = await Promise.all(taskQueries);
87
- for (let i = 0; i < columnIds.length; i++) {
88
- const columnId = columnIds[i];
89
- const tasks = taskResults[i];
90
- if (columnId && tasks) {
91
- tasksByColumn.set(columnId, tasks);
92
- }
93
- }
94
- }
95
- const result = boards.map((board) => {
96
- const columns = (board.column || []).sort((a, b) => a.order - b.order).map((col) => ({
97
- ...col,
98
- tasks: tasksByColumn.get(col.id) || []
99
- }));
100
- const { column: _, ...boardWithoutJoin } = board;
101
- return {
102
- ...boardWithoutJoin,
103
- columns
104
- };
105
- });
61
+ const result = await getAllBoards(adapter, query);
106
62
  if (hooks?.onBoardsRead) {
107
- await hooks.onBoardsRead(result, query, context);
63
+ await hooks.onBoardsRead(result.items, query, context);
108
64
  }
109
65
  return result;
110
66
  } catch (error) {
@@ -132,52 +88,10 @@ const kanbanBackendPlugin = (hooks) => defineBackendPlugin({
132
88
  });
133
89
  }
134
90
  }
135
- const board = await adapter.findOne({
136
- model: "kanbanBoard",
137
- where: [
138
- { field: "id", value: params.id, operator: "eq" }
139
- ],
140
- join: {
141
- kanbanColumn: true
142
- }
143
- });
144
- if (!board) {
91
+ const result = await getBoardById(adapter, params.id);
92
+ if (!result) {
145
93
  throw ctx.error(404, { message: "Board not found" });
146
94
  }
147
- const columnIds = (board.column || []).map((c) => c.id);
148
- const tasksByColumn = /* @__PURE__ */ new Map();
149
- if (columnIds.length > 0) {
150
- const taskQueries = columnIds.map(
151
- (columnId) => adapter.findMany({
152
- model: "kanbanTask",
153
- where: [
154
- {
155
- field: "columnId",
156
- value: columnId,
157
- operator: "eq"
158
- }
159
- ],
160
- sortBy: { field: "order", direction: "asc" }
161
- })
162
- );
163
- const taskResults = await Promise.all(taskQueries);
164
- for (let i = 0; i < columnIds.length; i++) {
165
- const columnId = columnIds[i];
166
- const tasks = taskResults[i];
167
- if (columnId && tasks) {
168
- tasksByColumn.set(columnId, tasks);
169
- }
170
- }
171
- }
172
- const columns = (board.column || []).sort((a, b) => a.order - b.order).map((col) => ({
173
- ...col,
174
- tasks: tasksByColumn.get(col.id) || []
175
- }));
176
- const { column: _, ...boardWithoutJoin } = board;
177
- const result = {
178
- ...boardWithoutJoin,
179
- columns
180
- };
181
95
  if (hooks?.onBoardRead) {
182
96
  await hooks.onBoardRead(result, context);
183
97
  }
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ function boardsListDiscriminator(params) {
4
+ return {
5
+ slug: params?.slug,
6
+ ownerId: params?.ownerId,
7
+ organizationId: params?.organizationId,
8
+ limit: params?.limit ?? 50,
9
+ offset: params?.offset ?? 0
10
+ };
11
+ }
12
+ const KANBAN_QUERY_KEYS = {
13
+ /**
14
+ * Key for boards.list(params) query.
15
+ * Full key: ["boards", "list", { slug, ownerId, organizationId, limit, offset }]
16
+ */
17
+ boardsList: (params) => ["boards", "list", boardsListDiscriminator(params)],
18
+ /**
19
+ * Key for boards.detail(boardId) query.
20
+ * Full key: ["boards", "detail", boardId]
21
+ */
22
+ boardDetail: (boardId) => ["boards", "detail", boardId]
23
+ };
24
+
25
+ exports.KANBAN_QUERY_KEYS = KANBAN_QUERY_KEYS;
26
+ exports.boardsListDiscriminator = boardsListDiscriminator;
@@ -0,0 +1,23 @@
1
+ function boardsListDiscriminator(params) {
2
+ return {
3
+ slug: params?.slug,
4
+ ownerId: params?.ownerId,
5
+ organizationId: params?.organizationId,
6
+ limit: params?.limit ?? 50,
7
+ offset: params?.offset ?? 0
8
+ };
9
+ }
10
+ const KANBAN_QUERY_KEYS = {
11
+ /**
12
+ * Key for boards.list(params) query.
13
+ * Full key: ["boards", "list", { slug, ownerId, organizationId, limit, offset }]
14
+ */
15
+ boardsList: (params) => ["boards", "list", boardsListDiscriminator(params)],
16
+ /**
17
+ * Key for boards.detail(boardId) query.
18
+ * Full key: ["boards", "detail", boardId]
19
+ */
20
+ boardDetail: (boardId) => ["boards", "detail", boardId]
21
+ };
22
+
23
+ export { KANBAN_QUERY_KEYS, boardsListDiscriminator };
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ function serializeTask(task) {
4
+ return {
5
+ ...task,
6
+ completedAt: task.completedAt?.toISOString(),
7
+ createdAt: task.createdAt.toISOString(),
8
+ updatedAt: task.updatedAt.toISOString()
9
+ };
10
+ }
11
+ function serializeColumn(col) {
12
+ return {
13
+ ...col,
14
+ createdAt: col.createdAt.toISOString(),
15
+ updatedAt: col.updatedAt.toISOString(),
16
+ tasks: col.tasks.map(serializeTask)
17
+ };
18
+ }
19
+ function serializeBoard(board) {
20
+ return {
21
+ ...board,
22
+ createdAt: board.createdAt.toISOString(),
23
+ updatedAt: board.updatedAt.toISOString(),
24
+ columns: board.columns.map(serializeColumn)
25
+ };
26
+ }
27
+
28
+ exports.serializeBoard = serializeBoard;
29
+ exports.serializeColumn = serializeColumn;
30
+ exports.serializeTask = serializeTask;
@@ -0,0 +1,26 @@
1
+ function serializeTask(task) {
2
+ return {
3
+ ...task,
4
+ completedAt: task.completedAt?.toISOString(),
5
+ createdAt: task.createdAt.toISOString(),
6
+ updatedAt: task.updatedAt.toISOString()
7
+ };
8
+ }
9
+ function serializeColumn(col) {
10
+ return {
11
+ ...col,
12
+ createdAt: col.createdAt.toISOString(),
13
+ updatedAt: col.updatedAt.toISOString(),
14
+ tasks: col.tasks.map(serializeTask)
15
+ };
16
+ }
17
+ function serializeBoard(board) {
18
+ return {
19
+ ...board,
20
+ createdAt: board.createdAt.toISOString(),
21
+ updatedAt: board.updatedAt.toISOString(),
22
+ columns: board.columns.map(serializeColumn)
23
+ };
24
+ }
25
+
26
+ export { serializeBoard, serializeColumn, serializeTask };
@@ -51,6 +51,11 @@ function createBoardsLoader(config) {
51
51
  await hooks.onLoadError(error, context);
52
52
  }
53
53
  } catch (error) {
54
+ if (client.isConnectionError(error)) {
55
+ console.warn(
56
+ "[btst/kanban] route.loader() failed \u2014 no server running at build time. Use myStack.api.kanban.prefetchForRoute() for SSG data prefetching."
57
+ );
58
+ }
54
59
  if (hooks?.onLoadError) {
55
60
  await hooks.onLoadError(error, context);
56
61
  }
@@ -103,6 +108,11 @@ function createBoardLoader(boardId, config) {
103
108
  await hooks.onLoadError(error, context);
104
109
  }
105
110
  } catch (error) {
111
+ if (client.isConnectionError(error)) {
112
+ console.warn(
113
+ "[btst/kanban] route.loader() failed \u2014 no server running at build time. Use myStack.api.kanban.prefetchForRoute() for SSG data prefetching."
114
+ );
115
+ }
106
116
  if (hooks?.onLoadError) {
107
117
  await hooks.onLoadError(error, context);
108
118
  }
@@ -266,7 +276,7 @@ const kanbanClientPlugin = (config) => client.defineClientPlugin({
266
276
  method: "GET",
267
277
  query: { limit: 100 }
268
278
  });
269
- boards = res.data ?? [];
279
+ boards = res.data?.items ?? [];
270
280
  } catch {
271
281
  }
272
282
  const entries = [
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { defineClientPlugin, createApiClient } from '@btst/stack/plugins/client';
2
+ import { defineClientPlugin, createApiClient, isConnectionError } from '@btst/stack/plugins/client';
3
3
  import { createRoute } from '@btst/yar';
4
4
  import { createKanbanQueryKeys } from '../../../../../../plugins/kanban/query-keys.mjs';
5
5
  import { BoardsListPageComponent } from './components/pages/boards-list-page.mjs';
@@ -49,6 +49,11 @@ function createBoardsLoader(config) {
49
49
  await hooks.onLoadError(error, context);
50
50
  }
51
51
  } catch (error) {
52
+ if (isConnectionError(error)) {
53
+ console.warn(
54
+ "[btst/kanban] route.loader() failed \u2014 no server running at build time. Use myStack.api.kanban.prefetchForRoute() for SSG data prefetching."
55
+ );
56
+ }
52
57
  if (hooks?.onLoadError) {
53
58
  await hooks.onLoadError(error, context);
54
59
  }
@@ -101,6 +106,11 @@ function createBoardLoader(boardId, config) {
101
106
  await hooks.onLoadError(error, context);
102
107
  }
103
108
  } catch (error) {
109
+ if (isConnectionError(error)) {
110
+ console.warn(
111
+ "[btst/kanban] route.loader() failed \u2014 no server running at build time. Use myStack.api.kanban.prefetchForRoute() for SSG data prefetching."
112
+ );
113
+ }
104
114
  if (hooks?.onLoadError) {
105
115
  await hooks.onLoadError(error, context);
106
116
  }
@@ -264,7 +274,7 @@ const kanbanClientPlugin = (config) => defineClientPlugin({
264
274
  method: "GET",
265
275
  query: { limit: 100 }
266
276
  });
267
- boards = res.data ?? [];
277
+ boards = res.data?.items ?? [];
268
278
  } catch {
269
279
  }
270
280
  const entries = [
@@ -2,6 +2,11 @@
2
2
 
3
3
  const client = require('better-call/client');
4
4
 
5
+ function isConnectionError(err) {
6
+ if (!(err instanceof Error)) return false;
7
+ const code = err.cause?.code ?? err.code;
8
+ return err.message.includes("ECONNREFUSED") || err.message.includes("fetch failed") || err.message.includes("ERR_CONNECTION_REFUSED") || code === "ECONNREFUSED" || code === "ERR_CONNECTION_REFUSED";
9
+ }
5
10
  function createApiClient(options) {
6
11
  const { baseURL = "", basePath = "/" } = options ?? {};
7
12
  const normalizedBaseURL = baseURL ? baseURL.replace(/\/$/, "") : "";
@@ -14,3 +19,4 @@ function createApiClient(options) {
14
19
  }
15
20
 
16
21
  exports.createApiClient = createApiClient;
22
+ exports.isConnectionError = isConnectionError;
@@ -1,5 +1,10 @@
1
1
  import { createClient } from 'better-call/client';
2
2
 
3
+ function isConnectionError(err) {
4
+ if (!(err instanceof Error)) return false;
5
+ const code = err.cause?.code ?? err.code;
6
+ return err.message.includes("ECONNREFUSED") || err.message.includes("fetch failed") || err.message.includes("ERR_CONNECTION_REFUSED") || code === "ECONNREFUSED" || code === "ERR_CONNECTION_REFUSED";
7
+ }
3
8
  function createApiClient(options) {
4
9
  const { baseURL = "", basePath = "/" } = options ?? {};
5
10
  const normalizedBaseURL = baseURL ? baseURL.replace(/\/$/, "") : "";
@@ -11,4 +16,4 @@ function createApiClient(options) {
11
16
  });
12
17
  }
13
18
 
14
- export { createApiClient };
19
+ export { createApiClient, isConnectionError };
@@ -1,9 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  const plugin = require('../../../packages/stack/src/plugins/ai-chat/api/plugin.cjs');
4
+ const getters = require('../../../packages/stack/src/plugins/ai-chat/api/getters.cjs');
4
5
  const plugins_aiChat_queryKeys = require('../query-keys.cjs');
5
6
 
6
7
 
7
8
 
8
9
  exports.aiChatBackendPlugin = plugin.aiChatBackendPlugin;
10
+ exports.getAllConversations = getters.getAllConversations;
11
+ exports.getConversationById = getters.getConversationById;
9
12
  exports.createAiChatQueryKeys = plugins_aiChat_queryKeys.createAiChatQueryKeys;
@@ -1,9 +1,32 @@
1
- export { d as AiChatApiRouter, b as AiChatBackendConfig, A as AiChatBackendHooks, a as AiChatMode, C as ChatApiContext, c as aiChatBackendPlugin, createAiChatQueryKeys } from '../query-keys.cjs';
1
+ export { e as AiChatApiRouter, b as AiChatBackendConfig, A as AiChatBackendHooks, a as AiChatMode, C as ChatApiContext, d as aiChatBackendPlugin, c as createAiChatQueryKeys } from '../../../shared/stack.CMh_EdxW.cjs';
2
+ import { Adapter } from '@btst/db';
3
+ import { C as Conversation, M as Message } from '../../../shared/stack.Be1QIHEn.cjs';
4
+ import '@tanstack/react-query';
5
+ import '@btst/stack/plugins/client';
2
6
  import '@btst/stack/plugins/api';
3
7
  import 'better-call';
4
8
  import 'zod/v4/core';
5
9
  import 'zod';
6
10
  import 'ai';
7
- import '../../../shared/stack.Be1QIHEn.cjs';
8
- import '@tanstack/react-query';
9
- import '@btst/stack/plugins/client';
11
+
12
+ /**
13
+ * Retrieve all conversations, optionally filtered by userId.
14
+ * Pure DB function - no hooks, no HTTP context. Safe for server-side use.
15
+ *
16
+ * @param adapter - The database adapter
17
+ * @param userId - Optional user ID to filter conversations by owner
18
+ */
19
+ declare function getAllConversations(adapter: Adapter, userId?: string): Promise<Conversation[]>;
20
+ /**
21
+ * Retrieve a single conversation by its ID, including all messages.
22
+ * Returns null if the conversation is not found.
23
+ * Pure DB function - no hooks, no HTTP context. Safe for server-side use.
24
+ *
25
+ * @param adapter - The database adapter
26
+ * @param id - The conversation ID
27
+ */
28
+ declare function getConversationById(adapter: Adapter, id: string): Promise<(Conversation & {
29
+ messages: Message[];
30
+ }) | null>;
31
+
32
+ export { getAllConversations, getConversationById };
@@ -1,9 +1,32 @@
1
- export { d as AiChatApiRouter, b as AiChatBackendConfig, A as AiChatBackendHooks, a as AiChatMode, C as ChatApiContext, c as aiChatBackendPlugin, createAiChatQueryKeys } from '../query-keys.mjs';
1
+ export { e as AiChatApiRouter, b as AiChatBackendConfig, A as AiChatBackendHooks, a as AiChatMode, C as ChatApiContext, d as aiChatBackendPlugin, c as createAiChatQueryKeys } from '../../../shared/stack.Dg09R0oB.mjs';
2
+ import { Adapter } from '@btst/db';
3
+ import { C as Conversation, M as Message } from '../../../shared/stack.Be1QIHEn.mjs';
4
+ import '@tanstack/react-query';
5
+ import '@btst/stack/plugins/client';
2
6
  import '@btst/stack/plugins/api';
3
7
  import 'better-call';
4
8
  import 'zod/v4/core';
5
9
  import 'zod';
6
10
  import 'ai';
7
- import '../../../shared/stack.Be1QIHEn.mjs';
8
- import '@tanstack/react-query';
9
- import '@btst/stack/plugins/client';
11
+
12
+ /**
13
+ * Retrieve all conversations, optionally filtered by userId.
14
+ * Pure DB function - no hooks, no HTTP context. Safe for server-side use.
15
+ *
16
+ * @param adapter - The database adapter
17
+ * @param userId - Optional user ID to filter conversations by owner
18
+ */
19
+ declare function getAllConversations(adapter: Adapter, userId?: string): Promise<Conversation[]>;
20
+ /**
21
+ * Retrieve a single conversation by its ID, including all messages.
22
+ * Returns null if the conversation is not found.
23
+ * Pure DB function - no hooks, no HTTP context. Safe for server-side use.
24
+ *
25
+ * @param adapter - The database adapter
26
+ * @param id - The conversation ID
27
+ */
28
+ declare function getConversationById(adapter: Adapter, id: string): Promise<(Conversation & {
29
+ messages: Message[];
30
+ }) | null>;
31
+
32
+ export { getAllConversations, getConversationById };