multisite-cms-mcp 1.2.4 → 1.3.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.
@@ -1,8 +1,14 @@
1
1
  /**
2
2
  * Fetches the complete schema for a specific project including:
3
- * - Built-in collections with their standard fields
4
- * - Custom fields added to built-in collections
5
- * - Custom collections with all their fields
3
+ * - All custom collections with their fields
4
+ * - Field tokens, types, and descriptions for AI context
5
+ * - Template patterns and example usage
6
+ *
7
+ * Each field includes:
8
+ * - Field Name (display name)
9
+ * - Token (the exact syntax to use in templates)
10
+ * - Type (text, richText, image, etc.)
11
+ * - Description (help text for context)
6
12
  *
7
13
  * Uses stored credentials or triggers device flow for authentication.
8
14
  *
@@ -1 +1 @@
1
- {"version":3,"file":"get-tenant-schema.d.ts","sourceRoot":"","sources":["../../src/tools/get-tenant-schema.ts"],"names":[],"mappings":"AA2EA;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmFxE"}
1
+ {"version":3,"file":"get-tenant-schema.d.ts","sourceRoot":"","sources":["../../src/tools/get-tenant-schema.ts"],"names":[],"mappings":"AA2EA;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmFxE"}
@@ -38,9 +38,15 @@ async function resolveProjectId(projectIdentifier) {
38
38
  }
39
39
  /**
40
40
  * Fetches the complete schema for a specific project including:
41
- * - Built-in collections with their standard fields
42
- * - Custom fields added to built-in collections
43
- * - Custom collections with all their fields
41
+ * - All custom collections with their fields
42
+ * - Field tokens, types, and descriptions for AI context
43
+ * - Template patterns and example usage
44
+ *
45
+ * Each field includes:
46
+ * - Field Name (display name)
47
+ * - Token (the exact syntax to use in templates)
48
+ * - Type (text, richText, image, etc.)
49
+ * - Description (help text for context)
44
50
  *
45
51
  * Uses stored credentials or triggers device flow for authentication.
46
52
  *
@@ -137,7 +143,13 @@ The API returned a successful response but the prompt data was missing.
137
143
 
138
144
  **Project ID:** \`${tenantId}\`
139
145
 
140
- The following schema is specific to this project and includes all custom collections and custom fields.
146
+ This schema is specific to this project and includes all collections with their fields.
147
+
148
+ **Field Table Format:**
149
+ - **Field Name** - Display name for the field
150
+ - **Token** - The exact token to use in templates (use triple braces for richText)
151
+ - **Type** - Field type (text, richText, image, url, boolean, number, date, select, relation)
152
+ - **Description** - Help text explaining what the field is for
141
153
 
142
154
  ---
143
155
 
@@ -23,7 +23,7 @@ interface CollectionToCreate {
23
23
  }
24
24
  interface FieldsToAdd {
25
25
  collectionSlug: string;
26
- isBuiltin: boolean;
26
+ isBuiltin?: boolean;
27
27
  fields: FieldToCreate[];
28
28
  }
29
29
  interface SyncSchemaInput {
@@ -1 +1 @@
1
- {"version":3,"file":"sync-schema.d.ts","sourceRoot":"","sources":["../../src/tools/sync-schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,UAAU,WAAW;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;CAC7B;AAyKD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CA8UxE"}
1
+ {"version":3,"file":"sync-schema.d.ts","sourceRoot":"","sources":["../../src/tools/sync-schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,UAAU,WAAW;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;CAC7B;AAgJD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAmSxE"}
@@ -11,7 +11,6 @@ const api_client_1 = require("../lib/api-client");
11
11
  const device_flow_1 = require("../lib/device-flow");
12
12
  const get_field_types_1 = require("./get-field-types");
13
13
  // ============ Constants ============
14
- const BUILTIN_COLLECTION_TYPES = ['blogs', 'authors', 'team', 'downloads'];
15
14
  const VALID_FIELD_TYPES = get_field_types_1.AVAILABLE_FIELD_TYPES.map(ft => ft.value);
16
15
  const AUTH_REQUIRED_MESSAGE = `# Authentication Required
17
16
 
@@ -95,26 +94,14 @@ async function resolveProjectId(projectIdentifier) {
95
94
  };
96
95
  }
97
96
  /**
98
- * Fetch existing schema for a project
97
+ * Fetch existing collections for a project
99
98
  */
