@contentrain/mcp 1.0.7 → 1.1.1
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/README.md +13 -2
- package/dist/{apply-manager-BDtHM8z-.mjs → apply-manager-Cf5OWOJ7.mjs} +7 -7
- package/dist/{apply-manager-BDtHM8z-.mjs.map → apply-manager-Cf5OWOJ7.mjs.map} +1 -1
- package/dist/{branch-lifecycle-DB6sgeJi.mjs → branch-lifecycle-wGJg25LT.mjs} +31 -12
- package/dist/branch-lifecycle-wGJg25LT.mjs.map +1 -0
- package/dist/{config-CPlhDwsv.mjs → config-B5kc2oeV.mjs} +2 -2
- package/dist/{config-CPlhDwsv.mjs.map → config-B5kc2oeV.mjs.map} +1 -1
- package/dist/{context-CUFulSi2.mjs → context-DIDt_TxX.mjs} +4 -4
- package/dist/{context-CUFulSi2.mjs.map → context-DIDt_TxX.mjs.map} +1 -1
- package/dist/core/apply-manager.mjs +10 -5
- package/dist/core/config.mjs +3 -1
- package/dist/core/content-manager.d.mts +1 -9
- package/dist/core/content-manager.d.mts.map +1 -1
- package/dist/core/content-manager.mjs +4 -1
- package/dist/core/context.mjs +6 -2
- package/dist/core/graph-builder.mjs +4 -1
- package/dist/core/meta-manager.mjs +3 -1
- package/dist/core/model-manager.mjs +4 -1
- package/dist/core/scan-config.mjs +3 -1
- package/dist/core/scanner.mjs +4 -1
- package/dist/core/validator.d.mts.map +1 -1
- package/dist/core/validator.mjs +6 -2
- package/dist/{detect-C8XmJ4S6.mjs → detect-DinOQAmK.mjs} +2 -2
- package/dist/{detect-C8XmJ4S6.mjs.map → detect-DinOQAmK.mjs.map} +1 -1
- package/dist/{fs-BbuGnVcG.mjs → fs-BnIQ4dKj.mjs} +2 -2
- package/dist/{fs-BbuGnVcG.mjs.map → fs-BnIQ4dKj.mjs.map} +1 -1
- package/dist/git/branch-lifecycle.d.mts.map +1 -1
- package/dist/git/branch-lifecycle.mjs +4 -1
- package/dist/git/transaction.d.mts +7 -3
- package/dist/git/transaction.d.mts.map +1 -1
- package/dist/git/transaction.mjs +9 -4
- package/dist/{graph-builder-fPrDDwnu.mjs → graph-builder-tM7zj-SX.mjs} +3 -3
- package/dist/{graph-builder-fPrDDwnu.mjs.map → graph-builder-tM7zj-SX.mjs.map} +1 -1
- package/dist/{id-BdD4HvMY.mjs → id-DV_T9Ic8.mjs} +3 -5
- package/dist/id-DV_T9Ic8.mjs.map +1 -0
- package/dist/index.mjs +16 -9
- package/dist/index.mjs.map +1 -1
- package/dist/{meta-manager-CECrfsKm.mjs → meta-manager--0IwAPCl.mjs} +2 -2
- package/dist/{meta-manager-CECrfsKm.mjs.map → meta-manager--0IwAPCl.mjs.map} +1 -1
- package/dist/{model-manager-kl78AAMg.mjs → model-manager-DW_fOGRM.mjs} +16 -186
- package/dist/model-manager-DW_fOGRM.mjs.map +1 -0
- package/dist/{scan-config-AJGxCdNd.mjs → scan-config-QgnJ1-52.mjs} +2 -2
- package/dist/{scan-config-AJGxCdNd.mjs.map → scan-config-QgnJ1-52.mjs.map} +1 -1
- package/dist/{scanner-BdTiv8sw.mjs → scanner-DPrbU-oK.mjs} +3 -3
- package/dist/{scanner-BdTiv8sw.mjs.map → scanner-DPrbU-oK.mjs.map} +1 -1
- package/dist/serializer-CnHTtmc1.mjs +2 -0
- package/dist/{server-QgivXkem.mjs → server-BXguCuJb.mjs} +62 -47
- package/dist/server-BXguCuJb.mjs.map +1 -0
- package/dist/server.mjs +16 -9
- package/dist/templates/index.mjs +1 -1
- package/dist/{templates-3HWi-PTu.mjs → templates-bRPzYq4M.mjs} +1 -1
- package/dist/{templates-3HWi-PTu.mjs.map → templates-bRPzYq4M.mjs.map} +1 -1
- package/dist/transaction-B0V0_SE2.mjs +288 -0
- package/dist/transaction-B0V0_SE2.mjs.map +1 -0
- package/dist/util/detect.mjs +3 -1
- package/dist/util/fs.mjs +2 -1
- package/dist/util/id.d.mts +2 -1
- package/dist/util/id.d.mts.map +1 -1
- package/dist/util/id.mjs +1 -1
- package/dist/util/serializer.d.mts +2 -14
- package/dist/util/serializer.mjs +2 -2
- package/dist/{validator-CyckZAVr.mjs → validator-SQYUV2JP.mjs} +23 -197
- package/dist/validator-SQYUV2JP.mjs.map +1 -0
- package/package.json +3 -3
- package/dist/branch-lifecycle-DB6sgeJi.mjs.map +0 -1
- package/dist/id-BdD4HvMY.mjs.map +0 -1
- package/dist/model-manager-kl78AAMg.mjs.map +0 -1
- package/dist/serializer-Dueih-i2.mjs +0 -33
- package/dist/serializer-Dueih-i2.mjs.map +0 -1
- package/dist/server-QgivXkem.mjs.map +0 -1
- package/dist/transaction-_AiXqGuD.mjs +0 -143
- package/dist/transaction-_AiXqGuD.mjs.map +0 -1
- package/dist/util/serializer.d.mts.map +0 -1
- package/dist/validator-CyckZAVr.mjs.map +0 -1
|
@@ -1,180 +1,10 @@
|
|
|
1
|
-
import { a as readJson, c as writeText, i as readDir, n as ensureDir, o as readText, s as writeJson, t as contentrainDir } from "./fs-
|
|
2
|
-
import {
|
|
3
|
-
import { r as writeMeta, t as deleteMeta } from "./meta-manager-CECrfsKm.mjs";
|
|
1
|
+
import { a as readJson, c as writeText, i as readDir, n as ensureDir, o as readText, s as writeJson, t as contentrainDir } from "./fs-BnIQ4dKj.mjs";
|
|
2
|
+
import { r as writeMeta, t as deleteMeta } from "./meta-manager--0IwAPCl.mjs";
|
|
4
3
|
import { join } from "node:path";
|
|
5
4
|
import { z } from "zod";
|
|
6
5
|
import { rm } from "node:fs/promises";
|
|
6
|
+
import { generateEntryId, parseMarkdownFrontmatter, parseMarkdownFrontmatter as parseFrontmatter, serializeMarkdownFrontmatter, serializeMarkdownFrontmatter as serializeFrontmatter, validateEntryId, validateLocale, validateSlug } from "@contentrain/types";
|
|
7
7
|
//#region src/core/content-manager.ts
|
|
8
|
-
const SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
9
|
-
const ENTRY_ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,39}$/;
|
|
10
|
-
const LOCALE_RE = /^[a-z]{2}(?:-[A-Z]{2})?$/;
|
|
11
|
-
function validateSlug(slug) {
|
|
12
|
-
if (!slug) return "Slug is required";
|
|
13
|
-
if (!SLUG_RE.test(slug)) return `Invalid slug "${slug}": must be kebab-case alphanumeric (a-z, 0-9, hyphens)`;
|
|
14
|
-
if (slug.startsWith(".") || slug.includes("..")) return `Invalid slug "${slug}": path traversal not allowed`;
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
function validateEntryId(id) {
|
|
18
|
-
if (!ENTRY_ID_RE.test(id)) return `Invalid entry ID "${id}": must be 1-40 alphanumeric characters (hyphens and underscores allowed)`;
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
function validateLocale(locale, config) {
|
|
22
|
-
if (!LOCALE_RE.test(locale)) return `Invalid locale "${locale}": must be ISO 639-1 format (e.g. "en", "tr", "pt-BR")`;
|
|
23
|
-
if (!config.locales.supported.includes(locale)) return `Locale "${locale}" is not in supported locales [${config.locales.supported.join(", ")}]. Update config first.`;
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
function parseFrontmatter(content) {
|
|
27
|
-
const normalized = content.replace(/\r\n/g, "\n");
|
|
28
|
-
const match = normalized.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
29
|
-
if (!match) return {
|
|
30
|
-
frontmatter: {},
|
|
31
|
-
body: normalized
|
|
32
|
-
};
|
|
33
|
-
const frontmatterStr = match[1];
|
|
34
|
-
const body = match[2].trim();
|
|
35
|
-
const frontmatter = {};
|
|
36
|
-
let currentKey = null;
|
|
37
|
-
let currentArray = null;
|
|
38
|
-
for (const line of frontmatterStr.split("\n")) {
|
|
39
|
-
if (/^\s+-\s+/.test(line) && currentKey) {
|
|
40
|
-
const value = line.replace(/^\s+-\s+/, "").trim();
|
|
41
|
-
if (!currentArray) currentArray = [];
|
|
42
|
-
currentArray.push(value);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
if (currentKey && currentArray) {
|
|
46
|
-
frontmatter[currentKey] = currentArray;
|
|
47
|
-
currentKey = null;
|
|
48
|
-
currentArray = null;
|
|
49
|
-
}
|
|
50
|
-
const kvMatch = line.match(/^([\w][\w.-]*)\s*:\s*(.*)$/);
|
|
51
|
-
if (!kvMatch) continue;
|
|
52
|
-
const key = kvMatch[1];
|
|
53
|
-
const rawValue = kvMatch[2].trim();
|
|
54
|
-
if (rawValue === "") {
|
|
55
|
-
currentKey = key;
|
|
56
|
-
currentArray = [];
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (rawValue.startsWith("[") && rawValue.endsWith("]")) {
|
|
60
|
-
frontmatter[key] = rawValue.slice(1, -1).split(",").map((s) => s.trim()).filter(Boolean);
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
frontmatter[key] = parseYamlValue(rawValue);
|
|
64
|
-
}
|
|
65
|
-
if (currentKey && currentArray) frontmatter[currentKey] = currentArray;
|
|
66
|
-
return {
|
|
67
|
-
frontmatter,
|
|
68
|
-
body
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function parseYamlValue(raw) {
|
|
72
|
-
if (raw === "true") return true;
|
|
73
|
-
if (raw === "false") return false;
|
|
74
|
-
if (raw === "null") return null;
|
|
75
|
-
if (/^-?\d+$/.test(raw)) return parseInt(raw, 10);
|
|
76
|
-
if (/^-?\d+\.\d+$/.test(raw)) return parseFloat(raw);
|
|
77
|
-
if (raw.startsWith("\"") && raw.endsWith("\"") || raw.startsWith("'") && raw.endsWith("'")) return raw.slice(1, -1);
|
|
78
|
-
return raw;
|
|
79
|
-
}
|
|
80
|
-
function yamlScalar(value) {
|
|
81
|
-
const str = String(value);
|
|
82
|
-
if (str.includes(":") || str.includes("#") || str.includes("{") || str.includes("}") || str.includes("[") || str.includes("]") || str.includes("*") || str.includes("&") || str.includes("!") || str.includes("|") || str.includes(">") || str.includes("'") || str.includes("\"") || str.includes("%") || str.includes("@") || str.includes("`") || str.startsWith("-") || str.startsWith("?") || str === "true" || str === "false" || str === "null" || str === "yes" || str === "no" || str === "") return `"${str.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
83
|
-
return str;
|
|
84
|
-
}
|
|
85
|
-
function serializeYamlValue(value, indent) {
|
|
86
|
-
const pad = " ".repeat(indent);
|
|
87
|
-
if (value === null || value === void 0) return [];
|
|
88
|
-
if (typeof value === "object" && !Array.isArray(value)) {
|
|
89
|
-
const lines = [];
|
|
90
|
-
for (const [k, v] of Object.entries(value)) {
|
|
91
|
-
if (v === null || v === void 0) continue;
|
|
92
|
-
if (typeof v === "object" && !Array.isArray(v)) {
|
|
93
|
-
lines.push(`${pad}${k}:`);
|
|
94
|
-
lines.push(...serializeYamlValue(v, indent + 1));
|
|
95
|
-
} else if (Array.isArray(v)) {
|
|
96
|
-
lines.push(`${pad}${k}:`);
|
|
97
|
-
for (const item of v) if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
98
|
-
const nested = serializeYamlValue(item, indent + 2);
|
|
99
|
-
if (nested.length > 0) {
|
|
100
|
-
lines.push(`${pad} - ${nested[0].trimStart()}`);
|
|
101
|
-
lines.push(...nested.slice(1));
|
|
102
|
-
}
|
|
103
|
-
} else lines.push(`${pad} - ${yamlScalar(item)}`);
|
|
104
|
-
} else lines.push(`${pad}${k}: ${yamlScalar(v)}`);
|
|
105
|
-
}
|
|
106
|
-
return lines;
|
|
107
|
-
}
|
|
108
|
-
if (Array.isArray(value)) {
|
|
109
|
-
const lines = [];
|
|
110
|
-
for (const item of value) if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
111
|
-
const nested = serializeYamlValue(item, indent + 1);
|
|
112
|
-
if (nested.length > 0) {
|
|
113
|
-
lines.push(`${pad}- ${nested[0].trimStart()}`);
|
|
114
|
-
lines.push(...nested.slice(1));
|
|
115
|
-
}
|
|
116
|
-
} else lines.push(`${pad}- ${yamlScalar(item)}`);
|
|
117
|
-
return lines;
|
|
118
|
-
}
|
|
119
|
-
return [`${pad}${yamlScalar(value)}`];
|
|
120
|
-
}
|
|
121
|
-
function serializeYamlFields(data) {
|
|
122
|
-
const lines = [];
|
|
123
|
-
for (const [key, value] of Object.entries(data)) {
|
|
124
|
-
if (key === "body") continue;
|
|
125
|
-
if (value === null || value === void 0) continue;
|
|
126
|
-
if (typeof value === "object" && !Array.isArray(value)) {
|
|
127
|
-
lines.push(`${key}:`);
|
|
128
|
-
lines.push(...serializeYamlValue(value, 1));
|
|
129
|
-
} else if (Array.isArray(value)) {
|
|
130
|
-
lines.push(`${key}:`);
|
|
131
|
-
for (const item of value) if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
132
|
-
const nested = serializeYamlValue(item, 2);
|
|
133
|
-
if (nested.length > 0) {
|
|
134
|
-
lines.push(` - ${nested[0].trimStart()}`);
|
|
135
|
-
lines.push(...nested.slice(1));
|
|
136
|
-
}
|
|
137
|
-
} else lines.push(` - ${yamlScalar(item)}`);
|
|
138
|
-
} else lines.push(`${key}: ${yamlScalar(value)}`);
|
|
139
|
-
}
|
|
140
|
-
return lines;
|
|
141
|
-
}
|
|
142
|
-
function serializeFrontmatter(data, body) {
|
|
143
|
-
const trimmedBody = body.trimStart();
|
|
144
|
-
if (trimmedBody.startsWith("---")) {
|
|
145
|
-
const endIdx = trimmedBody.indexOf("---", 3);
|
|
146
|
-
if (endIdx > 0) {
|
|
147
|
-
const bodyFmContent = trimmedBody.slice(3, endIdx).trim();
|
|
148
|
-
const afterFm = trimmedBody.slice(endIdx + 3);
|
|
149
|
-
const lines = ["---"];
|
|
150
|
-
lines.push(...serializeYamlFields(data));
|
|
151
|
-
if (bodyFmContent) {
|
|
152
|
-
const modelKeys = new Set(Object.keys(data));
|
|
153
|
-
for (const line of bodyFmContent.split("\n")) {
|
|
154
|
-
if (line.length > 0 && line[0] !== " " && line[0] !== "-") {
|
|
155
|
-
const colonIdx = line.indexOf(":");
|
|
156
|
-
const lineKey = colonIdx > 0 ? line.slice(0, colonIdx).trim() : "";
|
|
157
|
-
if (lineKey && modelKeys.has(lineKey)) continue;
|
|
158
|
-
}
|
|
159
|
-
lines.push(line);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
lines.push("---");
|
|
163
|
-
if (afterFm.trim()) lines.push(afterFm.trimStart());
|
|
164
|
-
lines.push("");
|
|
165
|
-
return lines.join("\n");
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
const lines = ["---"];
|
|
169
|
-
lines.push(...serializeYamlFields(data));
|
|
170
|
-
lines.push("---");
|
|
171
|
-
lines.push("");
|
|
172
|
-
if (body) {
|
|
173
|
-
lines.push(body);
|
|
174
|
-
lines.push("");
|
|
175
|
-
}
|
|
176
|
-
return lines.join("\n");
|
|
177
|
-
}
|
|
178
8
|
function resolveContentDir(projectRoot, model) {
|
|
179
9
|
if (model.content_path) return join(projectRoot, model.content_path);
|
|
180
10
|
return join(contentrainDir(projectRoot), "content", model.domain, model.id);
|
|
@@ -268,7 +98,7 @@ async function writeContent(projectRoot, model, entries, config) {
|
|
|
268
98
|
if (!fmData["slug"]) fmData["slug"] = slug;
|
|
269
99
|
const docPath = resolveMdFilePath(resolveContentDir(projectRoot, model), model, locale, slug);
|
|
270
100
|
const action = await readText(docPath) ? "updated" : "created";
|
|
271
|
-
await writeText(docPath,
|
|
101
|
+
await writeText(docPath, serializeMarkdownFrontmatter(fmData, bodyContent));
|
|
272
102
|
await writeMeta(projectRoot, model, {
|
|
273
103
|
locale,
|
|
274
104
|
slug
|
|
@@ -423,7 +253,7 @@ async function listContent(projectRoot, model, opts, config) {
|
|
|
423
253
|
const slug = f.replace(".md", "");
|
|
424
254
|
const raw = await readText(join(cDir, f));
|
|
425
255
|
if (!raw) continue;
|
|
426
|
-
const { frontmatter, body } =
|
|
256
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
427
257
|
entries.push({
|
|
428
258
|
slug,
|
|
429
259
|
frontmatter,
|
|
@@ -435,7 +265,7 @@ async function listContent(projectRoot, model, opts, config) {
|
|
|
435
265
|
for (const slug of slugDirs) {
|
|
436
266
|
const raw = await readText(join(cDir, slug, `${locale}.md`));
|
|
437
267
|
if (!raw) continue;
|
|
438
|
-
const { frontmatter, body } =
|
|
268
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
439
269
|
entries.push({
|
|
440
270
|
slug,
|
|
441
271
|
frontmatter,
|
|
@@ -450,7 +280,7 @@ async function listContent(projectRoot, model, opts, config) {
|
|
|
450
280
|
const slug = f.slice(0, -suffix.length);
|
|
451
281
|
const raw = await readText(join(cDir, f));
|
|
452
282
|
if (!raw) continue;
|
|
453
|
-
const { frontmatter, body } =
|
|
283
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
454
284
|
entries.push({
|
|
455
285
|
slug,
|
|
456
286
|
frontmatter,
|
|
@@ -465,7 +295,7 @@ async function listContent(projectRoot, model, opts, config) {
|
|
|
465
295
|
const slug = f.replace(".md", "");
|
|
466
296
|
const raw = await readText(join(localeDir, f));
|
|
467
297
|
if (!raw) continue;
|
|
468
|
-
const { frontmatter, body } =
|
|
298
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
469
299
|
entries.push({
|
|
470
300
|
slug,
|
|
471
301
|
frontmatter,
|
|
@@ -479,7 +309,7 @@ async function listContent(projectRoot, model, opts, config) {
|
|
|
479
309
|
const slug = f.replace(".md", "");
|
|
480
310
|
const raw = await readText(join(cDir, f));
|
|
481
311
|
if (!raw) continue;
|
|
482
|
-
const { frontmatter, body } =
|
|
312
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
483
313
|
entries.push({
|
|
484
314
|
slug,
|
|
485
315
|
frontmatter,
|
|
@@ -526,7 +356,7 @@ async function readContent(projectRoot, model, opts) {
|
|
|
526
356
|
if (!opts.slug) return null;
|
|
527
357
|
const raw = await readText(resolveMdFilePath(cDir, model, opts.locale, opts.slug));
|
|
528
358
|
if (!raw) return null;
|
|
529
|
-
const { frontmatter, body } =
|
|
359
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
530
360
|
return {
|
|
531
361
|
slug: opts.slug,
|
|
532
362
|
...frontmatter,
|
|
@@ -567,7 +397,7 @@ async function resolveRelations(projectRoot, model, entries, locale) {
|
|
|
567
397
|
const slug = f.replace(".md", "");
|
|
568
398
|
const raw = await readText(join(cDir, f));
|
|
569
399
|
if (!raw) continue;
|
|
570
|
-
const { frontmatter, body } =
|
|
400
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
571
401
|
docCache[slug] = {
|
|
572
402
|
slug,
|
|
573
403
|
...frontmatter,
|
|
@@ -579,7 +409,7 @@ async function resolveRelations(projectRoot, model, entries, locale) {
|
|
|
579
409
|
for (const slug of slugDirs) {
|
|
580
410
|
const raw = await readText(join(cDir, slug, `${locale}.md`));
|
|
581
411
|
if (!raw) continue;
|
|
582
|
-
const { frontmatter, body } =
|
|
412
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
583
413
|
docCache[slug] = {
|
|
584
414
|
slug,
|
|
585
415
|
...frontmatter,
|
|
@@ -594,7 +424,7 @@ async function resolveRelations(projectRoot, model, entries, locale) {
|
|
|
594
424
|
const slug = f.slice(0, -suffix.length);
|
|
595
425
|
const raw = await readText(join(cDir, f));
|
|
596
426
|
if (!raw) continue;
|
|
597
|
-
const { frontmatter, body } =
|
|
427
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
598
428
|
docCache[slug] = {
|
|
599
429
|
slug,
|
|
600
430
|
...frontmatter,
|
|
@@ -609,7 +439,7 @@ async function resolveRelations(projectRoot, model, entries, locale) {
|
|
|
609
439
|
const slug = f.replace(".md", "");
|
|
610
440
|
const raw = await readText(join(localeDir, f));
|
|
611
441
|
if (!raw) continue;
|
|
612
|
-
const { frontmatter, body } =
|
|
442
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
613
443
|
docCache[slug] = {
|
|
614
444
|
slug,
|
|
615
445
|
...frontmatter,
|
|
@@ -623,7 +453,7 @@ async function resolveRelations(projectRoot, model, entries, locale) {
|
|
|
623
453
|
const slug = f.replace(".md", "");
|
|
624
454
|
const raw = await readText(join(cDir, f));
|
|
625
455
|
if (!raw) continue;
|
|
626
|
-
const { frontmatter, body } =
|
|
456
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(raw);
|
|
627
457
|
docCache[slug] = {
|
|
628
458
|
slug,
|
|
629
459
|
...frontmatter,
|
|
@@ -1013,4 +843,4 @@ function validateModelDefinition(input) {
|
|
|
1013
843
|
//#endregion
|
|
1014
844
|
export { writeContent as S, resolveMdFilePath as _, fieldDefZodSchema as a, validateLocale as b, validateModelDefinition as c, listContent as d, parseFrontmatter as f, resolveLocaleStrategy as g, resolveJsonFilePath as h, deleteModel as i, writeModel as l, resolveContentDir as m, checkReferences as n, listModels as o, readContent as p, countEntries as r, readModel as s, FIELD_TYPE_ENUM as t, deleteContent as u, serializeFrontmatter as v, validateSlug as x, validateEntryId as y };
|
|
1015
845
|
|
|
1016
|
-
//# sourceMappingURL=model-manager-
|
|
846
|
+
//# sourceMappingURL=model-manager-DW_fOGRM.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-manager-DW_fOGRM.mjs","names":[],"sources":["../src/core/content-manager.ts","../src/core/model-manager.ts"],"sourcesContent":["import type { ModelDefinition, ContentrainConfig, EntryMeta, LocaleStrategy, DocumentEntry } from '@contentrain/types'\nimport { validateSlug, validateEntryId, validateLocale, generateEntryId, parseMarkdownFrontmatter, serializeMarkdownFrontmatter } from '@contentrain/types'\nimport { join } from 'node:path'\nimport { rm } from 'node:fs/promises'\nimport { contentrainDir, readDir, readJson, readText, writeJson, writeText } from '../util/fs.js'\nimport { writeMeta, deleteMeta } from './meta-manager.js'\nimport { readModel } from './model-manager.js'\n\n// Re-export for backward compatibility (MCP internal consumers)\nexport { validateSlug, validateEntryId, validateLocale }\nexport { parseMarkdownFrontmatter as parseFrontmatter, serializeMarkdownFrontmatter as serializeFrontmatter } from '@contentrain/types'\n\n// ─── Content paths ───\n\nexport function resolveContentDir(projectRoot: string, model: ModelDefinition): string {\n if (model.content_path) {\n return join(projectRoot, model.content_path)\n }\n return join(contentrainDir(projectRoot), 'content', model.domain, model.id)\n}\n\nexport function resolveLocaleStrategy(model: ModelDefinition): LocaleStrategy {\n return model.locale_strategy ?? 'file'\n}\n\n/** Build the file path for a JSON content file (singleton/collection/dictionary) */\nexport function resolveJsonFilePath(dir: string, model: ModelDefinition, locale: string): string {\n // When i18n is disabled, always use data.json (locale parameter is ignored)\n if (!model.i18n) return join(dir, 'data.json')\n\n switch (resolveLocaleStrategy(model)) {\n case 'suffix': return join(dir, `${model.id}.${locale}.json`)\n case 'directory': return join(dir, locale, `${model.id}.json`)\n case 'none': return join(dir, `${model.id}.json`)\n case 'file':\n default: return join(dir, `${locale}.json`)\n }\n}\n\n/** Build the file path for a markdown document */\nexport function resolveMdFilePath(dir: string, model: ModelDefinition, locale: string, slug: string): string {\n // When i18n is disabled, always use {slug}.md (locale parameter is ignored)\n if (!model.i18n) return join(dir, `${slug}.md`)\n\n switch (resolveLocaleStrategy(model)) {\n case 'suffix': return join(dir, `${slug}.${locale}.md`)\n case 'directory': return join(dir, locale, `${slug}.md`)\n case 'none': return join(dir, `${slug}.md`)\n case 'file':\n default: return join(dir, slug, `${locale}.md`)\n }\n}\n\n// ─── Types ───\n\nexport interface ContentEntry {\n id?: string\n slug?: string\n locale?: string\n data: Record<string, unknown>\n}\n\nexport interface WriteResult {\n action: 'created' | 'updated'\n id?: string\n slug?: string\n locale: string\n}\n\nexport interface DeleteOpts {\n id?: string\n slug?: string\n locale?: string\n keys?: string[]\n}\n\nexport interface ListOpts {\n locale?: string\n filter?: Record<string, unknown>\n resolve?: boolean\n limit?: number\n offset?: number\n}\n\n// ─── Default meta ───\n\nfunction defaultMeta(data?: Record<string, unknown>): EntryMeta {\n const meta: EntryMeta = {\n status: 'draft',\n source: 'agent',\n updated_by: 'contentrain-mcp',\n }\n if (data?.['publish_at'] !== undefined) {\n meta.publish_at = data['publish_at'] as string\n }\n if (data?.['expire_at'] !== undefined) {\n meta.expire_at = data['expire_at'] as string\n }\n return meta\n}\n\n// ─── writeContent ───\n\nexport async function writeContent(\n projectRoot: string,\n model: ModelDefinition,\n entries: ContentEntry[],\n config: ContentrainConfig,\n): Promise<WriteResult[]> {\n const results: WriteResult[] = []\n const defaultLocale = config.locales.default\n\n for (const entry of entries) {\n const locale = entry.locale ?? defaultLocale\n\n // Validate locale\n const localeErr = validateLocale(locale, config)\n if (localeErr) throw new Error(localeErr)\n\n // Validate entry ID if provided\n if (entry.id) {\n const idErr = validateEntryId(entry.id)\n if (idErr) throw new Error(idErr)\n }\n\n // Validate slug if provided\n if (entry.slug) {\n const slugErr = validateSlug(entry.slug)\n if (slugErr) throw new Error(slugErr)\n }\n\n switch (model.kind) {\n case 'singleton': {\n const filePath = resolveJsonFilePath(resolveContentDir(projectRoot, model), model, locale)\n await writeJson(filePath, entry.data)\n await writeMeta(projectRoot, model, { locale }, defaultMeta(entry.data))\n results.push({ action: 'updated', locale })\n break\n }\n\n case 'collection': {\n const isNew = !entry.id\n const id = entry.id ?? generateEntryId()\n const filePath = resolveJsonFilePath(resolveContentDir(projectRoot, model), model, locale)\n const existing = await readJson<Record<string, Record<string, unknown>>>(filePath) ?? {}\n\n const action: 'created' | 'updated' = (isNew || !(id in existing)) ? 'created' : 'updated'\n existing[id] = entry.data\n\n // Sort by entry ID for canonical output\n const sorted: Record<string, Record<string, unknown>> = {}\n for (const key of Object.keys(existing).toSorted()) {\n sorted[key] = existing[key]!\n }\n\n await writeJson(filePath, sorted)\n await writeMeta(projectRoot, model, { locale, entryId: id }, defaultMeta(entry.data))\n results.push({ action, id, locale })\n break\n }\n\n case 'document': {\n const slug = entry.slug ?? entry.data['slug'] as string\n if (!slug) throw new Error('Document entries require a slug')\n const slugErr = validateSlug(slug)\n if (slugErr) throw new Error(slugErr)\n\n const bodyContent = (entry.data['body'] as string) ?? ''\n const fmData = { ...entry.data }\n delete fmData['body']\n if (!fmData['slug']) fmData['slug'] = slug\n\n // Check if existing\n const docPath = resolveMdFilePath(resolveContentDir(projectRoot, model), model, locale, slug)\n const existingRaw = await readText(docPath)\n const action: 'created' | 'updated' = existingRaw ? 'updated' : 'created'\n\n const mdContent = serializeMarkdownFrontmatter(fmData, bodyContent)\n await writeText(docPath, mdContent)\n await writeMeta(projectRoot, model, { locale, slug }, defaultMeta(entry.data))\n results.push({ action, slug, locale })\n break\n }\n\n case 'dictionary': {\n const filePath = resolveJsonFilePath(resolveContentDir(projectRoot, model), model, locale)\n const existing = await readJson<Record<string, string>>(filePath) ?? {}\n const collisions: string[] = []\n for (const key of Object.keys(entry.data as Record<string, string>)) {\n if (key in existing && existing[key] !== (entry.data as Record<string, string>)[key]) {\n collisions.push(key)\n }\n }\n if (collisions.length > 0) {\n throw new Error(\n `Dictionary \"${model.id}\" (${locale}): ${collisions.length} key collision(s) — [${collisions.join(', ')}] already exist with different values. ` +\n `Read existing keys with contentrain_content_list first, or include all keys in a single save call.`,\n )\n }\n const merged = { ...existing, ...entry.data as Record<string, string> }\n await writeJson(filePath, merged)\n await writeMeta(projectRoot, model, { locale }, defaultMeta(entry.data))\n results.push({ action: 'updated', locale })\n break\n }\n }\n }\n\n return results\n}\n\n// ─── deleteContent ───\n\nexport async function deleteContent(\n projectRoot: string,\n model: ModelDefinition,\n opts: DeleteOpts,\n): Promise<string[]> {\n const removed: string[] = []\n const cDir = resolveContentDir(projectRoot, model)\n\n switch (model.kind) {\n case 'collection': {\n if (!opts.id) throw new Error('Collection delete requires an entry ID')\n const locales = opts.locale\n ? [opts.locale]\n : (await readDir(cDir)).filter(f => f.endsWith('.json')).map(f => f.replace('.json', '').replace(`${model.id}.`, ''))\n\n for (const loc of locales) {\n const filePath = resolveJsonFilePath(cDir, model, loc)\n const data = await readJson<Record<string, unknown>>(filePath)\n if (data && opts.id in data) {\n delete data[opts.id]\n await writeJson(filePath, data)\n removed.push(`content/${model.domain}/${model.id}/${loc}.json#${opts.id}`)\n }\n }\n\n for (const loc of locales) {\n await deleteMeta(projectRoot, model, { locale: loc, entryId: opts.id })\n }\n break\n }\n\n case 'document': {\n if (!opts.slug) throw new Error('Document delete requires a slug')\n const slugDelErr = validateSlug(opts.slug)\n if (slugDelErr) throw new Error(slugDelErr)\n\n const strategy = resolveLocaleStrategy(model)\n if (!model.i18n) {\n // No i18n: single {slug}.md file\n await rm(join(cDir, `${opts.slug}.md`), { force: true })\n } else if (strategy === 'file') {\n // slug is a directory — remove entire slug directory\n await rm(join(cDir, opts.slug), { recursive: true, force: true })\n } else if (opts.locale) {\n // Remove specific locale file\n const filePath = resolveMdFilePath(cDir, model, opts.locale, opts.slug)\n await rm(filePath, { force: true })\n } else {\n // Remove all locale files for this slug\n const files = await readDir(strategy === 'directory' ? cDir : cDir)\n for (const f of files) {\n if (strategy === 'suffix' && f.startsWith(`${opts.slug}.`) && f.endsWith('.md')) {\n await rm(join(cDir, f), { force: true })\n } else if (strategy === 'directory') {\n // f is a locale dir — remove slug.md from it\n await rm(join(cDir, f, `${opts.slug}.md`), { force: true })\n } else if (strategy === 'none') {\n if (f === `${opts.slug}.md`) await rm(join(cDir, f), { force: true })\n }\n }\n }\n removed.push(`${model.content_path ?? `content/${model.domain}/${model.id}`}/${opts.slug}`)\n\n await deleteMeta(projectRoot, model, { slug: opts.slug, locale: opts.locale })\n break\n }\n\n case 'singleton': {\n if (model.i18n && !opts.locale) throw new Error('Singleton delete requires a locale when i18n is enabled')\n const locale = opts.locale ?? 'data'\n const filePath = resolveJsonFilePath(cDir, model, locale)\n await rm(filePath, { force: true })\n removed.push(model.i18n\n ? `content/${model.domain}/${model.id}/${locale}.json`\n : `content/${model.domain}/${model.id}/data.json`)\n await deleteMeta(projectRoot, model, { locale: model.i18n ? locale : undefined })\n break\n }\n\n case 'dictionary': {\n if (model.i18n && !opts.locale) throw new Error('Dictionary delete requires a locale when i18n is enabled')\n const locale = opts.locale ?? 'data'\n const filePath = resolveJsonFilePath(cDir, model, locale)\n\n if (opts.keys?.length) {\n // Delete specific keys from dictionary\n const existing = await readJson<Record<string, string>>(filePath) ?? {}\n const notFound: string[] = []\n for (const key of opts.keys) {\n if (key in existing) {\n delete existing[key]\n } else {\n notFound.push(key)\n }\n }\n if (notFound.length > 0) {\n throw new Error(`Dictionary \"${model.id}\" (${locale}): keys not found — [${notFound.join(', ')}]`)\n }\n await writeJson(filePath, existing)\n removed.push(...opts.keys.map(k => `${model.id}/${locale}:${k}`))\n } else {\n // Delete entire locale file\n await rm(filePath, { force: true })\n removed.push(model.i18n\n ? `content/${model.domain}/${model.id}/${locale}.json`\n : `content/${model.domain}/${model.id}/data.json`)\n await deleteMeta(projectRoot, model, { locale: model.i18n ? locale : undefined })\n }\n break\n }\n }\n\n return removed\n}\n\n// ─── listContent ───\n\nexport async function listContent(\n projectRoot: string,\n model: ModelDefinition,\n opts: ListOpts,\n config: ContentrainConfig,\n): Promise<unknown> {\n const cDir = resolveContentDir(projectRoot, model)\n const locale = opts.locale ?? config.locales.default\n\n switch (model.kind) {\n case 'singleton': {\n const data = await readJson<Record<string, unknown>>(resolveJsonFilePath(cDir, model, locale))\n return { kind: 'singleton', data: data ?? {}, locale }\n }\n\n case 'collection': {\n const data = await readJson<Record<string, Record<string, unknown>>>(resolveJsonFilePath(cDir, model, locale)) ?? {}\n let entries: Array<Record<string, unknown>> = Object.entries(data).map(([id, fields]) => {\n const entry: Record<string, unknown> = { id }\n Object.assign(entry, fields)\n return entry\n })\n\n // Filter\n if (opts.filter) {\n entries = entries.filter(entry => {\n for (const [key, value] of Object.entries(opts.filter!)) {\n if (entry[key] !== value) return false\n }\n return true\n })\n }\n\n const total = entries.length\n\n // Pagination\n const offset = opts.offset ?? 0\n const limit = opts.limit ?? entries.length\n entries = entries.slice(offset, offset + limit)\n\n // Resolve relations\n if (opts.resolve && model.fields) {\n entries = await resolveRelations(projectRoot, model, entries, locale)\n }\n\n return { kind: 'collection', data: entries, total, locale, offset, limit }\n }\n\n case 'document': {\n const entries: DocumentEntry[] = []\n const strategy = resolveLocaleStrategy(model)\n\n if (!model.i18n) {\n // No i18n: flat {slug}.md files, no locale in path\n const files = await readDir(cDir)\n for (const f of files) {\n if (!f.endsWith('.md')) continue\n const slug = f.replace('.md', '')\n const raw = await readText(join(cDir, f))\n if (!raw) continue\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n entries.push({ slug, frontmatter, body })\n }\n } else if (strategy === 'file') {\n // Each slug is a subdirectory containing locale.md files\n const slugDirs = await readDir(cDir)\n for (const slug of slugDirs) {\n const raw = await readText(join(cDir, slug, `${locale}.md`))\n if (!raw) continue\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n entries.push({ slug, frontmatter, body })\n }\n } else if (strategy === 'suffix') {\n // Files like {slug}.{locale}.md in flat directory\n const files = await readDir(cDir)\n const suffix = `.${locale}.md`\n for (const f of files) {\n if (!f.endsWith(suffix)) continue\n const slug = f.slice(0, -suffix.length)\n const raw = await readText(join(cDir, f))\n if (!raw) continue\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n entries.push({ slug, frontmatter, body })\n }\n } else if (strategy === 'directory') {\n // Files in {locale}/ directory as {slug}.md\n const localeDir = join(cDir, locale)\n const files = await readDir(localeDir)\n for (const f of files) {\n if (!f.endsWith('.md')) continue\n const slug = f.replace('.md', '')\n const raw = await readText(join(localeDir, f))\n if (!raw) continue\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n entries.push({ slug, frontmatter, body })\n }\n } else {\n // none — {slug}.md, no locale in filename\n const files = await readDir(cDir)\n for (const f of files) {\n if (!f.endsWith('.md')) continue\n const slug = f.replace('.md', '')\n const raw = await readText(join(cDir, f))\n if (!raw) continue\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n entries.push({ slug, frontmatter, body })\n }\n }\n\n const total = entries.length\n const offset = opts.offset ?? 0\n const limit = opts.limit ?? entries.length\n const paged = entries.slice(offset, offset + limit)\n\n return { kind: 'document', data: paged, total, locale, offset, limit }\n }\n\n case 'dictionary': {\n const data = await readJson<Record<string, string>>(resolveJsonFilePath(cDir, model, locale)) ?? {}\n return { kind: 'dictionary', data, total_keys: Object.keys(data).length, locale }\n }\n }\n}\n\n// ─── readContent (for external use / relation resolving) ───\n\nexport async function readContent(\n projectRoot: string,\n model: ModelDefinition,\n opts: { locale: string; entryId?: string; slug?: string },\n): Promise<unknown> {\n const cDir = resolveContentDir(projectRoot, model)\n\n switch (model.kind) {\n case 'singleton':\n return readJson<Record<string, unknown>>(resolveJsonFilePath(cDir, model, opts.locale))\n\n case 'collection': {\n if (!opts.entryId) return null\n const data = await readJson<Record<string, Record<string, unknown>>>(resolveJsonFilePath(cDir, model, opts.locale))\n return data?.[opts.entryId] ? { id: opts.entryId, ...data[opts.entryId] } : null\n }\n\n case 'document': {\n if (!opts.slug) return null\n const raw = await readText(resolveMdFilePath(cDir, model, opts.locale, opts.slug))\n if (!raw) return null\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n return { slug: opts.slug, ...frontmatter, body }\n }\n\n case 'dictionary':\n return readJson<Record<string, string>>(resolveJsonFilePath(cDir, model, opts.locale))\n }\n}\n\n// ─── Relation resolving ───\n\nasync function resolveRelations(\n projectRoot: string,\n model: ModelDefinition,\n entries: Array<Record<string, unknown>>,\n locale: string,\n): Promise<Array<Record<string, unknown>>> {\n if (!model.fields) return entries\n\n const relationFields: Array<{ name: string; targetModels: string[]; multi: boolean }> = []\n for (const [name, field] of Object.entries(model.fields)) {\n if (field.type === 'relation' || field.type === 'relations') {\n const targets = Array.isArray(field.model) ? field.model : field.model ? [field.model] : []\n relationFields.push({ name, targetModels: targets, multi: field.type === 'relations' })\n }\n }\n\n if (relationFields.length === 0) return entries\n\n // Load target model contents into cache (with circular reference protection)\n const targetCache: Record<string, Record<string, Record<string, unknown>>> = {}\n const visited = new Set<string>([model.id])\n\n for (const rf of relationFields) {\n for (const targetModelId of rf.targetModels) {\n if (targetCache[targetModelId] || visited.has(targetModelId)) continue\n visited.add(targetModelId)\n\n const targetModel = await readModel(projectRoot, targetModelId)\n if (!targetModel) continue\n\n if (targetModel.kind === 'collection') {\n const targetData = await readJson<Record<string, Record<string, unknown>>>(\n resolveJsonFilePath(resolveContentDir(projectRoot, targetModel), targetModel, locale),\n ) ?? {}\n targetCache[targetModelId] = targetData\n } else if (targetModel.kind === 'document') {\n // Build slug-keyed cache by listing document entries for this locale\n const docCache: Record<string, Record<string, unknown>> = {}\n const cDir = resolveContentDir(projectRoot, targetModel)\n const strategy = resolveLocaleStrategy(targetModel)\n\n if (!targetModel.i18n) {\n const files = await readDir(cDir)\n for (const f of files) {\n if (!f.endsWith('.md')) continue\n const slug = f.replace('.md', '')\n const raw = await readText(join(cDir, f))\n if (!raw) continue\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n docCache[slug] = { slug, ...frontmatter, body }\n }\n } else if (strategy === 'file') {\n const slugDirs = await readDir(cDir)\n for (const slug of slugDirs) {\n const raw = await readText(join(cDir, slug, `${locale}.md`))\n if (!raw) continue\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n docCache[slug] = { slug, ...frontmatter, body }\n }\n } else if (strategy === 'suffix') {\n const files = await readDir(cDir)\n const suffix = `.${locale}.md`\n for (const f of files) {\n if (!f.endsWith(suffix)) continue\n const slug = f.slice(0, -suffix.length)\n const raw = await readText(join(cDir, f))\n if (!raw) continue\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n docCache[slug] = { slug, ...frontmatter, body }\n }\n } else if (strategy === 'directory') {\n const localeDir = join(cDir, locale)\n const files = await readDir(localeDir)\n for (const f of files) {\n if (!f.endsWith('.md')) continue\n const slug = f.replace('.md', '')\n const raw = await readText(join(localeDir, f))\n if (!raw) continue\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n docCache[slug] = { slug, ...frontmatter, body }\n }\n } else {\n // 'none' strategy\n const files = await readDir(cDir)\n for (const f of files) {\n if (!f.endsWith('.md')) continue\n const slug = f.replace('.md', '')\n const raw = await readText(join(cDir, f))\n if (!raw) continue\n const { frontmatter, body } = parseMarkdownFrontmatter(raw)\n docCache[slug] = { slug, ...frontmatter, body }\n }\n }\n\n targetCache[targetModelId] = docCache\n }\n }\n }\n\n return entries.map(entry => {\n const resolved = { ...entry }\n for (const rf of relationFields) {\n const value = resolved[rf.name]\n if (!value) continue\n\n if (rf.multi && Array.isArray(value)) {\n resolved[rf.name] = value.map(id => {\n for (const targetModelId of rf.targetModels) {\n const cached = targetCache[targetModelId]?.[id as string]\n if (cached) return { id, ...cached }\n }\n return id\n })\n } else if (typeof value === 'string') {\n for (const targetModelId of rf.targetModels) {\n const cached = targetCache[targetModelId]?.[value]\n if (cached) {\n resolved[rf.name] = { id: value, ...cached }\n break\n }\n }\n }\n }\n return resolved\n })\n}\n","import type { ModelDefinition, ModelSummary } from '@contentrain/types'\nimport { join } from 'node:path'\nimport { rm } from 'node:fs/promises'\nimport { z } from 'zod'\nimport { contentrainDir, ensureDir, readDir, readJson, writeJson } from '../util/fs.js'\nimport { resolveContentDir, resolveLocaleStrategy } from './content-manager.js'\n\nexport type { ModelSummary } from '@contentrain/types'\n\nexport async function listModels(projectRoot: string): Promise<ModelSummary[]> {\n const modelsDir = join(contentrainDir(projectRoot), 'models')\n const files = await readDir(modelsDir)\n const jsonFiles = files.filter(f => f.endsWith('.json'))\n\n const models = await Promise.all(\n jsonFiles.map(file => readJson<ModelDefinition>(join(modelsDir, file))),\n )\n\n return models\n .filter((m): m is ModelDefinition => m !== null && !!m.id)\n .map(model => ({\n id: model.id,\n kind: model.kind,\n domain: model.domain,\n i18n: model.i18n,\n fields: model.fields ? Object.keys(model.fields).length : 0,\n }))\n .toSorted((a, b) => a.id.localeCompare(b.id, 'en'))\n}\n\nexport async function readModel(projectRoot: string, modelId: string): Promise<ModelDefinition | null> {\n const filePath = join(contentrainDir(projectRoot), 'models', `${modelId}.json`)\n return readJson<ModelDefinition>(filePath)\n}\n\nasync function countDocumentFileStrategy(\n contentDir: string,\n entries: string[],\n): Promise<{ total: number; locales: Record<string, number> }> {\n const locales: Record<string, number> = {}\n let total = 0\n\n const results = await Promise.all(\n entries.map(async (entry) => {\n const localeFiles = await readDir(join(contentDir, entry))\n return localeFiles\n .map(lf => lf.replace(/\\.(json|md|mdx)$/, ''))\n .filter((locale, i) => locale !== localeFiles[i])\n }),\n )\n\n for (const entryLocales of results) {\n for (const locale of entryLocales) {\n locales[locale] = (locales[locale] ?? 0) + 1\n total++\n }\n }\n\n return { total, locales }\n}\n\nasync function countDocumentSuffixStrategy(\n _contentDir: string,\n files: string[],\n): Promise<{ total: number; locales: Record<string, number> }> {\n const locales: Record<string, number> = {}\n const slugsByLocale: Record<string, Set<string>> = {}\n\n for (const f of files) {\n if (!f.endsWith('.md')) continue\n // Pattern: {slug}.{locale}.md\n const match = f.match(/^(.+)\\.([a-z]{2}(?:-[A-Z]{2})?)\\.md$/)\n if (!match) continue\n const locale = match[2]!\n if (!slugsByLocale[locale]) slugsByLocale[locale] = new Set()\n slugsByLocale[locale].add(match[1]!)\n }\n\n let total = 0\n for (const [locale, slugs] of Object.entries(slugsByLocale)) {\n locales[locale] = slugs.size\n total += slugs.size\n }\n\n return { total, locales }\n}\n\nasync function countDocumentDirectoryStrategy(\n contentDir: string,\n localeDirs: string[],\n): Promise<{ total: number; locales: Record<string, number> }> {\n const locales: Record<string, number> = {}\n let total = 0\n\n const results = await Promise.all(\n localeDirs.map(async (localeDir) => {\n const files = await readDir(join(contentDir, localeDir))\n const mdFiles = files.filter(f => f.endsWith('.md'))\n return { locale: localeDir, count: mdFiles.length }\n }),\n )\n\n for (const { locale, count } of results) {\n locales[locale] = count\n total += count\n }\n\n return { total, locales }\n}\n\nasync function countDocumentNoneStrategy(\n projectRoot: string,\n modelId: string,\n files: string[],\n i18n: boolean,\n): Promise<{ total: number; locales: Record<string, number> }> {\n const mdFiles = files.filter(f => f.endsWith('.md'))\n\n if (!i18n) {\n return { total: mdFiles.length, locales: { _: mdFiles.length } }\n }\n\n // With i18n + none strategy, content files have no locale info.\n // Use meta/{modelId}/{slug}/ directories to determine locale counts.\n const metaDir = join(contentrainDir(projectRoot), 'meta', modelId)\n const locales: Record<string, number> = {}\n let total = 0\n\n const slugs = mdFiles.map(f => f.replace('.md', ''))\n const results = await Promise.all(\n slugs.map(async (slug) => {\n const metaFiles = await readDir(join(metaDir, slug))\n return metaFiles\n .filter(f => f.endsWith('.json'))\n .map(f => f.replace('.json', ''))\n }),\n )\n\n for (const slugLocales of results) {\n for (const locale of slugLocales) {\n locales[locale] = (locales[locale] ?? 0) + 1\n total++\n }\n }\n\n return { total, locales }\n}\n\nasync function countCollectionEntries(\n contentDir: string,\n jsonFiles: string[],\n): Promise<{ total: number; locales: Record<string, number> }> {\n const locales: Record<string, number> = {}\n let total = 0\n\n const results = await Promise.all(\n jsonFiles.map(async (file) => {\n const locale = file.replace(/\\.json$/, '')\n const data = await readJson<Record<string, unknown>>(join(contentDir, file))\n return { locale, count: data ? Object.keys(data).length : 0 }\n }),\n )\n\n for (const { locale, count } of results) {\n locales[locale] = count\n total += count\n }\n\n return { total, locales }\n}\n\nexport async function countEntries(\n projectRoot: string,\n model: ModelDefinition,\n): Promise<{ total: number; locales: Record<string, number> }> {\n const cDir = resolveContentDir(projectRoot, model)\n const strategy = resolveLocaleStrategy(model)\n const files = await readDir(cDir)\n\n if (model.kind === 'document') {\n if (!model.i18n) {\n // No i18n: flat {slug}.md files\n return countDocumentNoneStrategy(projectRoot, model.id, files, false)\n }\n\n switch (strategy) {\n case 'file':\n return countDocumentFileStrategy(cDir, files)\n case 'suffix':\n return countDocumentSuffixStrategy(cDir, files)\n case 'directory':\n return countDocumentDirectoryStrategy(cDir, files)\n case 'none':\n return countDocumentNoneStrategy(projectRoot, model.id, files, true)\n }\n }\n\n if (model.kind === 'collection') {\n if (!model.i18n) {\n // Non-i18n: single data.json\n const jsonFiles = files.filter(f => f.endsWith('.json'))\n return countCollectionEntries(cDir, jsonFiles)\n }\n switch (strategy) {\n case 'suffix': {\n // Files: {model}.{locale}.json\n const jsonFiles = files.filter(f => f.endsWith('.json'))\n const locales: Record<string, number> = {}\n for (const f of jsonFiles) {\n const match = f.match(/^.+\\.([a-z]{2}(?:-[A-Z]{2})?)\\.json$/)\n if (match) {\n const data = await readJson<Record<string, unknown>>(join(cDir, f))\n locales[match[1]!] = data ? Object.keys(data).length : 0\n }\n }\n const total = Object.values(locales).reduce((a, b) => a + b, 0)\n return { total, locales }\n }\n case 'directory': {\n // Dirs: {locale}/... containing json files\n const locales: Record<string, number> = {}\n let total = 0\n for (const localeDir of files) {\n const subFiles = await readDir(join(cDir, localeDir))\n const jsonFile = subFiles.find(f => f.endsWith('.json'))\n if (jsonFile) {\n const data = await readJson<Record<string, unknown>>(join(cDir, localeDir, jsonFile))\n const count = data ? Object.keys(data).length : 0\n locales[localeDir] = count\n total += count\n }\n }\n return { total, locales }\n }\n case 'none': {\n // Single file: {model}.json — count entries inside\n const noneFile = files.find(f => f === `${model.id}.json`)\n if (!noneFile) return { total: 0, locales: {} }\n const data = await readJson<Record<string, unknown>>(join(cDir, noneFile))\n const count = data ? Object.keys(data).length : 0\n return { total: count, locales: { _: count } }\n }\n default: {\n // 'file': {locale}.json\n const jsonFiles = files.filter(f => f.endsWith('.json'))\n return countCollectionEntries(cDir, jsonFiles)\n }\n }\n }\n\n // singleton / dictionary — one entry per locale file\n if (!model.i18n) {\n // Non-i18n: single data.json\n const hasData = files.some(f => f === 'data.json')\n return { total: hasData ? 1 : 0, locales: {} }\n }\n\n switch (strategy) {\n case 'suffix': {\n // Files: {model}.{locale}.json\n const locales: Record<string, number> = {}\n for (const f of files) {\n const match = f.match(/^.+\\.([a-z]{2}(?:-[A-Z]{2})?)\\.json$/)\n if (match) locales[match[1]!] = 1\n }\n return { total: Object.keys(locales).length, locales }\n }\n case 'directory': {\n // Dirs: {locale}/...\n const locales: Record<string, number> = {}\n for (const localeDir of files) {\n const subFiles = await readDir(join(cDir, localeDir))\n if (subFiles.some(f => f.endsWith('.json'))) {\n locales[localeDir] = 1\n }\n }\n return { total: Object.keys(locales).length, locales }\n }\n case 'none': {\n // Single file: {model}.json\n const hasFile = files.some(f => f === `${model.id}.json`)\n return { total: hasFile ? 1 : 0, locales: {} }\n }\n default: {\n // 'file': {locale}.json\n const jsonFiles = files.filter(f => f.endsWith('.json'))\n const locales: Record<string, number> = {}\n for (const file of jsonFiles) {\n locales[file.replace(/\\.json$/, '')] = 1\n }\n return { total: jsonFiles.length, locales }\n }\n }\n}\n\nconst MODEL_FIELD_ORDER = ['id', 'name', 'kind', 'domain', 'i18n', 'description', 'content_path', 'locale_strategy', 'fields']\n\nexport async function writeModel(projectRoot: string, model: ModelDefinition): Promise<void> {\n const filePath = join(contentrainDir(projectRoot), 'models', `${model.id}.json`)\n await writeJson(filePath, model, MODEL_FIELD_ORDER)\n\n // Ensure content and meta directories exist (respects content_path)\n await ensureDir(resolveContentDir(projectRoot, model))\n await ensureDir(join(contentrainDir(projectRoot), 'meta', model.id))\n}\n\nexport async function deleteModel(projectRoot: string, modelId: string): Promise<string[]> {\n const model = await readModel(projectRoot, modelId)\n if (!model) return []\n\n const crDir = contentrainDir(projectRoot)\n const removed: string[] = []\n\n const modelPath = join(crDir, 'models', `${modelId}.json`)\n const contentPath = resolveContentDir(projectRoot, model)\n const metaPath = join(crDir, 'meta', modelId)\n\n await rm(modelPath, { force: true })\n removed.push(`models/${modelId}.json`)\n\n try {\n await rm(contentPath, { recursive: true, force: true })\n removed.push(model.content_path ?? `content/${model.domain}/${modelId}/`)\n } catch {\n // directory may not exist\n }\n\n try {\n await rm(metaPath, { recursive: true, force: true })\n removed.push(`meta/${modelId}/`)\n } catch {\n // directory may not exist\n }\n\n return removed\n}\n\nexport interface ModelReference {\n model: string\n field: string\n type: 'relation' | 'relations'\n}\n\nexport async function checkReferences(projectRoot: string, modelId: string): Promise<ModelReference[]> {\n const summaries = await listModels(projectRoot)\n const others = summaries.filter(s => s.id !== modelId)\n\n const models = await Promise.all(\n others.map(s => readModel(projectRoot, s.id)),\n )\n\n const refs: ModelReference[] = []\n for (const model of models) {\n if (!model?.fields) continue\n\n for (const [fieldName, fieldDef] of Object.entries(model.fields)) {\n if (fieldDef.type !== 'relation' && fieldDef.type !== 'relations') continue\n const targets = Array.isArray(fieldDef.model) ? fieldDef.model : [fieldDef.model]\n if (targets.includes(modelId)) {\n refs.push({ model: model.id, field: fieldName, type: fieldDef.type })\n }\n }\n }\n\n return refs\n}\n\n// ─── Shared Field Type Catalog ───\n\nexport const FIELD_TYPE_ENUM = [\n 'string', 'text', 'email', 'url', 'slug', 'color', 'phone', 'code', 'icon',\n 'markdown', 'richtext',\n 'number', 'integer', 'decimal', 'percent', 'rating',\n 'boolean', 'date', 'datetime',\n 'image', 'video', 'file',\n 'relation', 'relations',\n 'select', 'array', 'object',\n] as const\n\n/**\n * Shared Zod schema for field definitions.\n * Used by both model_save and normalize extract for full parity.\n */\nexport const fieldDefZodSchema: z.ZodType<Record<string, unknown>> = z.record(z.string(), z.object({\n type: z.enum(FIELD_TYPE_ENUM).describe('Field type from the 27-type catalog'),\n required: z.boolean().optional(),\n unique: z.boolean().optional(),\n default: z.unknown().optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n pattern: z.string().optional(),\n options: z.array(z.string()).optional(),\n model: z.union([z.string(), z.array(z.string())]).optional(),\n items: z.union([z.string(), z.lazy(() => z.record(z.string(), z.unknown()))]).optional(),\n fields: z.lazy(() => z.record(z.string(), z.unknown())).optional(),\n accept: z.string().optional(),\n maxSize: z.number().optional(),\n description: z.string().optional(),\n}).refine(\n (f) => {\n if ((f.type === 'relation' || f.type === 'relations') && !f.model) return false\n if (f.type === 'select' && (!f.options || f.options.length === 0)) return false\n return true\n },\n { message: 'relation/relations requires \"model\", select requires non-empty \"options\"' },\n))\n\n// ─── Model Validation (shared between model_save and normalize extract) ───\n\nconst VALID_FIELD_TYPES = new Set<string>(FIELD_TYPE_ENUM)\n\n/**\n * Validate a model definition before writing.\n * Returns array of error messages (empty = valid).\n * Used by both model_save tool and normalize extract.\n */\nexport function validateModelDefinition(input: { id: string; kind: string; fields?: Record<string, unknown> }): string[] {\n const errors: string[] = []\n\n // ID format: kebab-case\n if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(input.id)) {\n errors.push(`Invalid model ID \"${input.id}\": must be kebab-case`)\n }\n\n // Dictionary models cannot have fields\n if (input.kind === 'dictionary' && input.fields && Object.keys(input.fields).length > 0) {\n errors.push('Dictionary models cannot have fields. Dictionaries store flat key-value pairs.')\n }\n\n // Fields validation\n if (input.fields) {\n for (const [fieldName, fieldDef] of Object.entries(input.fields)) {\n const def = fieldDef as { type?: string; model?: unknown; options?: unknown }\n\n // Field name format\n if (!/^[a-z][a-z0-9_]*$/.test(fieldName)) {\n errors.push(`Field \"${fieldName}\": invalid name — must be snake_case starting with letter`)\n }\n\n // Type check\n if (!def.type || !VALID_FIELD_TYPES.has(def.type)) {\n errors.push(`Field \"${fieldName}\": invalid type \"${def.type}\"`)\n }\n\n // Relation requires model\n if ((def.type === 'relation' || def.type === 'relations') && !def.model) {\n errors.push(`Field \"${fieldName}\": ${def.type} type requires \"model\" property`)\n }\n\n // Select requires options\n if (def.type === 'select' && (!def.options || !Array.isArray(def.options) || def.options.length === 0)) {\n errors.push(`Field \"${fieldName}\": select type requires non-empty \"options\" array`)\n }\n }\n }\n\n return errors\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as pathExists } from "./fs-
|
|
1
|
+
import { r as pathExists } from "./fs-BnIQ4dKj.mjs";
|
|
2
2
|
import { basename, extname, join } from "node:path";
|
|
3
3
|
import { readdir } from "node:fs/promises";
|
|
4
4
|
//#region src/core/scan-config.ts
|
|
@@ -156,4 +156,4 @@ async function discoverFiles(projectRoot, options) {
|
|
|
156
156
|
//#endregion
|
|
157
157
|
export { classifyFile as a, autoDetectSourceDirs as i, SCAN_EXTENSIONS as n, discoverFiles as o, SCAN_IGNORE_DIRS as r, MAX_SCAN_FILES as t };
|
|
158
158
|
|
|
159
|
-
//# sourceMappingURL=scan-config-
|
|
159
|
+
//# sourceMappingURL=scan-config-QgnJ1-52.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan-config-
|
|
1
|
+
{"version":3,"file":"scan-config-QgnJ1-52.mjs","names":[],"sources":["../src/core/scan-config.ts"],"sourcesContent":["import { readdir } from 'node:fs/promises'\nimport { join, extname, basename } from 'node:path'\nimport { pathExists } from '../util/fs.js'\n\n// ─── Shared Scan Constants ───\n\n/** File extensions to scan across all JS/TS ecosystem projects */\nexport const SCAN_EXTENSIONS = new Set([\n '.tsx', '.jsx', '.vue', '.ts', '.js', '.mjs', '.astro', '.svelte',\n])\n\n/** Directory names to always exclude from scanning */\nexport const SCAN_IGNORE_DIRS = new Set([\n // Package managers / deps\n 'node_modules', '.pnpm',\n // Build outputs\n 'dist', 'build', 'out', '.output',\n // Framework caches\n '.nuxt', '.next', '.svelte-kit', '.expo', '.turbo', '.parcel-cache', '.vercel', '.netlify',\n // Test / coverage\n 'coverage', '__tests__', '__mocks__',\n // VCS / IDE\n '.git', '.vscode', '.idea',\n // Contentrain\n '.contentrain',\n])\n\n/** Max files per scan operation */\nexport const MAX_SCAN_FILES = 500\n\n/** File patterns to skip regardless of extension */\nconst SKIP_FILE_RE = /\\.(test|spec)\\.[^.]+$|\\.d\\.ts$|\\.min\\.[^.]+$/\n\n// ─── File Classification ───\n// Covers: React, Next.js, Nuxt, Vue, Astro, SvelteKit, Remix,\n// React Native/Expo, NestJS, Express, Fastify, Koa, Hapi\n\n/** Directories that represent entry points / pages / screens */\nconst PAGE_DIR_NAMES = new Set([\n // Frontend routing\n 'pages', 'routes', 'screens', 'views',\n // Backend entry points\n 'controllers', 'handlers', 'resolvers',\n])\n\n/** Directories that represent reusable components / modules */\nconst COMPONENT_DIR_NAMES = new Set([\n // UI components\n 'components', 'ui', 'widgets', 'elements',\n // Feature modules\n 'features', 'modules',\n // Backend services\n 'services', 'providers',\n // Shared / common\n 'shared', 'common',\n])\n\n/** Directories that represent layouts / templates */\nconst LAYOUT_DIR_NAMES = new Set([\n 'layouts', 'templates',\n])\n\n/** Next.js App Router special files that are page-like */\nconst NEXTJS_PAGE_FILES = new Set([\n 'page.tsx', 'page.jsx', 'page.ts', 'page.js',\n 'layout.tsx', 'layout.jsx', 'layout.ts', 'layout.js',\n 'error.tsx', 'error.jsx',\n 'loading.tsx', 'loading.jsx',\n 'not-found.tsx', 'not-found.jsx',\n])\n\n/** Classify a file into page/component/layout/other based on path heuristics */\nexport function classifyFile(relPath: string): 'page' | 'component' | 'layout' | 'other' {\n const parts = relPath.split('/')\n const fileName = parts[parts.length - 1] ?? ''\n\n // Next.js App Router: files in app/ with special names are pages\n if (parts.includes('app') && NEXTJS_PAGE_FILES.has(fileName)) {\n return fileName.startsWith('layout') ? 'layout' : 'page'\n }\n\n // Check directory names in path (check layout first — more specific)\n for (const part of parts) {\n const lower = part.toLowerCase()\n if (LAYOUT_DIR_NAMES.has(lower)) return 'layout'\n if (PAGE_DIR_NAMES.has(lower)) return 'page'\n if (COMPONENT_DIR_NAMES.has(lower)) return 'component'\n }\n\n return 'other'\n}\n\n// ─── Source Directory Detection ───\n\n/** Common source directories across all JS/TS project types */\nconst AUTO_DETECT_DIRS = [\n // Standard source\n 'src', 'app', 'lib',\n // Frontend specific\n 'pages', 'components', 'layouts', 'views',\n // Mobile\n 'screens',\n // Backend\n 'modules', 'routes', 'controllers', 'services',\n // Shared\n 'features', 'shared', 'common',\n // Hooks / composables\n 'hooks', 'composables',\n // Stores\n 'stores',\n]\n\n/** Auto-detect which source directories exist in the project */\nexport async function autoDetectSourceDirs(projectRoot: string): Promise<string[]> {\n const found: string[] = []\n for (const dir of AUTO_DETECT_DIRS) {\n if (await pathExists(join(projectRoot, dir))) {\n found.push(dir)\n }\n }\n return found.length > 0 ? found : ['.']\n}\n\n// ─── File Discovery ───\n\nexport interface DiscoverFilesOptions {\n paths?: string[]\n include?: string[]\n exclude?: string[]\n}\n\n/**\n * Discover source files matching scan criteria.\n * Returns relative paths (relative to projectRoot).\n */\nexport async function discoverFiles(\n projectRoot: string,\n options?: DiscoverFilesOptions,\n): Promise<string[]> {\n const extensions = options?.include\n ? new Set(options.include.map(e => e.startsWith('.') ? e : `.${e}`))\n : SCAN_EXTENSIONS\n const extraExcludes = new Set(options?.exclude ?? [])\n const scanDirs = options?.paths ?? await autoDetectSourceDirs(projectRoot)\n\n const files: string[] = []\n\n for (const dir of scanDirs) {\n const absDir = join(projectRoot, dir)\n if (!(await pathExists(absDir))) continue\n\n let entries: string[]\n try {\n entries = await readdir(absDir, { recursive: true }) as unknown as string[]\n } catch {\n continue\n }\n\n for (const entry of entries) {\n if (files.length >= MAX_SCAN_FILES) break\n\n const fileName = basename(entry)\n\n // Check if any path segment is excluded\n const pathSegments = entry.split('/')\n if (pathSegments.some(seg => SCAN_IGNORE_DIRS.has(seg) || extraExcludes.has(seg))) continue\n\n // Check extension\n if (!extensions.has(extname(fileName))) continue\n\n // Skip test/spec/declaration/minified files\n if (SKIP_FILE_RE.test(fileName)) continue\n\n files.push(join(dir, entry))\n }\n\n if (files.length >= MAX_SCAN_FILES) break\n }\n\n return files.toSorted((a, b) => a.localeCompare(b)).slice(0, MAX_SCAN_FILES)\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { o as readText } from "./fs-
|
|
2
|
-
import { i as autoDetectSourceDirs, o as discoverFiles } from "./scan-config-
|
|
1
|
+
import { o as readText } from "./fs-BnIQ4dKj.mjs";
|
|
2
|
+
import { i as autoDetectSourceDirs, o as discoverFiles } from "./scan-config-QgnJ1-52.mjs";
|
|
3
3
|
import { t as parseTsx } from "./tsx-parser-CYVyBWjn.mjs";
|
|
4
4
|
import { extname, join } from "node:path";
|
|
5
5
|
//#region src/core/ast-scanner/pre-filter.ts
|
|
@@ -616,4 +616,4 @@ async function scanSummary(projectRoot, options) {
|
|
|
616
616
|
//#endregion
|
|
617
617
|
export { scanCandidates as n, scanSummary as r, isNonContent as t };
|
|
618
618
|
|
|
619
|
-
//# sourceMappingURL=scanner-
|
|
619
|
+
//# sourceMappingURL=scanner-DPrbU-oK.mjs.map
|