@grove-dev/core 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,346 @@
1
+ /**
2
+ * The record normalizer: the one place that turns the three record
3
+ * layers into the record every output is built from.
4
+ *
5
+ * 1. Human source — a record file (see "Formats" below), patched by
6
+ * `paths.overrides`.
7
+ * 2. Generated cache — `paths.githubCache/<slug>.json` (GitHub
8
+ * metadata and health, written by
9
+ * `grove sync github`), with a legacy inline
10
+ * `github` / `health` block and `paths.health`
11
+ * as fallbacks. Health from a stale sync resolves
12
+ * as `status: unknown` (`sync.github.maxAgeDays`).
13
+ * 3. Decisions — `paths.decisions`, which has the final say on
14
+ * visibility.
15
+ *
16
+ * Formats — all supported permanently, and mixable within one site:
17
+ *
18
+ * - YAML only: `paths.recordsDir/<slug>.yml`, for data-only sites.
19
+ * - YAML + pointer: the same file with `content: ./…/<slug>.md`
20
+ * pointing at a Markdown body. `records.deprecateContentPointer`
21
+ * opts in to a `record_format_deprecated` warning for these.
22
+ * - Markdown: `paths.bodiesDir/<slug>.md`, YAML frontmatter (at least
23
+ * `name`, or `title` on a resource hub) plus the review body. A `.md`
24
+ * file a YAML record points at through `content:` is that record's
25
+ * body, never a record of its own.
26
+ *
27
+ * `generate()` (browse, collections, SEO, sitemap, llms), `grove check`,
28
+ * `grove cleanup` and `grove readme generate` all read records through
29
+ * {@link loadNormalizedRecords}, so they cannot disagree about a
30
+ * record's fields or whether it is visible.
31
+ */
32
+ import { readdir, readFile } from 'node:fs/promises';
33
+ import { basename, join, relative, resolve, sep } from 'node:path';
34
+ import { parse as parseYaml } from 'yaml';
35
+ import { ZodError } from 'zod';
36
+ import { readContentFile, splitFrontmatter } from './content-body.js';
37
+ import { githubSyncFreshnessOptions, loadGithubCache, resolveRecordGithub, } from './github-cache.js';
38
+ import { classifyHealth } from './health.js';
39
+ import { blueprintKind, decisionsFileSchema, healthFileSchema, overridesFileSchema, recordsFileSchema, recordVisibility, unwrapDecisions, unwrapHealth, unwrapOverrides, } from './schema.js';
40
+ function isMapping(value) {
41
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
42
+ }
43
+ /** `./`-prefixed, `/`-separated path relative to `cwd`: the form `content:` pointers use. */
44
+ function contentPointerFor(cwd, path) {
45
+ return `./${relative(cwd, path).split(sep).join('/')}`;
46
+ }
47
+ /** Candidate absolute paths for a `content:` pointer, as the build resolves it. */
48
+ function pointerCandidates(cwd, pointer) {
49
+ return [resolve(cwd, pointer), resolve(cwd, 'apps', 'example', pointer)];
50
+ }
51
+ /**
52
+ * Discover and read every record file, in both formats, without merging
53
+ * any layer. A file that cannot be parsed is returned with an issue and
54
+ * no `data`. The same slug in both formats is a `duplicate_slug_format`
55
+ * error on both files: neither silently wins.
56
+ */
57
+ export async function readRecordSources(config, cwd = process.cwd()) {
58
+ const recordsDir = resolve(cwd, config.paths.recordsDir);
59
+ const bodiesDir = resolve(cwd, config.paths.bodiesDir ?? 'content/records');
60
+ // The field every record of this blueprint must have: frontmatter
61
+ // without it belongs to some other document.
62
+ const labelKey = blueprintKind[config.blueprint] === 'resource' ? 'title' : 'name';
63
+ const sources = [];
64
+ const issue = (source, code, message, severity = 'error') => ({
65
+ code,
66
+ severity,
67
+ slug: source.slug,
68
+ file: source.file,
69
+ message: `${source.slug}: ${message}`,
70
+ });
71
+ // ── YAML records ───────────────────────────────────────────────────
72
+ // Their `content:` pointers are collected first: a Markdown file one
73
+ // of them points at is that record's body, not a record.
74
+ const pointedAt = new Set();
75
+ const yamlFiles = (await readdir(recordsDir).catch(() => []))
76
+ .filter((name) => name.endsWith('.yml'))
77
+ .sort();
78
+ for (const name of yamlFiles) {
79
+ const path = join(recordsDir, name);
80
+ const source = {
81
+ slug: basename(name, '.yml'),
82
+ format: 'yaml',
83
+ path,
84
+ file: relative(cwd, path),
85
+ issues: [],
86
+ };
87
+ sources.push(source);
88
+ // `schema: 'core'` disables custom-tag interpretation: a record with
89
+ // `!!binary` / `!!js/function` must not become a host object.
90
+ let raw;
91
+ try {
92
+ raw = parseYaml(await readFile(path, 'utf8'), { schema: 'core' });
93
+ }
94
+ catch (err) {
95
+ source.issues.push(issue(source, 'schema_error', err.message));
96
+ continue;
97
+ }
98
+ if (!isMapping(raw)) {
99
+ source.issues.push(issue(source, 'schema_error', 'record file is empty or not a YAML mapping'));
100
+ continue;
101
+ }
102
+ source.data = raw;
103
+ if (typeof raw.content === 'string' && raw.content) {
104
+ source.format = 'yaml+content';
105
+ for (const candidate of pointerCandidates(cwd, raw.content))
106
+ pointedAt.add(candidate);
107
+ }
108
+ }
109
+ // ── Markdown records ───────────────────────────────────────────────
110
+ const markdownFiles = (await readdir(bodiesDir).catch(() => []))
111
+ .filter((name) => name.endsWith('.md'))
112
+ .sort();
113
+ for (const name of markdownFiles) {
114
+ const path = join(bodiesDir, name);
115
+ if (pointedAt.has(path))
116
+ continue;
117
+ const { frontmatter, body, hasFrontmatter } = splitFrontmatter(await readFile(path, 'utf8'));
118
+ // No frontmatter: a body nothing points at, or an unrelated page.
119
+ // Neither is a record.
120
+ if (!hasFrontmatter)
121
+ continue;
122
+ const source = {
123
+ slug: basename(name, '.md'),
124
+ format: 'markdown',
125
+ path,
126
+ file: relative(cwd, path),
127
+ issues: [],
128
+ };
129
+ let raw;
130
+ try {
131
+ raw = parseYaml(frontmatter, { schema: 'core' });
132
+ }
133
+ catch (err) {
134
+ source.issues.push(issue(source, 'schema_error', `frontmatter: ${err.message}`));
135
+ sources.push(source);
136
+ continue;
137
+ }
138
+ // A record declares at least its name (`title` on a resource hub);
139
+ // frontmatter without it is some other document's.
140
+ if (!isMapping(raw) || raw[labelKey] === undefined)
141
+ continue;
142
+ source.data = raw;
143
+ source.body = body;
144
+ sources.push(source);
145
+ }
146
+ // ── One slug, one format ───────────────────────────────────────────
147
+ const bySlug = new Map();
148
+ for (const source of sources) {
149
+ bySlug.set(source.slug, [...(bySlug.get(source.slug) ?? []), source]);
150
+ }
151
+ for (const group of bySlug.values()) {
152
+ if (group.length < 2)
153
+ continue;
154
+ const files = group.map((source) => source.file).join(' and ');
155
+ for (const source of group) {
156
+ source.issues.push(issue(source, 'duplicate_slug_format', `record is defined twice (${files}); keep one format and delete the other`));
157
+ }
158
+ }
159
+ sources.sort((a, b) => {
160
+ const nameA = basename(a.path);
161
+ const nameB = basename(b.path);
162
+ return nameA < nameB ? -1 : nameA > nameB ? 1 : 0;
163
+ });
164
+ return { sources };
165
+ }
166
+ /** Read an optional side file; a missing or invalid file yields `fallback`. */
167
+ async function readSideFile(path, cwd, parse, fallback) {
168
+ try {
169
+ const raw = await readFile(resolve(cwd, path), 'utf8');
170
+ return parse(parseYaml(raw, { schema: 'core' }) ?? {});
171
+ }
172
+ catch {
173
+ return fallback;
174
+ }
175
+ }
176
+ function loadDecisionVisibility(config, cwd) {
177
+ return readSideFile(config.paths.decisions, cwd, (raw) => new Map(unwrapDecisions(decisionsFileSchema.parse(raw)).map((d) => [d.id, d.decision.visibility])), new Map());
178
+ }
179
+ function loadHealthEntries(config, cwd) {
180
+ return readSideFile(config.paths.health, cwd, (raw) => new Map(unwrapHealth(healthFileSchema.parse(raw)).map((entry) => [entry.id, entry.health])), new Map());
181
+ }
182
+ function loadOverridePatches(config, cwd) {
183
+ return readSideFile(config.paths.overrides, cwd, (raw) => new Map(unwrapOverrides(overridesFileSchema.parse(raw)).map((entry) => [entry.id, entry.patch])), new Map());
184
+ }
185
+ /**
186
+ * Apply a `paths.decisions` visibility to a record. For project records
187
+ * the decision overwrites `health.visibility`; a project with no health
188
+ * block gets a fabricated "unknown" one from {@link classifyHealth},
189
+ * because list and index payloads read visibility from `health`. For
190
+ * resource-hub and ecosystem-map records (no `health` block) it sets the
191
+ * top-level `visibility`.
192
+ */
193
+ export function applyDecisionVisibility(record, visibility) {
194
+ if (!visibility)
195
+ return record;
196
+ if (record.kind === 'project') {
197
+ const health = record.health ?? classifyHealth(record.slug).health;
198
+ return { ...record, health: { ...health, visibility } };
199
+ }
200
+ return { ...record, visibility };
201
+ }
202
+ /**
203
+ * Discover, parse and merge every record, in either format (see
204
+ * {@link readRecordSources}).
205
+ *
206
+ * Per record, lowest to highest precedence:
207
+ *
208
+ * 1. the record file (YAML, or a Markdown record's frontmatter)
209
+ * 2. the GitHub sync cache, which supplies `github` / `health` over a
210
+ * legacy inline copy ({@link resolveRecordGithub})
211
+ * 3. the `paths.overrides` patch (shallow, top-level fields)
212
+ * 4. schema parse (`recordsFileSchema`); the slug is the file name
213
+ * 5. the `paths.health` entry, for a project record nothing above
214
+ * gave a health block
215
+ * 6. the `paths.decisions` visibility
216
+ *
217
+ * Effective visibility is then decision > health > the record's own
218
+ * `visibility` field ({@link recordVisibility}).
219
+ *
220
+ * Never throws for a bad record: a file that cannot be parsed or fails
221
+ * the schema is reported in `issues` and left out of `records`. Missing
222
+ * or invalid side files are treated as empty; `grove check` reports them.
223
+ */
224
+ export async function loadNormalizedRecords(config, cwd = process.cwd()) {
225
+ const expectedKind = blueprintKind[config.blueprint];
226
+ const [{ sources }, githubCache, decisions, healthBySlug, patches] = await Promise.all([
227
+ readRecordSources(config, cwd),
228
+ loadGithubCache(config, cwd),
229
+ loadDecisionVisibility(config, cwd),
230
+ loadHealthEntries(config, cwd),
231
+ loadOverridePatches(config, cwd),
232
+ ]);
233
+ const cachePath = relative(cwd, githubCache.dir) || '.';
234
+ // A stale sync resolves as `status: unknown` rather than old values.
235
+ const freshness = githubSyncFreshnessOptions(config);
236
+ const warnPointer = config.records?.deprecateContentPointer === true;
237
+ const entries = [];
238
+ for (const source of sources) {
239
+ const { slug, file, format } = source;
240
+ const entry = {
241
+ slug,
242
+ file,
243
+ format,
244
+ issues: [...source.issues],
245
+ syncStale: false,
246
+ };
247
+ entries.push(entry);
248
+ const push = (code, message, severity) => entry.issues.push({ code, severity, slug, file, message: `${slug}: ${message}` });
249
+ const data = source.data;
250
+ if (!data || entry.issues.some((issue) => issue.severity === 'error'))
251
+ continue;
252
+ const raw = { ...data };
253
+ if (format === 'markdown') {
254
+ // The file is the record's body. A pointer elsewhere would give it
255
+ // two bodies, so it is an error rather than silently ignored.
256
+ if (raw.content !== undefined) {
257
+ push('markdown_content_pointer', 'a Markdown record is its own body; remove `content:` from its frontmatter', 'error');
258
+ continue;
259
+ }
260
+ // `content` points at the record's own file, so every renderer that
261
+ // reads a body through `content` works unchanged.
262
+ raw.content = contentPointerFor(cwd, source.path);
263
+ if (raw.slug === undefined)
264
+ raw.slug = slug;
265
+ }
266
+ else if (format === 'yaml+content' && warnPointer) {
267
+ push('record_format_deprecated', `YAML record with a \`content:\` pointer; move it to one Markdown file with frontmatter (${config.paths.bodiesDir ?? 'content/records'}/${slug}.md)`, 'warning');
268
+ }
269
+ // Cache wins over a legacy inline copy; a disagreement is a second
270
+ // source of truth, so name the fields.
271
+ const resolved = resolveRecordGithub(raw, githubCache.entries.get(slug), freshness);
272
+ entry.syncStale = resolved.syncStale !== undefined;
273
+ if (resolved.conflicts.length > 0) {
274
+ push('github_cache_mismatch', `inline github/health disagrees with ${cachePath}/${slug}.json — ${resolved.conflicts.join(', ')}. The cache wins; run \`grove migrate github-cache\` to drop the inline copy.`, 'warning');
275
+ }
276
+ const merged = resolved.record;
277
+ if (!merged.kind)
278
+ merged.kind = expectedKind;
279
+ const patch = patches.get(slug);
280
+ const input = patch ? { ...merged, ...patch } : merged;
281
+ let base;
282
+ try {
283
+ base = recordsFileSchema.parse(input);
284
+ }
285
+ catch (err) {
286
+ if (err instanceof ZodError) {
287
+ for (const zodIssue of err.issues) {
288
+ const where = zodIssue.path.length > 0 ? zodIssue.path.join('.') : '(root)';
289
+ push('zod_error', `${where} ${zodIssue.message}`, 'error');
290
+ }
291
+ }
292
+ else {
293
+ push('schema_error', err.message, 'error');
294
+ }
295
+ continue;
296
+ }
297
+ base.slug = slug;
298
+ let healthSource = patch && 'health' in patch
299
+ ? 'override'
300
+ : resolved.health === 'none'
301
+ ? 'none'
302
+ : resolved.health;
303
+ let record = base;
304
+ if (record.kind === 'project' && !record.health) {
305
+ healthSource = 'none';
306
+ const fromFile = healthBySlug.get(slug);
307
+ if (fromFile) {
308
+ record = { ...record, health: fromFile };
309
+ healthSource = 'file';
310
+ }
311
+ }
312
+ const decision = decisions.get(slug);
313
+ if (decision && record.kind === 'project' && !record.health)
314
+ healthSource = 'decision';
315
+ record = applyDecisionVisibility(record, decision);
316
+ let body;
317
+ if (format === 'markdown')
318
+ body = source.body;
319
+ else if (base.content) {
320
+ body = readContentFile(base.content, pointerCandidates(cwd, base.content))?.body;
321
+ }
322
+ entry.record = {
323
+ slug,
324
+ file,
325
+ format,
326
+ ...(body !== undefined ? { body } : {}),
327
+ record,
328
+ base,
329
+ visibility: recordVisibility(record),
330
+ declaredSlug: format === 'markdown' ? data.slug : input.slug,
331
+ provenance: {
332
+ github: resolved.github,
333
+ health: healthSource,
334
+ decision: decision !== undefined,
335
+ override: patch !== undefined,
336
+ },
337
+ };
338
+ }
339
+ return {
340
+ records: entries.flatMap((entry) => (entry.record ? [entry.record] : [])),
341
+ entries,
342
+ issues: entries.flatMap((entry) => entry.issues),
343
+ githubCache,
344
+ };
345
+ }
346
+ //# sourceMappingURL=normalize-records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize-records.js","sourceRoot":"","sources":["../src/normalize-records.ts"],"names":[],"mappings":"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"}