canopycms 0.0.33 → 0.0.35

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 (83) hide show
  1. package/dist/ai/json-to-markdown.d.ts.map +1 -1
  2. package/dist/ai/json-to-markdown.js +4 -3
  3. package/dist/ai/json-to-markdown.js.map +1 -1
  4. package/dist/api/content.d.ts +9 -0
  5. package/dist/api/content.d.ts.map +1 -1
  6. package/dist/api/content.js +20 -3
  7. package/dist/api/content.js.map +1 -1
  8. package/dist/branch-metadata.d.ts.map +1 -1
  9. package/dist/branch-metadata.js +2 -20
  10. package/dist/branch-metadata.js.map +1 -1
  11. package/dist/cli/generate-ai-content.js +167 -114
  12. package/dist/cli/init.js +12 -3
  13. package/dist/cli/template-files/schemas.ts.template +45 -1
  14. package/dist/config/index.d.ts +1 -1
  15. package/dist/config/index.d.ts.map +1 -1
  16. package/dist/config/index.js.map +1 -1
  17. package/dist/config/schemas/field.d.ts +19 -0
  18. package/dist/config/schemas/field.d.ts.map +1 -1
  19. package/dist/config/schemas/field.js +9 -0
  20. package/dist/config/schemas/field.js.map +1 -1
  21. package/dist/config/types.d.ts +15 -2
  22. package/dist/config/types.d.ts.map +1 -1
  23. package/dist/config/types.js +1 -0
  24. package/dist/config/types.js.map +1 -1
  25. package/dist/config/validation.d.ts +13 -0
  26. package/dist/config/validation.d.ts.map +1 -1
  27. package/dist/config/validation.js +130 -0
  28. package/dist/config/validation.js.map +1 -1
  29. package/dist/content-store.d.ts +12 -0
  30. package/dist/content-store.d.ts.map +1 -1
  31. package/dist/content-store.js +166 -106
  32. package/dist/content-store.js.map +1 -1
  33. package/dist/editor/FormRenderer.d.ts.map +1 -1
  34. package/dist/editor/FormRenderer.js +6 -0
  35. package/dist/editor/FormRenderer.js.map +1 -1
  36. package/dist/editor/client-reference-resolver.d.ts.map +1 -1
  37. package/dist/editor/client-reference-resolver.js +2 -1
  38. package/dist/editor/client-reference-resolver.js.map +1 -1
  39. package/dist/editor/fields/InlineGroupField.d.ts +17 -0
  40. package/dist/editor/fields/InlineGroupField.d.ts.map +1 -0
  41. package/dist/editor/fields/InlineGroupField.js +16 -0
  42. package/dist/editor/fields/InlineGroupField.js.map +1 -0
  43. package/dist/editor/hooks/useDraftManager.d.ts.map +1 -1
  44. package/dist/editor/hooks/useDraftManager.js +7 -3
  45. package/dist/editor/hooks/useDraftManager.js.map +1 -1
  46. package/dist/editor/hooks/useEntryManager.d.ts +5 -0
  47. package/dist/editor/hooks/useEntryManager.d.ts.map +1 -1
  48. package/dist/editor/hooks/useEntryManager.js +29 -2
  49. package/dist/editor/hooks/useEntryManager.js.map +1 -1
  50. package/dist/editor/hooks/useReferenceResolution.d.ts.map +1 -1
  51. package/dist/editor/hooks/useReferenceResolution.js +2 -1
  52. package/dist/editor/hooks/useReferenceResolution.js.map +1 -1
  53. package/dist/entry-schema-registry.d.ts.map +1 -1
  54. package/dist/entry-schema-registry.js +4 -2
  55. package/dist/entry-schema-registry.js.map +1 -1
  56. package/dist/entry-schema.d.ts +69 -1
  57. package/dist/entry-schema.d.ts.map +1 -1
  58. package/dist/entry-schema.js +43 -0
  59. package/dist/entry-schema.js.map +1 -1
  60. package/dist/schema/schema-store.d.ts.map +1 -1
  61. package/dist/schema/schema-store.js +3 -2
  62. package/dist/schema/schema-store.js.map +1 -1
  63. package/dist/utils/async-mutex.d.ts +2 -0
  64. package/dist/utils/async-mutex.d.ts.map +1 -0
  65. package/dist/utils/async-mutex.js +28 -0
  66. package/dist/utils/async-mutex.js.map +1 -0
  67. package/dist/utils/body-field.d.ts.map +1 -1
  68. package/dist/utils/body-field.js +4 -3
  69. package/dist/utils/body-field.js.map +1 -1
  70. package/dist/utils/flatten-group-fields.d.ts +13 -0
  71. package/dist/utils/flatten-group-fields.d.ts.map +1 -0
  72. package/dist/utils/flatten-group-fields.js +23 -0
  73. package/dist/utils/flatten-group-fields.js.map +1 -0
  74. package/dist/utils/title-field.d.ts.map +1 -1
  75. package/dist/utils/title-field.js +55 -24
  76. package/dist/utils/title-field.js.map +1 -1
  77. package/dist/validation/deletion-checker.d.ts.map +1 -1
  78. package/dist/validation/deletion-checker.js +5 -0
  79. package/dist/validation/deletion-checker.js.map +1 -1
  80. package/dist/validation/field-traversal.d.ts.map +1 -1
  81. package/dist/validation/field-traversal.js +7 -1
  82. package/dist/validation/field-traversal.js.map +1 -1
  83. package/package.json +1 -1
