@btst/stack 2.11.3 → 2.11.4

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 (50) hide show
  1. package/dist/packages/stack/src/plugins/cms/api/mutations.cjs +12 -1
  2. package/dist/packages/stack/src/plugins/cms/api/mutations.mjs +12 -1
  3. package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +11 -51
  4. package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +3 -43
  5. package/dist/packages/stack/src/plugins/cms/api/relations.cjs +76 -0
  6. package/dist/packages/stack/src/plugins/cms/api/relations.mjs +70 -0
  7. package/dist/plugins/blog/api/index.d.cts +2 -2
  8. package/dist/plugins/blog/api/index.d.mts +2 -2
  9. package/dist/plugins/blog/api/index.d.ts +2 -2
  10. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  11. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  12. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  13. package/dist/plugins/blog/client/index.d.cts +2 -2
  14. package/dist/plugins/blog/client/index.d.mts +2 -2
  15. package/dist/plugins/blog/client/index.d.ts +2 -2
  16. package/dist/plugins/blog/query-keys.d.cts +2 -2
  17. package/dist/plugins/blog/query-keys.d.mts +2 -2
  18. package/dist/plugins/blog/query-keys.d.ts +2 -2
  19. package/dist/plugins/cms/api/index.d.cts +1 -1
  20. package/dist/plugins/cms/api/index.d.mts +1 -1
  21. package/dist/plugins/cms/api/index.d.ts +1 -1
  22. package/dist/plugins/cms/query-keys.d.cts +1 -1
  23. package/dist/plugins/cms/query-keys.d.mts +1 -1
  24. package/dist/plugins/cms/query-keys.d.ts +1 -1
  25. package/dist/plugins/kanban/api/index.d.cts +1 -1
  26. package/dist/plugins/kanban/api/index.d.mts +1 -1
  27. package/dist/plugins/kanban/api/index.d.ts +1 -1
  28. package/dist/plugins/kanban/query-keys.d.cts +1 -1
  29. package/dist/plugins/kanban/query-keys.d.mts +1 -1
  30. package/dist/plugins/kanban/query-keys.d.ts +1 -1
  31. package/dist/shared/{stack.DGsFF5qb.d.cts → stack.6mEHS2WH.d.mts} +6 -6
  32. package/dist/shared/{stack.NtflNnnH.d.mts → stack.AJTXI7kw.d.cts} +6 -6
  33. package/dist/shared/{stack.CntKf20s.d.cts → stack.C1jLzFZ6.d.mts} +13 -13
  34. package/dist/shared/{stack.BQSy-NDc.d.ts → stack.DF1ewpVE.d.ts} +13 -13
  35. package/dist/shared/{stack.BX7MHi0J.d.mts → stack.DbnCLdNF.d.mts} +34 -7
  36. package/dist/shared/{stack.QrBE0Bc8.d.ts → stack.QYn-Px94.d.ts} +6 -6
  37. package/dist/shared/{stack.CLrGRIj0.d.mts → stack.XulPeAD3.d.cts} +13 -13
  38. package/dist/shared/{stack.fQjVhw5a.d.ts → stack.fXlShnhv.d.ts} +34 -7
  39. package/dist/shared/{stack.C9zoS1TN.d.cts → stack.r6HohbyX.d.cts} +34 -7
  40. package/package.json +1 -1
  41. package/src/plugins/cms/api/index.ts +1 -0
  42. package/src/plugins/cms/api/mutations.ts +48 -3
  43. package/src/plugins/cms/api/plugin.ts +11 -101
  44. package/src/plugins/cms/api/relations.ts +174 -0
  45. package/dist/shared/{stack.eq5eg1yt.d.cts → stack.B6S3cgwN.d.cts} +6 -6
  46. package/dist/shared/{stack.Sod_PZhB.d.cts → stack.BQmuNl5p.d.cts} +9 -9
  47. package/dist/shared/{stack.Sod_PZhB.d.mts → stack.BQmuNl5p.d.mts} +9 -9
  48. package/dist/shared/{stack.Sod_PZhB.d.ts → stack.BQmuNl5p.d.ts} +9 -9
  49. package/dist/shared/{stack.Dj04W2c3.d.mts → stack.Bzfx-_lq.d.mts} +6 -6
  50. package/dist/shared/{stack.CMbX8Q5C.d.ts → stack.j5SFLC1d.d.ts} +6 -6
