@btst/stack 2.11.2 → 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.
- package/dist/packages/stack/src/plugins/cms/api/mutations.cjs +12 -1
- package/dist/packages/stack/src/plugins/cms/api/mutations.mjs +12 -1
- package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +11 -51
- package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +3 -43
- package/dist/packages/stack/src/plugins/cms/api/relations.cjs +76 -0
- package/dist/packages/stack/src/plugins/cms/api/relations.mjs +70 -0
- package/dist/plugins/blog/api/index.d.cts +2 -2
- package/dist/plugins/blog/api/index.d.mts +2 -2
- package/dist/plugins/blog/api/index.d.ts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
- package/dist/plugins/blog/client/index.d.cts +2 -2
- package/dist/plugins/blog/client/index.d.mts +2 -2
- package/dist/plugins/blog/client/index.d.ts +2 -2
- package/dist/plugins/blog/query-keys.d.cts +2 -2
- package/dist/plugins/blog/query-keys.d.mts +2 -2
- package/dist/plugins/blog/query-keys.d.ts +2 -2
- package/dist/plugins/cms/api/index.d.cts +1 -1
- package/dist/plugins/cms/api/index.d.mts +1 -1
- package/dist/plugins/cms/api/index.d.ts +1 -1
- package/dist/plugins/cms/query-keys.d.cts +1 -1
- package/dist/plugins/cms/query-keys.d.mts +1 -1
- package/dist/plugins/cms/query-keys.d.ts +1 -1
- package/dist/shared/{stack.DOZ1EXjM.d.mts → stack.6mEHS2WH.d.mts} +3 -3
- package/dist/shared/{stack.DX-tQ93o.d.cts → stack.AJTXI7kw.d.cts} +3 -3
- package/dist/shared/{stack.E17kSK1W.d.mts → stack.C1jLzFZ6.d.mts} +5 -5
- package/dist/shared/{stack.D0p6oNme.d.ts → stack.DF1ewpVE.d.ts} +5 -5
- package/dist/shared/{stack.BX7MHi0J.d.mts → stack.DbnCLdNF.d.mts} +34 -7
- package/dist/shared/{stack.VF6FhyZw.d.ts → stack.QYn-Px94.d.ts} +3 -3
- package/dist/shared/{stack.DWipT53I.d.cts → stack.XulPeAD3.d.cts} +5 -5
- package/dist/shared/{stack.fQjVhw5a.d.ts → stack.fXlShnhv.d.ts} +34 -7
- package/dist/shared/{stack.C9zoS1TN.d.cts → stack.r6HohbyX.d.cts} +34 -7
- package/package.json +4 -4
- package/src/plugins/cms/api/index.ts +1 -0
- package/src/plugins/cms/api/mutations.ts +48 -3
- package/src/plugins/cms/api/plugin.ts +11 -101
- package/src/plugins/cms/api/relations.ts +174 -0
- package/dist/shared/{stack.BWp0hcm9.d.cts → stack.BQmuNl5p.d.cts} +3 -3
- package/dist/shared/{stack.BWp0hcm9.d.mts → stack.BQmuNl5p.d.mts} +3 -3
- package/dist/shared/{stack.BWp0hcm9.d.ts → stack.BQmuNl5p.d.ts} +3 -3
|
@@ -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.
|
|
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';
|
|
@@ -324,11 +324,11 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
|
|
|
324
324
|
slug: string;
|
|
325
325
|
})[] | undefined;
|
|
326
326
|
slug?: string | undefined;
|
|
327
|
-
published?: boolean | undefined;
|
|
328
|
-
publishedAt?: unknown;
|
|
329
327
|
createdAt?: unknown;
|
|
330
328
|
updatedAt?: unknown;
|
|
329
|
+
publishedAt?: unknown;
|
|
331
330
|
image?: string | undefined;
|
|
331
|
+
published?: boolean | undefined;
|
|
332
332
|
}, {
|
|
333
333
|
title: string;
|
|
334
334
|
content: string;
|
|
@@ -341,11 +341,11 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
|
|
|
341
341
|
slug: string;
|
|
342
342
|
})[] | undefined;
|
|
343
343
|
slug?: string | undefined;
|
|
344
|
-
published?: boolean | undefined;
|
|
345
|
-
publishedAt?: unknown;
|
|
346
344
|
createdAt?: unknown;
|
|
347
345
|
updatedAt?: unknown;
|
|
346
|
+
publishedAt?: unknown;
|
|
348
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.
|
|
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';
|
|
@@ -324,11 +324,11 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
|
|
|
324
324
|
slug: string;
|
|
325
325
|
})[] | undefined;
|
|
326
326
|
slug?: string | undefined;
|
|
327
|
-
published?: boolean | undefined;
|
|
328
|
-
publishedAt?: unknown;
|
|
329
327
|
createdAt?: unknown;
|
|
330
328
|
updatedAt?: unknown;
|
|
329
|
+
publishedAt?: unknown;
|
|
331
330
|
image?: string | undefined;
|
|
331
|
+
published?: boolean | undefined;
|
|
332
332
|
}, {
|
|
333
333
|
title: string;
|
|
334
334
|
content: string;
|
|
@@ -341,11 +341,11 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
|
|
|
341
341
|
slug: string;
|
|
342
342
|
})[] | undefined;
|
|
343
343
|
slug?: string | undefined;
|
|
344
|
-
published?: boolean | undefined;
|
|
345
|
-
publishedAt?: unknown;
|
|
346
344
|
createdAt?: unknown;
|
|
347
345
|
updatedAt?: unknown;
|
|
346
|
+
publishedAt?: unknown;
|
|
348
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
|
|
94
|
-
* responsible for providing valid
|
|
95
|
-
* schema validation, use the
|
|
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
|
|
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.
|
|
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
|
/**
|
|
@@ -135,14 +135,14 @@ declare function useCreatePost(): _tanstack_react_query.UseMutationResult<Serial
|
|
|
135
135
|
name: string;
|
|
136
136
|
slug: string;
|
|
137
137
|
})[];
|
|
138
|
-
published: boolean;
|
|
139
138
|
title: string;
|
|
140
139
|
content: string;
|
|
141
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.
|
|
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';
|
|
@@ -324,11 +324,11 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
|
|
|
324
324
|
slug: string;
|
|
325
325
|
})[] | undefined;
|
|
326
326
|
slug?: string | undefined;
|
|
327
|
-
published?: boolean | undefined;
|
|
328
|
-
publishedAt?: unknown;
|
|
329
327
|
createdAt?: unknown;
|
|
330
328
|
updatedAt?: unknown;
|
|
329
|
+
publishedAt?: unknown;
|
|
331
330
|
image?: string | undefined;
|
|
331
|
+
published?: boolean | undefined;
|
|
332
332
|
}, {
|
|
333
333
|
title: string;
|
|
334
334
|
content: string;
|
|
@@ -341,11 +341,11 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
|
|
|
341
341
|
slug: string;
|
|
342
342
|
})[] | undefined;
|
|
343
343
|
slug?: string | undefined;
|
|
344
|
-
published?: boolean | undefined;
|
|
345
|
-
publishedAt?: unknown;
|
|
346
344
|
createdAt?: unknown;
|
|
347
345
|
updatedAt?: unknown;
|
|
346
|
+
publishedAt?: unknown;
|
|
348
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
|
|
94
|
-
* responsible for providing valid
|
|
95
|
-
* schema validation, use the
|
|
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
|
|
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
|
|
94
|
-
* responsible for providing valid
|
|
95
|
-
* schema validation, use the
|
|
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
|
|
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
|
+
"version": "2.11.4",
|
|
4
4
|
"description": "A composable, plugin-based library for building full-stack applications.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -742,7 +742,7 @@
|
|
|
742
742
|
}
|
|
743
743
|
},
|
|
744
744
|
"dependencies": {
|
|
745
|
-
"@btst/db": "2.
|
|
745
|
+
"@btst/db": "2.2.1",
|
|
746
746
|
"@lukemorales/query-key-factory": "^1.3.4",
|
|
747
747
|
"@milkdown/crepe": "^7.17.1",
|
|
748
748
|
"@milkdown/kit": "^7.17.1",
|
|
@@ -763,7 +763,7 @@
|
|
|
763
763
|
"@tanstack/react-query": "^5.0.0",
|
|
764
764
|
"@vercel/blob": ">=0.14.0",
|
|
765
765
|
"ai": ">=5.0.0",
|
|
766
|
-
"better-call": ">=1.3.
|
|
766
|
+
"better-call": ">=1.3.5",
|
|
767
767
|
"class-variance-authority": ">=0.7.0",
|
|
768
768
|
"clsx": ">=2.1.0",
|
|
769
769
|
"cmdk": ">=1.1.0",
|
|
@@ -801,7 +801,7 @@
|
|
|
801
801
|
"@ai-sdk/react": "^2.0.94",
|
|
802
802
|
"@aws-sdk/client-s3": "^3.1011.0",
|
|
803
803
|
"@aws-sdk/s3-request-presigner": "^3.1011.0",
|
|
804
|
-
"@btst/adapter-memory": "2.
|
|
804
|
+
"@btst/adapter-memory": "2.2.1",
|
|
805
805
|
"@btst/yar": "1.2.0",
|
|
806
806
|
"@types/react": "^19.0.0",
|
|
807
807
|
"@types/slug": "^5.0.9",
|
|
@@ -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
|
|
20
|
-
* responsible for providing valid
|
|
21
|
-
* schema validation, use the
|
|
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
|
}
|
|
@@ -15,7 +15,6 @@ import type {
|
|
|
15
15
|
CMSBackendConfig,
|
|
16
16
|
CMSHookContext,
|
|
17
17
|
SerializedContentItemWithType,
|
|
18
|
-
RelationConfig,
|
|
19
18
|
RelationValue,
|
|
20
19
|
InverseRelation,
|
|
21
20
|
} from "../types";
|
|
@@ -31,6 +30,12 @@ import {
|
|
|
31
30
|
serializeContentItemWithType,
|
|
32
31
|
} from "./getters";
|
|
33
32
|
import { createCMSContentItem } from "./mutations";
|
|
33
|
+
import {
|
|
34
|
+
extractRelationFields,
|
|
35
|
+
isExistingRelationValue,
|
|
36
|
+
isNewRelationValue,
|
|
37
|
+
syncRelations,
|
|
38
|
+
} from "./relations";
|
|
34
39
|
import type { QueryClient } from "@tanstack/react-query";
|
|
35
40
|
import { CMS_QUERY_KEYS } from "./query-key-defs";
|
|
36
41
|
import { runHookWithShim } from "../../utils";
|
|
@@ -145,67 +150,9 @@ function getContentTypeZodSchema(contentType: ContentType): z.ZodTypeAny {
|
|
|
145
150
|
}
|
|
146
151
|
|
|
147
152
|
// ========== Relation Helpers ==========
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
relation?: RelationConfig;
|
|
152
|
-
type?: string;
|
|
153
|
-
items?: JsonSchemaProperty;
|
|
154
|
-
[key: string]: unknown;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
interface JsonSchemaWithProperties {
|
|
158
|
-
properties?: Record<string, JsonSchemaProperty>;
|
|
159
|
-
[key: string]: unknown;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Extract relation field configurations from a content type's JSON Schema
|
|
164
|
-
*/
|
|
165
|
-
function extractRelationFields(
|
|
166
|
-
contentType: ContentType,
|
|
167
|
-
): Record<string, RelationConfig> {
|
|
168
|
-
const jsonSchema = JSON.parse(
|
|
169
|
-
contentType.jsonSchema,
|
|
170
|
-
) as JsonSchemaWithProperties;
|
|
171
|
-
const properties = jsonSchema.properties || {};
|
|
172
|
-
const relationFields: Record<string, RelationConfig> = {};
|
|
173
|
-
|
|
174
|
-
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
175
|
-
if (fieldSchema.fieldType === "relation" && fieldSchema.relation) {
|
|
176
|
-
relationFields[fieldName] = fieldSchema.relation;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return relationFields;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Check if a value is a "new" relation item (to be created)
|
|
185
|
-
*/
|
|
186
|
-
function isNewRelationValue(
|
|
187
|
-
value: unknown,
|
|
188
|
-
): value is { _new: true; data: Record<string, unknown> } {
|
|
189
|
-
return (
|
|
190
|
-
typeof value === "object" &&
|
|
191
|
-
value !== null &&
|
|
192
|
-
"_new" in value &&
|
|
193
|
-
(value as { _new: unknown })._new === true &&
|
|
194
|
-
"data" in value
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Check if a value is an existing relation reference
|
|
200
|
-
*/
|
|
201
|
-
function isExistingRelationValue(value: unknown): value is { id: string } {
|
|
202
|
-
return (
|
|
203
|
-
typeof value === "object" &&
|
|
204
|
-
value !== null &&
|
|
205
|
-
"id" in value &&
|
|
206
|
-
typeof (value as { id: unknown }).id === "string"
|
|
207
|
-
);
|
|
208
|
-
}
|
|
153
|
+
// `extractRelationFields`, `isNewRelationValue`, `isExistingRelationValue`,
|
|
154
|
+
// and `syncRelations` live in ./relations so both this HTTP route module
|
|
155
|
+
// and the programmatic mutations helper can share them.
|
|
209
156
|
|
|
210
157
|
/**
|
|
211
158
|
* Process relation fields in content data:
|
|
@@ -363,44 +310,6 @@ async function createRelatedItem(
|
|
|
363
310
|
return item;
|
|
364
311
|
}
|
|
365
312
|
|
|
366
|
-
/**
|
|
367
|
-
* Sync relations in the junction table for a content item.
|
|
368
|
-
*
|
|
369
|
-
* Only updates relations for fields explicitly present in relationIds.
|
|
370
|
-
* Fields not in relationIds are left unchanged - this preserves existing
|
|
371
|
-
* relations during partial updates.
|
|
372
|
-
*/
|
|
373
|
-
async function syncRelations(
|
|
374
|
-
adapter: Adapter,
|
|
375
|
-
sourceId: string,
|
|
376
|
-
relationIds: Record<string, string[]>,
|
|
377
|
-
): Promise<void> {
|
|
378
|
-
// Only sync fields that are explicitly included in relationIds
|
|
379
|
-
for (const [fieldName, targetIds] of Object.entries(relationIds)) {
|
|
380
|
-
// Delete existing relations for this specific field only
|
|
381
|
-
await adapter.delete({
|
|
382
|
-
model: "contentRelation",
|
|
383
|
-
where: [
|
|
384
|
-
{ field: "sourceId", value: sourceId, operator: "eq" as const },
|
|
385
|
-
{ field: "fieldName", value: fieldName, operator: "eq" as const },
|
|
386
|
-
],
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
// Create new relations for this field
|
|
390
|
-
for (const targetId of targetIds) {
|
|
391
|
-
await adapter.create<ContentRelation>({
|
|
392
|
-
model: "contentRelation",
|
|
393
|
-
data: {
|
|
394
|
-
sourceId,
|
|
395
|
-
targetId,
|
|
396
|
-
fieldName,
|
|
397
|
-
createdAt: new Date(),
|
|
398
|
-
},
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
313
|
/**
|
|
405
314
|
* Populate relations for a content item by fetching related items
|
|
406
315
|
*/
|
|
@@ -575,9 +484,10 @@ export const cmsBackendPlugin = (config: CMSBackendConfig) => {
|
|
|
575
484
|
createContentItem: async (
|
|
576
485
|
typeSlug: string,
|
|
577
486
|
input: Parameters<typeof createCMSContentItem>[2],
|
|
487
|
+
options?: Parameters<typeof createCMSContentItem>[3],
|
|
578
488
|
) => {
|
|
579
489
|
await ensureSynced(adapter);
|
|
580
|
-
return createCMSContentItem(adapter, typeSlug, input);
|
|
490
|
+
return createCMSContentItem(adapter, typeSlug, input, options);
|
|
581
491
|
},
|
|
582
492
|
}),
|
|
583
493
|
|