@btst/plugin-cms 0.0.0-stage → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/LICENSE.md +17 -0
  2. package/dist/api/getters.cjs +328 -0
  3. package/dist/api/getters.mjs +314 -0
  4. package/dist/api/index.cjs +29 -0
  5. package/dist/api/index.d.cts +209 -0
  6. package/dist/api/index.d.mts +209 -0
  7. package/dist/api/index.d.ts +209 -0
  8. package/dist/api/index.mjs +7 -0
  9. package/dist/api/lifecycle-migrations.cjs +13 -0
  10. package/dist/api/lifecycle-migrations.mjs +11 -0
  11. package/dist/api/mutations.cjs +59 -0
  12. package/dist/api/mutations.mjs +57 -0
  13. package/dist/api/operations.cjs +1769 -0
  14. package/dist/api/operations.mjs +1762 -0
  15. package/dist/api/plugin.cjs +337 -0
  16. package/dist/api/plugin.mjs +331 -0
  17. package/dist/api/query-key-defs.cjs +44 -0
  18. package/dist/api/query-key-defs.mjs +39 -0
  19. package/dist/api/relations.cjs +76 -0
  20. package/dist/api/relations.mjs +70 -0
  21. package/dist/client/components/forms/content-form.cjs +286 -0
  22. package/dist/client/components/forms/content-form.mjs +284 -0
  23. package/dist/client/components/forms/file-upload.cjs +160 -0
  24. package/dist/client/components/forms/file-upload.mjs +158 -0
  25. package/dist/client/components/forms/relation-field.cjs +237 -0
  26. package/dist/client/components/forms/relation-field.mjs +235 -0
  27. package/dist/client/components/index.cjs +14 -0
  28. package/dist/client/components/index.d.cts +20 -0
  29. package/dist/client/components/index.d.mts +20 -0
  30. package/dist/client/components/index.d.ts +20 -0
  31. package/dist/client/components/index.mjs +10 -0
  32. package/dist/client/components/inverse-relations-panel.cjs +272 -0
  33. package/dist/client/components/inverse-relations-panel.mjs +270 -0
  34. package/dist/client/components/loading/dashboard-skeleton.cjs +18 -0
  35. package/dist/client/components/loading/dashboard-skeleton.mjs +16 -0
  36. package/dist/client/components/loading/editor-skeleton.cjs +32 -0
  37. package/dist/client/components/loading/editor-skeleton.mjs +30 -0
  38. package/dist/client/components/loading/list-skeleton.cjs +32 -0
  39. package/dist/client/components/loading/list-skeleton.mjs +30 -0
  40. package/dist/client/components/pages/404-page.cjs +26 -0
  41. package/dist/client/components/pages/404-page.mjs +24 -0
  42. package/dist/client/components/pages/content-editor-page.cjs +84 -0
  43. package/dist/client/components/pages/content-editor-page.internal.cjs +170 -0
  44. package/dist/client/components/pages/content-editor-page.internal.mjs +168 -0
  45. package/dist/client/components/pages/content-editor-page.mjs +82 -0
  46. package/dist/client/components/pages/content-list-page.cjs +49 -0
  47. package/dist/client/components/pages/content-list-page.internal.cjs +334 -0
  48. package/dist/client/components/pages/content-list-page.internal.mjs +332 -0
  49. package/dist/client/components/pages/content-list-page.mjs +47 -0
  50. package/dist/client/components/pages/dashboard-page.cjs +42 -0
  51. package/dist/client/components/pages/dashboard-page.internal.cjs +102 -0
  52. package/dist/client/components/pages/dashboard-page.internal.mjs +100 -0
  53. package/dist/client/components/pages/dashboard-page.mjs +40 -0
  54. package/dist/client/components/shared/default-error.cjs +17 -0
  55. package/dist/client/components/shared/default-error.mjs +15 -0
  56. package/dist/client/components/shared/empty-state.cjs +16 -0
  57. package/dist/client/components/shared/empty-state.mjs +14 -0
  58. package/dist/client/components/shared/page-wrapper.cjs +29 -0
  59. package/dist/client/components/shared/page-wrapper.mjs +27 -0
  60. package/dist/client/constants.cjs +5 -0
  61. package/dist/client/constants.mjs +3 -0
  62. package/dist/client/hooks/cms-hooks.cjs +300 -0
  63. package/dist/client/hooks/cms-hooks.mjs +280 -0
  64. package/dist/client/hooks/cms-resource.cjs +13 -0
  65. package/dist/client/hooks/cms-resource.mjs +11 -0
  66. package/dist/client/hooks/index.cjs +25 -0
  67. package/dist/client/hooks/index.d.cts +693 -0
  68. package/dist/client/hooks/index.d.mts +693 -0
  69. package/dist/client/hooks/index.d.ts +693 -0
  70. package/dist/client/hooks/index.mjs +1 -0
  71. package/dist/client/index.cjs +15 -0
  72. package/dist/client/index.d.cts +654 -0
  73. package/dist/client/index.d.mts +654 -0
  74. package/dist/client/index.d.ts +654 -0
  75. package/dist/client/index.mjs +3 -0
  76. package/dist/client/overrides.cjs +26 -0
  77. package/dist/client/overrides.mjs +24 -0
  78. package/dist/client/plugin.cjs +316 -0
  79. package/dist/client/plugin.mjs +314 -0
  80. package/dist/client.css +3 -0
  81. package/dist/db.cjs +118 -0
  82. package/dist/db.mjs +116 -0
  83. package/dist/permissions.cjs +46 -0
  84. package/dist/permissions.d.cts +42 -0
  85. package/dist/permissions.d.mts +42 -0
  86. package/dist/permissions.d.ts +42 -0
  87. package/dist/permissions.mjs +44 -0
  88. package/dist/query-keys.cjs +229 -0
  89. package/dist/query-keys.d.cts +8 -0
  90. package/dist/query-keys.d.mts +8 -0
  91. package/dist/query-keys.d.ts +8 -0
  92. package/dist/query-keys.mjs +225 -0
  93. package/dist/schemas.cjs +81 -0
  94. package/dist/schemas.mjs +72 -0
  95. package/dist/shared/plugin-cms.BBshPRkb.d.cts +6 -0
  96. package/dist/shared/plugin-cms.BBshPRkb.d.mts +6 -0
  97. package/dist/shared/plugin-cms.BBshPRkb.d.ts +6 -0
  98. package/dist/shared/plugin-cms.Bptwe33B.d.cts +1749 -0
  99. package/dist/shared/plugin-cms.Csx7tNDO.d.mts +1749 -0
  100. package/dist/shared/plugin-cms.eNW9_FO5.d.ts +1749 -0
  101. package/dist/style.css +6 -0
  102. package/dist/types.cjs +2 -0
  103. package/dist/types.d.cts +406 -0
  104. package/dist/types.d.mts +406 -0
  105. package/dist/types.d.ts +406 -0
  106. package/dist/types.mjs +1 -0
  107. package/dist/ui/src/components/accordion.cjs +81 -0
  108. package/dist/ui/src/components/accordion.mjs +62 -0
  109. package/dist/ui/src/components/alert-dialog.cjs +157 -0
  110. package/dist/ui/src/components/alert-dialog.mjs +132 -0
  111. package/dist/ui/src/components/auto-form/common/label.cjs +18 -0
  112. package/dist/ui/src/components/auto-form/common/label.mjs +16 -0
  113. package/dist/ui/src/components/auto-form/common/tooltip.cjs +9 -0
  114. package/dist/ui/src/components/auto-form/common/tooltip.mjs +7 -0
  115. package/dist/ui/src/components/auto-form/config.cjs +40 -0
  116. package/dist/ui/src/components/auto-form/config.mjs +37 -0
  117. package/dist/ui/src/components/auto-form/dependencies.cjs +48 -0
  118. package/dist/ui/src/components/auto-form/dependencies.mjs +46 -0
  119. package/dist/ui/src/components/auto-form/fields/array.cjs +266 -0
  120. package/dist/ui/src/components/auto-form/fields/array.mjs +264 -0
  121. package/dist/ui/src/components/auto-form/fields/checkbox.cjs +38 -0
  122. package/dist/ui/src/components/auto-form/fields/checkbox.mjs +36 -0
  123. package/dist/ui/src/components/auto-form/fields/date.cjs +67 -0
  124. package/dist/ui/src/components/auto-form/fields/date.mjs +65 -0
  125. package/dist/ui/src/components/auto-form/fields/enum.cjs +73 -0
  126. package/dist/ui/src/components/auto-form/fields/enum.mjs +71 -0
  127. package/dist/ui/src/components/auto-form/fields/input.cjs +32 -0
  128. package/dist/ui/src/components/auto-form/fields/input.mjs +30 -0
  129. package/dist/ui/src/components/auto-form/fields/number.cjs +31 -0
  130. package/dist/ui/src/components/auto-form/fields/number.mjs +29 -0
  131. package/dist/ui/src/components/auto-form/fields/object.cjs +233 -0
  132. package/dist/ui/src/components/auto-form/fields/object.mjs +217 -0
  133. package/dist/ui/src/components/auto-form/fields/radio-group.cjs +67 -0
  134. package/dist/ui/src/components/auto-form/fields/radio-group.mjs +65 -0
  135. package/dist/ui/src/components/auto-form/fields/switch.cjs +38 -0
  136. package/dist/ui/src/components/auto-form/fields/switch.mjs +36 -0
  137. package/dist/ui/src/components/auto-form/fields/textarea.cjs +31 -0
  138. package/dist/ui/src/components/auto-form/fields/textarea.mjs +29 -0
  139. package/dist/ui/src/components/auto-form/helpers.cjs +270 -0
  140. package/dist/ui/src/components/auto-form/helpers.mjs +259 -0
  141. package/dist/ui/src/components/auto-form/index.cjs +92 -0
  142. package/dist/ui/src/components/auto-form/index.mjs +86 -0
  143. package/dist/ui/src/components/auto-form/stepped-auto-form.cjs +374 -0
  144. package/dist/ui/src/components/auto-form/stepped-auto-form.mjs +368 -0
  145. package/dist/ui/src/components/auto-form/types.cjs +11 -0
  146. package/dist/ui/src/components/auto-form/types.mjs +9 -0
  147. package/dist/ui/src/components/badge.cjs +42 -0
  148. package/dist/ui/src/components/badge.mjs +39 -0
  149. package/dist/ui/src/components/button.cjs +56 -0
  150. package/dist/ui/src/components/button.mjs +53 -0
  151. package/dist/ui/src/components/calendar.cjs +207 -0
  152. package/dist/ui/src/components/calendar.mjs +190 -0
  153. package/dist/ui/src/components/card.cjs +56 -0
  154. package/dist/ui/src/components/card.mjs +51 -0
  155. package/dist/ui/src/components/checkbox.cjs +48 -0
  156. package/dist/ui/src/components/checkbox.mjs +32 -0
  157. package/dist/ui/src/components/command.cjs +78 -0
  158. package/dist/ui/src/components/command.mjs +73 -0
  159. package/dist/ui/src/components/date-picker.cjs +41 -0
  160. package/dist/ui/src/components/date-picker.mjs +39 -0
  161. package/dist/ui/src/components/dialog.cjs +119 -0
  162. package/dist/ui/src/components/dialog.mjs +97 -0
  163. package/dist/ui/src/components/form.cjs +120 -0
  164. package/dist/ui/src/components/form.mjs +98 -0
  165. package/dist/ui/src/components/input.cjs +23 -0
  166. package/dist/ui/src/components/input.mjs +21 -0
  167. package/dist/ui/src/components/label.cjs +39 -0
  168. package/dist/ui/src/components/label.mjs +23 -0
  169. package/dist/ui/src/components/multi-select.cjs +555 -0
  170. package/dist/ui/src/components/multi-select.mjs +540 -0
  171. package/dist/ui/src/components/page-layout.cjs +25 -0
  172. package/dist/ui/src/components/page-layout.mjs +23 -0
  173. package/dist/ui/src/components/page-wrapper.cjs +20 -0
  174. package/dist/ui/src/components/page-wrapper.mjs +18 -0
  175. package/dist/ui/src/components/popover.cjs +60 -0
  176. package/dist/ui/src/components/popover.mjs +42 -0
  177. package/dist/ui/src/components/radio-group.cjs +62 -0
  178. package/dist/ui/src/components/radio-group.mjs +45 -0
  179. package/dist/ui/src/components/select.cjs +161 -0
  180. package/dist/ui/src/components/select.mjs +139 -0
  181. package/dist/ui/src/components/separator.cjs +43 -0
  182. package/dist/ui/src/components/separator.mjs +27 -0
  183. package/dist/ui/src/components/skeleton.cjs +17 -0
  184. package/dist/ui/src/components/skeleton.mjs +15 -0
  185. package/dist/ui/src/components/stack-attribution.cjs +24 -0
  186. package/dist/ui/src/components/stack-attribution.mjs +22 -0
  187. package/dist/ui/src/components/switch.cjs +48 -0
  188. package/dist/ui/src/components/switch.mjs +32 -0
  189. package/dist/ui/src/components/table.cjs +89 -0
  190. package/dist/ui/src/components/table.mjs +82 -0
  191. package/dist/ui/src/components/textarea.cjs +20 -0
  192. package/dist/ui/src/components/textarea.mjs +18 -0
  193. package/dist/ui/src/hooks/use-debounce.cjs +16 -0
  194. package/dist/ui/src/hooks/use-debounce.mjs +14 -0
  195. package/dist/ui/src/hooks/use-route-lifecycle.cjs +31 -0
  196. package/dist/ui/src/hooks/use-route-lifecycle.mjs +29 -0
  197. package/dist/ui/src/lib/schema-converter.cjs +133 -0
  198. package/dist/ui/src/lib/schema-converter.mjs +127 -0
  199. package/dist/ui/src/lib/utils.cjs +10 -0
  200. package/dist/ui/src/lib/utils.mjs +8 -0
  201. package/dist/utils.cjs +19 -0
  202. package/dist/utils.mjs +12 -0
  203. package/package.json +170 -3
  204. package/README.md +0 -3