@@ -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.CMbX8Q5C.js';
2
+ import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.j5SFLC1d.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 * as _tanstack_react_query from '@tanstack/react-query';
2
- import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from './stack.Sod_PZhB.cjs';
2
+ import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from './stack.BQmuNl5p.mjs';
3
3
  import { z } from 'zod';
4
4
 
5
5
  /**
@@ -128,10 +128,6 @@ declare function useSuspenseTags(): {
128
128
  };
129
129
  /** Create a new post */
130
130
  declare function useCreatePost(): _tanstack_react_query.UseMutationResult<SerializedPost | null, Error, {
131
- published: boolean;
132
- title: string;
133
- content: string;
134
- excerpt: string;
135
131
  tags: ({
136
132
  name: string;
137
133
  } | {
@@ -139,10 +135,14 @@ declare function useCreatePost(): _tanstack_react_query.UseMutationResult<Serial
139
135
  name: string;
140
136
  slug: string;
141
137
  })[];
138
+ title: string;
139
+ content: string;
140
+ excerpt: string;
141
+ published: boolean;
142
142
  slug?: string | undefined;
143
- publishedAt?: Date | undefined;
144
143
  createdAt?: Date | undefined;
145
144
  updatedAt?: Date | undefined;
145
+ publishedAt?: Date | undefined;
146
146
  image?: string | undefined;
147
147
  }, unknown>;
148
148
  /** Update an existing post by id */
@@ -1,5 +1,5 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from './stack.Sod_PZhB.mjs';
2
+ import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from './stack.BQmuNl5p.cjs';
3
3
  import { z } from 'zod';
4
4
 
5
5
  /**
@@ -128,10 +128,6 @@ declare function useSuspenseTags(): {
128
128
  };
129
129
  /** Create a new post */
130
130
  declare function useCreatePost(): _tanstack_react_query.UseMutationResult<SerializedPost | null, Error, {
131
- published: boolean;
132
- title: string;
133
- content: string;
134
- excerpt: string;
135
131
  tags: ({
136
132
  name: string;
137
133
  } | {
@@ -139,10 +135,14 @@ declare function useCreatePost(): _tanstack_react_query.UseMutationResult<Serial
139
135
  name: string;
140
136
  slug: string;
141
137
  })[];
138
+ title: string;
139
+ content: string;
140
+ excerpt: string;
141
+ published: boolean;
142
142
  slug?: string | undefined;
143
- publishedAt?: Date | undefined;
144
143
  createdAt?: Date | undefined;
145
144
  updatedAt?: Date | undefined;
145
+ publishedAt?: Date | undefined;
146
146
  image?: string | undefined;
147
147
  }, unknown>;
148
148
  /** Update an existing post by id */
@@ -1,7 +1,7 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
2
  import { QueryClient } from '@tanstack/react-query';
3
3
  import { createApiClient } from '@btst/stack/plugins/client';
4
- import { P as Post, T as Tag, c as createPostSchema, u as updatePostSchema, S as SerializedPost, a as SerializedTag } from './stack.Sod_PZhB.cjs';
4
+ import { P as Post, T as Tag, c as createPostSchema, u as updatePostSchema, S as SerializedPost, a as SerializedTag } from './stack.BQmuNl5p.mjs';
5
5
  import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
6
6
  import { DBAdapter } from '@btst/db';
7
7
  import * as better_call from 'better-call';
@@ -316,12 +316,6 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
316
316
  title: string;
317
317
  content: string;
318
318
  excerpt: string;
319
- slug?: string | undefined;
320
- published?: boolean | undefined;
321
- publishedAt?: unknown;
322
- createdAt?: unknown;
323
- updatedAt?: unknown;
324
- image?: string | undefined;
325
319
  tags?: ({
326
320
  name: string;
327
321
  } | {
@@ -329,16 +323,16 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
329
323
  name: string;
330
324
  slug: string;
331
325
  })[] | undefined;
