@btst/stack 3.1.2 → 4.1.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 (129) hide show
  1. package/README.md +2 -2
  2. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-input.cjs +112 -103
  3. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-input.mjs +112 -103
  4. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.cjs +5 -2
  5. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.mjs +5 -2
  6. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.cjs +99 -88
  7. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.mjs +99 -88
  8. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-message.cjs +12 -3
  9. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-message.mjs +12 -3
  10. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-sidebar.cjs +6 -2
  11. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-sidebar.mjs +6 -2
  12. package/dist/packages/stack/src/plugins/ai-chat/client/hooks/chat-hooks.cjs +5 -5
  13. package/dist/packages/stack/src/plugins/ai-chat/client/hooks/chat-hooks.mjs +5 -5
  14. package/dist/packages/stack/src/plugins/blog/client/components/shared/page-wrapper.cjs +3 -2
  15. package/dist/packages/stack/src/plugins/blog/client/components/shared/page-wrapper.mjs +3 -2
  16. package/dist/packages/stack/src/plugins/blog/client/components/shared/post-card.cjs +81 -52
  17. package/dist/packages/stack/src/plugins/blog/client/components/shared/post-card.mjs +82 -53
  18. package/dist/packages/stack/src/plugins/blog/client/components/shared/posts-list.cjs +3 -2
  19. package/dist/packages/stack/src/plugins/blog/client/components/shared/posts-list.mjs +3 -2
  20. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-list-page.internal.cjs +95 -75
  21. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-list-page.internal.mjs +95 -75
  22. package/dist/packages/stack/src/plugins/cms/client/components/pages/dashboard-page.internal.cjs +16 -3
  23. package/dist/packages/stack/src/plugins/cms/client/components/pages/dashboard-page.internal.mjs +16 -3
  24. package/dist/packages/stack/src/plugins/cms/client/components/shared/page-wrapper.cjs +3 -2
  25. package/dist/packages/stack/src/plugins/cms/client/components/shared/page-wrapper.mjs +3 -2
  26. package/dist/packages/stack/src/plugins/cms/utils.cjs +6 -2
  27. package/dist/packages/stack/src/plugins/cms/utils.mjs +6 -3
  28. package/dist/packages/stack/src/plugins/form-builder/client/components/forms/form-renderer.cjs +14 -8
  29. package/dist/packages/stack/src/plugins/form-builder/client/components/forms/form-renderer.mjs +14 -8
  30. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.cjs +94 -87
  31. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.mjs +94 -87
  32. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.cjs +81 -74
  33. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.mjs +81 -74
  34. package/dist/packages/stack/src/plugins/form-builder/client/components/shared/page-wrapper.cjs +3 -2
  35. package/dist/packages/stack/src/plugins/form-builder/client/components/shared/page-wrapper.mjs +3 -2
  36. package/dist/packages/stack/src/plugins/form-builder/client/components/shared/pagination.cjs +25 -16
  37. package/dist/packages/stack/src/plugins/form-builder/client/components/shared/pagination.mjs +25 -16
  38. package/dist/packages/stack/src/plugins/form-builder/utils.cjs +6 -2
  39. package/dist/packages/stack/src/plugins/form-builder/utils.mjs +6 -3
  40. package/dist/packages/stack/src/plugins/kanban/api/getters.cjs +7 -5
  41. package/dist/packages/stack/src/plugins/kanban/api/getters.mjs +7 -5
  42. package/dist/packages/stack/src/plugins/media/client/hooks/use-media.cjs +10 -8
  43. package/dist/packages/stack/src/plugins/media/client/hooks/use-media.mjs +10 -8
  44. package/dist/plugins/ai-chat/client/components/index.d.cts +1 -1
  45. package/dist/plugins/ai-chat/client/components/index.d.mts +1 -1
  46. package/dist/plugins/ai-chat/client/components/index.d.ts +1 -1
  47. package/dist/plugins/ai-chat/client/index.d.cts +2 -2
  48. package/dist/plugins/ai-chat/client/index.d.mts +2 -2
  49. package/dist/plugins/ai-chat/client/index.d.ts +2 -2
  50. package/dist/plugins/blog/api/index.d.cts +2 -2
  51. package/dist/plugins/blog/api/index.d.mts +2 -2
  52. package/dist/plugins/blog/api/index.d.ts +2 -2
  53. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  54. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  55. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  56. package/dist/plugins/blog/client/index.d.cts +20 -2
  57. package/dist/plugins/blog/client/index.d.mts +20 -2
  58. package/dist/plugins/blog/client/index.d.ts +20 -2
  59. package/dist/plugins/blog/query-keys.d.cts +2 -2
  60. package/dist/plugins/blog/query-keys.d.mts +2 -2
  61. package/dist/plugins/blog/query-keys.d.ts +2 -2
  62. package/dist/plugins/cms/api/index.d.cts +1 -1
  63. package/dist/plugins/cms/api/index.d.mts +1 -1
  64. package/dist/plugins/cms/api/index.d.ts +1 -1
  65. package/dist/plugins/cms/client/index.d.cts +18 -0
  66. package/dist/plugins/cms/client/index.d.mts +18 -0
  67. package/dist/plugins/cms/client/index.d.ts +18 -0
  68. package/dist/plugins/cms/query-keys.d.cts +1 -1
  69. package/dist/plugins/cms/query-keys.d.mts +1 -1
  70. package/dist/plugins/cms/query-keys.d.ts +1 -1
  71. package/dist/plugins/form-builder/client/index.d.cts +16 -0
  72. package/dist/plugins/form-builder/client/index.d.mts +16 -0
  73. package/dist/plugins/form-builder/client/index.d.ts +16 -0
  74. package/dist/plugins/ui-builder/client/hooks/index.d.cts +1 -1
  75. package/dist/plugins/ui-builder/client/hooks/index.d.mts +1 -1
  76. package/dist/plugins/ui-builder/client/hooks/index.d.ts +1 -1
  77. package/dist/plugins/ui-builder/client/index.d.cts +1 -1
  78. package/dist/plugins/ui-builder/client/index.d.mts +1 -1
  79. package/dist/plugins/ui-builder/client/index.d.ts +1 -1
  80. package/dist/plugins/ui-builder/query-keys.d.cts +1 -1
  81. package/dist/plugins/ui-builder/query-keys.d.mts +1 -1
  82. package/dist/plugins/ui-builder/query-keys.d.ts +1 -1
  83. package/dist/shared/{stack.BTStHVTy.d.cts → stack.BGzgFx3R.d.cts} +5 -5
  84. package/dist/shared/{stack.CtrnoQgH.d.cts → stack.BOZRaVpu.d.cts} +35 -35
  85. package/dist/shared/{stack.94kN3pEK.d.mts → stack.CjCSDfWy.d.mts} +5 -5
  86. package/dist/shared/{stack.lPKwd_v0.d.ts → stack.DlBoNqYu.d.ts} +35 -35
  87. package/dist/shared/{stack.DfrRYOIl.d.ts → stack.Dpg_JjIo.d.ts} +5 -5
  88. package/dist/shared/{stack.BggTHKlo.d.mts → stack.Dvy7FmaA.d.mts} +35 -35
  89. package/dist/shared/{stack.5_zzCejP.d.cts → stack.rVF0f509.d.cts} +20 -0
  90. package/dist/shared/{stack.5_zzCejP.d.mts → stack.rVF0f509.d.mts} +20 -0
  91. package/dist/shared/{stack.5_zzCejP.d.ts → stack.rVF0f509.d.ts} +20 -0
  92. package/package.json +11 -7
  93. package/src/__tests__/package-metadata.test.ts +4 -4
  94. package/src/plugins/ai-chat/__tests__/classnames-overrides.test.tsx +248 -0
  95. package/src/plugins/ai-chat/__tests__/hooks.test.tsx +70 -54
  96. package/src/plugins/ai-chat/client/components/chat-input.tsx +6 -1
  97. package/src/plugins/ai-chat/client/components/chat-interface.tsx +3 -0
  98. package/src/plugins/ai-chat/client/components/chat-layout.tsx +9 -2
  99. package/src/plugins/ai-chat/client/components/chat-message.tsx +13 -5
  100. package/src/plugins/ai-chat/client/components/chat-sidebar.tsx +6 -2
  101. package/src/plugins/ai-chat/client/hooks/chat-hooks.tsx +6 -9
  102. package/src/plugins/ai-chat/client/overrides.ts +21 -0
  103. package/src/plugins/blog/__tests__/classnames-overrides.test.tsx +206 -0
  104. package/src/plugins/blog/client/components/shared/page-wrapper.tsx +3 -2
  105. package/src/plugins/blog/client/components/shared/post-card.tsx +32 -12
  106. package/src/plugins/blog/client/components/shared/posts-list.tsx +3 -2
  107. package/src/plugins/blog/client/overrides.ts +18 -0
  108. package/src/plugins/cms/__tests__/classnames-overrides.test.tsx +200 -0
  109. package/src/plugins/cms/client/components/pages/content-list-page.internal.tsx +17 -4
  110. package/src/plugins/cms/client/components/pages/dashboard-page.internal.tsx +14 -3
  111. package/src/plugins/cms/client/components/shared/page-wrapper.tsx +3 -2
  112. package/src/plugins/cms/client/overrides.ts +19 -0
  113. package/src/plugins/form-builder/__tests__/classnames-overrides.test.tsx +312 -0
  114. package/src/plugins/form-builder/client/components/forms/form-renderer.tsx +14 -9
  115. package/src/plugins/form-builder/client/components/pages/form-list-page.internal.tsx +9 -5
  116. package/src/plugins/form-builder/client/components/pages/submissions-page.internal.tsx +9 -5
  117. package/src/plugins/form-builder/client/components/shared/page-wrapper.tsx +3 -2
  118. package/src/plugins/form-builder/client/components/shared/pagination.tsx +7 -1
  119. package/src/plugins/form-builder/client/overrides.ts +17 -0
  120. package/src/plugins/kanban/__tests__/getters.test.ts +14 -1
  121. package/src/plugins/kanban/api/getters.ts +9 -7
  122. package/src/plugins/kanban/types.ts +4 -6
  123. package/src/plugins/media/client/hooks/use-media.tsx +7 -8
  124. package/dist/shared/{stack.lsbdKRj7.d.mts → stack.B8PG21zX.d.mts} +16 -16
  125. package/dist/shared/{stack.CKc_Bais.d.cts → stack.Csi-4Mgz.d.cts} +7 -7
  126. package/dist/shared/{stack.CKc_Bais.d.mts → stack.Csi-4Mgz.d.mts} +7 -7
  127. package/dist/shared/{stack.CKc_Bais.d.ts → stack.Csi-4Mgz.d.ts} +7 -7
  128. package/dist/shared/{stack.BMlTZnZp.d.ts → stack.CyalJ0j6.d.ts} +16 -16
  129. package/dist/shared/{stack.BV0uNV4f.d.cts → stack.D6Pg-ufX.d.cts} +16 -16