@@ -153,14 +153,15 @@ var init_types = __esm({
153
153
  "select",
154
154
  "reference",
155
155
  "object",
156
- "block"
156
+ "block",
157
+ "group"
157
158
  ];
158
159
  }
159
160
  });
160
161
 
161
162
  // dist/config/schemas/field.js
162
163
  import { z } from "zod";
163
- var fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema;
164
+ var fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema;
164
165
  var init_field = __esm({
165
166
  "dist/config/schemas/field.js"() {
166
167
  "use strict";
@@ -217,6 +218,13 @@ var init_field = __esm({
217
218
  type: z.literal("object"),
218
219
  fields: z.array(z.lazy(() => fieldHolder[0])).min(1)
219
220
  });
221
+ inlineGroupFieldSchema = z.object({
222
+ type: z.literal("group"),
223
+ name: z.string().min(1),
224
+ label: z.string().optional(),
225
+ description: z.string().optional(),
226
+ fields: z.array(z.lazy(() => fieldHolder[0])).min(1)
227
+ });
220
228
  customFieldSchema = z.lazy(() => fieldBaseSchema.extend({
221
229
  type: z.string().min(1).refine((val) => !fieldTypes.includes(val), {
222
230
  message: "Custom field types must not conflict with built-in types"
@@ -227,7 +235,8 @@ var init_field = __esm({
227
235
  selectFieldSchema,
228
236
  referenceFieldSchema,
229
237
  objectFieldSchema,
230
- blockFieldSchema
238
+ blockFieldSchema,
239
+ inlineGroupFieldSchema
231
240
  ]);
232
241
  fieldSchema = z.lazy(() => z.union([knownFieldSchema, customFieldSchema]));
233
242
  fieldHolder[0] = fieldSchema;
@@ -721,9 +730,41 @@ async function atomicWriteFile(filePath, content) {
721
730
  }
722
731
  }
723
732
 
733
+ // dist/utils/async-mutex.js
734
+ var locks = /* @__PURE__ */ new Map();
735
+ async function withLock(key, fn) {
736
+ const prev = locks.get(key) ?? Promise.resolve();
737
+ let resolve;
738
+ const next = new Promise((r) => {
739
+ resolve = r;
740
+ });
741
+ locks.set(key, next);
742
+ await prev;
743
+ try {
744
+ return await fn();
745
+ } finally {
746
+ resolve();
747
+ if (locks.get(key) === next)
748
+ locks.delete(key);
749
+ }
750
+ }
751
+
752
+ // dist/utils/flatten-group-fields.js
753
+ function flattenGroupFields(fields) {
754
+ const result = [];
755
+ for (const field of fields) {
756
+ if (field.type === "group") {
757
+ result.push(...flattenGroupFields(field.fields));
758
+ } else {
759
+ result.push(field);
760
+ }
761
+ }
762
+ return result;
763
+ }
764
+
724
765
  // dist/utils/body-field.js
725
766
  function findBodyFieldName(fields) {
726
- for (const field of fields) {
767
+ for (const field of flattenGroupFields(fields)) {
727
768
  if ("isBody" in field && field.isBody)
728
769
  return field.name;
729
770
  }
@@ -1160,6 +1201,12 @@ var ContentStoreError = class extends Error {
1160
1201
  this.code = code;
1161
1202
  }
1162
1203
  };
1204
+ var ContentConflictError = class extends Error {
1205
+ constructor() {
1206
+ super("Content was modified by another editor");
1207
+ this.name = "ContentConflictError";
1208
+ }
1209
+ };
1163
1210
  function getDefaultEntryType(entries) {
1164
1211
  if (!entries || entries.length === 0)
1165
1212
  return void 0;
@@ -1337,6 +1384,7 @@ var ContentStore = class {
1337
1384
  async read(collectionPath, slug = "", options = {}) {
1338
1385
  const schemaItem = this.assertSchemaItem(collectionPath);
1339
1386
  const { absolutePath, relativePath, entryTypeName: resolvedEntryTypeName } = await this.buildPaths(schemaItem, slug);
1387
+ const stat = await fs4.stat(absolutePath);
1340
1388
  const raw = await fs4.readFile(absolutePath, "utf8");
1341
1389
  let doc;
1342
1390
  let format;
@@ -1391,6 +1439,7 @@ var ContentStore = class {
1391
1439
  if (options.resolveReferences !== false) {
1392
1440
  doc.data = await this.resolveReferencesInData(doc.data, fields);
1393
1441
  }
1442
+ doc.version = stat.mtimeMs;
1394
1443
  return doc;
1395
1444
  }
1396
1445
  async write(collectionPath, slug = "", input, entryTypeName) {
@@ -1420,64 +1469,70 @@ var ContentStore = class {
1420
1469
  const { absolutePath, relativePath, id } = await this.buildPaths(schemaItem, slug, {
1421
1470
  entryTypeName
1422
1471
  });
1423
- await fs4.mkdir(path5.dirname(absolutePath), { recursive: true });
1424
- const updateIdIndex = () => {
1425
- if (!id)
1426
- return;
1427
- const existing = idIndex.findById(id);
1428
- if (existing) {
1429
- if (existing.relativePath !== relativePath) {
1430
- idIndex.updatePath(existing.id, relativePath);
1472
+ return withLock(absolutePath, async () => {
1473
+ await fs4.mkdir(path5.dirname(absolutePath), { recursive: true });
1474
+ if (input.expectedVersion !== void 0) {
1475
+ try {
1476
+ const existing = await fs4.stat(absolutePath);
1477
+ if (existing.mtimeMs !== input.expectedVersion) {
1478
+ throw new ContentConflictError();
1479
+ }
1480
+ } catch (err) {
1481
+ if (err instanceof ContentConflictError)
1482
+ throw err;
1483
+ if (isNodeError(err) && err.code === "ENOENT") {
1484
+ } else {
1485
+ throw err;
1486
+ }
1431
1487
  }
1488
+ }
1489
+ let content;
1490
+ if (input.format === "json") {
1491
+ content = `${JSON.stringify(input.data ?? {}, null, 2)}
1492
+ `;
1493
+ } else if (input.format === "yaml") {
1494
+ content = yamlStringify(input.data ?? {});
1432
1495
  } else {
1433
- idIndex.add({
1434
- type: "entry",
1435
- relativePath,
1436
- collection: collectionPath,
1437
- slug: slug || void 0
1438
- });
1496
+ content = matter.stringify(input.body, input.data ?? {});
1497
+ }
1498
+ await atomicWriteFile(absolutePath, content);
1499
+ if (id) {
1500
+ const existing = idIndex.findById(id);
1501
+ if (existing) {
1502
+ if (existing.relativePath !== relativePath) {
1503
+ idIndex.updatePath(existing.id, relativePath);
1504
+ }
1505
+ } else {
1506
+ idIndex.add({
1507
+ type: "entry",
1508
+ relativePath,
1509
+ collection: collectionPath,
1510
+ slug: slug || void 0
1511
+ });
1512
+ }
1439
1513
  }
1440
- };
1441
- if (input.format === "json") {
1442
- const json = JSON.stringify(input.data ?? {}, null, 2);
1443
- await atomicWriteFile(absolutePath, `${json}
1444
- `);
1445
- updateIdIndex();
1446
- return {
1514
+ const afterStat = await fs4.stat(absolutePath);
1515
+ const base = {
1447
1516
  collection: schemaItem.logicalPath,
1448
1517
  collectionName: schemaItem.name,
1449
- format: "json",
1450
- data: input.data ?? {},
1451
1518
  relativePath,
1452
- absolutePath
1519
+ absolutePath,
1520
+ version: afterStat.mtimeMs
1453
1521
  };
1454
- }
1455
- if (input.format === "yaml") {
1456
- const yaml = yamlStringify(input.data ?? {});
1457
- await atomicWriteFile(absolutePath, yaml);
1458
- updateIdIndex();
1522
+ if (input.format === "json") {
1523
+ return { ...base, format: "json", data: input.data ?? {} };
1524
+ }
1525
+ if (input.format === "yaml") {
1526
+ return { ...base, format: "yaml", data: input.data ?? {} };
1527
+ }
1459
1528
  return {
1460
- collection: schemaItem.logicalPath,
1461
- collectionName: schemaItem.name,
1462
- format: "yaml",
1529
+ ...base,
1530
+ format: input.format,
1463
1531
  data: input.data ?? {},
1464
- relativePath,
1465
- absolutePath
1532
+ body: input.body,
1533
+ bodyFieldName: findBodyFieldName(fields)
1466
1534
  };
1467
- }
1468
- const file = matter.stringify(input.body, input.data ?? {});
1469
- await atomicWriteFile(absolutePath, file);
1470
- updateIdIndex();
1471
- return {
1472
- collection: schemaItem.logicalPath,
1473
- collectionName: schemaItem.name,
1474
- format: input.format,
1475
- data: input.data ?? {},
1476
- body: input.body,
1477
- bodyFieldName: findBodyFieldName(fields),
1478
- relativePath,
1479
- absolutePath
1480
- };
1535
+ });
1481
1536
  }
1482
1537
  /**
1483
1538
  * Read an entry by its ID (UUID).
@@ -1506,11 +1561,13 @@ var ContentStore = class {
1506
1561
  const idIndex = await this.idIndex();
1507
1562
  const collection = this.assertCollection(collectionPath);
1508
1563
  const { absolutePath, relativePath } = await this.buildPaths(collection, slug);
1509
- const id = idIndex.findByPath(relativePath);
1510
- await fs4.unlink(absolutePath);
1511
- if (id) {
1512
- idIndex.remove(id);
1513
- }
1564
+ return withLock(absolutePath, async () => {
1565
+ const id = idIndex.findByPath(relativePath);
1566
+ await fs4.unlink(absolutePath);
1567
+ if (id) {
1568
+ idIndex.remove(id);
1569
+ }
1570
+ });
1514
1571
  }
1515
1572
  /**
1516
1573
  * Rename an entry by changing its slug (middle segment of filename).
@@ -1530,49 +1587,57 @@ var ContentStore = class {
1530
1587
  if (!safeNewSlug) {
1531
1588
  throw new ContentStoreError("New slug cannot be empty", "VALIDATION");
1532
1589
  }
1533
- const { absolutePath: currentPath, relativePath: currentRelPath } = await this.buildPaths(collection, currentSlug);
1534
- try {
1535
- await fs4.access(currentPath);
1536
- } catch {
1537
- throw new ContentStoreError(`Entry not found: ${currentSlug}`, "NOT_FOUND");
1538
- }
1539
1590
  if (currentSlug === safeNewSlug) {
1540
1591
  return { newPath: `${collectionPath}/${currentSlug}` };
1541
1592
  }
1542
- const currentFilename = path5.basename(currentPath);
1543
- const parts = currentFilename.split(".");
1544
- if (parts.length < 4) {
1545
- throw new ContentStoreError(`Invalid entry filename format: ${currentFilename}`, "VALIDATION");
1546
- }
1547
- const entryTypeName = parts[0];
1548
- const contentId = parts[parts.length - 2];
1549
- const ext = `.${parts[parts.length - 1]}`;
1550
- const newFilename = `${entryTypeName}.${safeNewSlug}.${contentId}${ext}`;
1551
- const parentDir = path5.dirname(currentPath);
1552
- const newPath = path5.join(parentDir, newFilename);
1553
- try {
1554
- const entries = await fs4.readdir(parentDir, { withFileTypes: true });
1555
- for (const entry of entries) {
1556
- if (entry.isDirectory())
1557
- continue;
1558
- const existingSlug = extractSlugFromFilename(entry.name, entryTypeName);
1559
- if (existingSlug === safeNewSlug) {
1560
- throw new ContentStoreError(`Entry with slug "${safeNewSlug}" already exists in collection "${collectionPath}"`, "VALIDATION");
1593
+ const { absolutePath: currentPath, relativePath: currentRelPath } = await this.buildPaths(collection, currentSlug);
1594
+ return withLock(currentPath, async () => {
1595
+ try {
1596
+ await fs4.access(currentPath);
1597
+ } catch {
1598
+ throw new ContentStoreError(`Entry not found: ${currentSlug}`, "NOT_FOUND");
1599
+ }
1600
+ const currentFilename = path5.basename(currentPath);
1601
+ const parts = currentFilename.split(".");
1602
+ if (parts.length < 4) {
1603
+ throw new ContentStoreError(`Invalid entry filename format: ${currentFilename}`, "VALIDATION");
1604
+ }
1605
+ const entryTypeName = parts[0];
1606
+ const contentId = parts[parts.length - 2];
1607
+ const ext = `.${parts[parts.length - 1]}`;
1608
+ const newFilename = `${entryTypeName}.${safeNewSlug}.${contentId}${ext}`;
1609
+ const parentDir = path5.dirname(currentPath);
1610
+ const newPath = path5.join(parentDir, newFilename);
1611
+ try {
1612
+ const entries = await fs4.readdir(parentDir, { withFileTypes: true });
1613
+ for (const entry of entries) {
1614
+ if (entry.isDirectory())
1615
+ continue;
1616
+ const existingSlug = extractSlugFromFilename(entry.name, entryTypeName);
1617
+ if (existingSlug === safeNewSlug) {
1618
+ throw new ContentStoreError(`Entry with slug "${safeNewSlug}" already exists in collection "${collectionPath}"`, "VALIDATION");
1619
+ }
1561
1620
  }
1621
+ } catch (err) {
1622
+ if (err instanceof ContentStoreError)
1623
+ throw err;
1562
1624
  }
1563
- } catch (err) {
1564
- if (err instanceof ContentStoreError) {
1625
+ try {
1626
+ await fs4.link(currentPath, newPath);
1627
+ } catch (err) {
1628
+ if (isNodeError(err) && err.code === "EEXIST") {
1629
+ throw new ContentStoreError(`Entry with slug "${safeNewSlug}" already exists in collection "${collectionPath}"`, "VALIDATION");
1630
+ }
1565
1631
  throw err;
1566
1632
  }
1567
- }
1568
- await fs4.rename(currentPath, newPath);
1569
- const newRelativePath = path5.relative(this.root, newPath);
1570
- const entryId = idIndex.findByPath(currentRelPath);
1571
- if (entryId) {
1572
- idIndex.updatePath(entryId, newRelativePath);
1573
- }
1574
- const newLogicalPath = `${collectionPath}/${safeNewSlug}`;
1575
- return { newPath: newLogicalPath };
1633
+ await fs4.unlink(currentPath);
1634
+ const newRelativePath = path5.relative(this.root, newPath);
1635
+ const entryId = idIndex.findByPath(currentRelPath);
1636
+ if (entryId) {
1637
+ idIndex.updatePath(entryId, newRelativePath);
1638
+ }
1639
+ return { newPath: `${collectionPath}/${safeNewSlug}` };
1640
+ });
1576
1641
  }
1577
1642
  /**
1578
1643
  * List all entries in a collection tree (including subcollections).
@@ -1621,6 +1686,11 @@ var ContentStore = class {
1621
1686
  const resolved = { ...data };
1622
1687
  const idIndex = await this.idIndex();
1623
1688
  for (const field of fields) {
1689
+ if (field.type === "group") {
1690
+ const groupResolved = await this.resolveReferencesInData(resolved, field.fields);
1691
+ Object.assign(resolved, groupResolved);
1692
+ continue;
1693
+ }
1624
1694
  const value = data[field.name];
1625
1695
  if (field.type === "reference") {
1626
1696
  if (typeof value === "string" && value) {
@@ -2194,7 +2264,7 @@ function entryToMarkdown(entry, config) {
2194
2264
  function renderMarkdownEntry(entry, config, skipFields) {
2195
2265
  const parts = [];
2196
2266
  const bodyFieldTypes = /* @__PURE__ */ new Set(["rich-text", "markdown", "mdx"]);
2197
- const metadataFields = entry.fields.filter((f) => !bodyFieldTypes.has(f.type) && !skipFields.has(f.name));
2267
+ const metadataFields = flattenGroupFields(entry.fields).filter((f) => !bodyFieldTypes.has(f.type) && !skipFields.has(f.name));
2198
2268
  for (const field of metadataFields) {
2199
2269
  const value = entry.data[field.name];
2200
2270
  if (value === void 0 || value === null)
@@ -2227,7 +2297,7 @@ function renderMarkdownEntry(entry, config, skipFields) {
2227
2297
  }
2228
2298
  function renderJsonEntry(entry, config, skipFields) {
2229
2299
  const parts = [];
2230
- for (const field of entry.fields) {
2300
+ for (const field of flattenGroupFields(entry.fields)) {
2231
2301
  if (skipFields.has(field.name))
2232
2302
  continue;
2233
2303
  const value = entry.data[field.name];
@@ -2251,7 +2321,7 @@ function renderField(field, value, depth, entry, config) {
2251
2321
  const descriptionLine = "description" in field && field.description ? `
2252
2322
 
2253
2323
  *${field.description}*` : "";
2254
- if (field.list && Array.isArray(value)) {
2324
+ if ("list" in field && field.list && Array.isArray(value)) {
2255
2325
  return renderListField(field, value, depth, label, heading, descriptionLine, entry, config);
2256
2326
  }
2257
2327
  switch (field.type) {
@@ -3009,23 +3079,6 @@ var BranchMetadataConflictError = class extends Error {
3009
3079
  this.name = "BranchMetadataConflictError";
3010
3080
  }
3011
3081
  };
3012
- var fileLocks = /* @__PURE__ */ new Map();
3013
- async function withFileLock(filePath, fn) {
3014
- while (fileLocks.has(filePath)) {
3015
- await fileLocks.get(filePath);
3016
- }
3017
- let resolve;
3018
- const lockPromise = new Promise((r) => {
3019
- resolve = r;
3020
- });
3021
- fileLocks.set(filePath, lockPromise);
3022
- try {
3023
- return await fn();
3024
- } finally {
3025
- fileLocks.delete(filePath);
3026
- resolve();
3027
- }
3028
- }
3029
3082
  var BranchMetadataFileManager = class _BranchMetadataFileManager {
3030
3083
  constructor(branchRoot, baseRoot) {
3031
3084
  this.branchRoot = path9.resolve(branchRoot);
@@ -3142,7 +3195,7 @@ var BranchMetadataFileManager = class _BranchMetadataFileManager {
3142
3195
  throw new Error("Unreachable");
3143
3196
  }
3144
3197
  async save(incoming) {
3145
- return withFileLock(this.filePath, () => this.withRetry(async () => {
3198
+ return withLock(this.filePath, () => this.withRetry(async () => {
3146
3199
  const { meta: existing, version } = await this.load();
3147
3200
  const now = (/* @__PURE__ */ new Date()).toISOString();
3148
3201
  const defaults = {
package/dist/cli/init.js CHANGED
@@ -30,14 +30,15 @@ var init_types = __esm({
30
30
  "select",
31
31
  "reference",
32
32
  "object",
33
- "block"
33
+ "block",
34
+ "group"
34
35
  ];
35
36
  }
36
37
  });
37
38
 
38
39
  // dist/config/schemas/field.js
39
40
  import { z } from "zod";
40
- var fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema;
41
+ var fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema;
41
42
  var init_field = __esm({
42
43
  "dist/config/schemas/field.js"() {
43
44
  "use strict";
@@ -94,6 +95,13 @@ var init_field = __esm({
94
95
  type: z.literal("object"),
95
96
  fields: z.array(z.lazy(() => fieldHolder[0])).min(1)
96
97
  });
98
+ inlineGroupFieldSchema = z.object({
99
+ type: z.literal("group"),
100
+ name: z.string().min(1),
101
+ label: z.string().optional(),
102
+ description: z.string().optional(),
103
+ fields: z.array(z.lazy(() => fieldHolder[0])).min(1)
104
+ });
97
105
  customFieldSchema = z.lazy(() => fieldBaseSchema.extend({
98
106
  type: z.string().min(1).refine((val) => !fieldTypes.includes(val), {
99
107
  message: "Custom field types must not conflict with built-in types"
@@ -104,7 +112,8 @@ var init_field = __esm({
104
112
  selectFieldSchema,
105
113
  referenceFieldSchema,
106
114
  objectFieldSchema,
107
- blockFieldSchema
115
+ blockFieldSchema,
116
+ inlineGroupFieldSchema
108
117
  ]);
109
118
  fieldSchema = z.lazy(() => z.union([knownFieldSchema, customFieldSchema]));
110
119
  fieldHolder[0] = fieldSchema;
@@ -1,4 +1,9 @@
1
- import { defineEntrySchema, type TypeFromEntrySchema } from 'canopycms'
1
+ import {
2
+ defineEntrySchema,
3
+ defineInlineFieldGroup,
4
+ defineNestedFieldGroup,
5
+ type TypeFromEntrySchema,
6
+ } from 'canopycms'
2
7
  import { createEntrySchemaRegistry } from 'canopycms/server'
3
8
 
4
9
  // Define your entry schemas here.
@@ -13,6 +18,45 @@ export const pageSchema = defineEntrySchema([
13
18
 
14
19
  export type PageContent = TypeFromEntrySchema<typeof pageSchema>
15
20
 
21
+ // --- Field Groups ---
22
+ //
23
+ // Use defineInlineFieldGroup() to define reusable field sets that render as a
24
+ // visually grouped section in the editor. Fields are stored flat in the content
25
+ // file — no nested key is created.
26
+ //
27
+ // const seoGroup = defineInlineFieldGroup({
28
+ // name: 'seo',
29
+ // label: 'SEO',
30
+ // fields: [
31
+ // { name: 'metaTitle', type: 'string', label: 'Meta Title' },
32
+ // { name: 'metaDescription', type: 'string', label: 'Meta Description' },
33
+ // ],
34
+ // })
35
+ // // TypeFromEntrySchema: { ..., metaTitle: string, metaDescription: string }
36
+ //
37
+ // Use defineNestedFieldGroup() when you want fields stored under a named key:
38
+ //
39
+ // const seoGroup = defineNestedFieldGroup({
40
+ // name: 'seo',
41
+ // label: 'SEO',
42
+ // fields: [
43
+ // { name: 'metaTitle', type: 'string', label: 'Meta Title' },
44
+ // { name: 'metaDescription', type: 'string', label: 'Meta Description' },
45
+ // ],
46
+ // })
47
+ // // TypeFromEntrySchema: { ..., seo: { metaTitle: string, metaDescription: string } }
48
+ //
49
+ // Include either in a schema by passing it directly as an array element:
50
+ //
51
+ // const docSchema = defineEntrySchema([
52
+ // { name: 'title', type: 'string' },
53
+ // seoGroup,
54
+ // { name: 'body', type: 'markdown' },
55
+ // ])
56
+
57
+ // Uncomment to use:
58
+ // export { defineInlineFieldGroup, defineNestedFieldGroup }
59
+
16
60
  export const entrySchemaRegistry = createEntrySchemaRegistry({
17
61
  pageSchema,
18
62
  })
@@ -8,7 +8,7 @@
8
8
  * import { defineCanopyConfig, type FieldConfig } from 'canopycms/config'
9
9
  * ```
10
10
  */
11
- export type { PrimitiveFieldType, FieldType, ContentFormat, MediaAdapterKind, PermissionLevel, PermissionTarget, PathPermission, SelectOption, ReferenceOption, FieldConfig, BlockConfig, BlockFieldConfig, SelectFieldConfig, ReferenceFieldConfig, ObjectFieldConfig, CustomFieldConfig, EntrySchema, BranchSchema, EntryTypeConfig, CollectionConfig, RootCollectionConfig, MediaConfig, CanopyEditorConfig, DefaultBranchAccess, DefaultPathAccess, DefaultBaseBranch, DefaultRemoteName, DefaultRemoteUrl, GitBotAuthorName, GitBotAuthorEmail, GithubTokenEnvVar, CanopyOperatingMode, ContentRoot, SourceRoot, CanopyConfig, CanopyConfigInput, CanopyConfigFragment, FlatSchemaItem, CanopyClientConfig, ClientOnlyFields, } from './types';
11
+ export type { PrimitiveFieldType, FieldType, ContentFormat, MediaAdapterKind, PermissionLevel, PermissionTarget, PathPermission, SelectOption, ReferenceOption, FieldConfig, BlockConfig, BlockFieldConfig, SelectFieldConfig, ReferenceFieldConfig, ObjectFieldConfig, InlineGroupFieldConfig, CustomFieldConfig, EntrySchema, BranchSchema, EntryTypeConfig, CollectionConfig, RootCollectionConfig, MediaConfig, CanopyEditorConfig, DefaultBranchAccess, DefaultPathAccess, DefaultBaseBranch, DefaultRemoteName, DefaultRemoteUrl, GitBotAuthorName, GitBotAuthorEmail, GithubTokenEnvVar, CanopyOperatingMode, ContentRoot, SourceRoot, CanopyConfig, CanopyConfigInput, CanopyConfigFragment, FlatSchemaItem, CanopyClientConfig, ClientOnlyFields, } from './types';
12
12
  export { primitiveFieldTypes, fieldTypes } from './types';
13
13
  export { CanopyConfigSchema, getConfigDefaults, DEFAULT_PROD_WORKSPACE } from './schemas/config';
14
14
  export { fieldSchema, blockSchema, selectOptionSchema, referenceOptionSchema, } from './schemas/field';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,YAAY,EAEV,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,gBAAgB,EAEhB,eAAe,EACf,gBAAgB,EAChB,cAAc,EAEd,YAAY,EACZ,eAAe,EACf,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EAEjB,WAAW,EACX,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EAEpB,WAAW,EAEX,kBAAkB,EAElB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGzD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAChG,OAAO,EACL,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,kBAAkB,GACnB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACpF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG7C,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAA;AACvF,OAAO,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAA;AAClF,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,KAAK,qBAAqB,EAAE,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,YAAY,EAEV,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,gBAAgB,EAEhB,eAAe,EACf,gBAAgB,EAChB,cAAc,EAEd,YAAY,EACZ,eAAe,EACf,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,iBAAiB,EAEjB,WAAW,EACX,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EAEpB,WAAW,EAEX,kBAAkB,EAElB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGzD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAChG,OAAO,EACL,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,kBAAkB,GACnB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACpF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG7C,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAA;AACvF,OAAO,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAA;AAClF,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,KAAK,qBAAqB,EAAE,MAAM,WAAW,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAqDH,2BAA2B;AAC3B,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzD,6CAA6C;AAC7C,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAChG,OAAO,EACL,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,kBAAkB,GACnB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACpF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE7C,sBAAsB;AACtB,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAA;AACvF,OAAO,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAA;AAClF,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAA8B,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsDH,2BAA2B;AAC3B,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzD,6CAA6C;AAC7C,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAChG,OAAO,EACL,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,kBAAkB,GACnB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACpF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE7C,sBAAsB;AACtB,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAA;AACvF,OAAO,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAA;AAClF,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAA8B,MAAM,WAAW,CAAA"}
@@ -284,6 +284,25 @@ export declare const objectFieldSchema: z.ZodObject<{
284
284
  isTitle?: boolean | undefined;
285
285
  isBody?: boolean | undefined;
286
286
  }>;
287
+ export declare const inlineGroupFieldSchema: z.ZodObject<{
288
+ type: z.ZodLiteral<"group">;
289
+ name: z.ZodString;
290
+ label: z.ZodOptional<z.ZodString>;
291
+ description: z.ZodOptional<z.ZodString>;
292
+ fields: z.ZodArray<z.ZodLazy<z.ZodTypeAny>, "many">;
293
+ }, "strip", z.ZodTypeAny, {
294
+ type: "group";
295
+ name: string;
296
+ fields: any[];
297
+ description?: string | undefined;
298
+ label?: string | undefined;
299
+ }, {
300
+ type: "group";
301
+ name: string;
302
+ fields: any[];
303
+ description?: string | undefined;
304
+ label?: string | undefined;
305
+ }>;
287
306
  export declare const customFieldSchema: z.ZodLazy<z.ZodObject<{
288
307
  name: z.ZodString;
289
308
  label: z.ZodOptional<z.ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../../src/config/schemas/field.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;EAQ1B,CAAA;AAGF,eAAO,MAAM,kBAAkB;;;;;;;;;IAM7B,CAAA;AAGF,eAAO,MAAM,qBAAqB;;;;;;;;;IAMhC,CAAA;AAGF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAE/B,CAAA;AAGF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG5B,CAAA;AAIF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAM/B,CAAA;AAQF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;EAKtB,CAAA;AAGF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG3B,CAAA;AAGF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG5B,CAAA;AAGF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iCAW7B,CAAA;AAGD,QAAA,MAAM,gBAAgB,EAAE,CAAC,CAAC,UAMxB,CAAA;AAGF,QAAA,MAAM,WAAW,EAAE,CAAC,CAAC,UAAyE,CAAA;AAG9F,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAA"}
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../../src/config/schemas/field.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;EAQ1B,CAAA;AAGF,eAAO,MAAM,kBAAkB;;;;;;;;;IAM7B,CAAA;AAGF,eAAO,MAAM,qBAAqB;;;;;;;;;IAMhC,CAAA;AAGF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAE/B,CAAA;AAGF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG5B,CAAA;AAIF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAM/B,CAAA;AAQF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;EAKtB,CAAA;AAGF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG3B,CAAA;AAGF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG5B,CAAA;AAGF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;EAMjC,CAAA;AAGF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iCAW7B,CAAA;AAGD,QAAA,MAAM,gBAAgB,EAAE,CAAC,CAAC,UAOxB,CAAA;AAGF,QAAA,MAAM,WAAW,EAAE,CAAC,CAAC,UAAyE,CAAA;AAG9F,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAA"}
@@ -68,6 +68,14 @@ export const objectFieldSchema = fieldBaseSchema.extend({
68
68
  type: z.literal('object'),
69
69
  fields: z.array(z.lazy(() => fieldHolder[0])).min(1),
70
70
  });
71
+ // Inline group field: visual grouping only, no data nesting
72
+ export const inlineGroupFieldSchema = z.object({
73
+ type: z.literal('group'),
74
+ name: z.string().min(1),
75
+ label: z.string().optional(),
76
+ description: z.string().optional(),
77
+ fields: z.array(z.lazy(() => fieldHolder[0])).min(1),
78
+ });
71
79
  // Custom field (user-defined type)
72
80
  export const customFieldSchema = z.lazy(() => fieldBaseSchema
73
81
  .extend({
@@ -86,6 +94,7 @@ const knownFieldSchema = z.discriminatedUnion('type', [
86
94
  referenceFieldSchema,
87
95
  objectFieldSchema,
88
96
  blockFieldSchema,
97
+ inlineGroupFieldSchema,
89
98
  ]);
90
99
  // Complete field schema (built-in or custom)
91
100
  const fieldSchema = z.lazy(() => z.union([knownFieldSchema, customFieldSchema]));
@@ -1 +1 @@
1
- {"version":3,"file":"field.js","sourceRoot":"","sources":["../../../src/config/schemas/field.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"field.js","sourceRoot":"","sources":["../../../src/config/schemas/field.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ import type { LogicalPath, ContentId } from '../paths/types';
9
9
  import type { PermissionPath } from '../authorization/types';
10
10
  import type { EntryLinkUrlResolver } from '../entry-link-resolver';
11
11
  export declare const primitiveFieldTypes: readonly ["string", "number", "boolean", "datetime", "rich-text", "markdown", "mdx", "image", "code"];
12
- export declare const fieldTypes: readonly ["string", "number", "boolean", "datetime", "rich-text", "markdown", "mdx", "image", "code", "select", "reference", "object", "block"];
12
+ export declare const fieldTypes: readonly ["string", "number", "boolean", "datetime", "rich-text", "markdown", "mdx", "image", "code", "select", "reference", "object", "block", "group"];
13
13
  export type PrimitiveFieldType = (typeof primitiveFieldTypes)[number];
14
14
  export type FieldType = (typeof fieldTypes)[number];
15
15
  export type ContentFormat = 'md' | 'mdx' | 'json' | 'yaml';
@@ -82,6 +82,19 @@ export interface ObjectFieldConfig extends BaseFieldConfig {
82
82
  type: 'object';
83
83
  fields: FieldConfig[];
84
84
  }
85
+ /**
86
+ * Inline group field config: visually groups fields in the editor without creating
87
+ * a nested data key. Fields inside the group are stored flat in the parent content.
88
+ * Use defineInlineFieldGroup() to create these.
89
+ * For data-nested grouping, use ObjectFieldConfig / defineNestedFieldGroup() instead.
90
+ */
91
+ export interface InlineGroupFieldConfig {
92
+ type: 'group';
93
+ name: string;
94
+ label?: string;
95
+ description?: string;
96
+ fields: FieldConfig[];
97
+ }
85
98
  /**
86
99
  * Custom field config for user-defined field types.
87
100
  * The type must not conflict with built-in types.
@@ -90,7 +103,7 @@ export interface ObjectFieldConfig extends BaseFieldConfig {
90
103
  export type CustomFieldConfig = BaseFieldConfig & {
91
104
  type: Exclude<string, FieldType>;
92
105
  };
93
- export type FieldConfig = PrimitiveFieldConfig | SelectFieldConfig | ReferenceFieldConfig | BlockFieldConfig | ObjectFieldConfig | CustomFieldConfig;
106
+ export type FieldConfig = PrimitiveFieldConfig | SelectFieldConfig | ReferenceFieldConfig | BlockFieldConfig | ObjectFieldConfig | InlineGroupFieldConfig | CustomFieldConfig;
94
107
  export type MediaConfig = {
95
108
  adapter: 'local';
96
109
  publicBaseUrl?: string;