332
- }, {
333
- title: string;
334
- content: string;
335
- excerpt: string;
336
326
  slug?: string | undefined;
337
- published?: boolean | undefined;
338
- publishedAt?: unknown;
339
327
  createdAt?: unknown;
340
328
  updatedAt?: unknown;
329
+ publishedAt?: unknown;
341
330
  image?: string | undefined;
331
+ published?: boolean | undefined;
332
+ }, {
333
+ title: string;
334
+ content: string;
335
+ excerpt: string;
342
336
  tags?: ({
343
337
  name: string;
344
338
  } | {
@@ -346,6 +340,12 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
346
340
  name: string;
347
341
  slug: string;
348
342
  })[] | undefined;
343
+ slug?: string | undefined;
344
+ createdAt?: unknown;
345
+ updatedAt?: unknown;
346
+ publishedAt?: unknown;
347
+ image?: string | undefined;
348
+ published?: boolean | undefined;
349
349
  }>;
350
350
  }, Post>;
351
351
  readonly updatePost: better_call.StrictEndpoint<"/posts/:id", {} & {
@@ -1,7 +1,7 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
2
  import { QueryClient } from '@tanstack/react-query';
3
3
  import { createApiClient } from '@btst/stack/plugins/client';
4
- import { P as Post, T as Tag, c as createPostSchema, u as updatePostSchema, S as SerializedPost, a as SerializedTag } from './stack.Sod_PZhB.js';
4
+ import { P as Post, T as Tag, c as createPostSchema, u as updatePostSchema, S as SerializedPost, a as SerializedTag } from './stack.BQmuNl5p.js';
5
5
  import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
6
6
  import { DBAdapter } from '@btst/db';
7
7
  import * as better_call from 'better-call';
@@ -316,12 +316,6 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
316
316
  title: string;
317
317
  content: string;
318
318
  excerpt: string;
319
- slug?: string | undefined;
320
- published?: boolean | undefined;
321
- publishedAt?: unknown;
322
- createdAt?: unknown;
323
- updatedAt?: unknown;
324
- image?: string | undefined;
325
319
  tags?: ({
326
320
  name: string;
327
321
  } | {
@@ -329,16 +323,16 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
329
323
  name: string;
330
324
  slug: string;
331
325
  })[] | undefined;
332
- }, {
333
- title: string;
334
- content: string;
335
- excerpt: string;
336
326
  slug?: string | undefined;
337
- published?: boolean | undefined;
338
- publishedAt?: unknown;
339
327
  createdAt?: unknown;
340
328
  updatedAt?: unknown;
329
+ publishedAt?: unknown;
341
330
  image?: string | undefined;
331
+ published?: boolean | undefined;
332
+ }, {
333
+ title: string;
334
+ content: string;
335
+ excerpt: string;
342
336
  tags?: ({
343
337
  name: string;
344
338
  } | {
@@ -346,6 +340,12 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
346
340
  name: string;
347
341
  slug: string;
348
342
  })[] | undefined;
343
+ slug?: string | undefined;
344
+ createdAt?: unknown;
345
+ updatedAt?: unknown;
346
+ publishedAt?: unknown;
347
+ image?: string | undefined;
348
+ published?: boolean | undefined;
349
349
  }>;
350
350
  }, Post>;