@@ -18,6 +18,7 @@ import type { FormBuilderPluginOverrides } from "../../overrides";
18
18
  import type { SerializedFormSubmission } from "../../../types";
19
19
  import { formBuilderPermissions } from "../../../permissions";
20
20
  import { FORM_BUILDER_PLUGIN_ID } from "../../constants";
21
+ import { cn } from "../../../utils";
21
22
 
22
23
  export interface FormRendererProps {
23
24
  /** Form slug to render */
@@ -132,9 +133,13 @@ export function FormRenderer({
132
133
  className,
133
134
  }: FormRendererProps) {
134
135
  const t = useTranslate();
135
- const { fieldComponents: overrideFieldComponents, localization } =
136
- usePluginOverrides<FormBuilderPluginOverrides>(FORM_BUILDER_PLUGIN_ID);
136
+ const {
137
+ fieldComponents: overrideFieldComponents,
138
+ localization,
139
+ classNames,
140
+ } = usePluginOverrides<FormBuilderPluginOverrides>(FORM_BUILDER_PLUGIN_ID);
137
141
 
142
+ const formClassName = cn(className, classNames?.form);
138
143
  const { form, isLoading, error } = useFormBySlug(slug);
139
144
  const submitMutation = useSubmitForm(slug);
140
145
 
@@ -201,7 +206,7 @@ export function FormRenderer({
201
206
  // Loading state
202
207
  if (isLoading) {
203
208
  return (
204
- <div className={className}>
209
+ <div className={formClassName}>
205
210
  <LoadingComponent />
206
211
  </div>
207
212
  );
@@ -210,7 +215,7 @@ export function FormRenderer({
210
215
  // Error state
211
216
  if (error) {
212
217
  return (
213
- <div className={className}>
218
+ <div className={formClassName}>
214
219
  <ErrorComponent error={error} />
215
220
  </div>
216
221
  );
@@ -219,7 +224,7 @@ export function FormRenderer({
219
224
  // Form not found
220
225
  if (!form) {
221
226
  return (
222
- <div className={className}>
227
+ <div className={formClassName}>
223
228
  <ErrorComponent
224
229
  error={
225
230
  new Error(
@@ -235,7 +240,7 @@ export function FormRenderer({
235
240
  // Form not active
236
241
  if (form.status !== "active") {
237
242
  return (
238
- <div className={className}>
243
+ <div className={formClassName}>
239
244
  <ErrorComponent
240
245
  error={
241
246
  new Error(
@@ -254,7 +259,7 @@ export function FormRenderer({
254
259
  // Schema parsing failed
255
260
  if (!zodSchema) {
256
261
  return (
257
- <div className={className}>
262
+ <div className={formClassName}>
258
263
  <ErrorComponent
259
264
  error={
260
265
  new Error(
@@ -273,7 +278,7 @@ export function FormRenderer({
273
278
  // Success state
274
279
  if (submitted && finalSuccessMessage) {
275
280
  return (
276
- <div className={className}>
281
+ <div className={formClassName}>
277
282
  <DefaultSuccessComponent message={finalSuccessMessage} />
278
283
  </div>
279
284
  );
@@ -300,7 +305,7 @@ export function FormRenderer({
300
305
  status: form.status,
301
306
  })}
302
307
  >
303
- <div className={className} data-testid="form-renderer">
308
+ <div className={formClassName} data-testid="form-renderer">
304
309
  <SteppedAutoForm
305
310
  formSchema={zodSchema}
306
311
  fieldConfig={fieldConfig}
@@ -52,6 +52,7 @@ import { EmptyState } from "../shared/empty-state";
52
52
  import { Pagination } from "../shared/pagination";
53
53
  import { formBuilderPermissions } from "../../../permissions";
54
54
  import { FORM_BUILDER_PLUGIN_ID } from "../../constants";
55
+ import { cn } from "../../../utils";
55
56
 
56
57
  // URL-synced search state: `?q=...` while typing (history: replace), clean
57
58
  // URL when the query is empty (the default is omitted from the URL).
@@ -64,9 +65,8 @@ const SEARCH_DEBOUNCE_MS = 300;
64
65
  export function FormListPage() {
65
66
  const t = useTranslate();
66
67
  const notify = useNotify();
67
- const { localization } = usePluginOverrides<FormBuilderPluginOverrides>(
68
- FORM_BUILDER_PLUGIN_ID,
69
- );
68
+ const { localization, classNames } =
69
+ usePluginOverrides<FormBuilderPluginOverrides>(FORM_BUILDER_PLUGIN_ID);
70
70
  const { Link, navigate, resolve } = usePluginSiteNavigation(
71
71
  FORM_BUILDER_PLUGIN_ID,
72
72
  );
@@ -233,7 +233,10 @@ export function FormListPage() {
233
233
  )
234
234
  ) : (
235
235
  <>
236
- <div className="rounded-lg border">
236
+ <div
237
+ className={cn("rounded-lg border", classNames?.table)}
238
+ data-testid="form-builder-list-table"
239
+ >
237
240
  <Table>
238
241
  <TableHeader>
239
242
  <TableRow>
@@ -261,7 +264,7 @@ export function FormListPage() {
261
264
  </TableHeader>
262
265
  <TableBody>
263
266
  {forms.map((form) => (
264
- <TableRow key={form.id}>
267
+ <TableRow key={form.id} className={classNames?.tableRow}>
265
268
  <TableCell className="font-medium">{form.name}</TableCell>
266
269
  <TableCell className="text-muted-foreground font-mono text-sm">
267
270
  {form.slug}
@@ -362,6 +365,7 @@ export function FormListPage() {
362
365
  hasMore={hasMore}
363
366
  isLoadingMore={isLoadingMore}
364
367
  onLoadMore={loadMore}
368
+ className={classNames?.pagination}
365
369
  labels={{
366
370
  showing:
367
371
  localization?.FORM_BUILDER_LIST_PAGINATION_SHOWING ??
@@ -48,6 +48,7 @@ import { EmptyState } from "../shared/empty-state";
48
48
  import { Pagination } from "../shared/pagination";
49
49
  import { formBuilderPermissions } from "../../../permissions";
50
50
  import { FORM_BUILDER_PLUGIN_ID } from "../../constants";
51
+ import { cn } from "../../../utils";
51
52
 
52
53
  export interface SubmissionsPageProps {
53
54
  formId: string;
@@ -56,9 +57,8 @@ export interface SubmissionsPageProps {
56
57
  export function SubmissionsPage({ formId }: SubmissionsPageProps) {
57
58
  const t = useTranslate();
58
59
  const notify = useNotify();
59
- const { localization } = usePluginOverrides<FormBuilderPluginOverrides>(
60
- FORM_BUILDER_PLUGIN_ID,
61
- );
60
+ const { localization, classNames } =
61
+ usePluginOverrides<FormBuilderPluginOverrides>(FORM_BUILDER_PLUGIN_ID);
62
62
  const { identity } = useIdentity();
63
63
  const { Link: LinkComponent, resolve } = usePluginSiteNavigation(
64
64
  FORM_BUILDER_PLUGIN_ID,
@@ -143,7 +143,10 @@ export function SubmissionsPage({ formId }: SubmissionsPageProps) {
143
143
  />
144
144
  ) : (
145
145
  <>
146
- <div className="rounded-lg border">
146
+ <div
147
+ className={cn("rounded-lg border", classNames?.table)}
148
+ data-testid="form-builder-submissions-table"
149
+ >
147
150
  <Table>
148
151
  <TableHeader>
149
152
  <TableRow>
@@ -166,7 +169,7 @@ export function SubmissionsPage({ formId }: SubmissionsPageProps) {
166
169
  </TableHeader>
167
170
  <TableBody>
168
171
  {submissions.map((sub) => (
169
- <TableRow key={sub.id}>
172
+ <TableRow key={sub.id} className={classNames?.tableRow}>
170
173
  <TableCell className="font-mono text-xs">
171
174
  {sub.id.slice(0, 8)}...
172
175
  </TableCell>
@@ -249,6 +252,7 @@ export function SubmissionsPage({ formId }: SubmissionsPageProps) {
249
252
  hasMore={hasMore}
250
253
  isLoadingMore={isLoadingMore}
251
254
  onLoadMore={loadMore}
255
+ className={classNames?.pagination}
252
256
  labels={{
253
257
  showing:
254
258
  localization?.FORM_BUILDER_LIST_PAGINATION_SHOWING ??
@@ -4,6 +4,7 @@ import { usePluginOverrides } from "@btst/stack/context";
4
4
  import { PageWrapper as SharedPageWrapper } from "@workspace/ui/components/page-wrapper";
5
5
  import type { FormBuilderPluginOverrides } from "../../overrides";
6
6
  import { FORM_BUILDER_PLUGIN_ID } from "../../constants";
7
+ import { cn } from "../../../utils";
7
8
 
8
9
  export function PageWrapper({
9
10
  children,
@@ -14,7 +15,7 @@ export function PageWrapper({
14
15
  className?: string;
15
16
  testId?: string;
16
17
  }) {
17
- const { showAttribution } = usePluginOverrides<
18
+ const { showAttribution, classNames } = usePluginOverrides<
18
19
  FormBuilderPluginOverrides,
19
20
  Partial<FormBuilderPluginOverrides>
20
21
  >(FORM_BUILDER_PLUGIN_ID, {
@@ -23,7 +24,7 @@ export function PageWrapper({
23
24
 
24
25
  return (
25
26
  <SharedPageWrapper
26
- className={className}
27
+ className={cn(className, classNames?.container)}
27
28
  testId={testId}
28
29
  showAttribution={showAttribution}
29
30
  >
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { Button } from "@workspace/ui/components/button";
4
4
  import { ChevronRight } from "lucide-react";
5
+ import { cn } from "../../../utils";
5
6
 
6
7
  interface PaginationProps {
7
8
  total: number;
@@ -9,6 +10,7 @@ interface PaginationProps {
9
10
  hasMore: boolean;
10
11
  isLoadingMore: boolean;
11
12
  onLoadMore: () => void;
13
+ className?: string;
12
14
  labels?: {
13
15
  showing?: string;
14
16
  previous?: string;
@@ -23,6 +25,7 @@ export function Pagination({
23
25
  hasMore,
24
26
  isLoadingMore,
25
27
  onLoadMore,
28
+ className,
26
29
  labels = {},
27
30
  }: PaginationProps) {
28
31
  const {
@@ -36,7 +39,10 @@ export function Pagination({
36
39
  .replace("{total}", String(total));
37
40
 
38
41
  return (
39
- <div className="flex items-center justify-between py-4">
42
+ <div
43
+ className={cn("flex items-center justify-between py-4", className)}
44
+ data-testid="form-builder-pagination"
45
+ >
40
46
  <p className="text-sm text-muted-foreground">{showingText}</p>
41
47
  {hasMore && (
42
48
  <Button
@@ -51,6 +51,23 @@ export interface FormBuilderPluginOverrides {
51
51
  */
52
52
  fieldComponents?: Record<string, ComponentType<AutoFormInputComponentProps>>;
53
53
 
54
+ /**
55
+ * Optional class names merged onto specific UI elements.
56
+ * Use these for targeted styling without replacing whole components.
57
+ */
58
+ classNames?: {
59
+ /** Page container shared by every Form Builder page */
60
+ container?: string;
61
+ /** Wrapper around the forms or submissions table */
62
+ table?: string;
63
+ /** Each body row in the forms or submissions table */
64
+ tableRow?: string;
65
+ /** Pagination footer under the forms or submissions list */
66
+ pagination?: string;
67
+ /** Root of `FormRenderer`, including loading, error, and success states */
68
+ form?: string;
69
+ };
70
+
54
71
  /**
55
72
  * Localization object for the Form Builder plugin
56
73
  */
@@ -3,7 +3,7 @@ import { createMemoryAdapter } from "@btst/adapter-memory";
3
3
  import { defineDb } from "@btst/db";
4
4
  import type { DBAdapter as Adapter } from "@btst/db";
5
5
  import { kanbanSchema } from "../db";
6
- import { getAllBoards, getBoardById } from "../api/getters";
6
+ import { getAllBoards, getBoardById, getBoardSummaries } from "../api/getters";
7
7
 
8
8
  const createTestAdapter = (): Adapter => {
9
9
  const db = defineDb({}).use(kanbanSchema);
@@ -90,6 +90,7 @@ describe("kanban getters", () => {
90
90
  expect(total).toBe(1);
91
91
  expect(boards[0]!.slug).toBe("my-board");
92
92
  expect(boards[0]!.columns).toHaveLength(1);
93
+ expect(boards[0]).not.toHaveProperty("kanbanColumn");
93
94
  expect(boards[0]!.columns[0]!.title).toBe("To Do");
94
95
  expect(boards[0]!.columns[0]!.tasks).toHaveLength(1);
95
96
  expect(boards[0]!.columns[0]!.tasks[0]!.title).toBe("Task 1");
@@ -139,6 +140,18 @@ describe("kanban getters", () => {
139
140
  });
140
141
  });
141
142
 
143
+ it("returns joined column summaries without leaking raw relations or tasks", async () => {
144
+ const board = await createBoard(adapter, "Board", "board");
145
+ const column = await createColumn(adapter, board.id, "To Do", 0);
146
+ await createTask(adapter, column.id, "Private task", 0);
147
+
148
+ const { items } = await getBoardSummaries(adapter);
149
+ expect(items[0]?.columns).toHaveLength(1);
150
+ expect(items[0]?.columns[0]?.title).toBe("To Do");
151
+ expect(items[0]).not.toHaveProperty("kanbanColumn");
152
+ expect(items[0]?.columns[0]).not.toHaveProperty("tasks");
153
+ });
154
+
142
155
  describe("getBoardById", () => {
143
156
  it("returns null when board does not exist", async () => {
144
157
  const board = await getBoardById(adapter, "nonexistent");
@@ -65,15 +65,15 @@ async function getBoardRows(
65
65
  }
66
66
 
67
67
  /**
68
- * Given a raw board record (with a `column` join), fetches tasks for every
68
+ * Given a raw board record (with a `kanbanColumn` join), fetches tasks for every
69
69
  * column in parallel and returns the sorted columns with their tasks attached.
70
- * Strips the raw `column` join field from the returned board object.
70
+ * Strips the raw `kanbanColumn` join field from the returned board object.
71
71
  */
72
72
  async function hydrateColumnsWithTasks(
73
73
  adapter: Adapter,
74
74
  board: BoardWithKanbanColumn,
75
75
  ): Promise<BoardWithColumns> {
76
- const columnIds = (board.column || []).map((c) => c.id);
76
+ const columnIds = (board.kanbanColumn || []).map((c) => c.id);
77
77
  const tasksByColumn = new Map<string, Task[]>();
78
78
 
79
79
  if (columnIds.length > 0) {
@@ -97,11 +97,11 @@ async function hydrateColumnsWithTasks(
97
97
  }
98
98
  }
99
99
 
100
- const columns: ColumnWithTasks[] = (board.column || [])
100
+ const columns: ColumnWithTasks[] = (board.kanbanColumn || [])
101
101
  .sort((a, b) => a.order - b.order)
102
102
  .map((col) => ({ ...col, tasks: tasksByColumn.get(col.id) || [] }));
103
103
 
104
- const { column: _, ...boardWithoutJoin } = board;
104
+ const { kanbanColumn: _, ...boardWithoutJoin } = board;
105
105
  return { ...boardWithoutJoin, columns };
106
106
  }
107
107
 
@@ -131,9 +131,11 @@ export async function getBoardSummaries(
131
131
  const result = await getBoardRows(adapter, params);
132
132
  return {
133
133
  ...result,
134
- items: result.items.map(({ column = [], ...board }) => ({
134
+ items: result.items.map(({ kanbanColumn = [], ...board }) => ({
135
135
  ...board,
136
- columns: [...column].sort((left, right) => left.order - right.order),
136
+ columns: [...kanbanColumn].sort(
137
+ (left, right) => left.order - right.order,
138
+ ),
137
139
  })),
138
140
  };
139
141
  }
@@ -85,20 +85,18 @@ export type BoardWithColumnsOnly = Board & {
85
85
 
86
86
  /**
87
87
  * Board with joined column relationships from the database
88
- * Note: The adapter returns joined data under the schema key name ("column"),
89
- * not the model name ("kanbanColumn")
88
+ * Better DB 3 returns joined data under the canonical model name.
90
89
  */
91
90
  export type BoardWithKanbanColumn = Board & {
92
- column?: Column[];
91
+ kanbanColumn?: Column[];
93
92
  };
94
93
 
95
94
  /**
96
95
  * Column with joined task relationships from the database
97
- * Note: The adapter returns joined data under the schema key name ("task"),
98
- * not the model name ("kanbanTask")
96
+ * Better DB 3 returns joined data under the canonical model name.
99
97
  */
100
98
  export type ColumnWithKanbanTask = Column & {
101
- task?: Task[];
99
+ kanbanTask?: Task[];
102
100
  };
103
101
 
104
102
  // Serialized types for API responses (dates as strings)
@@ -157,12 +157,10 @@ function withCurrentListRefresh<TData, TVariables>(
157
157
  mapResult: (data: TData) => TData = (data) => data,
158
158
  data: TData | undefined = mutation.data,
159
159
  ): UseMutationResult<TData, Error, TVariables> {
160
- const mutateAsync: typeof mutation.mutateAsync = async (
161
- variables,
162
- options,
163
- ) => {
160
+ const mutateAsync: typeof mutation.mutateAsync = async (...args) => {
161
+ const options = args[1];
164
162
  const startedAs = listRefresh.currentScope();
165
- const result = await mutation.mutateAsync(variables, {
163
+ args[1] = {
166
164
  ...options,
167
165
  onSuccess: options?.onSuccess
168
166
  ? (data, ...args) => options.onSuccess?.(mapResult(data), ...args)
@@ -174,14 +172,15 @@ function withCurrentListRefresh<TData, TVariables>(
174
172
  ...args,
175
173
  )
176
174
  : undefined,
177
- });
175
+ };
176
+ const result = await mutation.mutateAsync(...args);
178
177
  await listRefresh.refreshAfterSuccess(startedAs);
179
178
  return mapResult(result);
180
179
  };
181
- const mutate: typeof mutation.mutate = (variables, options) => {
180
+ const mutate: typeof mutation.mutate = (...args) => {
182
181
  // Each invocation owns an awaited promise, so a later mutation cannot
183
182
  // detach the successful call's required cache refresh.
184
- void mutateAsync(variables, options).catch(() => {});
183
+ void mutateAsync(...args).catch(() => {});
185
184
  };
186
185
  return {
187
186
  ...mutation,
@@ -95,8 +95,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
95
95
  readonly id: string;
96
96
  readonly slug: string;
97
97
  readonly contentTypeId: string;
98
- readonly data: string;
99
98
  readonly authorId?: string | undefined;
99
+ readonly data: string;
100
100
  }[];
101
101
  readonly total: number;
102
102
  }>;
@@ -128,8 +128,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
128
128
  readonly id: string;
129
129
  readonly slug: string;
130
130
  readonly contentTypeId: string;
131
- readonly data: string;
132
131
  readonly authorId?: string | undefined;
132
+ readonly data: string;
133
133
  }>;
134
134
  readonly createContentItem: better_call.StrictEndpoint<"/content/:typeSlug", {
135
135
  params: z.ZodObject<{
@@ -165,8 +165,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
165
165
  readonly id: string;
166
166
  readonly slug: string;
167
167
  readonly contentTypeId: string;
168
- readonly data: string;
169
168
  readonly authorId?: string | undefined;
169
+ readonly data: string;
170
170
  }>;
171
171
  readonly updateContentItem: better_call.StrictEndpoint<"/content/:typeSlug/:id", {
172
172
  params: z.ZodObject<{
@@ -203,8 +203,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
203
203
  readonly id: string;
204
204
  readonly slug: string;
205
205
  readonly contentTypeId: string;
206
- readonly data: string;
207
206
  readonly authorId?: string | undefined;
207
+ readonly data: string;
208
208
  }>;
209
209
  readonly deleteContentItem: better_call.StrictEndpoint<"/content/:typeSlug/:id", {
210
210
  params: z.ZodObject<{
@@ -248,8 +248,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
248
248
  readonly id: string;
249
249
  readonly slug: string;
250
250
  readonly contentTypeId: string;
251
- readonly data: string;
252
251
  readonly authorId?: string | undefined;
252
+ readonly data: string;
253
253
  }[];
254
254
  };
255
255
  readonly contentType?: {
@@ -271,8 +271,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
271
271
  readonly id: string;
272
272
  readonly slug: string;
273
273
  readonly contentTypeId: string;
274
- readonly data: string;
275
274
  readonly authorId?: string | undefined;
275
+ readonly data: string;
276
276
  }>;
277
277
  readonly listContentByRelation: better_call.StrictEndpoint<"/content/:typeSlug/by-relation", {
278
278
  params: z.ZodObject<{
@@ -314,8 +314,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
314
314
  readonly id: string;
315
315
  readonly slug: string;
316
316
  readonly contentTypeId: string;
317
- readonly data: string;
318
317
  readonly authorId?: string | undefined;
318
+ readonly data: string;
319
319
  }[];
320
320
  readonly total: number;
321
321
  readonly limit: number;
@@ -383,8 +383,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
383
383
  readonly id: string;
384
384
  readonly slug: string;
385
385
  readonly contentTypeId: string;
386
- readonly data: string;
387
386
  readonly authorId?: string | undefined;
387
+ readonly data: string;
388
388
  }[];
389
389
  readonly total: number;
390
390
  readonly limit: number;
@@ -459,8 +459,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
459
459
  id: string;
460
460
  slug: string;
461
461
  contentTypeId: string;
462
- data: string;
463
462
  authorId?: string | undefined;
463
+ data: string;
464
464
  }[];
465
465
  total: number;
466
466
  }, false>;
@@ -493,8 +493,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
493
493
  id: string;
494
494
  slug: string;
495
495
  contentTypeId: string;
496
- data: string;
497
496
  authorId?: string | undefined;
497
+ data: string;
498
498
  }, false>;
499
499
  readonly createContentItem: _btst_stack_plugins_api.Operation<z.ZodObject<{
500
500
  typeSlug: z.ZodString;
@@ -522,8 +522,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
522
522
  id: string;
523
523
  slug: string;
524
524
  contentTypeId: string;
525
- data: string;
526
525
  authorId?: string | undefined;
526
+ data: string;
527
527
  }, false>;
528
528
  readonly updateContentItem: _btst_stack_plugins_api.Operation<z.ZodObject<{
529
529
  typeSlug: z.ZodString;
@@ -554,8 +554,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
554
554
  id: string;
555
555
  slug: string;
556
556
  contentTypeId: string;
557
- data: string;
558
557
  authorId?: string | undefined;
558
+ data: string;
559
559
  }, false>;
560
560
  readonly deleteContentItem: _btst_stack_plugins_api.Operation<z.ZodObject<{
561
561
  typeSlug: z.ZodString;
@@ -598,8 +598,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
598
598
  id: string;
599
599
  slug: string;
600
600
  contentTypeId: string;
601
- data: string;
602
601
  authorId?: string | undefined;
602
+ data: string;
603
603
  }[];
604
604
  };
605
605
  contentType?: {
@@ -619,8 +619,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
619
619
  id: string;
620
620
  slug: string;
621
621
  contentTypeId: string;
622
- data: string;
623
622
  authorId?: string | undefined;
623
+ data: string;
624
624
  }, false>;
625
625
  readonly listContentByRelation: _btst_stack_plugins_api.Operation<z.ZodObject<{
626
626
  typeSlug: z.ZodString;
@@ -657,8 +657,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
657
657
  id: string;
658
658
  slug: string;
659
659
  contentTypeId: string;
660
- data: string;
661
660
  authorId?: string | undefined;
661
+ data: string;
662
662
  }[];
663
663
  total: number;
664
664
  limit: number;
@@ -715,8 +715,8 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
715
715
  id: string;
716
716
  slug: string;
717
717
  contentTypeId: string;
718
- data: string;
719
718
  authorId?: string | undefined;
719
+ data: string;
720
720
  }[];
721
721
  total: number;
722
722
  limit: number;
@@ -1,6 +1,13 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  declare const createPostSchema: z.ZodObject<{
4
+ tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
5
+ name: z.ZodString;
6
+ }, z.core.$strip>, z.ZodObject<{
7
+ id: z.ZodString;
8
+ name: z.ZodString;
9
+ slug: z.ZodString;
10
+ }, z.core.$strip>]>>>>;
4
11
  slug: z.ZodOptional<z.ZodString>;
5
12
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
6
13
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -10,13 +17,6 @@ declare const createPostSchema: z.ZodObject<{
10
17
  excerpt: z.ZodString;
11
18
  image: z.ZodOptional<z.ZodString>;
12
19
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
13
- tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
14
- name: z.ZodString;
15
- }, z.core.$strip>, z.ZodObject<{
16
- id: z.ZodString;
17
- name: z.ZodString;
18
- slug: z.ZodString;
19
- }, z.core.$strip>]>>>>;
20
20
  }, z.core.$strip>;
21
21
  declare const updatePostSchema: z.ZodObject<{
22
22
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -1,6 +1,13 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  declare const createPostSchema: z.ZodObject<{
4
+ tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
5
+ name: z.ZodString;
6
+ }, z.core.$strip>, z.ZodObject<{
7
+ id: z.ZodString;
8
+ name: z.ZodString;
9
+ slug: z.ZodString;
10
+ }, z.core.$strip>]>>>>;
4
11
  slug: z.ZodOptional<z.ZodString>;
5
12
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
6
13
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -10,13 +17,6 @@ declare const createPostSchema: z.ZodObject<{
10
17
  excerpt: z.ZodString;
11
18
  image: z.ZodOptional<z.ZodString>;
12
19
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
13
- tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
14
- name: z.ZodString;
15
- }, z.core.$strip>, z.ZodObject<{
16
- id: z.ZodString;
17
- name: z.ZodString;
18
- slug: z.ZodString;
19
- }, z.core.$strip>]>>>>;
20
20
  }, z.core.$strip>;
21
21
  declare const updatePostSchema: z.ZodObject<{
22
22
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -1,6 +1,13 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  declare const createPostSchema: z.ZodObject<{
4
+ tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
5
+ name: z.ZodString;
6
+ }, z.core.$strip>, z.ZodObject<{
7
+ id: z.ZodString;
8
+ name: z.ZodString;
9
+ slug: z.ZodString;
10
+ }, z.core.$strip>]>>>>;
4
11
  slug: z.ZodOptional<z.ZodString>;
5
12
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
6
13
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -10,13 +17,6 @@ declare const createPostSchema: z.ZodObject<{
10
17
  excerpt: z.ZodString;
11
18
  image: z.ZodOptional<z.ZodString>;
12
19
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
13
- tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
14
- name: z.ZodString;
15
- }, z.core.$strip>, z.ZodObject<{
16
- id: z.ZodString;
17
- name: z.ZodString;
18
- slug: z.ZodString;
19
- }, z.core.$strip>]>>>>;
20
20
  }, z.core.$strip>;
21
21
  declare const updatePostSchema: z.ZodObject<{
22
22
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;