@nuasite/cms-marker 0.0.66 → 0.0.69

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.
@@ -87,6 +87,8 @@ export interface ColorClasses {
87
87
  hoverBg?: string;
88
88
  /** Hover text color class (e.g., 'hover:text-gray-100') */
89
89
  hoverText?: string;
90
+ /** Hover border color class (e.g., 'hover:border-blue-700') */
91
+ hoverBorder?: string;
90
92
  /** All color-related classes as found in the element */
91
93
  allColorClasses?: string[];
92
94
  }
@@ -182,6 +184,44 @@ export interface CollectionEntry {
182
184
  /** ID of the wrapper element containing the rendered markdown */
183
185
  wrapperId?: string;
184
186
  }
187
+ /** Field types for collection schema inference */
188
+ export type FieldType = 'text' | 'textarea' | 'date' | 'boolean' | 'number' | 'image' | 'url' | 'select' | 'array' | 'object' | 'reference';
189
+ /** Definition of a single field in a collection's schema */
190
+ export interface FieldDefinition {
191
+ /** Field name as it appears in frontmatter */
192
+ name: string;
193
+ /** Inferred or specified field type */
194
+ type: FieldType;
195
+ /** Whether the field is required (present in all entries) */
196
+ required: boolean;
197
+ /** Default value for the field */
198
+ defaultValue?: unknown;
199
+ /** Options for 'select' type fields */
200
+ options?: string[];
201
+ /** Item type for 'array' fields */
202
+ itemType?: FieldType;
203
+ /** Nested fields for 'object' type */
204
+ fields?: FieldDefinition[];
205
+ /** Sample values seen across entries */
206
+ examples?: unknown[];
207
+ }
208
+ /** Definition of a content collection with inferred schema */
209
+ export interface CollectionDefinition {
210
+ /** Collection identifier (directory name) */
211
+ name: string;
212
+ /** Human-readable label for the collection */
213
+ label: string;
214
+ /** Path to the collection directory */
215
+ path: string;
216
+ /** Number of entries in the collection */
217
+ entryCount: number;
218
+ /** Inferred field definitions */
219
+ fields: FieldDefinition[];
220
+ /** Whether the collection has draft support */
221
+ supportsDraft?: boolean;
222
+ /** File extension used by entries */
223
+ fileExtension: 'md' | 'mdx';
224
+ }
185
225
  /** Manifest metadata for versioning and conflict detection */