351
351
  readonly updatePost: better_call.StrictEndpoint<"/posts/:id", {} & {
@@ -87,12 +87,36 @@ interface CreateCMSContentItemInput {
87
87
  /** Arbitrary data payload — should match the content type schema */
88
88
  data: Record<string, unknown>;
89
89
  }
90
+ /**
91
+ * Options for {@link createCMSContentItem}.
92
+ */
93
+ interface CreateCMSContentItemOptions {
94
+ /**
95
+ * When `true`, persist relation fields (`belongsTo`, `hasMany`,
96
+ * `manyToMany`) into the `contentRelation` junction table in addition to
97
+ * the item's JSON payload.
98
+ *
99
+ * This is what the HTTP `POST /content/:typeSlug` route does, and is
100
+ * required for the admin "Related Items" panel / inverse-relation queries
101
+ * to find the item.
102
+ *
103
+ * Seeds and other programmatic callers that want the admin UI to work
104
+ * should enable this flag. Callers are still expected to pass
105
+ * pre-created target IDs (`{ id }` or `[{ id }, ...]`) — inline `_new`
106
+ * creation is only supported via the HTTP route.
107
+ *
108
+ * Defaults to `false` for backwards compatibility (a no-op for content
109
+ * types without relations).
110
+ */
111
+ syncRelations?: boolean;
112
+ }
90
113
  /**
91
114
  * Create a new content item for a content type (looked up by slug).
92
115
  *
93
- * Bypasses Zod schema validation and relation processing — the caller is
94
- * responsible for providing valid, relation-free data. For relation fields or
95
- * schema validation, use the HTTP endpoint instead.
116
+ * Bypasses Zod schema validation and inline `_new` relation creation — the
117
+ * caller is responsible for providing valid data and pre-created relation
118
+ * IDs. For schema validation or inline creation of related items, use the
119
+ * HTTP endpoint instead.
96
120
  *
97
121
  * Throws if the content type is not found or the slug is already taken within
98
122
  * that content type.
@@ -104,8 +128,11 @@ interface CreateCMSContentItemInput {
104
128
  * @param adapter - The database adapter
105
129
  * @param contentTypeSlug - Slug of the target content type
106
130
  * @param input - Item slug and data payload
131
+ * @param options - See {@link CreateCMSContentItemOptions}. Pass
132
+ * `{ syncRelations: true }` to also populate the `contentRelation`
133
+ * junction table for relation fields in the payload.
107
134
  */
108
- declare function createCMSContentItem(adapter: DBAdapter, contentTypeSlug: string, input: CreateCMSContentItemInput): Promise<SerializedContentItem>;
135
+ declare function createCMSContentItem(adapter: DBAdapter, contentTypeSlug: string, input: CreateCMSContentItemInput, options?: CreateCMSContentItemOptions): Promise<SerializedContentItem>;
109
136
 
110
137
  /**
111
138
  * Route keys for the CMS plugin — matches the keys returned by
@@ -331,7 +358,7 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
331
358
  getContentItemBySlug: (contentTypeSlug: string, slug: string) => Promise<SerializedContentItemWithType<Record<string, unknown>> | null>;
332
359
  getContentItemById: (id: string) => Promise<SerializedContentItemWithType<Record<string, unknown>> | null>;
333
360
  prefetchForRoute: CMSPrefetchForRoute;
334
- createContentItem: (typeSlug: string, input: Parameters<typeof createCMSContentItem>[2]) => Promise<SerializedContentItem>;
361
+ createContentItem: (typeSlug: string, input: Parameters<typeof createCMSContentItem>[2], options?: Parameters<typeof createCMSContentItem>[3]) => Promise<SerializedContentItem>;
335
362
  }>;
336
363
  type CMSApiRouter = ReturnType<ReturnType<typeof cmsBackendPlugin>["routes"]>;
337
364
 
@@ -368,5 +395,5 @@ declare const CMS_QUERY_KEYS: {
368
395
  contentDetail: (typeSlug: string, id: string) => readonly ["cmsContent", "detail", string, string];
369
396
  };
370
397
 
371
- export { cmsBackendPlugin as c, getAllContentItems as d, getContentItemBySlug as e, getContentItemById as f, getAllContentTypes as g, serializeContentItem as h, serializeContentItemWithType as i, createCMSContentItem as j, CMS_QUERY_KEYS as l, serializeContentType as s };
372
- export type { CMSApiRouter as C, ContentListDiscriminator as a, CMSRouteKey as b, CreateCMSContentItemInput as k };
398
+ export { cmsBackendPlugin as c, getAllContentItems as d, getContentItemBySlug as e, getContentItemById as f, getAllContentTypes as g, serializeContentItem as h, serializeContentItemWithType as i, createCMSContentItem as j, CMS_QUERY_KEYS as m, serializeContentType as s };
399
+ export type { CMSApiRouter as C, ContentListDiscriminator as a, CMSRouteKey as b, CreateCMSContentItemInput as k, CreateCMSContentItemOptions as l };
@@ -1,5 +1,5 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from './stack.Sod_PZhB.js';
2
+ import { S as SerializedPost, c as createPostSchema, u as updatePostSchema, a as SerializedTag } from './stack.BQmuNl5p.js';
3
3
  import { z } from 'zod';
4
4
 
5
5
  /**
@@ -128,10 +128,6 @@ declare function useSuspenseTags(): {
128
128
  };
129
129
  /** Create a new post */
130
130
  declare function useCreatePost(): _tanstack_react_query.UseMutationResult<SerializedPost | null, Error, {
131
- published: boolean;
132
- title: string;
133
- content: string;
134
- excerpt: string;
135
131
  tags: ({
136
132
  name: string;
137
133
  } | {
@@ -139,10 +135,14 @@ declare function useCreatePost(): _tanstack_react_query.UseMutationResult<Serial
139
135
  name: string;
140
136
  slug: string;
141
137
  })[];
138
+ title: string;
139
+ content: string;
140
+ excerpt: string;
141
+ published: boolean;
142
142
  slug?: string | undefined;
143
- publishedAt?: Date | undefined;
144
143
  createdAt?: Date | undefined;
145
144
  updatedAt?: Date | undefined;
145
+ publishedAt?: Date | undefined;
146
146
  image?: string | undefined;
147
147
  }, unknown>;
148
148
  /** Update an existing post by id */
@@ -1,7 +1,7 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
2
  import { QueryClient } from '@tanstack/react-query';
3
3
  import { createApiClient } from '@btst/stack/plugins/client';
4
- import { P as Post, T as Tag, c as createPostSchema, u as updatePostSchema, S as SerializedPost, a as SerializedTag } from './stack.Sod_PZhB.mjs';
4
+ import { P as Post, T as Tag, c as createPostSchema, u as updatePostSchema, S as SerializedPost, a as SerializedTag } from './stack.BQmuNl5p.cjs';
5
5
  import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
6
6
  import { DBAdapter } from '@btst/db';
7
7
  import * as better_call from 'better-call';
@@ -316,12 +316,6 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
316
316
  title: string;
317
317
  content: string;
318
318
  excerpt: string;
319
- slug?: string | undefined;
320
- published?: boolean | undefined;
321
- publishedAt?: unknown;
322
- createdAt?: unknown;
323
- updatedAt?: unknown;
324
- image?: string | undefined;
325
319
  tags?: ({
326
320
  name: string;
327
321
  } | {
@@ -329,16 +323,16 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
329
323
  name: string;
330
324
  slug: string;
331
325
  })[] | undefined;
332
- }, {
333
- title: string;
334
- content: string;
335
- excerpt: string;
336
326
  slug?: string | undefined;
337
- published?: boolean | undefined;
338
- publishedAt?: unknown;
339
327
  createdAt?: unknown;
340
328
  updatedAt?: unknown;
329
+ publishedAt?: unknown;
341
330
  image?: string | undefined;
331
+ published?: boolean | undefined;
332
+ }, {
333
+ title: string;
334
+ content: string;
335
+ excerpt: string;
342
336
  tags?: ({
343
337
  name: string;
344
338
  } | {
@@ -346,6 +340,12 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
346
340
  name: string;
347
341
  slug: string;
348
342
  })[] | undefined;
343
+ slug?: string | undefined;
344
+ createdAt?: unknown;
345
+ updatedAt?: unknown;
346
+ publishedAt?: unknown;
347
+ image?: string | undefined;
348
+ published?: boolean | undefined;
349
349
  }>;
