@btst/stack 2.11.8 → 2.12.1
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.
- package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +14 -11
- package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +14 -11
- package/dist/packages/stack/src/plugins/cms/client/components/forms/relation-field.cjs +87 -15
- package/dist/packages/stack/src/plugins/cms/client/components/forms/relation-field.mjs +87 -15
- package/dist/packages/stack/src/plugins/cms/schemas.cjs +11 -6
- package/dist/packages/stack/src/plugins/cms/schemas.mjs +10 -6
- package/dist/packages/stack/src/plugins/comments/api/query-key-defs.cjs +2 -1
- package/dist/packages/stack/src/plugins/comments/api/query-key-defs.mjs +2 -1
- package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.cjs +6 -2
- package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.mjs +6 -2
- package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.cjs +5 -3
- package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.mjs +5 -3
- package/dist/packages/ui/src/components/auto-form/fields/array.cjs +181 -4
- package/dist/packages/ui/src/components/auto-form/fields/array.mjs +184 -7
- package/dist/packages/ui/src/components/auto-form/index.cjs +16 -1
- package/dist/packages/ui/src/components/auto-form/index.mjs +17 -2
- package/dist/plugins/cms/api/index.cjs +2 -0
- package/dist/plugins/cms/api/index.d.cts +3 -2
- package/dist/plugins/cms/api/index.d.mts +3 -2
- package/dist/plugins/cms/api/index.d.ts +3 -2
- package/dist/plugins/cms/api/index.mjs +1 -0
- package/dist/plugins/cms/client/hooks/index.d.cts +1 -1
- package/dist/plugins/cms/client/hooks/index.d.mts +1 -1
- package/dist/plugins/cms/client/hooks/index.d.ts +1 -1
- package/dist/plugins/cms/query-keys.d.cts +398 -7
- package/dist/plugins/cms/query-keys.d.mts +398 -7
- package/dist/plugins/cms/query-keys.d.ts +398 -7
- package/dist/plugins/comments/api/index.d.cts +4 -4
- package/dist/plugins/comments/api/index.d.mts +4 -4
- package/dist/plugins/comments/api/index.d.ts +4 -4
- package/dist/plugins/comments/client/components/index.d.cts +11 -0
- package/dist/plugins/comments/client/components/index.d.mts +11 -0
- package/dist/plugins/comments/client/components/index.d.ts +11 -0
- package/dist/plugins/comments/client/hooks/index.d.cts +17 -1
- package/dist/plugins/comments/client/hooks/index.d.mts +17 -1
- package/dist/plugins/comments/client/hooks/index.d.ts +17 -1
- package/dist/plugins/comments/client/index.d.cts +9 -0
- package/dist/plugins/comments/client/index.d.mts +9 -0
- package/dist/plugins/comments/client/index.d.ts +9 -0
- package/dist/plugins/comments/query-keys.cjs +1 -0
- package/dist/plugins/comments/query-keys.d.cts +3 -2
- package/dist/plugins/comments/query-keys.d.mts +3 -2
- package/dist/plugins/comments/query-keys.d.ts +3 -2
- package/dist/plugins/comments/query-keys.mjs +1 -0
- package/dist/plugins/kanban/api/index.d.cts +1 -1
- package/dist/plugins/kanban/api/index.d.mts +1 -1
- package/dist/plugins/kanban/api/index.d.ts +1 -1
- package/dist/plugins/kanban/query-keys.d.cts +1 -1
- package/dist/plugins/kanban/query-keys.d.mts +1 -1
- package/dist/plugins/kanban/query-keys.d.ts +1 -1
- package/dist/plugins/ui-builder/index.d.cts +1 -1
- package/dist/plugins/ui-builder/index.d.mts +1 -1
- package/dist/plugins/ui-builder/index.d.ts +1 -1
- package/dist/shared/{stack.CJE9sAjV.d.ts → stack.Bq3kiUaM.d.ts} +1 -1
- package/dist/shared/{stack.C-b3Sn8j.d.cts → stack.C7KNNiJm.d.cts} +3 -1
- package/dist/shared/{stack.C-b3Sn8j.d.mts → stack.C7KNNiJm.d.mts} +3 -1
- package/dist/shared/{stack.C-b3Sn8j.d.ts → stack.C7KNNiJm.d.ts} +3 -1
- package/dist/shared/{stack.Dcz6636A.d.mts → stack.CCncwK6g.d.cts} +1 -1
- package/dist/shared/{stack.CmHRdhl8.d.cts → stack.CO1g47IS.d.mts} +1 -1
- package/dist/shared/{stack.B8QD11QU.d.cts → stack.DFtpFS00.d.cts} +10 -0
- package/dist/shared/{stack.B8QD11QU.d.mts → stack.DFtpFS00.d.mts} +10 -0
- package/dist/shared/{stack.B8QD11QU.d.ts → stack.DFtpFS00.d.ts} +10 -0
- package/package.json +1 -1
- package/src/plugins/cms/__tests__/schema-roundtrip.test.ts +203 -0
- package/src/plugins/cms/api/index.ts +1 -0
- package/src/plugins/cms/api/plugin.ts +33 -16
- package/src/plugins/cms/client/components/forms/relation-field.tsx +121 -36
- package/src/plugins/cms/schemas.ts +26 -6
- package/src/plugins/cms/types.ts +10 -0
- package/src/plugins/comments/api/query-key-defs.ts +5 -1
- package/src/plugins/comments/client/components/comment-thread.tsx +15 -0
- package/src/plugins/comments/client/hooks/use-comments.tsx +26 -3
- package/src/plugins/comments/client/overrides.ts +10 -0
- package/src/plugins/comments/query-keys.ts +2 -0
- package/dist/shared/stack.DbnCLdNF.d.mts +0 -399
- package/dist/shared/stack.fXlShnhv.d.ts +0 -399
- package/dist/shared/stack.r6HohbyX.d.cts +0 -399
- package/dist/shared/{stack.B6S3cgwN.d.cts → stack.CuI_-z-_.d.cts} +16 -16
- package/dist/shared/{stack.Bzfx-_lq.d.mts → stack.D2RVkQD8.d.mts} +16 -16
- package/dist/shared/{stack.j5SFLC1d.d.ts → stack.DXrOtuek.d.ts} +16 -16
|
@@ -90,6 +90,7 @@ function createCommentsThreadQueries(client, headers) {
|
|
|
90
90
|
// The server resolves the caller's identity server-side via the
|
|
91
91
|
// resolveCurrentUserId hook. It is still included in the queryKey
|
|
92
92
|
// above for client-side cache segregation.
|
|
93
|
+
sort: params?.sort,
|
|
93
94
|
limit: params?.limit ?? 20,
|
|
94
95
|
offset: pageParam ?? 0
|
|
95
96
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
-
import { b as CommentsListDiscriminator, C as CommentListResult, c as CommentCountDiscriminator, a as CommentsThreadDiscriminator } from '../../shared/stack.
|
|
2
|
+
import { b as CommentsListDiscriminator, C as CommentListResult, c as CommentCountDiscriminator, a as CommentsThreadDiscriminator } from '../../shared/stack.C7KNNiJm.cjs';
|
|
3
3
|
import { createApiClient } from '@btst/stack/plugins/client';
|
|
4
|
-
import { C as CommentsApiRouter } from '../../shared/stack.
|
|
4
|
+
import { C as CommentsApiRouter } from '../../shared/stack.CCncwK6g.cjs';
|
|
5
5
|
import '@btst/stack/plugins/api';
|
|
6
6
|
import 'better-call';
|
|
7
7
|
import 'zod';
|
|
@@ -56,6 +56,7 @@ declare function createCommentsQueryKeys(client: ReturnType<typeof createApiClie
|
|
|
56
56
|
parentId?: string | null;
|
|
57
57
|
status?: "pending" | "approved" | "spam";
|
|
58
58
|
currentUserId?: string;
|
|
59
|
+
sort?: "asc" | "desc";
|
|
59
60
|
limit?: number;
|
|
60
61
|
} | undefined) => Omit<Omit<{
|
|
61
62
|
queryKey: readonly ["commentsThread", "list", CommentsThreadDiscriminator];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
-
import { b as CommentsListDiscriminator, C as CommentListResult, c as CommentCountDiscriminator, a as CommentsThreadDiscriminator } from '../../shared/stack.
|
|
2
|
+
import { b as CommentsListDiscriminator, C as CommentListResult, c as CommentCountDiscriminator, a as CommentsThreadDiscriminator } from '../../shared/stack.C7KNNiJm.mjs';
|
|
3
3
|
import { createApiClient } from '@btst/stack/plugins/client';
|
|
4
|
-
import { C as CommentsApiRouter } from '../../shared/stack.
|
|
4
|
+
import { C as CommentsApiRouter } from '../../shared/stack.CO1g47IS.mjs';
|
|
5
5
|
import '@btst/stack/plugins/api';
|
|
6
6
|
import 'better-call';
|
|
7
7
|
import 'zod';
|
|
@@ -56,6 +56,7 @@ declare function createCommentsQueryKeys(client: ReturnType<typeof createApiClie
|
|
|
56
56
|
parentId?: string | null;
|
|
57
57
|
status?: "pending" | "approved" | "spam";
|
|
58
58
|
currentUserId?: string;
|
|
59
|
+
sort?: "asc" | "desc";
|
|
59
60
|
limit?: number;
|
|
60
61
|
} | undefined) => Omit<Omit<{
|
|
61
62
|
queryKey: readonly ["commentsThread", "list", CommentsThreadDiscriminator];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
-
import { b as CommentsListDiscriminator, C as CommentListResult, c as CommentCountDiscriminator, a as CommentsThreadDiscriminator } from '../../shared/stack.
|
|
2
|
+
import { b as CommentsListDiscriminator, C as CommentListResult, c as CommentCountDiscriminator, a as CommentsThreadDiscriminator } from '../../shared/stack.C7KNNiJm.js';
|
|
3
3
|
import { createApiClient } from '@btst/stack/plugins/client';
|
|
4
|
-
import { C as CommentsApiRouter } from '../../shared/stack.
|
|
4
|
+
import { C as CommentsApiRouter } from '../../shared/stack.Bq3kiUaM.js';
|
|
5
5
|
import '@btst/stack/plugins/api';
|
|
6
6
|
import 'better-call';
|
|
7
7
|
import 'zod';
|
|
@@ -56,6 +56,7 @@ declare function createCommentsQueryKeys(client: ReturnType<typeof createApiClie
|
|
|
56
56
|
parentId?: string | null;
|
|
57
57
|
status?: "pending" | "approved" | "spam";
|
|
58
58
|
currentUserId?: string;
|
|
59
|
+
sort?: "asc" | "desc";
|
|
59
60
|
limit?: number;
|
|
60
61
|
} | undefined) => Omit<Omit<{
|
|
61
62
|
queryKey: readonly ["commentsThread", "list", CommentsThreadDiscriminator];
|
|
@@ -88,6 +88,7 @@ function createCommentsThreadQueries(client, headers) {
|
|
|
88
88
|
// The server resolves the caller's identity server-side via the
|
|
89
89
|
// resolveCurrentUserId hook. It is still included in the queryKey
|
|
90
90
|
// above for client-side cache segregation.
|
|
91
|
+
sort: params?.sort,
|
|
91
92
|
limit: params?.limit ?? 20,
|
|
92
93
|
offset: pageParam ?? 0
|
|
93
94
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.
|
|
1
|
+
export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.CuI_-z-_.cjs';
|
|
2
2
|
import { B as BoardWithColumns, S as SerializedBoardWithColumns, C as ColumnWithTasks, a as SerializedColumn, T as Task, b as SerializedTask } from '../../../shared/stack.DJaKVY7v.cjs';
|
|
3
3
|
import '@btst/stack/plugins/api';
|
|
4
4
|
import '@btst/db';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.
|
|
1
|
+
export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.D2RVkQD8.mjs';
|
|
2
2
|
import { B as BoardWithColumns, S as SerializedBoardWithColumns, C as ColumnWithTasks, a as SerializedColumn, T as Task, b as SerializedTask } from '../../../shared/stack.DJaKVY7v.mjs';
|
|
3
3
|
import '@btst/stack/plugins/api';
|
|
4
4
|
import '@btst/db';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.
|
|
1
|
+
export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.DXrOtuek.js';
|
|
2
2
|
import { B as BoardWithColumns, S as SerializedBoardWithColumns, C as ColumnWithTasks, a as SerializedColumn, T as Task, b as SerializedTask } from '../../../shared/stack.DJaKVY7v.js';
|
|
3
3
|
import '@btst/stack/plugins/api';
|
|
4
4
|
import '@btst/db';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
-
import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.
|
|
2
|
+
import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.CuI_-z-_.cjs';
|
|
3
3
|
import { createApiClient } from '@btst/stack/plugins/client';
|
|
4
4
|
import { S as SerializedBoardWithColumns } from '../../shared/stack.DJaKVY7v.cjs';
|
|
5
5
|
import '@btst/stack/plugins/api';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
-
import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.
|
|
2
|
+
import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.D2RVkQD8.mjs';
|
|
3
3
|
import { createApiClient } from '@btst/stack/plugins/client';
|
|
4
4
|
import { S as SerializedBoardWithColumns } from '../../shared/stack.DJaKVY7v.mjs';
|
|
5
5
|
import '@btst/stack/plugins/api';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
-
import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.
|
|
2
|
+
import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.DXrOtuek.js';
|
|
3
3
|
import { createApiClient } from '@btst/stack/plugins/client';
|
|
4
4
|
import { S as SerializedBoardWithColumns } from '../../shared/stack.DJaKVY7v.js';
|
|
5
5
|
import '@btst/stack/plugins/api';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { C as ContentTypeConfig } from '../../shared/stack.
|
|
2
|
+
import { C as ContentTypeConfig } from '../../shared/stack.DFtpFS00.cjs';
|
|
3
3
|
export { L as LoaderContext, b as PaginatedUIBuilderPages, P as ParsedUIBuilderPage, S as SerializedUIBuilderPage, c as UIBuilderClientHooks, a as UIBuilderPage, U as UIBuilderPageData } from '../../shared/stack.C1nXGBr6.cjs';
|
|
4
4
|
export { C as ComponentLayer, a as ComponentRegistry, F as FunctionRegistry, R as RegistryEntry, V as Variable } from '../../shared/stack.BOCvd9HK.cjs';
|
|
5
5
|
import 'react';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { C as ContentTypeConfig } from '../../shared/stack.
|
|
2
|
+
import { C as ContentTypeConfig } from '../../shared/stack.DFtpFS00.mjs';
|
|
3
3
|
export { L as LoaderContext, b as PaginatedUIBuilderPages, P as ParsedUIBuilderPage, S as SerializedUIBuilderPage, c as UIBuilderClientHooks, a as UIBuilderPage, U as UIBuilderPageData } from '../../shared/stack.DLPa6Gzm.mjs';
|
|
4
4
|
export { C as ComponentLayer, a as ComponentRegistry, F as FunctionRegistry, R as RegistryEntry, V as Variable } from '../../shared/stack.BFoBvGML.mjs';
|
|
5
5
|
import 'react';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { C as ContentTypeConfig } from '../../shared/stack.
|
|
2
|
+
import { C as ContentTypeConfig } from '../../shared/stack.DFtpFS00.js';
|
|
3
3
|
export { L as LoaderContext, b as PaginatedUIBuilderPages, P as ParsedUIBuilderPage, S as SerializedUIBuilderPage, c as UIBuilderClientHooks, a as UIBuilderPage, U as UIBuilderPageData } from '../../shared/stack.DxJ-tHLt.js';
|
|
4
4
|
export { C as ComponentLayer, a as ComponentRegistry, F as FunctionRegistry, R as RegistryEntry, V as Variable } from '../../shared/stack.BmMB0LNC.js';
|
|
5
5
|
import 'react';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
|
|
2
|
-
import { d as Comment, S as SerializedComment, C as CommentListResult } from './stack.
|
|
2
|
+
import { d as Comment, S as SerializedComment, C as CommentListResult } from './stack.C7KNNiJm.js';
|
|
3
3
|
import * as better_call from 'better-call';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
@@ -90,6 +90,7 @@ interface CommentsThreadDiscriminator {
|
|
|
90
90
|
parentId: string | null | undefined;
|
|
91
91
|
status: string | undefined;
|
|
92
92
|
currentUserId: string | undefined;
|
|
93
|
+
sort: string | undefined;
|
|
93
94
|
limit: number;
|
|
94
95
|
}
|
|
95
96
|
/** Full query key builders — use with queryClient.setQueryData() */
|
|
@@ -125,7 +126,7 @@ declare const COMMENTS_QUERY_KEYS: {
|
|
|
125
126
|
}) => readonly ["comments", "count", CommentCountDiscriminator];
|
|
126
127
|
/**
|
|
127
128
|
* Key for the infinite thread query (top-level comments, load-more).
|
|
128
|
-
* Full key: ["commentsThread", "list", { resourceId, resourceType, parentId, status, currentUserId, limit }]
|
|
129
|
+
* Full key: ["commentsThread", "list", { resourceId, resourceType, parentId, status, currentUserId, sort, limit }]
|
|
129
130
|
* Offset is excluded — it is driven by `pageParam`, not baked into the key.
|
|
130
131
|
*/
|
|
131
132
|
commentsThread: (params?: {
|
|
@@ -134,6 +135,7 @@ declare const COMMENTS_QUERY_KEYS: {
|
|
|
134
135
|
parentId?: string | null;
|
|
135
136
|
status?: string;
|
|
136
137
|
currentUserId?: string;
|
|
138
|
+
sort?: string;
|
|
137
139
|
limit?: number;
|
|
138
140
|
}) => readonly ["commentsThread", "list", CommentsThreadDiscriminator];
|
|
139
141
|
};
|
|
@@ -90,6 +90,7 @@ interface CommentsThreadDiscriminator {
|
|
|
90
90
|
parentId: string | null | undefined;
|
|
91
91
|
status: string | undefined;
|
|
92
92
|
currentUserId: string | undefined;
|
|
93
|
+
sort: string | undefined;
|
|
93
94
|
limit: number;
|
|
94
95
|
}
|
|
95
96
|
/** Full query key builders — use with queryClient.setQueryData() */
|
|
@@ -125,7 +126,7 @@ declare const COMMENTS_QUERY_KEYS: {
|
|
|
125
126
|
}) => readonly ["comments", "count", CommentCountDiscriminator];
|
|
126
127
|
/**
|
|
127
128
|
* Key for the infinite thread query (top-level comments, load-more).
|
|
128
|
-
* Full key: ["commentsThread", "list", { resourceId, resourceType, parentId, status, currentUserId, limit }]
|
|
129
|
+
* Full key: ["commentsThread", "list", { resourceId, resourceType, parentId, status, currentUserId, sort, limit }]
|
|
129
130
|
* Offset is excluded — it is driven by `pageParam`, not baked into the key.
|
|
130
131
|
*/
|
|
131
132
|
commentsThread: (params?: {
|
|
@@ -134,6 +135,7 @@ declare const COMMENTS_QUERY_KEYS: {
|
|
|
134
135
|
parentId?: string | null;
|
|
135
136
|
status?: string;
|
|
136
137
|
currentUserId?: string;
|
|
138
|
+
sort?: string;
|
|
137
139
|
limit?: number;
|
|
138
140
|
}) => readonly ["commentsThread", "list", CommentsThreadDiscriminator];
|
|
139
141
|
};
|
|
@@ -90,6 +90,7 @@ interface CommentsThreadDiscriminator {
|
|
|
90
90
|
parentId: string | null | undefined;
|
|
91
91
|
status: string | undefined;
|
|
92
92
|
currentUserId: string | undefined;
|
|
93
|
+
sort: string | undefined;
|
|
93
94
|
limit: number;
|
|
94
95
|
}
|
|
95
96
|
/** Full query key builders — use with queryClient.setQueryData() */
|
|
@@ -125,7 +126,7 @@ declare const COMMENTS_QUERY_KEYS: {
|
|
|
125
126
|
}) => readonly ["comments", "count", CommentCountDiscriminator];
|
|
126
127
|
/**
|
|
127
128
|
* Key for the infinite thread query (top-level comments, load-more).
|
|
128
|
-
* Full key: ["commentsThread", "list", { resourceId, resourceType, parentId, status, currentUserId, limit }]
|
|
129
|
+
* Full key: ["commentsThread", "list", { resourceId, resourceType, parentId, status, currentUserId, sort, limit }]
|
|
129
130
|
* Offset is excluded — it is driven by `pageParam`, not baked into the key.
|
|
130
131
|
*/
|
|
131
132
|
commentsThread: (params?: {
|
|
@@ -134,6 +135,7 @@ declare const COMMENTS_QUERY_KEYS: {
|
|
|
134
135
|
parentId?: string | null;
|
|
135
136
|
status?: string;
|
|
136
137
|
currentUserId?: string;
|
|
138
|
+
sort?: string;
|
|
137
139
|
limit?: number;
|
|
138
140
|
}) => readonly ["commentsThread", "list", CommentsThreadDiscriminator];
|
|
139
141
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
|
|
2
|
-
import { d as Comment, S as SerializedComment, C as CommentListResult } from './stack.
|
|
2
|
+
import { d as Comment, S as SerializedComment, C as CommentListResult } from './stack.C7KNNiJm.cjs';
|
|
3
3
|
import * as better_call from 'better-call';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
|
|
2
|
-
import { d as Comment, S as SerializedComment, C as CommentListResult } from './stack.
|
|
2
|
+
import { d as Comment, S as SerializedComment, C as CommentListResult } from './stack.C7KNNiJm.mjs';
|
|
3
3
|
import * as better_call from 'better-call';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
@@ -158,6 +158,16 @@ interface CMSBackendConfig {
|
|
|
158
158
|
contentTypes: ContentTypeConfig[];
|
|
159
159
|
/** Optional hooks for customizing behavior */
|
|
160
160
|
hooks?: CMSBackendHooks;
|
|
161
|
+
/**
|
|
162
|
+
* Maximum number of items that can be requested in a single page.
|
|
163
|
+
* Applied to all list endpoints (`/content/:typeSlug`, by-relation, and inverse-relation).
|
|
164
|
+
*
|
|
165
|
+
* Raise this when your content types have many items and you need to fetch
|
|
166
|
+
* large pages (e.g. for SSG, CSV export, or programmatic access).
|
|
167
|
+
*
|
|
168
|
+
* @default 1000
|
|
169
|
+
*/
|
|
170
|
+
maxPageSize?: number;
|
|
161
171
|
}
|
|
162
172
|
|
|
163
173
|
export type { ContentTypeConfig as C, InverseRelation as I, PaginatedContentItems as P, SerializedContentType as S, SerializedContentItemWithType as a, ContentType as b, ContentItem as c, SerializedContentItem as d, ContentItemWithType as e, CMSBackendConfig as f };
|
|
@@ -158,6 +158,16 @@ interface CMSBackendConfig {
|
|
|
158
158
|
contentTypes: ContentTypeConfig[];
|
|
159
159
|
/** Optional hooks for customizing behavior */
|
|
160
160
|
hooks?: CMSBackendHooks;
|
|
161
|
+
/**
|
|
162
|
+
* Maximum number of items that can be requested in a single page.
|
|
163
|
+
* Applied to all list endpoints (`/content/:typeSlug`, by-relation, and inverse-relation).
|
|
164
|
+
*
|
|
165
|
+
* Raise this when your content types have many items and you need to fetch
|
|
166
|
+
* large pages (e.g. for SSG, CSV export, or programmatic access).
|
|
167
|
+
*
|
|
168
|
+
* @default 1000
|
|
169
|
+
*/
|
|
170
|
+
maxPageSize?: number;
|
|
161
171
|
}
|
|
162
172
|
|
|
163
173
|
export type { ContentTypeConfig as C, InverseRelation as I, PaginatedContentItems as P, SerializedContentType as S, SerializedContentItemWithType as a, ContentType as b, ContentItem as c, SerializedContentItem as d, ContentItemWithType as e, CMSBackendConfig as f };
|
|
@@ -158,6 +158,16 @@ interface CMSBackendConfig {
|
|
|
158
158
|
contentTypes: ContentTypeConfig[];
|
|
159
159
|
/** Optional hooks for customizing behavior */
|
|
160
160
|
hooks?: CMSBackendHooks;
|
|
161
|
+
/**
|
|
162
|
+
* Maximum number of items that can be requested in a single page.
|
|
163
|
+
* Applied to all list endpoints (`/content/:typeSlug`, by-relation, and inverse-relation).
|
|
164
|
+
*
|
|
165
|
+
* Raise this when your content types have many items and you need to fetch
|
|
166
|
+
* large pages (e.g. for SSG, CSV export, or programmatic access).
|
|
167
|
+
*
|
|
168
|
+
* @default 1000
|
|
169
|
+
*/
|
|
170
|
+
maxPageSize?: number;
|
|
161
171
|
}
|
|
162
172
|
|
|
163
173
|
export type { ContentTypeConfig as C, InverseRelation as I, PaginatedContentItems as P, SerializedContentType as S, SerializedContentItemWithType as a, ContentType as b, ContentItem as c, SerializedContentItem as d, ContentItemWithType as e, CMSBackendConfig as f };
|
package/package.json
CHANGED
|
@@ -804,3 +804,206 @@ describe("Zod to JSON Schema roundtrip", () => {
|
|
|
804
804
|
});
|
|
805
805
|
});
|
|
806
806
|
});
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* These tests confirm and protect against a bug where `useFieldArray` in
|
|
810
|
+
* AutoFormArray corrupts primitive string values in arrays like
|
|
811
|
+
* `structuredContraindications: z.array(z.string()).default([])`.
|
|
812
|
+
*
|
|
813
|
+
* When react-hook-form's `useFieldArray` processes a primitive array
|
|
814
|
+
* (e.g. `["pregnancy", "active malignancy"]`), it wraps each element as a
|
|
815
|
+
* tracking object `{ id: "rhf_generated_id" }`, discarding the original
|
|
816
|
+
* string value. When `form.watch()` fires, it returns these objects. The
|
|
817
|
+
* `handleValuesChange` callback then calls `setFormData(values)` which stores
|
|
818
|
+
* the corrupted objects. On form submit, `zodResolver` validates the corrupted
|
|
819
|
+
* values against `z.array(z.string())` and FAILS — causing the Save button
|
|
820
|
+
* to do nothing (no error shown, no API request made).
|
|
821
|
+
*
|
|
822
|
+
* The fix: AutoFormArray must NOT use `useFieldArray` for primitive (non-object)
|
|
823
|
+
* arrays. Instead it uses `form.watch` + `form.setValue` directly so primitive
|
|
824
|
+
* values are always preserved in the form state.
|
|
825
|
+
*/
|
|
826
|
+
describe("Primitive string array — useFieldArray corruption bug", () => {
|
|
827
|
+
/**
|
|
828
|
+
* Simulates what zodToFormSchema + formSchemaToZod does:
|
|
829
|
+
* Zod schema → JSON Schema (stored in DB) → reconstructed Zod schema.
|
|
830
|
+
*/
|
|
831
|
+
function roundtripSchema(schema: z.ZodType): z.ZodType {
|
|
832
|
+
const jsonSchema = z.toJSONSchema(schema, { unrepresentable: "any" });
|
|
833
|
+
return z.fromJSONSchema(jsonSchema as z.core.JSONSchema.JSONSchema);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
it("z.array(z.string()) survives JSON Schema roundtrip and accepts string values", () => {
|
|
837
|
+
const schema = z.object({
|
|
838
|
+
structuredContraindications: z.array(z.string()).default([]),
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
const reconstructed = roundtripSchema(schema);
|
|
842
|
+
|
|
843
|
+
// Actual compound data — should PASS
|
|
844
|
+
expect(
|
|
845
|
+
reconstructed.safeParse({
|
|
846
|
+
structuredContraindications: [
|
|
847
|
+
"pregnancy",
|
|
848
|
+
"active malignancy",
|
|
849
|
+
"active cancer",
|
|
850
|
+
"trying to conceive",
|
|
851
|
+
],
|
|
852
|
+
}).success,
|
|
853
|
+
).toBe(true);
|
|
854
|
+
|
|
855
|
+
// Empty array — should PASS (default)
|
|
856
|
+
expect(
|
|
857
|
+
reconstructed.safeParse({ structuredContraindications: [] }).success,
|
|
858
|
+
).toBe(true);
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
it("useFieldArray-corrupted values (objects) fail z.array(z.string()) validation — this is the root cause of the silent save failure", () => {
|
|
862
|
+
const schema = z.object({
|
|
863
|
+
structuredContraindications: z.array(z.string()).default([]),
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
const reconstructed = roundtripSchema(schema);
|
|
867
|
+
|
|
868
|
+
// Simulates what react-hook-form's useFieldArray returns when used with
|
|
869
|
+
// primitive string arrays: each string is replaced by a tracking object
|
|
870
|
+
// { id: "rhf_generated_id" } and the original value is lost.
|
|
871
|
+
const corruptedByUseFieldArray = {
|
|
872
|
+
structuredContraindications: [
|
|
873
|
+
{ id: "rhf_internal_id_1" },
|
|
874
|
+
{ id: "rhf_internal_id_2" },
|
|
875
|
+
{ id: "rhf_internal_id_3" },
|
|
876
|
+
{ id: "rhf_internal_id_4" },
|
|
877
|
+
],
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
// This is the actual validation error that occurs when Save is clicked:
|
|
881
|
+
// zodResolver validates the corrupted objects against z.array(z.string())
|
|
882
|
+
// and FAILS, so onSubmit is never called → no API request → "nothing happens"
|
|
883
|
+
const result = reconstructed.safeParse(corruptedByUseFieldArray);
|
|
884
|
+
expect(result.success).toBe(false);
|
|
885
|
+
if (!result.success) {
|
|
886
|
+
// Confirm the error is specifically about the string array elements
|
|
887
|
+
const paths = result.error.issues.map((i) => i.path.join("."));
|
|
888
|
+
expect(
|
|
889
|
+
paths.some((p) => p.startsWith("structuredContraindications")),
|
|
890
|
+
).toBe(true);
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
it("primitive string array values are preserved correctly (NOT corrupted) when form state is managed without useFieldArray", () => {
|
|
895
|
+
// After the fix, AutoFormArray uses form.watch + form.setValue for primitive
|
|
896
|
+
// arrays. The values remain as strings throughout the lifecycle:
|
|
897
|
+
// initialData → formData → form state → zodResolver → submit
|
|
898
|
+
|
|
899
|
+
const schema = z.object({
|
|
900
|
+
structuredContraindications: z.array(z.string()).default([]),
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
const reconstructed = roundtripSchema(schema);
|
|
904
|
+
|
|
905
|
+
// The correctly-preserved values (no useFieldArray wrapping)
|
|
906
|
+
const preservedValues = {
|
|
907
|
+
structuredContraindications: [
|
|
908
|
+
"pregnancy",
|
|
909
|
+
"active malignancy",
|
|
910
|
+
"active cancer",
|
|
911
|
+
"trying to conceive",
|
|
912
|
+
],
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
// With the fix applied, these values pass validation → Save works
|
|
916
|
+
expect(reconstructed.safeParse(preservedValues).success).toBe(true);
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
it("compound schema with structuredContraindications passes full validation with string array values", () => {
|
|
920
|
+
// Representative subset of CompoundSchema fields that appear on the
|
|
921
|
+
// Epitalon compound page — verifies the full schema round-trip for the
|
|
922
|
+
// fields that are relevant to the reported bug.
|
|
923
|
+
const compoundSchema = z.object({
|
|
924
|
+
name: z.string().min(1),
|
|
925
|
+
compoundType: z.enum([
|
|
926
|
+
"healing-peptide",
|
|
927
|
+
"gh-axis",
|
|
928
|
+
"metabolic-peptide",
|
|
929
|
+
"sarm",
|
|
930
|
+
"steroid",
|
|
931
|
+
"nootropic",
|
|
932
|
+
"supplement",
|
|
933
|
+
"ancillary-pct",
|
|
934
|
+
"longevity",
|
|
935
|
+
"hair",
|
|
936
|
+
"skin",
|
|
937
|
+
"sexual",
|
|
938
|
+
"other",
|
|
939
|
+
]),
|
|
940
|
+
researchStatus: z.enum([
|
|
941
|
+
"research-only",
|
|
942
|
+
"approved",
|
|
943
|
+
"banned",
|
|
944
|
+
"grey-market",
|
|
945
|
+
"supplement",
|
|
946
|
+
]),
|
|
947
|
+
legalStatus: z.enum([
|
|
948
|
+
"OTC",
|
|
949
|
+
"Research",
|
|
950
|
+
"Grey-Market",
|
|
951
|
+
"Rx-Only",
|
|
952
|
+
"Schedule-III",
|
|
953
|
+
"Banned",
|
|
954
|
+
]),
|
|
955
|
+
doseUnit: z.enum(["mcg", "mg", "IU", "ml", "g"]),
|
|
956
|
+
doseFrequency: z.enum([
|
|
957
|
+
"once-daily",
|
|
958
|
+
"twice-daily",
|
|
959
|
+
"three-times-daily",
|
|
960
|
+
"every-other-day",
|
|
961
|
+
"weekly",
|
|
962
|
+
"twice-weekly",
|
|
963
|
+
"three-times-weekly",
|
|
964
|
+
"as-needed",
|
|
965
|
+
"custom",
|
|
966
|
+
]),
|
|
967
|
+
structuredContraindications: z.array(z.string()).default([]),
|
|
968
|
+
affiliates: z
|
|
969
|
+
.array(
|
|
970
|
+
z.object({
|
|
971
|
+
partnerId: z.object({ id: z.string() }).optional(),
|
|
972
|
+
title: z.string().optional(),
|
|
973
|
+
url: z.string().min(1),
|
|
974
|
+
}),
|
|
975
|
+
)
|
|
976
|
+
.default([]),
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
const reconstructed = roundtripSchema(compoundSchema);
|
|
980
|
+
|
|
981
|
+
// Epitalon-like data — all values as they come from parsedData
|
|
982
|
+
const epitalon = {
|
|
983
|
+
name: "Epitalon",
|
|
984
|
+
compoundType: "longevity",
|
|
985
|
+
researchStatus: "research-only",
|
|
986
|
+
legalStatus: "Research",
|
|
987
|
+
doseUnit: "mg",
|
|
988
|
+
doseFrequency: "once-daily",
|
|
989
|
+
// The 4 string items that were causing the silent save failure
|
|
990
|
+
structuredContraindications: [
|
|
991
|
+
"pregnancy",
|
|
992
|
+
"active malignancy",
|
|
993
|
+
"active cancer",
|
|
994
|
+
"trying to conceive",
|
|
995
|
+
],
|
|
996
|
+
affiliates: [
|
|
997
|
+
{
|
|
998
|
+
partnerId: { id: "v6yAqOSO_example_id" },
|
|
999
|
+
title: "Buy Epitalon 10mg",
|
|
1000
|
+
url: "https://swisschems.is/product/epitalon-10mg-price-is-per-vial/",
|
|
1001
|
+
},
|
|
1002
|
+
],
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
const result = reconstructed.safeParse(epitalon);
|
|
1006
|
+
// After the fix, this should PASS (the save button works)
|
|
1007
|
+
expect(result.success).toBe(true);
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
@@ -18,7 +18,10 @@ import type {
|
|
|
18
18
|
RelationValue,
|
|
19
19
|
InverseRelation,
|
|
20
20
|
} from "../types";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
createListContentQuerySchema,
|
|
23
|
+
DEFAULT_MAX_PAGE_SIZE,
|
|
24
|
+
} from "../schemas";
|
|
22
25
|
import { slugify } from "../utils";
|
|
23
26
|
import {
|
|
24
27
|
getAllContentTypes,
|
|
@@ -492,6 +495,20 @@ export const cmsBackendPlugin = (config: CMSBackendConfig) => {
|
|
|
492
495
|
}),
|
|
493
496
|
|
|
494
497
|
routes: (adapter: Adapter) => {
|
|
498
|
+
// Build pagination schemas once — honours config.maxPageSize
|
|
499
|
+
const listContentQuerySchema = createListContentQuerySchema(
|
|
500
|
+
config.maxPageSize,
|
|
501
|
+
);
|
|
502
|
+
const paginationQuerySchema = z.object({
|
|
503
|
+
limit: z.coerce
|
|
504
|
+
.number()
|
|
505
|
+
.min(1)
|
|
506
|
+
.max(config.maxPageSize ?? DEFAULT_MAX_PAGE_SIZE)
|
|
507
|
+
.optional()
|
|
508
|
+
.default(20),
|
|
509
|
+
offset: z.coerce.number().min(0).optional().default(0),
|
|
510
|
+
});
|
|
511
|
+
|
|
495
512
|
// Helper to get content type by slug
|
|
496
513
|
const getContentType = async (
|
|
497
514
|
slug: string,
|
|
@@ -525,10 +542,10 @@ export const cmsBackendPlugin = (config: CMSBackendConfig) => {
|
|
|
525
542
|
sortBy: { field: "name", direction: "asc" },
|
|
526
543
|
});
|
|
527
544
|
|
|
528
|
-
// Get item counts for each content type
|
|
545
|
+
// Get item counts for each content type via adapter.count() (avoids N+1 scan)
|
|
529
546
|
const typesWithCounts = await Promise.all(
|
|
530
547
|
contentTypes.map(async (ct) => {
|
|
531
|
-
const
|
|
548
|
+
const itemCount: number = await adapter.count({
|
|
532
549
|
model: "contentItem",
|
|
533
550
|
where: [
|
|
534
551
|
{
|
|
@@ -540,7 +557,7 @@ export const cmsBackendPlugin = (config: CMSBackendConfig) => {
|
|
|
540
557
|
});
|
|
541
558
|
return {
|
|
542
559
|
...serializeContentType(ct),
|
|
543
|
-
itemCount
|
|
560
|
+
itemCount,
|
|
544
561
|
};
|
|
545
562
|
}),
|
|
546
563
|
);
|
|
@@ -964,12 +981,12 @@ export const cmsBackendPlugin = (config: CMSBackendConfig) => {
|
|
|
964
981
|
{
|
|
965
982
|
method: "GET",
|
|
966
983
|
params: z.object({ typeSlug: z.string() }),
|
|
967
|
-
query: z
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
984
|
+
query: z
|
|
985
|
+
.object({
|
|
986
|
+
field: z.string(),
|
|
987
|
+
targetId: z.string(),
|
|
988
|
+
})
|
|
989
|
+
.merge(paginationQuerySchema),
|
|
973
990
|
},
|
|
974
991
|
async (ctx) => {
|
|
975
992
|
const { typeSlug } = ctx.params;
|
|
@@ -1144,12 +1161,12 @@ export const cmsBackendPlugin = (config: CMSBackendConfig) => {
|
|
|
1144
1161
|
slug: z.string(),
|
|
1145
1162
|
sourceType: z.string(),
|
|
1146
1163
|
}),
|
|
1147
|
-
query: z
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1164
|
+
query: z
|
|
1165
|
+
.object({
|
|
1166
|
+
itemId: z.string(),
|
|
1167
|
+
fieldName: z.string(),
|
|
1168
|
+
})
|
|
1169
|
+
.merge(paginationQuerySchema),
|
|
1153
1170
|
},
|
|
1154
1171
|
async (ctx) => {
|
|
1155
1172
|
const { slug, sourceType } = ctx.params;
|