186
226
  export interface ManifestMetadata {
187
227
  /** Manifest schema version */
@@ -205,6 +245,8 @@ export interface CmsManifest {
205
245
  componentDefinitions: Record<string, ComponentDefinition>;
206
246
  /** Content collection entries indexed by "collectionName/slug" */
207
247
  collections?: Record<string, CollectionEntry>;
248
+ /** Collection definitions with inferred schemas */
249
+ collectionDefinitions?: Record<string, CollectionDefinition>;
208
250
  /** Available Tailwind colors from the project's config */
209
251
  availableColors?: AvailableColors;
210
252
  /** Available text styles from the project's Tailwind config */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "directory": "packages/cms-marker"
15
15
  },
16
16
  "license": "Apache-2.0",
17
- "version": "0.0.66",
17
+ "version": "0.0.69",
18
18
  "module": "src/index.ts",
19
19
  "types": "src/index.ts",
20
20
  "type": "module",
@@ -0,0 +1,325 @@
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import { getProjectRoot } from './config'
4
+ import type { CollectionDefinition, FieldDefinition, FieldType } from './types'
5
+
6
+ /** Regex patterns for type inference */
7
+ const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}/
8
+ const URL_PATTERN = /^(https?:\/\/|\/)/
9
+ const IMAGE_EXTENSIONS = /\.(jpg|jpeg|png|gif|webp|svg|avif)$/i
10
+
11
+ /** Maximum unique values before treating as free-form text instead of select */
12
+ const MAX_SELECT_OPTIONS = 10
13
+
14
+ /** Minimum length for textarea detection */
15
+ const TEXTAREA_MIN_LENGTH = 200
16
+
17
+ /**
18
+ * Observed values for a single field across multiple files
19
+ */
20
+ interface FieldObservation {
21
+ name: string
22
+ values: unknown[]
23
+ presentCount: number
24
+ totalEntries: number
25
+ }
26
+
27
+ /**
28
+ * Parse YAML frontmatter from markdown content
29
+ */
30
+ function parseFrontmatter(content: string): Record<string, unknown> | null {
31
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)
32
+ if (!match?.[1]) return null
33
+
34
+ const frontmatter: Record<string, unknown> = {}
35
+ const lines = match[1].split('\n')
36
+
37
+ let currentKey: string | null = null
38
+ let currentArrayItems: unknown[] = []
39
+ let isInArray = false
40
+
41
+ for (const line of lines) {
42
+ // Check for array item (starts with "- ")
43
+ const arrayMatch = line.match(/^(\s*)- (.*)$/)
44
+ if (arrayMatch && isInArray && arrayMatch[2] !== undefined) {
45
+ const value = parseYamlValue(arrayMatch[2].trim())
46
+ currentArrayItems.push(value)
47
+ continue
48
+ }
49
+
50
+ // Check for key-value pair
51
+ const kvMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)$/)
52
+ if (kvMatch && kvMatch[1] && kvMatch[2] !== undefined) {
53
+ // Save previous array if any
54
+ if (isInArray && currentKey) {
55
+ frontmatter[currentKey] = currentArrayItems
56
+ }
57
+
58
+ currentKey = kvMatch[1]
59
+ const rawValue = kvMatch[2].trim()
60
+
61
+ // Check if starting an array
62
+ if (rawValue === '' || rawValue === '[]') {
63
+ isInArray = true
64
+ currentArrayItems = []
65
+ } else if (rawValue.startsWith('[') && rawValue.endsWith(']')) {
66
+ // Inline array like [a, b, c]
67
+ const items = rawValue.slice(1, -1).split(',').map(s => parseYamlValue(s.trim()))
68
+ frontmatter[currentKey] = items
69
+ isInArray = false
70
+ } else {
71
+ frontmatter[currentKey] = parseYamlValue(rawValue)
72
+ isInArray = false
73
+ }
74
+ }
75
+ }
76
+
77
+ // Save final array if any
78
+ if (isInArray && currentKey) {
79
+ frontmatter[currentKey] = currentArrayItems
80
+ }
81
+
82
+ return frontmatter
83
+ }
84
+
85
+ /**
86
+ * Parse a YAML value into appropriate JS type
87
+ */
88
+ function parseYamlValue(value: string): unknown {
89
+ // Handle quoted strings
90
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
91
+ return value.slice(1, -1)
92
+ }
93
+
94
+ // Handle booleans
95
+ if (value === 'true') return true
96
+ if (value === 'false') return false
97
+
98
+ // Handle null/empty
99
+ if (value === '' || value === 'null' || value === '~') return null
100
+
101
+ // Handle numbers
102
+ const num = Number(value)
103
+ if (!isNaN(num) && value !== '') return num
104
+
105
+ return value
106
+ }
107
+
108
+ /**
109
+ * Infer the field type from a value
110
+ */
111
+ function inferFieldType(value: unknown, key: string): FieldType {
112
+ if (value === null || value === undefined) {
113
+ return 'text'
114
+ }
115
+
116
+ if (typeof value === 'boolean') {
117
+ return 'boolean'
118
+ }
119
+
120
+ if (typeof value === 'number') {
121
+ return 'number'
122
+ }
123
+
124
+ if (Array.isArray(value)) {
125
+ return 'array'
126
+ }
127
+
128
+ if (typeof value === 'object') {
129
+ return 'object'
130
+ }
131
+
132
+ if (typeof value === 'string') {
133
+ // Check for date pattern
134
+ if (DATE_PATTERN.test(value)) {
135
+ return 'date'
136
+ }
137
+
138
+ // Check for image paths
139
+ if (IMAGE_EXTENSIONS.test(value) || key.toLowerCase().includes('image') || key.toLowerCase().includes('thumbnail') || key.toLowerCase().includes('cover')) {
140
+ return 'image'
141
+ }
142
+
143
+ // Check for URLs
144
+ if (URL_PATTERN.test(value)) {
145
+ return 'url'
146
+ }
147
+
148
+ // Check for textarea (long text or contains newlines)
149
+ if (value.includes('\n') || value.length > TEXTAREA_MIN_LENGTH) {
150
+ return 'textarea'
151
+ }
152
+
153
+ return 'text'
154
+ }
155
+
156
+ return 'text'
157
+ }
158
+
159
+ /**
160
+ * Merge field observations from multiple files to determine final field definition
161
+ */
162
+ function mergeFieldObservations(observations: FieldObservation[]): FieldDefinition[] {
163
+ const fields: FieldDefinition[] = []
164
+
165
+ for (const obs of observations) {
166
+ const nonNullValues = obs.values.filter(v => v !== null && v !== undefined)
167
+ if (nonNullValues.length === 0) continue
168
+
169
+ // Determine type by consensus (most common inferred type)
170
+ const typeCounts = new Map<FieldType, number>()
171
+ for (const value of nonNullValues) {
172
+ const type = inferFieldType(value, obs.name)
173
+ typeCounts.set(type, (typeCounts.get(type) || 0) + 1)
174
+ }
175
+
176
+ // Get most common type
177
+ let fieldType: FieldType = 'text'
178
+ let maxCount = 0
179
+ for (const [type, count] of typeCounts) {
180
+ if (count > maxCount) {
181
+ maxCount = count
182
+ fieldType = type
183
+ }
184
+ }
185
+
186
+ const field: FieldDefinition = {
187
+ name: obs.name,
188
+ type: fieldType,
189
+ required: obs.presentCount === obs.totalEntries,
190
+ examples: nonNullValues.slice(0, 3),
191
+ }
192
+
193
+ // For text fields, check if we should treat as select (limited unique values)
194
+ if (fieldType === 'text') {
195
+ const uniqueValues = [...new Set(nonNullValues.map(v => String(v)))]
196
+ if (uniqueValues.length > 0 && uniqueValues.length <= MAX_SELECT_OPTIONS && nonNullValues.length >= 2) {
197
+ field.type = 'select'
198
+ field.options = uniqueValues.sort()
199
+ }
200
+ }
201
+
202
+ // For arrays, try to infer item type
203
+ if (fieldType === 'array') {
204
+ const allItems = nonNullValues.flatMap(v => (Array.isArray(v) ? v : []))
205
+ if (allItems.length > 0) {
206
+ const itemType = inferFieldType(allItems[0], obs.name)
207
+ field.itemType = itemType
208
+
209
+ // Check if array items should be select
210
+ if (itemType === 'text') {
211
+ const uniqueItems = [...new Set(allItems.map(v => String(v)))]
212
+ if (uniqueItems.length <= MAX_SELECT_OPTIONS * 2) {
213
+ field.options = uniqueItems.sort()
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ fields.push(field)
220
+ }
221
+
222
+ return fields
223
+ }
224
+
225
+ /**
226
+ * Scan a single collection directory and infer its schema
227
+ */
228
+ async function scanCollection(collectionPath: string, collectionName: string): Promise<CollectionDefinition | null> {
229
+ try {
230
+ const entries = await fs.readdir(collectionPath, { withFileTypes: true })
231
+ const markdownFiles = entries.filter(e => e.isFile() && (e.name.endsWith('.md') || e.name.endsWith('.mdx')))
232
+
233
+ if (markdownFiles.length === 0) return null
234
+
235
+ // Determine file extension (prefer md, use mdx if that's all we have)
236
+ const hasMd = markdownFiles.some(f => f.name.endsWith('.md'))
237
+ const fileExtension: 'md' | 'mdx' = hasMd ? 'md' : 'mdx'
238
+
239
+ // Collect field observations across all files
240
+ const fieldMap = new Map<string, FieldObservation>()
241
+ let hasDraft = false
242
+
243
+ for (const file of markdownFiles) {
244
+ const filePath = path.join(collectionPath, file.name)
245
+ const content = await fs.readFile(filePath, 'utf-8')
246
+ const frontmatter = parseFrontmatter(content)
247
+
248
+ if (!frontmatter) continue
249
+
250
+ for (const [key, value] of Object.entries(frontmatter)) {
251
+ if (key === 'draft' && typeof value === 'boolean') {
252
+ hasDraft = true
253
+ }
254
+
255
+ let obs = fieldMap.get(key)
256
+ if (!obs) {
257
+ obs = {
258
+ name: key,
259
+ values: [],
260
+ presentCount: 0,
261
+ totalEntries: markdownFiles.length,
262
+ }
263
+ fieldMap.set(key, obs)
264
+ }
265
+
266
+ obs.values.push(value)
267
+ obs.presentCount++
268
+ }
269
+ }
270
+
271
+ // Update totalEntries for all observations
272
+ for (const obs of fieldMap.values()) {
273
+ obs.totalEntries = markdownFiles.length
274
+ }
275
+
276
+ const fields = mergeFieldObservations(Array.from(fieldMap.values()))
277
+
278
+ // Generate a human-readable label
279
+ const label = collectionName
280
+ .replace(/[-_]/g, ' ')
281
+ .replace(/\b\w/g, c => c.toUpperCase())
282
+
283
+ return {
284
+ name: collectionName,
285
+ label,
286
+ path: collectionPath,
287
+ entryCount: markdownFiles.length,
288
+ fields,
289
+ supportsDraft: hasDraft,
290
+ fileExtension,
291
+ }
292
+ } catch {
293
+ return null
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Scan all collections in the content directory
299
+ */
300
+ export async function scanCollections(contentDir: string = 'src/content'): Promise<Record<string, CollectionDefinition>> {
301
+ const projectRoot = getProjectRoot()
302
+ const fullContentDir = path.isAbsolute(contentDir) ? contentDir : path.join(projectRoot, contentDir)
303
+
304
+ const collections: Record<string, CollectionDefinition> = {}
305
+
306
+ try {
307
+ const entries = await fs.readdir(fullContentDir, { withFileTypes: true })
308
+
309
+ const scanPromises = entries
310
+ .filter(entry => entry.isDirectory() && !entry.name.startsWith('_') && !entry.name.startsWith('.'))
311
+ .map(async entry => {
312
+ const collectionPath = path.join(fullContentDir, entry.name)
313
+ const definition = await scanCollection(collectionPath, entry.name)
314
+ if (definition) {
315
+ collections[entry.name] = definition
316
+ }
317
+ })
318
+
319
+ await Promise.all(scanPromises)
320
+ } catch {
321
+ // Content directory doesn't exist or isn't readable
322
+ }
323
+
324
+ return collections
325
+ }
@@ -35,19 +35,21 @@ export function createDevMiddleware(
35
35
  componentDefinitions: Record<string, ComponentDefinition>,
36
36
  idCounter: { value: number },
37
37
  ) {
38
- // Serve global CMS manifest (component definitions, available colors, and settings)
38
+ // Serve global CMS manifest (component definitions, available colors, collection definitions, and settings)
39
39
  server.middlewares.use((req, res, next) => {
40
40
  if (req.url === '/cms-manifest.json') {
41
41
  res.setHeader('Content-Type', 'application/json')
42
42
  res.setHeader('Access-Control-Allow-Origin', '*')
43
- res.end(JSON.stringify(
44
- {
45
- componentDefinitions,
46
- availableColors: manifestWriter.getAvailableColors(),
47
- },
48
- null,
49
- 2,
50
- ))
43
+ const manifest: Record<string, unknown> = {
44
+ componentDefinitions,
45
+ availableColors: manifestWriter.getAvailableColors(),
46
+ availableTextStyles: manifestWriter.getAvailableTextStyles(),
47
+ }
48
+ const collectionDefs = manifestWriter.getCollectionDefinitions()
49
+ if (Object.keys(collectionDefs).length > 0) {
50
+ manifest.collectionDefinitions = collectionDefs
51
+ }
52
+ res.end(JSON.stringify(manifest, null, 2))
51
53
  return
52
54
  }
53
55
  next()
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { AstroIntegration } from 'astro'
2
2
  import { processBuildOutput } from './build-processor'
3
+ import { scanCollections } from './collection-scanner'
3
4
  import { ComponentRegistry } from './component-registry'
4
5
  import { resetProjectRoot } from './config'
5
6
  import { createDevMiddleware } from './dev-middleware'
@@ -67,6 +68,15 @@ export default function cmsMarker(options: CmsMarkerOptions = {}): AstroIntegrat
67
68
  }
68
69
  }
69
70
 
71
+ // Scan content collections for schema inference
72
+ const collectionDefinitions = await scanCollections(contentDir)
73
+ manifestWriter.setCollectionDefinitions(collectionDefinitions)
74
+
75
+ const collectionCount = Object.keys(collectionDefinitions).length
76
+ if (collectionCount > 0) {
77
+ logger.info(`Found ${collectionCount} content collection(s)`)
78
+ }
79
+
70
80
  // Create Vite plugin context
71
81
  const pluginContext = {
72
82
  manifestWriter,
@@ -110,6 +120,8 @@ export default function cmsMarker(options: CmsMarkerOptions = {}): AstroIntegrat
110
120
 
111
121
  // Re-export config functions for testing
112
122
  export { getProjectRoot, resetProjectRoot, setProjectRoot } from './config'
123
+ // Re-export collection scanner
124
+ export { scanCollections } from './collection-scanner'
113
125
  // Re-export types for consumers
114
126
  export type { CollectionInfo, MarkdownContent, SourceLocation, VariableReference } from './source-finder'
115
127
  export { findCollectionSource, parseMarkdownContent } from './source-finder'
@@ -6,6 +6,7 @@ import type {
6
6
  AvailableColors,
7
7
  AvailableTextStyles,
8
8
  CmsManifest,
9
+ CollectionDefinition,
9
10
  CollectionEntry,
10
11
  ComponentDefinition,
11
12
  ComponentInstance,
@@ -31,6 +32,7 @@ export class ManifestWriter {
31
32
  private outDir: string = ''
32
33
  private manifestFile: string
33
34
  private componentDefinitions: Record<string, ComponentDefinition>
35
+ private collectionDefinitions: Record<string, CollectionDefinition> = {}
34
36
  private availableColors: AvailableColors | undefined
35
37
  private availableTextStyles: AvailableTextStyles | undefined
36
38
  private writeQueue: Promise<void> = Promise.resolve()
@@ -43,6 +45,7 @@ export class ManifestWriter {
43
45
  components: {},
44
46
  componentDefinitions,
45
47
  collections: {},
48
+ collectionDefinitions: {},
46
49
  }
47
50
  }
48
51
 
@@ -87,6 +90,21 @@ export class ManifestWriter {
87
90
  this.globalManifest.availableTextStyles = textStyles
88
91
  }
89
92
 
93
+ /**
94
+ * Set collection definitions (inferred schemas for content collections)
95
+ */
96
+ setCollectionDefinitions(definitions: Record<string, CollectionDefinition>): void {
97
+ this.collectionDefinitions = definitions
98
+ this.globalManifest.collectionDefinitions = definitions
99
+ }
100
+
101
+ /**
102
+ * Get collection definitions
103
+ */
104
+ getCollectionDefinitions(): Record<string, CollectionDefinition> {
105
+ return this.collectionDefinitions
106
+ }
107
+
90
108
  /**
91
109
  * Get the manifest path for a given page
92
110
  * Places manifest next to the page: /about -> /about.json, / -> /index.json
@@ -182,16 +200,20 @@ export class ManifestWriter {
182
200
  // Wait for all queued writes to complete
183
201
  await this.writeQueue
184
202
 
185
- // Write global manifest with settings (component definitions, colors, and text styles)
203
+ // Write global manifest with settings (component definitions, colors, text styles, and collection definitions)
186
204
  if (this.outDir) {
187
205
  const globalManifestPath = path.join(this.outDir, this.manifestFile)
188
206
  const globalSettings: {
189
207
  componentDefinitions: Record<string, ComponentDefinition>
208
+ collectionDefinitions?: Record<string, CollectionDefinition>
190
209
  availableColors?: AvailableColors
191
210
  availableTextStyles?: AvailableTextStyles
192
211
  } = {
193
212
  componentDefinitions: this.componentDefinitions,
194
213
  }
214
+ if (Object.keys(this.collectionDefinitions).length > 0) {
215
+ globalSettings.collectionDefinitions = this.collectionDefinitions
216
+ }
195
217
  if (this.availableColors) {
196
218
  globalSettings.availableColors = this.availableColors
197
219
  }
@@ -240,6 +262,7 @@ export class ManifestWriter {
240
262
  components: {},
241
263
  componentDefinitions: this.componentDefinitions,
242
264
  collections: {},
265
+ collectionDefinitions: this.collectionDefinitions,
243
266
  availableColors: this.availableColors,
244
267
  availableTextStyles: this.availableTextStyles,
245
268
  }
@@ -413,6 +413,7 @@ const COLOR_CLASS_PATTERNS = {
413
413
  border: buildColorPattern('border'),
414
414
  hoverBg: buildColorPattern('hover:bg'),
415
415
  hoverText: buildColorPattern('hover:text'),
416
+ hoverBorder: buildColorPattern('hover:border'),
416
417
  }
417
418
 
418
419
  /**
package/src/types.ts CHANGED
@@ -91,6 +91,8 @@ export interface ColorClasses {
91
91
  hoverBg?: string
92
92
  /** Hover text color class (e.g., 'hover:text-gray-100') */
93
93
  hoverText?: string
94
+ /** Hover border color class (e.g., 'hover:border-blue-700') */
95
+ hoverBorder?: string
94
96
  /** All color-related classes as found in the element */
95
97
  allColorClasses?: string[]
96
98
  }
@@ -193,6 +195,58 @@ export interface CollectionEntry {
193
195
  wrapperId?: string
194
196
  }
195
197
 
198
+ /** Field types for collection schema inference */
199
+ export type FieldType =
200
+ | 'text'
201
+ | 'textarea'
202
+ | 'date'
203
+ | 'boolean'
204
+ | 'number'
205
+ | 'image'
206
+ | 'url'
207
+ | 'select'
208
+ | 'array'
209
+ | 'object'
210
+ | 'reference'
211
+
212
+ /** Definition of a single field in a collection's schema */
213
+ export interface FieldDefinition {
214
+ /** Field name as it appears in frontmatter */
215
+ name: string
216
+ /** Inferred or specified field type */
217
+ type: FieldType
218
+ /** Whether the field is required (present in all entries) */
219
+ required: boolean
220
+ /** Default value for the field */
221
+ defaultValue?: unknown
222
+ /** Options for 'select' type fields */
223
+ options?: string[]
224
+ /** Item type for 'array' fields */
225
+ itemType?: FieldType
226
+ /** Nested fields for 'object' type */
227
+ fields?: FieldDefinition[]
228
+ /** Sample values seen across entries */
229
+ examples?: unknown[]
230
+ }
231
+
232
+ /** Definition of a content collection with inferred schema */
233
+ export interface CollectionDefinition {
234
+ /** Collection identifier (directory name) */
235
+ name: string
236
+ /** Human-readable label for the collection */
237
+ label: string
238
+ /** Path to the collection directory */
239
+ path: string
240
+ /** Number of entries in the collection */
241
+ entryCount: number
242
+ /** Inferred field definitions */
243
+ fields: FieldDefinition[]
244
+ /** Whether the collection has draft support */
245
+ supportsDraft?: boolean
246
+ /** File extension used by entries */
247
+ fileExtension: 'md' | 'mdx'
248
+ }
249
+
196
250
  /** Manifest metadata for versioning and conflict detection */
197
251
  export interface ManifestMetadata {
198
252
  /** Manifest schema version */
@@ -217,6 +271,8 @@ export interface CmsManifest {
217
271
  componentDefinitions: Record<string, ComponentDefinition>
218
272
  /** Content collection entries indexed by "collectionName/slug" */
219
273
  collections?: Record<string, CollectionEntry>
274
+ /** Collection definitions with inferred schemas */
275
+ collectionDefinitions?: Record<string, CollectionDefinition>
220
276
  /** Available Tailwind colors from the project's config */
221
277
  availableColors?: AvailableColors
222
278
  /** Available text styles from the project's Tailwind config */