350
350
  }, Post>;
351
351
  readonly updatePost: better_call.StrictEndpoint<"/posts/:id", {} & {
@@ -87,12 +87,36 @@ interface CreateCMSContentItemInput {
87
87
  /** Arbitrary data payload — should match the content type schema */
88
88
  data: Record<string, unknown>;
89
89
  }
90
+ /**
91
+ * Options for {@link createCMSContentItem}.
92
+ */
93
+ interface CreateCMSContentItemOptions {
94
+ /**
95
+ * When `true`, persist relation fields (`belongsTo`, `hasMany`,
96
+ * `manyToMany`) into the `contentRelation` junction table in addition to
97
+ * the item's JSON payload.
98
+ *
99
+ * This is what the HTTP `POST /content/:typeSlug` route does, and is
100
+ * required for the admin "Related Items" panel / inverse-relation queries
101
+ * to find the item.
102
+ *
103
+ * Seeds and other programmatic callers that want the admin UI to work
104
+ * should enable this flag. Callers are still expected to pass
105
+ * pre-created target IDs (`{ id }` or `[{ id }, ...]`) — inline `_new`
106
+ * creation is only supported via the HTTP route.
107
+ *
108
+ * Defaults to `false` for backwards compatibility (a no-op for content
109
+ * types without relations).
110
+ */
111
+ syncRelations?: boolean;
112
+ }
90
113
  /**
91
114
  * Create a new content item for a content type (looked up by slug).
92
115
  *
93
- * Bypasses Zod schema validation and relation processing — the caller is
94
- * responsible for providing valid, relation-free data. For relation fields or
95
- * schema validation, use the HTTP endpoint instead.
116
+ * Bypasses Zod schema validation and inline `_new` relation creation — the
117
+ * caller is responsible for providing valid data and pre-created relation
118
+ * IDs. For schema validation or inline creation of related items, use the
119
+ * HTTP endpoint instead.
96
120
  *
97
121
  * Throws if the content type is not found or the slug is already taken within
98
122
  * that content type.
@@ -104,8 +128,11 @@ interface CreateCMSContentItemInput {
104
128
  * @param adapter - The database adapter
105
129
  * @param contentTypeSlug - Slug of the target content type
106
130
  * @param input - Item slug and data payload
131
+ * @param options - See {@link CreateCMSContentItemOptions}. Pass
132
+ * `{ syncRelations: true }` to also populate the `contentRelation`
133
+ * junction table for relation fields in the payload.
107
134
  */
108
- declare function createCMSContentItem(adapter: DBAdapter, contentTypeSlug: string, input: CreateCMSContentItemInput): Promise<SerializedContentItem>;
135
+ declare function createCMSContentItem(adapter: DBAdapter, contentTypeSlug: string, input: CreateCMSContentItemInput, options?: CreateCMSContentItemOptions): Promise<SerializedContentItem>;
109
136
 
110
137
  /**
111
138
  * Route keys for the CMS plugin — matches the keys returned by
@@ -331,7 +358,7 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
331
358
  getContentItemBySlug: (contentTypeSlug: string, slug: string) => Promise<SerializedContentItemWithType<Record<string, unknown>> | null>;
332
359
  getContentItemById: (id: string) => Promise<SerializedContentItemWithType<Record<string, unknown>> | null>;
333
360
  prefetchForRoute: CMSPrefetchForRoute;
334
- createContentItem: (typeSlug: string, input: Parameters<typeof createCMSContentItem>[2]) => Promise<SerializedContentItem>;
361
+ createContentItem: (typeSlug: string, input: Parameters<typeof createCMSContentItem>[2], options?: Parameters<typeof createCMSContentItem>[3]) => Promise<SerializedContentItem>;
335
362
  }>;
336
363
  type CMSApiRouter = ReturnType<ReturnType<typeof cmsBackendPlugin>["routes"]>;
337
364
 
@@ -368,5 +395,5 @@ declare const CMS_QUERY_KEYS: {
368
395
  contentDetail: (typeSlug: string, id: string) => readonly ["cmsContent", "detail", string, string];
369
396
  };
370
397
 
371
- export { cmsBackendPlugin as c, getAllContentItems as d, getContentItemBySlug as e, getContentItemById as f, getAllContentTypes as g, serializeContentItem as h, serializeContentItemWithType as i, createCMSContentItem as j, CMS_QUERY_KEYS as l, serializeContentType as s };
372
- export type { CMSApiRouter as C, ContentListDiscriminator as a, CMSRouteKey as b, CreateCMSContentItemInput as k };
398
+ export { cmsBackendPlugin as c, getAllContentItems as d, getContentItemBySlug as e, getContentItemById as f, getAllContentTypes as g, serializeContentItem as h, serializeContentItemWithType as i, createCMSContentItem as j, CMS_QUERY_KEYS as m, serializeContentType as s };
399
+ export type { CMSApiRouter as C, ContentListDiscriminator as a, CMSRouteKey as b, CreateCMSContentItemInput as k, CreateCMSContentItemOptions as l };
@@ -87,12 +87,36 @@ interface CreateCMSContentItemInput {
87
87
  /** Arbitrary data payload — should match the content type schema */
88
88
  data: Record<string, unknown>;
89
89
  }