package/dist/style.css ADDED
@@ -0,0 +1,6 @@
1
+
2
+ /* CMS Plugin Client CSS */
3
+ /* No custom styles needed - uses shadcn/ui components */
4
+
5
+
6
+ @source "./**/*.{mjs,cjs}";
package/dist/types.cjs ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';
2
+
@@ -0,0 +1,406 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Configuration for a content type defined by the developer.
5
+ *
6
+ * Field types are now specified directly in the Zod schema via .meta():
7
+ * @example
8
+ * ```typescript
9
+ * const ProductSchema = z.object({
10
+ * description: z.string().meta({ fieldType: "textarea" }),
11
+ * image: z.string().optional().meta({ fieldType: "file" }),
12
+ * });
13
+ * ```
14
+ */
15
+ interface ContentTypeConfig {
16
+ /** Display name for the content type (e.g., "Product", "Testimonial") */
17
+ name: string;
18
+ /** URL-friendly slug (e.g., "product", "testimonial") */
19
+ slug: string;
20
+ /** Optional description shown in the admin UI */
21
+ description?: string;
22
+ /** Zod schema defining the content type's fields. Use .meta({ fieldType: "..." }) for field type overrides. */
23
+ schema: z.ZodObject<z.ZodRawShape>;
24
+ }
25
+ /**
26
+ * Content type stored in the database
27
+ */
28
+ type ContentType = {
29
+ id: string;
30
+ /** Display name */
31
+ name: string;
32
+ /** URL-friendly slug - unique identifier */
33
+ slug: string;
34
+ /** Optional description */
35
+ description?: string;
36
+ /** JSON Schema representation of the Zod schema (stringified) */
37
+ jsonSchema: string;
38
+ /** @deprecated Legacy field config - now embedded in jsonSchema. Kept for backwards compat. */
39
+ fieldConfig?: string;
40
+ /** AutoForm schema version. 1 = legacy (separate fieldConfig), 2 = unified (fieldType in jsonSchema) */
41
+ autoFormVersion?: number;
42
+ createdAt: Date;
43
+ updatedAt: Date;
44
+ };
45
+ /**
46
+ * Content item stored in the database
47
+ */
48
+ type ContentItem = {
49
+ id: string;
50
+ /** Reference to the content type */
51
+ contentTypeId: string;
52
+ /** URL-friendly slug - unique within content type */
53
+ slug: string;
54
+ /** JSON data matching the content type's schema (stringified) */
55
+ data: string;
56
+ /** Optional author ID for tracking who created/modified */
57
+ authorId?: string;
58
+ createdAt: Date;
59
+ updatedAt: Date;
60
+ };
61
+ /**
62
+ * Content item with its content type joined
63
+ */
64
+ type ContentItemWithType = ContentItem & {
65
+ contentType?: ContentType;
66
+ };
67
+ /**
68
+ * Content relation stored in the database (junction table)
69
+ * Links source content items to target content items for relationship fields
70
+ */
71
+ type ContentRelation = {
72
+ id: string;
73
+ /** The content item that has the relation field */
74
+ sourceId: string;
75
+ /** The content item being referenced */
76
+ targetId: string;
77
+ /** The field name in the source content type schema (e.g., "categoryIds") */
78
+ fieldName: string;
79
+ createdAt: Date;
80
+ };
81
+ /**
82
+ * Configuration for a relation field in schema metadata.
83
+ * Use with .meta({ fieldType: "relation", relation: {...} })
84
+ *
85
+ * The schema stores relation values as simple `{ id: string }` references.
86
+ * When `creatable: true`, the frontend sends `{ _new: true, data: {...} }`
87
+ * which the API processes before validation - creating new items and
88
+ * converting them to ID references.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * const ResourceSchema = z.object({
93
+ * // Simple array of ID references - API handles _new items before validation
94
+ * categoryIds: z.array(z.object({ id: z.string() })).default([]).meta({
95
+ * fieldType: "relation",
96
+ * relation: {
97
+ * type: "manyToMany",
98
+ * targetType: "category",
99
+ * displayField: "name",
100
+ * creatable: true,
101
+ * },
102
+ * }),
103
+ * });
104
+ * ```
105
+ */
106
+ interface RelationConfig {
107
+ /** Relation type */
108
+ type: "belongsTo" | "hasMany" | "manyToMany";
109
+ /** Target content type slug */
110
+ targetType: string;
111
+ /** Field to display in the dropdown (e.g., "name", "title") */
112
+ displayField: string;
113
+ /** Allow creating new items inline via modal (default: false) */
114
+ creatable?: boolean;
115
+ }
116
+ /**
117
+ * Value for a relation field - either a reference to existing item or a new item to create.
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * // Reference to existing item
122
+ * const existing: RelationValue = { id: "abc123" };
123
+ *
124
+ * // New item to create on save
125
+ * const newItem: RelationValue = {
126
+ * _new: true,
127
+ * data: { name: "New Category", description: "..." }
128
+ * };
129
+ * ```
130
+ */
131
+ type RelationValue = {
132
+ id: string;
133
+ } | {
134
+ _new: true;
135
+ data: Record<string, unknown>;
136
+ };
137
+ /**
138
+ * Represents an inverse relation (content types that reference this type via belongsTo)
139
+ */
140
+ interface InverseRelation {
141
+ /** The content type slug that has the belongsTo relation */
142
+ sourceType: string;
143
+ /** Display name of the source content type */
144
+ sourceTypeName: string;
145
+ /** The field name that contains the belongsTo relation */
146
+ fieldName: string;
147
+ /** Count of items with this relation (when itemId is provided) */
148
+ count: number;
149
+ }
150
+ /**
151
+ * Serialized content type for API responses (dates as strings)
152
+ */
153
+ interface SerializedContentType extends Omit<ContentType, "createdAt" | "updatedAt"> {
154
+ createdAt: string;
155
+ updatedAt: string;
156
+ }
157
+ /**
158
+ * Serialized content item for API responses (dates as strings)
159
+ */
160
+ interface SerializedContentItem extends Omit<ContentItem, "createdAt" | "updatedAt"> {
161
+ createdAt: string;
162
+ updatedAt: string;
163
+ }
164
+ /**
165
+ * Serialized content item with parsed data and joined content type
166
+ * @template TData - The type of the parsed data (defaults to Record<string, unknown>)
167
+ */
168
+ interface SerializedContentItemWithType<TData = Record<string, unknown>> extends SerializedContentItem {
169
+ /** Parsed data object (JSON.parse of data field). */
170
+ parsedData: TData;
171
+ /** Joined content type */
172
+ contentType?: SerializedContentType;
173
+ /**
174
+ * Populated relation data (only present when using populated endpoints/hooks).
175
+ * Keys are field names, values are arrays of related content items.
176
+ */
177
+ _relations?: Record<string, SerializedContentItemWithType[]>;
178
+ }
179
+ /** Output shape for CMS reads; omission retains the full editor envelope. */
180
+ type CMSReadView = "content" | undefined;
181
+ /** Literal top-level property names selected from parsed content. */
182
+ type CMSContentFields = readonly string[] | undefined;
183
+ /** Select known literal properties, or an honest partial shape for dynamic arrays. */
184
+ type CMSSelectedData<TData, TFields extends CMSContentFields = undefined> = TFields extends readonly string[] ? string extends TFields[number] ? Partial<TData> : Pick<TData, Extract<TFields[number], keyof TData>> : TData;
185
+ /** Optional output controls for CMS reads. These do not change access checks. */
186
+ interface CMSReadOptions<TView extends CMSReadView = CMSReadView, TFields extends CMSContentFields = CMSContentFields> {
187
+ /** Return identity, parsed content and timestamps without editor metadata. */
188
+ view?: TView;
189
+ /** Literal declared top-level content properties. Requires content view. */
190
+ fields?: TFields;
191
+ }
192
+ /** Serialized content envelope without raw JSON or editor metadata. */
193
+ type SerializedContentItemContent<TData = Record<string, unknown>> = Pick<SerializedContentItemWithType<TData>, "id" | "slug" | "parsedData" | "createdAt" | "updatedAt">;
194
+ /** Result envelope selected by a statically known read view. */
195
+ type CMSContentItemResult<TData = Record<string, unknown>, TView extends CMSReadView = undefined, TFields extends CMSContentFields = undefined> = TView extends "content" ? SerializedContentItemContent<CMSSelectedData<TData, TFields>> : SerializedContentItemWithType<TData>;
196
+ /**
197
+ * Paginated list response for content items
198
+ * @template TData - The type of the parsed data (defaults to Record<string, unknown>)
199
+ */
200
+ interface PaginatedContentItems<TData = Record<string, unknown>, TView extends CMSReadView = undefined, TFields extends CMSContentFields = undefined> {
201
+ items: CMSContentItemResult<TData, TView, TFields>[];
202
+ total: number;
203
+ limit: number;
204
+ offset: number;
205
+ }
206
+ /**
207
+ * Type helper to define a map of content type slugs to their data types.
208
+ * Use with z.infer to get the type from your Zod schemas.
209
+ *
210
+ * @example
211
+ * ```typescript
212
+ * import { z } from "zod"
213
+ *
214
+ * // Define your schemas
215
+ * export const ProductSchema = z.object({
216
+ * name: z.string(),
217
+ * price: z.number(),
218
+ * })
219
+ *
220
+ * export const TestimonialSchema = z.object({
221
+ * author: z.string(),
222
+ * quote: z.string(),
223
+ * })
224
+ *
225
+ * // Create the type map
226
+ * export type MyCMSTypes = {
227
+ * product: z.infer<typeof ProductSchema>
228
+ * testimonial: z.infer<typeof TestimonialSchema>
229
+ * }
230
+ *
231
+ * // Use in hooks for type-safe parsedData
232
+ * const { items } = useContent<MyCMSTypes, "product">("product")
233
+ * // items[0].parsedData.name is typed as string
234
+ * // items[0].parsedData.price is typed as number
235
+ * ```
236
+ */
237
+ type CMSContentTypeMap = Record<string, Record<string, unknown>>;
238
+ /** JSON-safe values accepted at the CMS operation boundary. */
239
+ type CMSOperationData = string | number | boolean | null | readonly CMSOperationData[] | {
240
+ readonly [key: string]: CMSOperationData;
241
+ };
242
+ /** @deprecated Use the operation-specific CMS lifecycle contexts instead. */
243
+ interface CMSHookContext {
244
+ readonly typeSlug: string;
245
+ readonly userId?: string;
246
+ readonly headers?: Headers;
247
+ }
248
+ interface CMSOperationIdentity {
249
+ readonly id: string;
250
+ readonly [key: string]: unknown;
251
+ }
252
+ type CMSDeepReadonly<T> = T extends (...args: any[]) => unknown ? T : T extends readonly unknown[] ? {
253
+ readonly [TKey in keyof T]: CMSDeepReadonly<T[TKey]>;
254
+ } : T extends object ? {
255
+ readonly [TKey in keyof T]: CMSDeepReadonly<T[TKey]>;
256
+ } : T;
257
+ interface CMSOperationContextBase<TInput, TFacts> {
258
+ /** Validated, deeply readonly input supplied to the operation. */
259
+ readonly input: Readonly<TInput>;
260
+ /** Trusted server-derived facts used by the authorization rule. */
261
+ readonly facts: Readonly<TFacts>;
262
+ /** Authorized request identity, or `null` for anonymous and trusted calls. */
263
+ readonly identity: Readonly<CMSOperationIdentity> | null;
264
+ /** Transport request when the operation was invoked through HTTP or `forRequest()`. */
265
+ readonly request?: Request;
266
+ /** Compatibility view of the transport request headers. */
267
+ readonly headers?: Headers;
268
+ /** Compatibility alias for the authorized identity ID. */
269
+ readonly userId?: string;
270
+ /** Compatibility alias for the operation's CMS content-type slug. */
271
+ readonly typeSlug?: string;
272
+ }
273
+ /** Authorized context supplied before a CMS record is created. */
274
+ interface CMSCreateOperationContext extends CMSOperationContextBase<{
275
+ readonly typeSlug: string;
276
+ readonly body: {
277
+ readonly slug: string;
278
+ readonly data: Readonly<Record<string, CMSOperationData>>;
279
+ };
280
+ }, {
281
+ contentType: string;
282
+ }> {
283
+ /** Server-resolved content-type slug for the record being created. */
284
+ readonly typeSlug: string;
285
+ /** Validated create payload supplied to the operation. */
286
+ readonly body: {
287
+ /** Requested record slug before canonical slugification. */
288
+ readonly slug: string;
289
+ /** Validated JSON-safe request fields, including any inline `_new` relation values. */
290
+ readonly data: Readonly<Record<string, CMSOperationData>>;
291
+ };
292
+ }
293
+ /** CMS create context after execution. */
294
+ interface CMSCreateResultContext extends CMSCreateOperationContext {
295
+ /** Fully serialized record returned by the successful create operation. */
296
+ readonly result: CMSDeepReadonly<SerializedContentItemWithType>;
297
+ }
298
+ /** Authorized context supplied before a CMS record is updated. */
299
+ interface CMSUpdateOperationContext extends CMSOperationContextBase<{
300
+ readonly typeSlug: string;
301
+ readonly id: string;
302
+ readonly body: {
303
+ readonly slug?: string;
304
+ readonly data?: Readonly<Record<string, CMSOperationData>>;
305
+ };
306
+ }, {
307
+ contentType: string;
308
+ recordId: string;
309
+ authorId?: string;
310
+ }> {
311
+ /** Server-resolved content-type slug for the record being updated. */
312
+ readonly typeSlug: string;
313
+ /** Canonical route parameters for the authorized record. */
314
+ readonly params: {
315
+ readonly typeSlug: string;
316
+ readonly id: string;
317
+ };
318
+ /** Validated partial update payload supplied to the operation. */
319
+ readonly body: {
320
+ /** Optional replacement slug before canonical slugification. */
321
+ readonly slug?: string;
322
+ /** Optional record-field fragment; hooks receive its merged validated result. */
323
+ readonly data?: Readonly<Record<string, CMSOperationData>>;
324
+ };
325
+ }
326
+ /** CMS update context after execution. */
327
+ interface CMSUpdateResultContext extends CMSUpdateOperationContext {
328
+ /** Fully serialized record returned by the successful update operation. */
329
+ readonly result: CMSDeepReadonly<SerializedContentItemWithType>;
330
+ }
331
+ /** Authorized context supplied before a CMS record is deleted. */
332
+ interface CMSDeleteOperationContext extends CMSOperationContextBase<{
333
+ readonly typeSlug: string;
334
+ readonly id: string;
335
+ }, {
336
+ contentType: string;
337
+ recordId: string;
338
+ authorId?: string;
339
+ }> {
340
+ /** Server-resolved content-type slug for the record being deleted. */
341
+ readonly typeSlug: string;
342
+ /** Canonical route parameters for the authorized record. */
343
+ readonly params: {
344
+ readonly typeSlug: string;
345
+ readonly id: string;
346
+ };
347
+ }
348
+ /** CMS delete context after execution. */
349
+ interface CMSDeleteResultContext extends CMSDeleteOperationContext {
350
+ /** Successful delete result. */
351
+ readonly result: {
352
+ readonly success: true;
353
+ };
354
+ }
355
+ interface CMSReadOperationContext extends CMSOperationContextBase<unknown, {
356
+ contentType?: string;
357
+ scope?: "collection" | "record";
358
+ recordId?: string;
359
+ authorId?: string;
360
+ }> {
361
+ }
362
+ /** Authorized context shared by the CMS operation lifecycle hooks. */
363
+ type CMSOperationLifecycleContext = CMSReadOperationContext | CMSCreateOperationContext | CMSUpdateOperationContext | CMSDeleteOperationContext;
364
+ /** Authorized lifecycle context supplied to the observational error hook. */
365
+ type CMSOperationErrorContext = CMSOperationLifecycleContext & {
366
+ /** Original post-authorization operation error observed by `onErrorExecuteContentOperation`. */
367
+ readonly error: unknown;
368
+ };
369
+ /** Domain lifecycle hooks that run only after successful CMS authorization. */
370
+ interface CMSBackendHooks {
371
+ /** Runs with validated canonical record data after authorization and before any database write. */
372
+ onBeforeCreateContent?: (data: Readonly<Record<string, CMSOperationData>>, context: CMSCreateOperationContext) => Promise<void> | void;
373
+ /** Observes the fully created record after the atomic create succeeds. */
374
+ onAfterCreateContent?: (item: CMSDeepReadonly<SerializedContentItemWithType>, context: CMSCreateResultContext) => Promise<void> | void;
375
+ /** Runs for every update with the complete merged, validated record before any database write. */
376
+ onBeforeUpdateContent?: (id: string, data: Readonly<Record<string, CMSOperationData>>, context: CMSUpdateOperationContext) => Promise<void> | void;
377
+ /** Observes the fully updated record after the atomic update succeeds. */
378
+ onAfterUpdateContent?: (item: CMSDeepReadonly<SerializedContentItemWithType>, context: CMSUpdateResultContext) => Promise<void> | void;
379
+ /** Runs after authorization and before the record is deleted. */
380
+ onBeforeDeleteContent?: (id: string, context: CMSDeleteOperationContext) => Promise<void> | void;
381
+ /** Observes the deleted record ID after the atomic delete succeeds. */
382
+ onAfterDeleteContent?: (id: string, context: CMSDeleteResultContext) => Promise<void> | void;
383
+ /** Observes post-authorization failures without replacing the original error. */
384
+ onErrorExecuteContentOperation?: (error: Error, operation: "create" | "update" | "delete" | "list" | "get", context: CMSOperationErrorContext) => Promise<void> | void;
385
+ }
386
+ /**
387
+ * Configuration for the CMS backend plugin
388
+ */
389
+ interface CMSBackendConfig {
390
+ /** Content types defined by the developer */
391
+ contentTypes: ContentTypeConfig[];
392
+ /** Optional hooks for customizing behavior */
393
+ hooks?: CMSBackendHooks;
394
+ /**
395
+ * Maximum number of items that can be requested in a single page.
396
+ * Applied to all list endpoints (`/content/:typeSlug`, by-relation, and inverse-relation).
397
+ *
398
+ * Raise this when your content types have many items and you need to fetch
399
+ * large pages (e.g. for SSG, CSV export, or programmatic access).
400
+ *
401
+ * @default 1000
402
+ */
403
+ maxPageSize?: number;
404
+ }
405
+
406
+ export type { CMSBackendConfig, CMSBackendHooks, CMSContentFields, CMSContentItemResult, CMSContentTypeMap, CMSCreateOperationContext, CMSCreateResultContext, CMSDeleteOperationContext, CMSDeleteResultContext, CMSHookContext, CMSOperationData, CMSOperationErrorContext, CMSOperationLifecycleContext, CMSReadOptions, CMSReadView, CMSSelectedData, CMSUpdateOperationContext, CMSUpdateResultContext, ContentItem, ContentItemWithType, ContentRelation, ContentType, ContentTypeConfig, InverseRelation, PaginatedContentItems, RelationConfig, RelationValue, SerializedContentItem, SerializedContentItemContent, SerializedContentItemWithType, SerializedContentType };