100
- async function fetchExistingSchema(tenantId) {
101
- // Fetch custom collections
99
+ async function fetchExistingCollections(tenantId) {
102
100
  const collectionsRes = await (0, api_client_1.apiRequest)('/api/collections', { tenantId });
103
101
  if ((0, api_client_1.isApiError)(collectionsRes)) {
104
102
  return { error: `Failed to fetch collections: ${collectionsRes.error}` };
105
103
  }
106
- // Fetch builtin collection fields
107
- const builtinRes = await (0, api_client_1.apiRequest)('/api/cms/builtin-collections', { tenantId });
108
- const builtinFields = {};
109
- if (!(0, api_client_1.isApiError)(builtinRes)) {
110
- for (const bc of builtinRes.data) {
111
- builtinFields[bc.type] = bc.fields.map(f => ({ slug: f.slug, name: f.name, type: f.type }));
112
- }
113
- }
114
- return {
115
- collections: collectionsRes.data,
116
- builtinFields,
117
- };
104
+ return collectionsRes.data;
118
105
  }
119
106
  // ============ Main Function ============
120
107
  /**
@@ -172,8 +159,9 @@ Use \`get_field_types\` to see available field types.
172
159
  allValidationErrors.push(`fieldsToAdd entry missing collectionSlug`);
173
160
  continue;
174
161
  }
175
- if (group.isBuiltin && !BUILTIN_COLLECTION_TYPES.includes(group.collectionSlug)) {
176
- allValidationErrors.push(`"${group.collectionSlug}" is not a valid builtin collection. Valid: ${BUILTIN_COLLECTION_TYPES.join(', ')}`);
162
+ // Note: isBuiltin is deprecated and ignored - all collections are now custom
163
+ if (group.isBuiltin) {
164
+ allValidationErrors.push(`isBuiltin is no longer supported. All collections are custom collections. Use the collection slug directly.`);
177
165
  }
178
166
  if (!group.fields || group.fields.length === 0) {
179
167
  allValidationErrors.push(`fieldsToAdd for "${group.collectionSlug}" has no fields`);
@@ -204,27 +192,26 @@ ${resolved.error}
204
192
  `;
205
193
  }
206
194
  const { tenantId } = resolved;
207
- // Fetch existing schema
208
- const existingSchema = await fetchExistingSchema(tenantId);
209
- if ('error' in existingSchema) {
195
+ // Fetch existing collections
196
+ const existingResult = await fetchExistingCollections(tenantId);
197
+ if ('error' in existingResult) {
210
198
  // Check if auth error
211
- if (existingSchema.error.includes('401') || existingSchema.error.includes('auth')) {
199
+ if (existingResult.error.includes('401') || existingResult.error.includes('auth')) {
212
200
  const authResult = await (0, device_flow_1.ensureAuthenticated)();
213
201
  if (!authResult.authenticated) {
214
202
  return AUTH_REQUIRED_MESSAGE;
215
203
  }
216
204
  // Retry
217
- const retry = await fetchExistingSchema(tenantId);
205
+ const retry = await fetchExistingCollections(tenantId);
218
206
  if ('error' in retry) {
219
207
  return `# Error\n\n${retry.error}`;
220
208
  }
221
- Object.assign(existingSchema, retry);
222
209
  }
223
210
  else {
224
- return `# Error\n\n${existingSchema.error}`;
211
+ return `# Error\n\n${existingResult.error}`;
225
212
  }
226
213
  }
227
- const { collections: existingCollections, builtinFields } = existingSchema;
214
+ const existingCollections = Array.isArray(existingResult) ? existingResult : [];
228
215
  // Track results
229
216
  const results = [];
230
217
  const created = { collections: 0, fields: 0 };
@@ -235,14 +222,14 @@ ${resolved.error}
235
222
  // Check if collection already exists
236
223
  const existing = existingCollections.find(c => c.slug.toLowerCase() === col.slug.toLowerCase());
237
224
  if (existing) {
238
- results.push(`⏭️ Skipped collection "${col.slug}" (already exists)`);
225
+ results.push(`Skipped collection "${col.slug}" (already exists)`);
239
226
  skipped.collections++;
240
227
  // But still try to add any new fields
241
228
  if (col.fields && col.fields.length > 0) {
242
229
  for (const field of col.fields) {
243
230
  const fieldExists = existing.fields.some(f => f.slug.toLowerCase() === field.slug.toLowerCase());
244
231
  if (fieldExists) {
245
- results.push(` ⏭️ Skipped field "${field.slug}" (already exists)`);
232
+ results.push(` Skipped field "${field.slug}" (already exists)`);
246
233
  skipped.fields++;
247
234
  }
248
235
  else {
@@ -261,10 +248,10 @@ ${resolved.error}
261
248
  },
262
249
  });
263
250
  if ((0, api_client_1.isApiError)(fieldRes)) {
264
- results.push(` Failed to add field "${field.slug}": ${fieldRes.error}`);
251
+ results.push(` Failed to add field "${field.slug}": ${fieldRes.error}`);
265
252
  }
266
253
  else {
267
- results.push(` Added field "${field.slug}" (${field.type})`);
254
+ results.push(` Added field "${field.slug}" (${field.type})`);
268
255
  created.fields++;
269
256
  }
270
257
  }
@@ -285,10 +272,10 @@ ${resolved.error}
285
272
  },
286
273
  });
287
274
  if ((0, api_client_1.isApiError)(createRes)) {
288
- results.push(`❌ Failed to create collection "${col.slug}": ${createRes.error}`);
275
+ results.push(`Failed to create collection "${col.slug}": ${createRes.error}`);
289
276
  continue;
290
277
  }
291
- results.push(`✅ Created collection "${col.name}" (${col.slug})`);
278
+ results.push(`Created collection "${col.name}" (${col.slug})`);
292
279
  created.collections++;
293
280
  // Add fields to the new collection
294
281
  if (col.fields && col.fields.length > 0) {
@@ -308,10 +295,10 @@ ${resolved.error}
308
295
  },
309
296
  });
310
297
  if ((0, api_client_1.isApiError)(fieldRes)) {
311
- results.push(` Failed to add field "${field.slug}": ${fieldRes.error}`);
298
+ results.push(` Failed to add field "${field.slug}": ${fieldRes.error}`);
312
299
  }
313
300
  else {
314
- results.push(` Added field "${field.slug}" (${field.type})`);
301
+ results.push(` Added field "${field.slug}" (${field.type})`);
315
302
  created.fields++;
316
303
  }
317
304
  }
@@ -321,72 +308,39 @@ ${resolved.error}
321
308
  // Add fields to existing collections
322
309
  if (fieldsToAdd && fieldsToAdd.length > 0) {
323
310
  for (const group of fieldsToAdd) {
324
- results.push(`\n📁 ${group.collectionSlug} (${group.isBuiltin ? 'builtin' : 'custom'}):`);
325
- if (group.isBuiltin) {
326
- // Add to builtin collection
327
- const existingBuiltinFields = builtinFields[group.collectionSlug] || [];
328
- for (const field of group.fields) {
329
- const fieldExists = existingBuiltinFields.some(f => f.slug.toLowerCase() === field.slug.toLowerCase());
330
- if (fieldExists) {
331
- results.push(` ⏭️ Skipped field "${field.slug}" (already exists)`);
332
- skipped.fields++;
333
- continue;
334
- }
335
- const fieldRes = await (0, api_client_1.apiRequest)(`/api/cms/builtin-collections/${group.collectionSlug}/fields`, {
336
- tenantId,
337
- method: 'POST',
338
- body: {
339
- slug: field.slug,
340
- name: field.name,
341
- type: field.type,
342
- description: field.description,
343
- isRequired: field.isRequired,
344
- options: field.options,
345
- },
346
- });
347
- if ((0, api_client_1.isApiError)(fieldRes)) {
348
- results.push(` ❌ Failed to add field "${field.slug}": ${fieldRes.error}`);
349
- }
350
- else {
351
- results.push(` ✅ Added field "${field.slug}" (${field.type})`);
352
- created.fields++;
353
- }
354
- }
311
+ results.push(`\nCollection ${group.collectionSlug}:`);
312
+ // All collections are custom collections now
313
+ const customCollection = existingCollections.find(c => c.slug.toLowerCase() === group.collectionSlug.toLowerCase());
314
+ if (!customCollection) {
315
+ results.push(` Collection "${group.collectionSlug}" not found. Create it first with the collections parameter.`);
316
+ continue;
355
317
  }
356
- else {
357
- // Add to custom collection
358
- const customCollection = existingCollections.find(c => c.slug.toLowerCase() === group.collectionSlug.toLowerCase());
359
- if (!customCollection) {
360
- results.push(` ❌ Collection "${group.collectionSlug}" not found. Create it first with the collections parameter.`);
318
+ for (const field of group.fields) {
319
+ const fieldExists = customCollection.fields.some(f => f.slug.toLowerCase() === field.slug.toLowerCase());
320
+ if (fieldExists) {
321
+ results.push(` Skipped field "${field.slug}" (already exists)`);
322
+ skipped.fields++;
361
323
  continue;
362
324
  }
363
- for (const field of group.fields) {
364
- const fieldExists = customCollection.fields.some(f => f.slug.toLowerCase() === field.slug.toLowerCase());
365
- if (fieldExists) {
366
- results.push(` ⏭️ Skipped field "${field.slug}" (already exists)`);
367
- skipped.fields++;
368
- continue;
369
- }
370
- const fieldRes = await (0, api_client_1.apiRequest)(`/api/collections/${customCollection.id}/fields`, {
371
- tenantId,
372
- method: 'POST',
373
- body: {
374
- slug: field.slug,
375
- name: field.name,
376
- type: field.type,
377
- description: field.description,
378
- isRequired: field.isRequired,
379
- options: field.options,
380
- referenceCollection: field.referenceCollection,
381
- },
382
- });
383
- if ((0, api_client_1.isApiError)(fieldRes)) {
384
- results.push(` ❌ Failed to add field "${field.slug}": ${fieldRes.error}`);
385
- }
386
- else {
387
- results.push(` ✅ Added field "${field.slug}" (${field.type})`);
388
- created.fields++;
389
- }
325
+ const fieldRes = await (0, api_client_1.apiRequest)(`/api/collections/${customCollection.id}/fields`, {
326
+ tenantId,
327
+ method: 'POST',
328
+ body: {
329
+ slug: field.slug,
330
+ name: field.name,
331
+ type: field.type,
332
+ description: field.description,
333
+ isRequired: field.isRequired,
334
+ options: field.options,
335
+ referenceCollection: field.referenceCollection,
336
+ },
337
+ });
338
+ if ((0, api_client_1.isApiError)(fieldRes)) {
339
+ results.push(` Failed to add field "${field.slug}": ${fieldRes.error}`);
340
+ }
341
+ else {
342
+ results.push(` Added field "${field.slug}" (${field.type})`);
343
+ created.fields++;
390
344
  }
391
345
  }
392
346
  }
@@ -1 +1 @@
1
- {"version":3,"file":"validate-manifest.d.ts","sourceRoot":"","sources":["../../src/tools/validate-manifest.ts"],"names":[],"mappings":"AAyCA;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0Q5E"}
1
+ {"version":3,"file":"validate-manifest.d.ts","sourceRoot":"","sources":["../../src/tools/validate-manifest.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkO5E"}
@@ -8,30 +8,9 @@ const pageSchema = zod_1.z.object({
8
8
  title: zod_1.z.string(),
9
9
  editable: zod_1.z.boolean().optional(),
10
10
  });
11
- // Built-in collection templates
12
- const builtInTemplatesSchema = zod_1.z.object({
13
- blogIndex: zod_1.z.string().optional(),
14
- blogIndexPath: zod_1.z.string().startsWith('/').optional(),
15
- blogPost: zod_1.z.string().optional(),
16
- blogPostPath: zod_1.z.string().startsWith('/').optional(),
17
- team: zod_1.z.string().optional(),
18
- teamPath: zod_1.z.string().startsWith('/').optional(),
19
- downloads: zod_1.z.string().optional(),
20
- downloadsPath: zod_1.z.string().startsWith('/').optional(),
21
- authorsIndex: zod_1.z.string().optional(),
22
- authorDetail: zod_1.z.string().optional(),
23
- authorsIndexPath: zod_1.z.string().startsWith('/').optional(),
24
- // Legacy format - deprecated
25
- collectionPaths: zod_1.z.record(zod_1.z.string().startsWith('/')).optional(),
26
- collectionTemplates: zod_1.z.record(zod_1.z.object({
27
- path: zod_1.z.string().startsWith('/').optional(),
28
- indexTemplate: zod_1.z.string().optional(),
29
- detailTemplate: zod_1.z.string().optional(),
30
- })).optional(),
31
- });
32
- // Custom collection templates use flat format: {slug}Index, {slug}Detail, {slug}IndexPath, {slug}DetailPath
33
- // This schema allows any additional string keys for custom collections
34
- const cmsTemplatesSchema = builtInTemplatesSchema.catchall(zod_1.z.string()).optional();
11
+ // CMS templates use flat format: {slug}Index, {slug}Detail, {slug}IndexPath, {slug}DetailPath
12
+ // This schema allows any string keys for collection templates
13
+ const cmsTemplatesSchema = zod_1.z.record(zod_1.z.string()).optional();
35
14
  const manifestSchema = zod_1.z.object({
36
15
  pages: zod_1.z.array(pageSchema),
37
16
  cmsTemplates: cmsTemplatesSchema,
@@ -56,7 +35,7 @@ async function validateManifest(manifestJson) {
56
35
  // Check for missing closing brace
57
36
  if (!trimmed.endsWith('}')) {
58
37
  diagnostics = `
59
- 🔍 Detected Issue: File does not end with a closing brace '}'
38
+ Detected Issue: File does not end with a closing brace '}'
60
39
  The manifest.json appears to be truncated or missing the final '}'.
61
40
 
62
41
  Fix: Add a closing '}' at the end of the file.`;
@@ -64,7 +43,7 @@ async function validateManifest(manifestJson) {
64
43
  // Check for missing opening brace
65
44
  else if (!trimmed.startsWith('{')) {
66
45
  diagnostics = `
67
- 🔍 Detected Issue: File does not start with an opening brace '{'
46
+ Detected Issue: File does not start with an opening brace '{'
68
47
  The manifest.json must be a JSON object starting with '{'.`;
69
48
  }
70
49
  // Check bracket balance
@@ -75,20 +54,20 @@ async function validateManifest(manifestJson) {
75
54
  const closeBrackets = (manifestJson.match(/]/g) || []).length;
76
55
  if (openBraces !== closeBraces) {
77
56
  diagnostics = `
78
- 🔍 Detected Issue: Mismatched braces
57
+ Detected Issue: Mismatched braces
79
58
  Found ${openBraces} opening '{' but ${closeBraces} closing '}'
80
59
  ${openBraces > closeBraces ? `Missing ${openBraces - closeBraces} closing brace(s) '}'` : `Extra ${closeBraces - openBraces} closing brace(s) '}'`}`;
81
60
  }
82
61
  else if (openBrackets !== closeBrackets) {
83
62
  diagnostics = `
84
- 🔍 Detected Issue: Mismatched brackets
63
+ Detected Issue: Mismatched brackets
85
64
  Found ${openBrackets} opening '[' but ${closeBrackets} closing ']'
86
65
  ${openBrackets > closeBrackets ? `Missing ${openBrackets - closeBrackets} closing bracket(s) ']'` : `Extra ${closeBrackets - openBrackets} closing bracket(s) ']'`}`;
87
66
  }
88
67
  else {
89
68
  // Generic guidance
90
69
  diagnostics = `
91
- 🔍 Common JSON issues to check:
70
+ Common JSON issues to check:
92
71
  - Missing or extra commas between items
93
72
  - Trailing comma after last item in arrays/objects
94
73
  - Unquoted property names (must use "key" not key)
@@ -96,12 +75,12 @@ async function validateManifest(manifestJson) {
96
75
  - Unescaped special characters in strings`;
97
76
  }
98
77
  }
99
- return `❌ INVALID JSON
78
+ return `INVALID JSON
100
79
 
101
80
  Error: ${errorMessage}
102
81
  ${diagnostics}
103
82
 
104
- 💡 Tip: Use a JSON validator (like jsonlint.com) to find the exact error location.`;
83
+ Tip: Use a JSON validator (like jsonlint.com) to find the exact error location.`;
105
84
  }
106
85
  // Validate against schema
107
86
  const result = manifestSchema.safeParse(manifest);
@@ -141,48 +120,12 @@ ${diagnostics}
141
120
  const templates = m.cmsTemplates;
142
121
  const customCollections = new Set();
143
122
  if (templates) {
144
- // Check template file paths
145
- const templateFields = ['blogIndex', 'blogPost', 'team', 'downloads', 'authorsIndex', 'authorDetail'];
146
- for (const field of templateFields) {
147
- const value = templates[field];
148
- if (typeof value === 'string') {
149
- if (!value.startsWith('templates/')) {
150
- warnings.push(`- cmsTemplates.${field}: should be in templates/ folder (got: ${value})`);
151
- }
152
- if (!value.endsWith('.html')) {
153
- warnings.push(`- cmsTemplates.${field}: should end with .html (got: ${value})`);
154
- }
155
- }
156
- }
157
- // Check path consistency
158
- if (templates.blogIndex && !templates.blogIndexPath) {
159
- warnings.push('- blogIndex template defined but no blogIndexPath - will default to /blog');
160
- }
161
- if (templates.blogPost && !templates.blogPostPath) {
162
- warnings.push('- blogPost template defined but no blogPostPath - will default to /blog');
163
- }
164
- if (templates.blogIndexPath !== templates.blogPostPath && templates.blogIndexPath && templates.blogPostPath) {
165
- warnings.push(`- blogIndexPath (${templates.blogIndexPath}) differs from blogPostPath (${templates.blogPostPath}) - usually these should match`);
166
- }
167
- // Check for common author path mistake
168
- if (templates.authorsPath && !templates.authorsIndexPath) {
169
- warnings.push('- Found "authorsPath" but expected "authorsIndexPath" - rename to authorsIndexPath');
170
- }
171
- // Check author template consistency
172
- if (templates.authorsIndex && !templates.authorsIndexPath) {
173
- warnings.push('- authorsIndex template defined but no authorsIndexPath - will default to /authors');
174
- }
175
- if (templates.authorDetail && !templates.authorsIndex) {
176
- warnings.push('- authorDetail template defined but no authorsIndex - authors need both templates');
177
- }
178
- // Detect and validate custom collection templates (flat format)
123
+ // Detect and validate collection templates (flat format)
179
124
  // Pattern: {slug}Index, {slug}Detail, {slug}IndexPath, {slug}DetailPath
180
125
  const allKeys = Object.keys(templates);
181
126
  for (const key of allKeys) {
182
- // Skip built-in keys
183
- if (['blogIndex', 'blogIndexPath', 'blogPost', 'blogPostPath', 'team', 'teamPath',
184
- 'downloads', 'downloadsPath', 'authorsIndex', 'authorDetail', 'authorsIndexPath',
185
- 'collectionPaths', 'collectionTemplates'].includes(key)) {
127
+ // Skip deprecated keys
128
+ if (['collectionPaths', 'collectionTemplates'].includes(key)) {
186
129
  continue;
187
130
  }
188
131
  // Extract collection slug from key pattern
@@ -203,7 +146,7 @@ ${diagnostics}
203
146
  customCollections.add(slug);
204
147
  }
205
148
  }
206
- // Validate each custom collection
149
+ // Validate each collection
207
150
  for (const slug of customCollections) {
208
151
  const indexTemplate = templates[`${slug}Index`];
209
152
  const detailTemplate = templates[`${slug}Detail`];
@@ -229,16 +172,16 @@ ${diagnostics}
229
172
  }
230
173
  // Check consistency - if you have one, you should have the others
231
174
  if (indexTemplate && !indexPath) {
232
- warnings.push(`- Custom collection "${slug}": has ${slug}Index but no ${slug}IndexPath`);
175
+ warnings.push(`- Collection "${slug}": has ${slug}Index but no ${slug}IndexPath`);
233
176
  }
234
177
  if (detailTemplate && !detailPath) {
235
- warnings.push(`- Custom collection "${slug}": has ${slug}Detail but no ${slug}DetailPath`);
178
+ warnings.push(`- Collection "${slug}": has ${slug}Detail but no ${slug}DetailPath`);
236
179
  }
237
180
  if (indexPath && !indexTemplate) {
238
- warnings.push(`- Custom collection "${slug}": has ${slug}IndexPath but no ${slug}Index template`);
181
+ warnings.push(`- Collection "${slug}": has ${slug}IndexPath but no ${slug}Index template`);
239
182
  }
240
183
  if (detailPath && !detailTemplate) {
241
- warnings.push(`- Custom collection "${slug}": has ${slug}DetailPath but no ${slug}Detail template`);
184
+ warnings.push(`- Collection "${slug}": has ${slug}DetailPath but no ${slug}Detail template`);
242
185
  }
243
186
  // Note: indexPath and detailPath can be the same (e.g., both "/videos")
244
187
  // The system distinguishes by whether a slug segment exists after the path
@@ -250,32 +193,32 @@ ${diagnostics}
250
193
  }
251
194
  // Build result
252
195
  let output = '';
253
- // Build custom collections summary
254
- const customCollectionsSummary = customCollections.size > 0
255
- ? `\n- Custom collections: ${Array.from(customCollections).join(', ')} (using flat format: {slug}Index, {slug}Detail, {slug}IndexPath, {slug}DetailPath)`
196
+ // Build collections summary
197
+ const collectionsSummary = customCollections.size > 0
198
+ ? `\n- Collections: ${Array.from(customCollections).join(', ')}`
256
199
  : '';
257
200
  if (errors.length === 0 && warnings.length === 0) {
258
- output = `✅ MANIFEST VALID
201
+ output = `MANIFEST VALID
259
202
 
260
203
  The manifest.json structure is correct.
261
204
 
262
205
  Summary:
263
206
  - ${m.pages?.length || 0} static page(s) defined
264
- - CMS templates: ${templates ? 'configured in manifest' : 'not configured (can be set via Settings → CMS Templates after upload)'}${customCollectionsSummary}
207
+ - CMS templates: ${templates ? 'configured in manifest' : 'not configured (can be set via Settings → CMS Templates after upload)'}${collectionsSummary}
265
208
  - Head HTML: ${m.defaultHeadHtml ? 'configured' : 'not configured'}`;
266
209
  }
267
210
  else if (errors.length === 0) {
268
- output = `⚠️ MANIFEST VALID WITH WARNINGS
211
+ output = `MANIFEST VALID WITH WARNINGS
269
212
 
270
213
  The manifest structure is valid but has potential issues:
271
214
 
272
215
  Warnings:
273
216
  ${warnings.join('\n')}
274
217
 
275
- 💡 Tip: CMS templates can also be configured after upload via Dashboard → Settings → CMS Templates`;
218
+ Tip: CMS templates can also be configured after upload via Dashboard → Settings → CMS Templates`;
276
219
  }
277
220
  else {
278
- output = `❌ MANIFEST INVALID
221
+ output = `MANIFEST INVALID
279
222
 
280
223
  Errors (must fix):
281
224
  ${errors.join('\n')}`;
@@ -1 +1 @@
1
- {"version":3,"file":"validate-package.d.ts","sourceRoot":"","sources":["../../src/tools/validate-package.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAAE,EAClB,eAAe,EAAE,MAAM,EACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACxC,OAAO,CAAC,MAAM,CAAC,CA6LjB"}
1
+ {"version":3,"file":"validate-package.d.ts","sourceRoot":"","sources":["../../src/tools/validate-package.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAAE,EAClB,eAAe,EAAE,MAAM,EACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACxC,OAAO,CAAC,MAAM,CAAC,CAwMjB"}