90
+ /**
91
+ * Options for {@link createCMSContentItem}.
92
+ */
93
+ interface CreateCMSContentItemOptions {
94
+ /**
95
+ * When `true`, persist relation fields (`belongsTo`, `hasMany`,
96
+ * `manyToMany`) into the `contentRelation` junction table in addition to
97
+ * the item's JSON payload.
98
+ *
99
+ * This is what the HTTP `POST /content/:typeSlug` route does, and is
100
+ * required for the admin "Related Items" panel / inverse-relation queries
101
+ * to find the item.
102
+ *
103
+ * Seeds and other programmatic callers that want the admin UI to work
104
+ * should enable this flag. Callers are still expected to pass
105
+ * pre-created target IDs (`{ id }` or `[{ id }, ...]`) — inline `_new`
106
+ * creation is only supported via the HTTP route.
107
+ *
108
+ * Defaults to `false` for backwards compatibility (a no-op for content
109
+ * types without relations).
110
+ */
111
+ syncRelations?: boolean;
112
+ }
90
113
  /**
91
114
  * Create a new content item for a content type (looked up by slug).
92
115
  *
93
- * Bypasses Zod schema validation and relation processing — the caller is
94
- * responsible for providing valid, relation-free data. For relation fields or
95
- * schema validation, use the HTTP endpoint instead.
116
+ * Bypasses Zod schema validation and inline `_new` relation creation — the
117
+ * caller is responsible for providing valid data and pre-created relation
118
+ * IDs. For schema validation or inline creation of related items, use the
119
+ * HTTP endpoint instead.
96
120
  *
97
121
  * Throws if the content type is not found or the slug is already taken within
98
122
  * that content type.
@@ -104,8 +128,11 @@ interface CreateCMSContentItemInput {
104
128
  * @param adapter - The database adapter
105
129
  * @param contentTypeSlug - Slug of the target content type
106
130
  * @param input - Item slug and data payload
131
+ * @param options - See {@link CreateCMSContentItemOptions}. Pass
132
+ * `{ syncRelations: true }` to also populate the `contentRelation`
133
+ * junction table for relation fields in the payload.
107
134
  */
