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.
- package/dist/ai/json-to-markdown.d.ts.map +1 -1
- package/dist/ai/json-to-markdown.js +4 -3
- package/dist/ai/json-to-markdown.js.map +1 -1
- package/dist/api/content.d.ts +9 -0
- package/dist/api/content.d.ts.map +1 -1
- package/dist/api/content.js +20 -3
- package/dist/api/content.js.map +1 -1
- package/dist/branch-metadata.d.ts.map +1 -1
- package/dist/branch-metadata.js +2 -20
- package/dist/branch-metadata.js.map +1 -1
- package/dist/cli/generate-ai-content.js +167 -114
- package/dist/cli/init.js +12 -3
- package/dist/cli/template-files/schemas.ts.template +45 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/schemas/field.d.ts +19 -0
- package/dist/config/schemas/field.d.ts.map +1 -1
- package/dist/config/schemas/field.js +9 -0
- package/dist/config/schemas/field.js.map +1 -1
- package/dist/config/types.d.ts +15 -2
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/config/validation.d.ts +13 -0
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +130 -0
- package/dist/config/validation.js.map +1 -1
- package/dist/content-store.d.ts +12 -0
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +166 -106
- package/dist/content-store.js.map +1 -1
- package/dist/editor/FormRenderer.d.ts.map +1 -1
- package/dist/editor/FormRenderer.js +6 -0
- package/dist/editor/FormRenderer.js.map +1 -1
- package/dist/editor/client-reference-resolver.d.ts.map +1 -1
- package/dist/editor/client-reference-resolver.js +2 -1
- package/dist/editor/client-reference-resolver.js.map +1 -1
- package/dist/editor/fields/InlineGroupField.d.ts +17 -0
- package/dist/editor/fields/InlineGroupField.d.ts.map +1 -0
- package/dist/editor/fields/InlineGroupField.js +16 -0
- package/dist/editor/fields/InlineGroupField.js.map +1 -0
- package/dist/editor/hooks/useDraftManager.d.ts.map +1 -1
- package/dist/editor/hooks/useDraftManager.js +7 -3
- package/dist/editor/hooks/useDraftManager.js.map +1 -1
- package/dist/editor/hooks/useEntryManager.d.ts +5 -0
- package/dist/editor/hooks/useEntryManager.d.ts.map +1 -1
- package/dist/editor/hooks/useEntryManager.js +29 -2
- package/dist/editor/hooks/useEntryManager.js.map +1 -1
- package/dist/editor/hooks/useReferenceResolution.d.ts.map +1 -1
- package/dist/editor/hooks/useReferenceResolution.js +2 -1
- package/dist/editor/hooks/useReferenceResolution.js.map +1 -1
- package/dist/entry-schema-registry.d.ts.map +1 -1
- package/dist/entry-schema-registry.js +4 -2
- package/dist/entry-schema-registry.js.map +1 -1
- package/dist/entry-schema.d.ts +69 -1
- package/dist/entry-schema.d.ts.map +1 -1
- package/dist/entry-schema.js +43 -0
- package/dist/entry-schema.js.map +1 -1
- package/dist/schema/schema-store.d.ts.map +1 -1
- package/dist/schema/schema-store.js +3 -2
- package/dist/schema/schema-store.js.map +1 -1
- package/dist/utils/async-mutex.d.ts +2 -0
- package/dist/utils/async-mutex.d.ts.map +1 -0
- package/dist/utils/async-mutex.js +28 -0
- package/dist/utils/async-mutex.js.map +1 -0
- package/dist/utils/body-field.d.ts.map +1 -1
- package/dist/utils/body-field.js +4 -3
- package/dist/utils/body-field.js.map +1 -1
- package/dist/utils/flatten-group-fields.d.ts +13 -0
- package/dist/utils/flatten-group-fields.d.ts.map +1 -0
- package/dist/utils/flatten-group-fields.js +23 -0
- package/dist/utils/flatten-group-fields.js.map +1 -0
- package/dist/utils/title-field.d.ts.map +1 -1
- package/dist/utils/title-field.js +55 -24
- package/dist/utils/title-field.js.map +1 -1
- package/dist/validation/deletion-checker.d.ts.map +1 -1
- package/dist/validation/deletion-checker.js +5 -0
- package/dist/validation/deletion-checker.js.map +1 -1
- package/dist/validation/field-traversal.d.ts.map +1 -1
- package/dist/validation/field-traversal.js +7 -1
- package/dist/validation/field-traversal.js.map +1 -1
- 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
|
-
|
|
1424
|
-
|
|
1425
|
-
if (
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
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
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
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
|
-
|
|
1461
|
-
|
|
1462
|
-
format: "yaml",
|
|
1529
|
+
...base,
|
|
1530
|
+
format: input.format,
|
|
1463
1531
|
data: input.data ?? {},
|
|
1464
|
-
|
|
1465
|
-
|
|
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
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
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
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
const
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
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
|
-
|
|
1564
|
-
|
|
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
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
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
|
|
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 {
|
|
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
|
})
|
package/dist/config/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/config/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
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,
|
|
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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"}
|
package/dist/config/types.d.ts
CHANGED
|
@@ -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;
|