108
- declare function createCMSContentItem(adapter: DBAdapter, contentTypeSlug: string, input: CreateCMSContentItemInput): Promise<SerializedContentItem>;
135
+ declare function createCMSContentItem(adapter: DBAdapter, contentTypeSlug: string, input: CreateCMSContentItemInput, options?: CreateCMSContentItemOptions): Promise<SerializedContentItem>;
109
136
 
110
137
  /**
111
138
  * Route keys for the CMS plugin — matches the keys returned by
@@ -331,7 +358,7 @@ declare const cmsBackendPlugin: (config: CMSBackendConfig) => _btst_stack_plugin
331
358
  getContentItemBySlug: (contentTypeSlug: string, slug: string) => Promise<SerializedContentItemWithType<Record<string, unknown>> | null>;
332
359
  getContentItemById: (id: string) => Promise<SerializedContentItemWithType<Record<string, unknown>> | null>;
333
360
  prefetchForRoute: CMSPrefetchForRoute;
334
- createContentItem: (typeSlug: string, input: Parameters<typeof createCMSContentItem>[2]) => Promise<SerializedContentItem>;
361
+ createContentItem: (typeSlug: string, input: Parameters<typeof createCMSContentItem>[2], options?: Parameters<typeof createCMSContentItem>[3]) => Promise<SerializedContentItem>;
335
362
  }>;
336
363
  type CMSApiRouter = ReturnType<ReturnType<typeof cmsBackendPlugin>["routes"]>;
337
364
 
@@ -368,5 +395,5 @@ declare const CMS_QUERY_KEYS: {
368
395
  contentDetail: (typeSlug: string, id: string) => readonly ["cmsContent", "detail", string, string];
369
396
  };
370
397
 
371
- export { cmsBackendPlugin as c, getAllContentItems as d, getContentItemBySlug as e, getContentItemById as f, getAllContentTypes as g, serializeContentItem as h, serializeContentItemWithType as i, createCMSContentItem as j, CMS_QUERY_KEYS as l, serializeContentType as s };
372
- export type { CMSApiRouter as C, ContentListDiscriminator as a, CMSRouteKey as b, CreateCMSContentItemInput as k };
398
+ export { cmsBackendPlugin as c, getAllContentItems as d, getContentItemBySlug as e, getContentItemById as f, getAllContentTypes as g, serializeContentItem as h, serializeContentItemWithType as i, createCMSContentItem as j, CMS_QUERY_KEYS as m, serializeContentType as s };
399
+ export type { CMSApiRouter as C, ContentListDiscriminator as a, CMSRouteKey as b, CreateCMSContentItemInput as k, CreateCMSContentItemOptions as l };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btst/stack",
3
- "version": "2.11.3",
3
+ "version": "2.11.4",
4
4
  "description": "A composable, plugin-based library for building full-stack applications.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -15,5 +15,6 @@ export {
15
15
  export {
16
16
  createCMSContentItem,
17
17
  type CreateCMSContentItemInput,
18
+ type CreateCMSContentItemOptions,
18
19
  } from "./mutations";
19
20
  export { CMS_QUERY_KEYS } from "./query-key-defs";
@@ -2,6 +2,11 @@ import type { DBAdapter as Adapter } from "@btst/db";
2
2
  import type { ContentType, ContentItem } from "../types";
3
3
  import { serializeContentItem } from "./getters";
4
4
  import type { SerializedContentItem } from "../types";
5
+ import {
6
+ collectExistingRelationIds,
7
+ extractRelationFields,
8
+ syncRelations,
9
+ } from "./relations";
5
10
 
6
11
  /**
7
12
  * Input for creating a new CMS content item.
@@ -13,12 +18,37 @@ export interface CreateCMSContentItemInput {
13
18
  data: Record<string, unknown>;
14
19
  }
15
20
 
21
+ /**
22
+ * Options for {@link createCMSContentItem}.
23
+ */
24
+ export interface CreateCMSContentItemOptions {
25
+ /**
26
+ * When `true`, persist relation fields (`belongsTo`, `hasMany`,
27
+ * `manyToMany`) into the `contentRelation` junction table in addition to
28
+ * the item's JSON payload.
29
+ *
30
+ * This is what the HTTP `POST /content/:typeSlug` route does, and is
31
+ * required for the admin "Related Items" panel / inverse-relation queries
32
+ * to find the item.
33
+ *
34
+ * Seeds and other programmatic callers that want the admin UI to work
35
+ * should enable this flag. Callers are still expected to pass
36
+ * pre-created target IDs (`{ id }` or `[{ id }, ...]`) — inline `_new`
37
+ * creation is only supported via the HTTP route.
38
+ *
39
+ * Defaults to `false` for backwards compatibility (a no-op for content
40
+ * types without relations).
41
+ */
42
+ syncRelations?: boolean;
43
+ }
44
+
16
45
  /**
17
46
  * Create a new content item for a content type (looked up by slug).
18
47
  *
19
- * Bypasses Zod schema validation and relation processing — the caller is
20
- * responsible for providing valid, relation-free data. For relation fields or
21
- * schema validation, use the HTTP endpoint instead.
48
+ * Bypasses Zod schema validation and inline `_new` relation creation — the
49
+ * caller is responsible for providing valid data and pre-created relation
50
+ * IDs. For schema validation or inline creation of related items, use the
51
+ * HTTP endpoint instead.
22
52
  *
23
53
  * Throws if the content type is not found or the slug is already taken within
24
54
  * that content type.
@@ -30,11 +60,15 @@ export interface CreateCMSContentItemInput {
30
60
  * @param adapter - The database adapter
31
61
  * @param contentTypeSlug - Slug of the target content type
32
62
  * @param input - Item slug and data payload
63
+ * @param options - See {@link CreateCMSContentItemOptions}. Pass
64
+ * `{ syncRelations: true }` to also populate the `contentRelation`
65
+ * junction table for relation fields in the payload.
33
66
  */
34
67
  export async function createCMSContentItem(
35
68
  adapter: Adapter,
36
69
  contentTypeSlug: string,
37
70
  input: CreateCMSContentItemInput,
71
+ options: CreateCMSContentItemOptions = {},
38
72
  ): Promise<SerializedContentItem> {
39
73
  const contentType = await adapter.findOne<ContentType>({
40
74
  model: "contentType",
@@ -80,5 +114,16 @@ export async function createCMSContentItem(
80
114
  },
81
115
  });
82
116
 
117
+ if (options.syncRelations) {
118
+ const relationFields = extractRelationFields(contentType);
119
+ if (Object.keys(relationFields).length > 0) {
120
+ const relationIds = collectExistingRelationIds(
121
+ input.data,
122
+ relationFields,
123
+ );
124
+ await syncRelations(adapter, item.id, relationIds);
125
+ }
126
+ }
127
+
83
128
  return serializeContentItem(item);
84
129
  }