@pagesmith/core 0.2.0 → 0.4.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.
Files changed (83) hide show
  1. package/README.md +9 -4
  2. package/REFERENCE.md +284 -0
  3. package/dist/ai/index.d.mts +7 -4
  4. package/dist/ai/index.d.mts.map +1 -1
  5. package/dist/ai/index.mjs +605 -136
  6. package/dist/ai/index.mjs.map +1 -1
  7. package/dist/assets/index.d.mts +10 -1
  8. package/dist/assets/index.d.mts.map +1 -1
  9. package/dist/assets/index.mjs +2 -2
  10. package/dist/{assets-bX08zEJm.mjs → assets-CAPOqQ_P.mjs} +42 -5
  11. package/dist/assets-CAPOqQ_P.mjs.map +1 -0
  12. package/dist/{content-config-wW-3r5gG.d.mts → content-config-DJXUOcNG.d.mts} +47 -15
  13. package/dist/{content-config-wW-3r5gG.d.mts.map → content-config-DJXUOcNG.d.mts.map} +1 -1
  14. package/dist/{content-layer-DWdgdBeI.mjs → content-layer-B5enqWeJ.mjs} +195 -63
  15. package/dist/content-layer-B5enqWeJ.mjs.map +1 -0
  16. package/dist/content-layer-CpHYUYNN.d.mts +121 -0
  17. package/dist/content-layer-CpHYUYNN.d.mts.map +1 -0
  18. package/dist/create/index.d.mts.map +1 -1
  19. package/dist/create/index.mjs +26 -28
  20. package/dist/create/index.mjs.map +1 -1
  21. package/dist/css/index.d.mts +1 -1
  22. package/dist/css/index.mjs +1 -1
  23. package/dist/{css-ekIt2Fdb.mjs → css-BneO430t.mjs} +5 -4
  24. package/dist/css-BneO430t.mjs.map +1 -0
  25. package/dist/index-B7NRZAxd.d.mts +13 -0
  26. package/dist/index-B7NRZAxd.d.mts.map +1 -0
  27. package/dist/{index-D79hUFbK.d.mts → index-C0QFHYwb.d.mts} +1 -1
  28. package/dist/{index-D79hUFbK.d.mts.map → index-C0QFHYwb.d.mts.map} +1 -1
  29. package/dist/{index-DpRBzO8Q.d.mts → index-CJkBs8YQ.d.mts} +2 -2
  30. package/dist/index-CJkBs8YQ.d.mts.map +1 -0
  31. package/dist/{index-Dbsw1QON.d.mts → index-DCznbvaV.d.mts} +4 -2
  32. package/dist/{index-Dbsw1QON.d.mts.map → index-DCznbvaV.d.mts.map} +1 -1
  33. package/dist/index.d.mts +13 -91
  34. package/dist/index.d.mts.map +1 -1
  35. package/dist/index.mjs +16 -13
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/loaders/index.d.mts +2 -2
  38. package/dist/loaders/index.mjs +2 -2
  39. package/dist/{loaders-Bla48ZN9.mjs → loaders-Cf-BXf2L.mjs} +10 -2
  40. package/dist/{loaders-Bla48ZN9.mjs.map → loaders-Cf-BXf2L.mjs.map} +1 -1
  41. package/dist/markdown/index.d.mts +1 -1
  42. package/dist/markdown/index.mjs +1 -1
  43. package/dist/{markdown-Cj5X26FL.mjs → markdown-BmDJgYeB.mjs} +59 -9
  44. package/dist/markdown-BmDJgYeB.mjs.map +1 -0
  45. package/dist/mcp/index.d.mts +23 -0
  46. package/dist/mcp/index.d.mts.map +1 -0
  47. package/dist/mcp/index.mjs +2 -0
  48. package/dist/mcp/server.d.mts +13 -0
  49. package/dist/mcp/server.d.mts.map +1 -0
  50. package/dist/mcp/server.mjs +2 -0
  51. package/dist/runtime/index.mjs +1 -1
  52. package/dist/schemas/index.d.mts +2 -2
  53. package/dist/schemas/index.mjs +1 -1
  54. package/dist/{schemas-BZEPTGWs.mjs → schemas-UL4ynWsA.mjs} +1 -1
  55. package/dist/{schemas-BZEPTGWs.mjs.map → schemas-UL4ynWsA.mjs.map} +1 -1
  56. package/dist/server-D3DHoh5f.mjs +202 -0
  57. package/dist/server-D3DHoh5f.mjs.map +1 -0
  58. package/dist/ssg-utils/index.d.mts +61 -0
  59. package/dist/ssg-utils/index.d.mts.map +1 -0
  60. package/dist/ssg-utils/index.mjs +118 -0
  61. package/dist/ssg-utils/index.mjs.map +1 -0
  62. package/dist/vite/index.d.mts +68 -33
  63. package/dist/vite/index.d.mts.map +1 -1
  64. package/dist/vite/index.mjs +302 -227
  65. package/dist/vite/index.mjs.map +1 -1
  66. package/docs/agents/AGENTS.md.template +9 -0
  67. package/docs/agents/changelog-notes.md +15 -0
  68. package/docs/agents/errors.md +96 -0
  69. package/docs/agents/migration.md +25 -0
  70. package/docs/agents/recipes.md +26 -0
  71. package/docs/agents/usage.md +58 -0
  72. package/docs/llms-full.txt +53 -0
  73. package/docs/llms.txt +29 -0
  74. package/package.json +58 -6
  75. package/dist/assets-bX08zEJm.mjs.map +0 -1
  76. package/dist/content-layer-DWdgdBeI.mjs.map +0 -1
  77. package/dist/convert-XdGgNqH0.mjs +0 -27
  78. package/dist/convert-XdGgNqH0.mjs.map +0 -1
  79. package/dist/css-ekIt2Fdb.mjs.map +0 -1
  80. package/dist/index-CeNDTM-y.d.mts +0 -7
  81. package/dist/index-CeNDTM-y.d.mts.map +0 -1
  82. package/dist/index-DpRBzO8Q.d.mts.map +0 -1
  83. package/dist/markdown-Cj5X26FL.mjs.map +0 -1
@@ -1,10 +1,20 @@
1
- import { t as processMarkdown } from "./markdown-Cj5X26FL.mjs";
2
- import { n as resolveLoader, t as defaultIncludePatterns } from "./loaders-Bla48ZN9.mjs";
1
+ import { t as processMarkdown } from "./markdown-BmDJgYeB.mjs";
2
+ import { r as resolveLoader, t as defaultIncludePatterns } from "./loaders-Cf-BXf2L.mjs";
3
+ import { existsSync, watch } from "fs";
3
4
  import { dirname, extname, relative, resolve } from "path";
4
5
  import remarkParse from "remark-parse";
5
6
  import { unified } from "unified";
6
7
  import fg from "fast-glob";
7
- import { existsSync } from "fs";
8
+ //#region src/convert.ts
9
+ async function convert(input, options = {}) {
10
+ const result = await processMarkdown(input, options.markdown || {});
11
+ return {
12
+ html: result.html,
13
+ toc: result.headings,
14
+ frontmatter: result.frontmatter
15
+ };
16
+ }
17
+ //#endregion
8
18
  //#region src/utils/read-time.ts
9
19
  /**
10
20
  * Read time estimation.
@@ -148,12 +158,15 @@ function validateSchema(data, schema) {
148
158
  issues: [],
149
159
  validatedData: result.data
150
160
  };
151
- const details = result.error.issues.map((issue) => ({
152
- field: issue.path.length > 0 ? formatPath(issue.path) : void 0,
153
- message: issue.message,
154
- severity: "error"
155
- })).map((i) => i.field ? `${i.field}: ${i.message}` : i.message).join("; ");
156
- throw new Error(`Schema validation failed: ${details}`);
161
+ return {
162
+ issues: result.error.issues.map((issue) => ({
163
+ field: issue.path.length > 0 ? formatPath(issue.path) : void 0,
164
+ message: issue.message,
165
+ severity: "error",
166
+ source: "schema"
167
+ })),
168
+ validatedData: data
169
+ };
157
170
  }
158
171
  //#endregion
159
172
  //#region src/validation/code-block-validator.ts
@@ -187,7 +200,7 @@ function collectCodeBlocks(node) {
187
200
  const codeBlockValidator = {
188
201
  name: "code-blocks",
189
202
  validate(ctx) {
190
- if (!ctx.rawContent || !ctx.mdast) return [];
203
+ if (!ctx.rawContent) return [];
191
204
  const issues = [];
192
205
  const tree = ctx.mdast;
193
206
  const codeBlocks = collectCodeBlocks(tree);
@@ -234,7 +247,7 @@ function collectHeadings(node) {
234
247
  const headingValidator = {
235
248
  name: "headings",
236
249
  validate(ctx) {
237
- if (!ctx.rawContent || !ctx.mdast) return [];
250
+ if (!ctx.rawContent) return [];
238
251
  const issues = [];
239
252
  const tree = ctx.mdast;
240
253
  const headings = collectHeadings(tree);
@@ -303,63 +316,80 @@ function isWellFormedUrl(url) {
303
316
  return false;
304
317
  }
305
318
  }
306
- const linkValidator = {
307
- name: "links",
308
- validate(ctx) {
309
- if (!ctx.rawContent || !ctx.mdast) return [];
310
- const issues = [];
311
- const tree = ctx.mdast;
312
- const links = collectLinks(tree);
313
- const fileDir = dirname(ctx.filePath);
314
- for (const link of links) {
315
- const lineInfo = link.line ? ` (line ${link.line})` : "";
316
- if (link.url.startsWith("http://") || link.url.startsWith("https://")) {
317
- if (!isWellFormedUrl(link.url)) issues.push({
318
- field: `links${lineInfo}`,
319
- message: `Malformed external URL: ${link.url}`,
320
- severity: "warn"
321
- });
322
- continue;
323
- }
324
- if (isInternalLink(link.url)) {
325
- const urlPath = link.url.split("#")[0].split("?")[0];
326
- if (!urlPath) continue;
327
- if (!existsSync(resolve(fileDir, urlPath))) issues.push({
328
- field: `links${lineInfo}`,
329
- message: `Broken internal link: ${link.url}`,
330
- severity: "error"
331
- });
319
+ function createLinkValidator(options) {
320
+ const skipPatterns = options?.skipPatterns ?? [];
321
+ function shouldSkip(url) {
322
+ return skipPatterns.some((pattern) => {
323
+ if (pattern.includes("*")) return new RegExp("^" + pattern.replace(/\*/g, ".*") + "$").test(url);
324
+ return url.startsWith(pattern);
325
+ });
326
+ }
327
+ return {
328
+ name: "links",
329
+ validate(ctx) {
330
+ if (!ctx.rawContent) return [];
331
+ const issues = [];
332
+ const tree = ctx.mdast;
333
+ const links = collectLinks(tree);
334
+ const fileDir = dirname(ctx.filePath);
335
+ for (const link of links) {
336
+ const lineInfo = link.line ? ` (line ${link.line})` : "";
337
+ if (link.url.startsWith("http://") || link.url.startsWith("https://")) {
338
+ if (!isWellFormedUrl(link.url)) issues.push({
339
+ field: `links${lineInfo}`,
340
+ message: `Malformed external URL: ${link.url}`,
341
+ severity: "warn"
342
+ });
343
+ continue;
344
+ }
345
+ if (isInternalLink(link.url)) {
346
+ if (shouldSkip(link.url)) continue;
347
+ const urlPath = link.url.split("#")[0].split("?")[0];
348
+ if (!urlPath) continue;
349
+ if (!existsSync(resolve(fileDir, urlPath))) issues.push({
350
+ field: `links${lineInfo}`,
351
+ message: `Broken internal link: ${link.url}`,
352
+ severity: "error"
353
+ });
354
+ }
332
355
  }
356
+ return issues;
333
357
  }
334
- return issues;
335
- }
336
- };
358
+ };
359
+ }
360
+ const linkValidator = createLinkValidator();
337
361
  //#endregion
338
362
  //#region src/validation/runner.ts
339
- /**
340
- * Validation runner — executes a list of content validators on an entry.
341
- *
342
- * Validators that throw are caught and converted to error-severity issues
343
- * so one failing validator does not block the rest.
344
- */
345
363
  /** The built-in validators for markdown content. */
346
364
  const builtinMarkdownValidators = [
347
365
  linkValidator,
348
366
  codeBlockValidator,
349
367
  headingValidator
350
368
  ];
369
+ /** Parse raw markdown to MDAST once, reusing any pre-parsed tree. */
370
+ function resolveContext(ctx) {
371
+ const mdast = ctx.mdast ?? unified().use(remarkParse).parse(ctx.rawContent ?? "");
372
+ return {
373
+ ...ctx,
374
+ mdast
375
+ };
376
+ }
351
377
  /** Run all validators on a single content entry. */
352
378
  async function runValidators(ctx, validators) {
353
- if (ctx.rawContent && !ctx.mdast) ctx.mdast = unified().use(remarkParse).parse(ctx.rawContent);
379
+ const resolved = resolveContext(ctx);
354
380
  const issues = [];
355
381
  for (const validator of validators) try {
356
- const result = await validator.validate(ctx);
357
- issues.push(...result);
382
+ const result = await validator.validate(resolved);
383
+ issues.push(...result.map((issue) => ({
384
+ ...issue,
385
+ source: "content"
386
+ })));
358
387
  } catch (err) {
359
388
  const message = err instanceof Error ? err.message : String(err);
360
389
  issues.push({
361
390
  message: `Validator "${validator.name}" threw: ${message}`,
362
- severity: "error"
391
+ severity: "error",
392
+ source: "content"
363
393
  });
364
394
  }
365
395
  return issues;
@@ -397,16 +427,21 @@ var ContentStore = class {
397
427
  exclude: def.exclude
398
428
  });
399
429
  const entries = /* @__PURE__ */ new Map();
430
+ const strict = this.config.strict ?? false;
400
431
  const results = await Promise.all(files.map(async (filePath) => {
401
432
  try {
402
433
  return await this.loadEntry(name, filePath, directory, loader, def);
403
434
  } catch (err) {
404
435
  const message = err instanceof Error ? err.message : String(err);
436
+ const loadError = new Error(`[${name}] Failed to load ${filePath}: ${message}`, { cause: err });
437
+ if (strict) throw loadError;
438
+ console.warn(`[pagesmith] ${loadError.message}`);
405
439
  return {
406
440
  entry: new ContentEntry(def.slugify ? def.slugify(filePath, directory) : toSlug(filePath, directory), name, filePath, {}, void 0, this.markdownConfig),
407
441
  issues: [{
408
- message: `Failed to load: ${message}`,
409
- severity: "error"
442
+ message: loadError.message,
443
+ severity: "error",
444
+ source: "schema"
410
445
  }]
411
446
  };
412
447
  }
@@ -434,7 +469,8 @@ var ContentStore = class {
434
469
  const customError = def.validate(raw);
435
470
  if (customError) issues.push({
436
471
  message: customError,
437
- severity: "error"
472
+ severity: "error",
473
+ source: "custom"
438
474
  });
439
475
  }
440
476
  if (raw.content !== void 0) {
@@ -445,7 +481,15 @@ var ContentStore = class {
445
481
  slug,
446
482
  collection: collectionName,
447
483
  rawContent: raw.content,
448
- data: raw.data
484
+ data: raw.data,
485
+ getEntry: (col, s) => {
486
+ const cached = this.cache.get(col)?.get(s);
487
+ if (!cached) return void 0;
488
+ return {
489
+ slug: cached.entry.slug,
490
+ data: cached.entry.data
491
+ };
492
+ }
449
493
  }, validators);
450
494
  issues.push(...contentIssues);
451
495
  }
@@ -457,7 +501,8 @@ var ContentStore = class {
457
501
  });
458
502
  for (const message of pluginIssues) issues.push({
459
503
  message,
460
- severity: "error"
504
+ severity: "error",
505
+ source: "plugin"
461
506
  });
462
507
  }
463
508
  return {
@@ -469,6 +514,9 @@ var ContentStore = class {
469
514
  getEntry(collection, slug) {
470
515
  return this.cache.get(collection)?.get(slug)?.entry;
471
516
  }
517
+ isCollectionLoaded(collection) {
518
+ return this.loaded.has(collection);
519
+ }
472
520
  /** Get validation issues for a collection. */
473
521
  getIssues(collection) {
474
522
  const result = /* @__PURE__ */ new Map();
@@ -496,7 +544,7 @@ var ContentStore = class {
496
544
  }
497
545
  }
498
546
  /** Invalidate an entire collection. */
499
- invalidateCollection(collection) {
547
+ async invalidateCollection(collection) {
500
548
  this.cache.delete(collection);
501
549
  this.loaded.delete(collection);
502
550
  }
@@ -505,6 +553,42 @@ var ContentStore = class {
505
553
  this.cache.clear();
506
554
  this.loaded.clear();
507
555
  }
556
+ /** Get cache statistics for debugging and monitoring. */
557
+ getCacheStats() {
558
+ const entries = {};
559
+ let totalEntries = 0;
560
+ for (const [name, cache] of this.cache) {
561
+ entries[name] = cache.size;
562
+ totalEntries += cache.size;
563
+ }
564
+ return {
565
+ collections: this.loaded.size,
566
+ entries,
567
+ totalEntries
568
+ };
569
+ }
570
+ /** Invalidate entries matching a predicate without reloading the entire collection. */
571
+ async invalidateWhere(collection, predicate) {
572
+ const collectionCache = this.cache.get(collection);
573
+ if (!collectionCache) return 0;
574
+ const def = this.config.collections[collection];
575
+ if (!def) return 0;
576
+ const loader = resolveLoader(def.loader);
577
+ const directory = resolve(this.rootDir, def.directory);
578
+ let count = 0;
579
+ for (const [slug, cached] of collectionCache) {
580
+ if (!predicate(cached.entry)) continue;
581
+ count++;
582
+ try {
583
+ const result = await this.loadEntry(collection, cached.entry.filePath, directory, loader, def);
584
+ if (result) collectionCache.set(slug, result);
585
+ else collectionCache.delete(slug);
586
+ } catch {
587
+ collectionCache.delete(slug);
588
+ }
589
+ }
590
+ return count;
591
+ }
508
592
  /** Resolve the list of content validators for a collection. */
509
593
  resolveValidators(def) {
510
594
  const builtin = def.disableBuiltinValidators ? [] : builtinMarkdownValidators;
@@ -529,6 +613,15 @@ var ContentStore = class {
529
613
  };
530
614
  //#endregion
531
615
  //#region src/content-layer.ts
616
+ /**
617
+ * ContentLayer — the main API for working with content collections.
618
+ *
619
+ * Created via createContentLayer(config). Provides methods to:
620
+ * - Load and query collections (getCollection, getEntry)
621
+ * - Convert markdown directly (convert)
622
+ * - Invalidate cache (invalidate, invalidateCollection, invalidateAll)
623
+ * - Validate all entries (validate)
624
+ */
532
625
  var ContentLayerImpl = class {
533
626
  store;
534
627
  config;
@@ -542,22 +635,27 @@ var ContentLayerImpl = class {
542
635
  return this.store.loadCollection(name, def);
543
636
  }
544
637
  async getEntry(collection, slug) {
638
+ const cached = this.store.getEntry(collection, slug);
639
+ if (cached) return cached;
640
+ if (this.store.isCollectionLoaded(collection)) return void 0;
545
641
  await this.getCollection(collection);
546
642
  return this.store.getEntry(collection, slug);
547
643
  }
548
644
  async convert(markdown, options) {
549
- const { convert: coreConvert } = await import("./convert-XdGgNqH0.mjs").then((n) => n.n);
550
- return coreConvert(markdown, { markdown: options?.markdown ?? this.config.markdown });
645
+ return convert(markdown, { markdown: options?.markdown ?? this.config.markdown });
551
646
  }
552
647
  async invalidate(collection, slug) {
553
648
  await this.store.invalidate(collection, slug);
554
649
  }
555
- invalidateCollection(collection) {
556
- this.store.invalidateCollection(collection);
650
+ async invalidateCollection(collection) {
651
+ await this.store.invalidateCollection(collection);
557
652
  }
558
653
  invalidateAll() {
559
654
  this.store.invalidateAll();
560
655
  }
656
+ async invalidateWhere(collection, predicate) {
657
+ return this.store.invalidateWhere(collection, predicate);
658
+ }
561
659
  async validate(collection) {
562
660
  const names = collection ? [collection] : Object.keys(this.config.collections);
563
661
  const results = [];
@@ -590,12 +688,46 @@ var ContentLayerImpl = class {
590
688
  getCollectionDef(name) {
591
689
  return this.config.collections[name];
592
690
  }
691
+ getCollections() {
692
+ return { ...this.config.collections };
693
+ }
694
+ watch(callback) {
695
+ const watchers = [];
696
+ const root = this.config.root ?? process.cwd();
697
+ const timers = /* @__PURE__ */ new Map();
698
+ for (const [name, def] of Object.entries(this.config.collections)) {
699
+ const dir = resolve(root, def.directory);
700
+ try {
701
+ const watcher = watch(dir, { recursive: true }, (event, filename) => {
702
+ const existing = timers.get(name);
703
+ if (existing) clearTimeout(existing);
704
+ timers.set(name, setTimeout(() => {
705
+ timers.delete(name);
706
+ this.store.invalidateCollection(name);
707
+ callback({
708
+ collection: name,
709
+ event,
710
+ filename
711
+ });
712
+ }, 100));
713
+ });
714
+ watchers.push(watcher);
715
+ } catch {}
716
+ }
717
+ return { close() {
718
+ for (const w of watchers) w.close();
719
+ for (const t of timers.values()) clearTimeout(t);
720
+ timers.clear();
721
+ } };
722
+ }
723
+ getCacheStats() {
724
+ return this.store.getCacheStats();
725
+ }
593
726
  };
594
- /** Create a new content layer from a configuration. */
595
727
  function createContentLayer(config) {
596
728
  return new ContentLayerImpl(config);
597
729
  }
598
730
  //#endregion
599
- export { headingValidator as a, ContentEntry as c, linkValidator as i, builtinMarkdownValidators as n, codeBlockValidator as o, runValidators as r, toSlug as s, createContentLayer as t };
731
+ export { linkValidator as a, validateSchema as c, convert as d, createLinkValidator as i, toSlug as l, builtinMarkdownValidators as n, headingValidator as o, runValidators as r, codeBlockValidator as s, createContentLayer as t, ContentEntry as u };
600
732
 
601
- //# sourceMappingURL=content-layer-DWdgdBeI.mjs.map
733
+ //# sourceMappingURL=content-layer-B5enqWeJ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-layer-B5enqWeJ.mjs","names":["coreConvert","pathResolve","fsWatch"],"sources":["../src/convert.ts","../src/utils/read-time.ts","../src/entry.ts","../src/plugins/index.ts","../src/utils/glob.ts","../src/utils/slug.ts","../src/validation/schema-validator.ts","../src/validation/code-block-validator.ts","../src/validation/heading-validator.ts","../src/validation/link-validator.ts","../src/validation/runner.ts","../src/store.ts","../src/content-layer.ts"],"sourcesContent":["import { processMarkdown } from './markdown'\nimport type { Heading } from './schemas/heading'\nimport type { MarkdownConfig } from './schemas/markdown-config'\n\nexport type ConvertOptions = {\n markdown?: MarkdownConfig\n}\n\nexport type ConvertResult = {\n html: string\n toc: Heading[]\n frontmatter: Record<string, unknown>\n}\n\nexport async function convert(input: string, options: ConvertOptions = {}): Promise<ConvertResult> {\n const result = await processMarkdown(input, options.markdown || {})\n return { html: result.html, toc: result.headings, frontmatter: result.frontmatter }\n}\n","/**\n * Read time estimation.\n *\n * Computes estimated reading time from markdown source (~200 words per minute).\n */\n\n/** Compute read time in minutes from raw markdown. */\nexport function computeReadTime(rawMarkdown: string): number {\n const plainText = rawMarkdown\n .replace(/```[\\s\\S]*?```/g, ' ')\n .replace(/^( {4}|\\t).+$/gm, ' ')\n .replace(/^---[\\s\\S]*?---/m, ' ')\n .replace(/<[^>]+>/g, ' ')\n .replace(/!?\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/[#*_~`>]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n const wordCount = plainText.split(' ').filter(Boolean).length\n return Math.max(1, Math.ceil(wordCount / 200))\n}\n","/**\n * ContentEntry — represents a single content entry in a collection.\n *\n * Supports lazy rendering: data is available immediately after loading,\n * but HTML rendering is deferred until render() is called.\n */\n\nimport type { Heading } from './schemas/heading'\nimport type { MarkdownConfig } from './schemas/markdown-config'\nimport { processMarkdown } from './markdown'\nimport { computeReadTime } from './utils/read-time'\n\nexport type RenderedContent = {\n /** Processed HTML */\n html: string\n /** Extracted headings for TOC */\n headings: Heading[]\n /** Estimated read time in minutes */\n readTime: number\n}\n\nexport class ContentEntry<T = Record<string, any>> {\n /** URL-friendly identifier */\n readonly slug: string\n /** Collection this entry belongs to */\n readonly collection: string\n /** Absolute path to source file */\n readonly filePath: string\n /** Validated data (frontmatter or parsed data) */\n readonly data: T\n /** Raw body content (markdown only) */\n readonly rawContent?: string\n\n /** Cached render result */\n private _rendered?: RenderedContent\n /** Markdown config for rendering */\n private _markdownConfig: MarkdownConfig\n\n constructor(\n slug: string,\n collection: string,\n filePath: string,\n data: T,\n rawContent: string | undefined,\n markdownConfig: MarkdownConfig,\n ) {\n this.slug = slug\n this.collection = collection\n this.filePath = filePath\n this.data = data\n this.rawContent = rawContent\n this._markdownConfig = markdownConfig\n }\n\n /** Render the entry content to HTML. Cached after first call. */\n async render(options?: { force?: boolean }): Promise<RenderedContent> {\n if (this._rendered && !options?.force) {\n return this._rendered\n }\n\n if (!this.rawContent) {\n // Non-markdown entries have no renderable content\n this._rendered = { html: '', headings: [], readTime: 0 }\n return this._rendered\n }\n\n const result = await processMarkdown(this.rawContent, this._markdownConfig, {\n content: this.rawContent,\n frontmatter: this.data as Record<string, unknown>,\n })\n const readTime = computeReadTime(this.rawContent)\n\n this._rendered = {\n html: result.html,\n headings: result.headings,\n readTime,\n }\n\n return this._rendered\n }\n\n /** Clear cached render result. */\n clearRenderCache(): void {\n this._rendered = undefined\n }\n}\n","/**\n * Plugin registration and execution.\n */\n\nimport type { ContentPlugin } from '../schemas/content-config'\n\n/** Collect all remark plugins from content plugins. */\nexport function collectRemarkPlugins(plugins: ContentPlugin[]): any[] {\n return plugins.filter((p) => p.remarkPlugin).map((p) => p.remarkPlugin!)\n}\n\n/** Collect all rehype plugins from content plugins. */\nexport function collectRehypePlugins(plugins: ContentPlugin[]): any[] {\n return plugins.filter((p) => p.rehypePlugin).map((p) => p.rehypePlugin!)\n}\n\n/** Run all plugin validators against an entry. */\nexport function runPluginValidators(\n plugins: ContentPlugin[],\n entry: { data: Record<string, any>; content?: string },\n): string[] {\n const issues: string[] = []\n for (const plugin of plugins) {\n if (plugin.validate) {\n issues.push(...plugin.validate(entry))\n }\n }\n return issues\n}\n\nexport type { ContentPlugin } from '../schemas/content-config'\n","/**\n * File discovery via glob patterns.\n */\n\nimport fg from 'fast-glob'\nimport { resolve } from 'path'\n\nexport interface DiscoverOptions {\n /** Directory to search in (absolute path) */\n directory: string\n /** Glob patterns to include */\n include: string[]\n /** Glob patterns to exclude */\n exclude?: string[]\n}\n\n/** Discover files matching glob patterns in a directory. */\nexport async function discoverFiles(options: DiscoverOptions): Promise<string[]> {\n const { directory, include, exclude = [] } = options\n\n const files = await fg(include, {\n cwd: directory,\n absolute: true,\n ignore: ['**/node_modules/**', '**/dist/**', '**/dev/**', ...exclude],\n })\n\n return files.map((p) => resolve(p))\n}\n","/**\n * Path-to-slug conversion.\n *\n * Generalized from packages/core/src/content/collector.ts.\n */\n\nimport { extname, relative } from 'path'\n\n/**\n * Convert a content file path to a URL-friendly slug.\n *\n * Examples:\n * content/posts/hello-world/README.md -> 'hello-world'\n * content/posts/hello-world/index.md -> 'hello-world'\n * content/posts/hello-world.md -> 'hello-world'\n * content/authors/john.json -> 'john'\n * content/posts/nested/deep/post.md -> 'nested/deep/post'\n */\nexport function toSlug(filePath: string, directory: string): string {\n const ext = extname(filePath)\n let slug = relative(directory, filePath).replace(/\\\\/g, '/')\n\n // Remove file extension\n if (ext) {\n slug = slug.slice(0, -ext.length)\n }\n\n // Strip README / index suffixes\n if (slug === 'README' || slug === 'index') return '/'\n if (slug.endsWith('/README')) slug = slug.slice(0, -7)\n if (slug.endsWith('/index')) slug = slug.slice(0, -6)\n\n return slug\n}\n","/**\n * Schema validation with rich error reporting.\n *\n * Wraps Zod's safeParse to produce human-readable validation issues.\n */\n\nimport type { ZodError, ZodType } from 'zod'\n\nexport type ValidationIssue = {\n /** Field path (e.g. 'tags[0]') */\n field?: string\n /** Human-readable error message */\n message: string\n /** Error severity */\n severity: 'error' | 'warn'\n /** Origin of this issue — helps consumers distinguish validation phases. */\n source?: 'schema' | 'content' | 'plugin' | 'custom'\n}\n\nexport type ValidationEntryResult = {\n slug: string\n filePath: string\n issues: ValidationIssue[]\n}\n\nexport type ValidationResult = {\n collection: string\n entries: ValidationEntryResult[]\n errors: number\n warnings: number\n}\n\n/** Format a Zod error path into a human-readable field path. */\nexport function formatPath(path: PropertyKey[]): string {\n return path\n .map((segment, i) => {\n if (typeof segment === 'number') return `[${segment}]`\n if (typeof segment === 'symbol') return `[${String(segment)}]`\n if (i === 0) return segment\n return `.${segment}`\n })\n .join('')\n}\n\n/** Validate data against a Zod schema and return structured issues. */\nexport function validateSchema(\n data: Record<string, any>,\n schema: ZodType,\n): {\n issues: ValidationIssue[]\n validatedData: any\n} {\n const result = schema.safeParse(data)\n if (result.success) {\n return {\n issues: [],\n validatedData: result.data,\n }\n }\n\n const issues = (result.error as ZodError).issues.map((issue) => ({\n field: issue.path.length > 0 ? formatPath(issue.path) : undefined,\n message: issue.message,\n severity: 'error' as const,\n source: 'schema' as const,\n }))\n\n return {\n issues,\n validatedData: data,\n }\n}\n","/**\n * Code block validator — checks fenced code block meta syntax.\n *\n * Walks the shared MDAST for `code` nodes. Validates known meta properties\n * and language identifiers. Meta syntax follows Expressive Code conventions.\n */\n\nimport type { ValidationIssue } from './schema-validator'\nimport type { ContentValidator, MdastNode, ResolvedValidatorContext } from './types'\n\n/** Known meta properties accepted by Expressive Code and its plugins. */\nconst KNOWN_META_PROPS = new Set([\n 'title',\n 'showLineNumbers',\n 'startLineNumber',\n 'wrap',\n 'frame',\n 'collapse',\n 'mark',\n 'ins',\n 'del',\n])\n\n/** Extract the property name portion of a meta token (before `=` or `{`). */\nfunction extractMetaPropNames(meta: string): string[] {\n const props: string[] = []\n\n // Match key=value, key={...}, or bare flags\n const tokenRegex = /(\\w+)(?:=(?:\\{[^}]*\\}|\"[^\"]*\"|'[^']*'|\\S+))?/g\n let match: RegExpExecArray | null\n while ((match = tokenRegex.exec(meta)) !== null) {\n props.push(match[1]!)\n }\n\n return props\n}\n\n/** Collect all `code` nodes from MDAST. */\nfunction collectCodeBlocks(node: MdastNode): MdastNode[] {\n const blocks: MdastNode[] = []\n\n if (node.type === 'code') {\n blocks.push(node)\n }\n\n if (node.children) {\n for (const child of node.children) {\n blocks.push(...collectCodeBlocks(child))\n }\n }\n\n return blocks\n}\n\nexport const codeBlockValidator: ContentValidator = {\n name: 'code-blocks',\n\n validate(ctx: ResolvedValidatorContext): ValidationIssue[] {\n if (!ctx.rawContent) return []\n\n const issues: ValidationIssue[] = []\n const tree = ctx.mdast as MdastNode\n\n const codeBlocks = collectCodeBlocks(tree)\n\n for (const block of codeBlocks) {\n const line = block.position?.start.line\n const lineInfo = line ? ` (line ${line})` : ''\n const meta = block.meta ?? ''\n const hasMeta = meta.trim().length > 0\n\n // Language required when using syntax features\n if (hasMeta && !block.lang) {\n issues.push({\n field: `code-block${lineInfo}`,\n message: 'Code block has meta properties but no language identifier',\n severity: 'warn',\n })\n }\n\n if (!hasMeta) continue\n\n // Check for unknown meta properties\n const propNames = extractMetaPropNames(meta)\n for (const prop of propNames) {\n if (!KNOWN_META_PROPS.has(prop)) {\n issues.push({\n field: `code-block${lineInfo}`,\n message: `Unknown code block meta property: \"${prop}\"`,\n severity: 'warn',\n })\n }\n }\n }\n\n return issues\n },\n}\n","/**\n * Heading validator — checks heading structure in markdown.\n *\n * Walks the shared MDAST for heading nodes. Validates level ordering and h1 uniqueness.\n */\n\nimport type { ValidationIssue } from './schema-validator'\nimport type { ContentValidator, MdastNode, ResolvedValidatorContext } from './types'\n\n/** Extract plain text from a heading node's children. */\nfunction getTextContent(node: MdastNode): string {\n if (node.type === 'text') return node.value ?? ''\n if (node.children) return node.children.map(getTextContent).join('')\n return ''\n}\n\n/** Collect all heading nodes from MDAST. */\nfunction collectHeadings(node: MdastNode): Array<{ depth: number; text: string; line?: number }> {\n const headings: Array<{ depth: number; text: string; line?: number }> = []\n\n if (node.type === 'heading' && node.depth) {\n headings.push({\n depth: node.depth,\n text: getTextContent(node),\n line: node.position?.start.line,\n })\n }\n\n if (node.children) {\n for (const child of node.children) {\n headings.push(...collectHeadings(child))\n }\n }\n\n return headings\n}\n\nexport const headingValidator: ContentValidator = {\n name: 'headings',\n\n validate(ctx: ResolvedValidatorContext): ValidationIssue[] {\n if (!ctx.rawContent) return []\n\n const issues: ValidationIssue[] = []\n const tree = ctx.mdast as MdastNode\n\n const headings = collectHeadings(tree)\n\n // No headings in a document with content is worth noting\n if (headings.length === 0) {\n const hasContent = ctx.rawContent.trim().length > 0\n if (hasContent) {\n issues.push({\n message: 'Document has content but no headings',\n severity: 'warn',\n })\n }\n return issues\n }\n\n // At most one h1\n const h1s = headings.filter((h) => h.depth === 1)\n if (h1s.length > 1) {\n for (const h of h1s.slice(1)) {\n const lineInfo = h.line ? ` (line ${h.line})` : ''\n issues.push({\n field: `headings${lineInfo}`,\n message: `Multiple h1 headings found: \"${h.text}\"`,\n severity: 'warn',\n })\n }\n }\n\n // No skipped levels (only flag when going deeper)\n for (let i = 1; i < headings.length; i++) {\n const prev = headings[i - 1]!\n const curr = headings[i]!\n if (curr.depth > prev.depth + 1) {\n const lineInfo = curr.line ? ` (line ${curr.line})` : ''\n issues.push({\n field: `headings${lineInfo}`,\n message: `Heading level skip: h${prev.depth} -> h${curr.depth} (\"${curr.text}\")`,\n severity: 'warn',\n })\n }\n }\n\n return issues\n },\n}\n","/**\n * Link validator — checks links in markdown content.\n *\n * Walks the shared MDAST for link/image nodes. Internal links are checked for file existence;\n * external links are checked for well-formed URL format.\n */\n\nimport { existsSync } from 'fs'\nimport { dirname, resolve } from 'path'\nimport type { ValidationIssue } from './schema-validator'\nimport type { ContentValidator, MdastNode, ResolvedValidatorContext } from './types'\n\n/** Walk MDAST tree, collecting link and image nodes. */\nfunction collectLinks(node: MdastNode): Array<{ url: string; line?: number }> {\n const links: Array<{ url: string; line?: number }> = []\n\n if ((node.type === 'link' || node.type === 'image') && node.url) {\n links.push({\n url: node.url,\n line: node.position?.start.line,\n })\n }\n\n if (node.children) {\n for (const child of node.children) {\n links.push(...collectLinks(child))\n }\n }\n\n return links\n}\n\nfunction isInternalLink(url: string): boolean {\n if (url.startsWith('#')) return false\n if (url.startsWith('http://') || url.startsWith('https://')) return false\n if (url.startsWith('//')) return false\n if (url.startsWith('mailto:')) return false\n if (url.startsWith('tel:')) return false\n return true\n}\n\nfunction isWellFormedUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\nexport type LinkValidatorOptions = {\n /** Glob patterns for internal links to skip file-existence checks on. */\n skipPatterns?: string[]\n}\n\nexport function createLinkValidator(options?: LinkValidatorOptions): ContentValidator {\n const skipPatterns = options?.skipPatterns ?? []\n\n function shouldSkip(url: string): boolean {\n return skipPatterns.some((pattern) => {\n if (pattern.includes('*')) {\n const regex = new RegExp('^' + pattern.replace(/\\*/g, '.*') + '$')\n return regex.test(url)\n }\n return url.startsWith(pattern)\n })\n }\n\n return {\n name: 'links',\n\n validate(ctx: ResolvedValidatorContext): ValidationIssue[] {\n if (!ctx.rawContent) return []\n\n const issues: ValidationIssue[] = []\n const tree = ctx.mdast as MdastNode\n\n const links = collectLinks(tree)\n const fileDir = dirname(ctx.filePath)\n\n for (const link of links) {\n const lineInfo = link.line ? ` (line ${link.line})` : ''\n\n // External links — check URL format\n if (link.url.startsWith('http://') || link.url.startsWith('https://')) {\n if (!isWellFormedUrl(link.url)) {\n issues.push({\n field: `links${lineInfo}`,\n message: `Malformed external URL: ${link.url}`,\n severity: 'warn',\n })\n }\n continue\n }\n\n // Internal links — check file exists\n if (isInternalLink(link.url)) {\n if (shouldSkip(link.url)) continue\n\n // Strip fragment and query\n const urlPath = link.url.split('#')[0]!.split('?')[0]!\n if (!urlPath) continue // pure fragment link\n\n const resolved = resolve(fileDir, urlPath)\n if (!existsSync(resolved)) {\n issues.push({\n field: `links${lineInfo}`,\n message: `Broken internal link: ${link.url}`,\n severity: 'error',\n })\n }\n }\n }\n\n return issues\n },\n }\n}\n\nexport const linkValidator: ContentValidator = createLinkValidator()\n","/**\n * Validation runner — executes a list of content validators on an entry.\n *\n * Validators that throw are caught and converted to error-severity issues\n * so one failing validator does not block the rest.\n */\n\nimport type { Root } from 'mdast'\nimport remarkParse from 'remark-parse'\nimport { unified } from 'unified'\nimport type { ValidationIssue } from './schema-validator'\nimport type { ContentValidator, ResolvedValidatorContext, ValidatorContext } from './types'\n\nimport { codeBlockValidator } from './code-block-validator'\nimport { headingValidator } from './heading-validator'\nimport { linkValidator } from './link-validator'\n\n/** The built-in validators for markdown content. */\nexport const builtinMarkdownValidators: ContentValidator[] = [\n linkValidator,\n codeBlockValidator,\n headingValidator,\n]\n\n/** Parse raw markdown to MDAST once, reusing any pre-parsed tree. */\nfunction resolveContext(ctx: ValidatorContext): ResolvedValidatorContext {\n const mdast: Root =\n ctx.mdast ??\n unified()\n .use(remarkParse)\n .parse(ctx.rawContent ?? '')\n return { ...ctx, mdast }\n}\n\n/** Run all validators on a single content entry. */\nexport async function runValidators(\n ctx: ValidatorContext,\n validators: ContentValidator[],\n): Promise<ValidationIssue[]> {\n // Parse the MDAST once before the loop so all validators share the same tree.\n const resolved = resolveContext(ctx)\n\n const issues: ValidationIssue[] = []\n\n for (const validator of validators) {\n try {\n const result = await validator.validate(resolved)\n issues.push(...result.map((issue) => ({ ...issue, source: 'content' as const })))\n } catch (err) {\n // Convert thrown errors into issues so one bad validator doesn't abort all\n const message = err instanceof Error ? err.message : String(err)\n issues.push({\n message: `Validator \"${validator.name}\" threw: ${message}`,\n severity: 'error',\n source: 'content',\n })\n }\n }\n\n return issues\n}\n","/**\n * ContentStore — in-memory cache for loaded collections.\n *\n * Handles file discovery, loading, validation, and caching.\n * Not exported directly — used internally by ContentLayer.\n */\n\nimport { resolve } from 'path'\nimport type { MarkdownConfig } from './schemas/markdown-config'\nimport type { ZodType } from 'zod'\nimport { ContentEntry } from './entry'\nimport { defaultIncludePatterns, resolveLoader } from './loaders'\nimport type { Loader } from './loaders/types'\nimport { collectRehypePlugins, collectRemarkPlugins, runPluginValidators } from './plugins'\nimport type { CollectionDef, RawEntry } from './schemas/collection'\nimport type { ContentLayerConfig } from './schemas/content-config'\nimport { discoverFiles } from './utils/glob'\nimport { toSlug } from './utils/slug'\nimport { validateSchema, type ValidationIssue } from './validation'\nimport { builtinMarkdownValidators, runValidators } from './validation/runner'\nimport type { ContentValidator } from './validation/types'\n\ntype CacheEntry = {\n entry: ContentEntry\n issues: ValidationIssue[]\n}\n\nexport class ContentStore {\n private cache = new Map<string, Map<string, CacheEntry>>()\n private loaded = new Set<string>()\n private config: ContentLayerConfig\n private rootDir: string\n private markdownConfig: MarkdownConfig\n\n constructor(config: ContentLayerConfig) {\n this.config = config\n this.rootDir = config.root ? resolve(config.root) : process.cwd()\n this.markdownConfig = this.createMarkdownConfig()\n }\n\n /** Load a collection (if not already loaded) and return entries. */\n async loadCollection<S extends ZodType>(\n name: string,\n def: CollectionDef<S>,\n ): Promise<ContentEntry[]> {\n if (this.loaded.has(name)) {\n const cached = this.cache.get(name)\n return cached ? Array.from(cached.values()).map((c) => c.entry) : []\n }\n\n const loader = resolveLoader(def.loader)\n const directory = resolve(this.rootDir, def.directory)\n const include = def.include ?? defaultIncludePatterns(loader)\n\n const files = await discoverFiles({\n directory,\n include,\n exclude: def.exclude,\n })\n\n const entries = new Map<string, CacheEntry>()\n const strict = this.config.strict ?? false\n const results = await Promise.all(\n files.map(async (filePath) => {\n try {\n return await this.loadEntry(name, filePath, directory, loader, def)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n const loadError = new Error(`[${name}] Failed to load ${filePath}: ${message}`, {\n cause: err,\n })\n\n if (strict) {\n throw loadError\n }\n\n console.warn(`[pagesmith] ${loadError.message}`)\n const slug = def.slugify ? def.slugify(filePath, directory) : toSlug(filePath, directory)\n return {\n entry: new ContentEntry(slug, name, filePath, {}, undefined, this.markdownConfig),\n issues: [\n { message: loadError.message, severity: 'error' as const, source: 'schema' as const },\n ],\n }\n }\n }),\n )\n\n for (const result of results) {\n if (result) {\n entries.set(result.entry.slug, result)\n }\n }\n\n this.cache.set(name, entries)\n this.loaded.add(name)\n\n return Array.from(entries.values()).map((c) => c.entry)\n }\n\n /** Load a single entry from a file. */\n private async loadEntry(\n collectionName: string,\n filePath: string,\n directory: string,\n loader: Loader,\n def: CollectionDef<any>,\n ): Promise<CacheEntry | null> {\n const loaded = await loader.load(filePath)\n const slug = def.slugify ? def.slugify(filePath, directory) : toSlug(filePath, directory)\n\n let raw: RawEntry = {\n data: loaded.data,\n content: loaded.content,\n filePath,\n slug,\n }\n\n // Apply transform\n if (def.transform) {\n raw = await def.transform(raw)\n }\n\n // Apply computed fields\n if (def.computed) {\n for (const [key, fn] of Object.entries(def.computed) as Array<\n [string, (entry: RawEntry) => any]\n >) {\n raw.data[key] = fn(raw)\n }\n }\n\n // Apply filter\n if (def.filter && !def.filter(raw)) {\n return null\n }\n\n // Validate schema once to collect issues and transformed data.\n const { issues, validatedData } = validateSchema(raw.data, def.schema)\n\n // Custom validation\n if (def.validate) {\n const customError = def.validate(raw)\n if (customError) {\n issues.push({ message: customError, severity: 'error', source: 'custom' })\n }\n }\n\n // Run content validators on markdown entries\n const isMarkdownEntry = raw.content !== undefined\n if (isMarkdownEntry) {\n const validators = this.resolveValidators(def)\n if (validators.length > 0) {\n const contentIssues = await runValidators(\n {\n filePath,\n slug,\n collection: collectionName,\n rawContent: raw.content,\n data: raw.data,\n getEntry: (col, s) => {\n const cached = this.cache.get(col)?.get(s)\n if (!cached) return undefined\n return { slug: cached.entry.slug, data: cached.entry.data }\n },\n },\n validators,\n )\n issues.push(...contentIssues)\n }\n }\n\n if (this.config.plugins?.length) {\n const pluginIssues = runPluginValidators(this.config.plugins, {\n data: raw.data,\n content: raw.content,\n })\n for (const message of pluginIssues) {\n issues.push({ message, severity: 'error', source: 'plugin' })\n }\n }\n\n const entry = new ContentEntry(\n slug,\n collectionName,\n filePath,\n validatedData,\n raw.content,\n this.markdownConfig,\n )\n\n return { entry, issues }\n }\n\n /** Get a single entry by slug. */\n getEntry(collection: string, slug: string): ContentEntry | undefined {\n return this.cache.get(collection)?.get(slug)?.entry\n }\n\n isCollectionLoaded(collection: string): boolean {\n return this.loaded.has(collection)\n }\n\n /** Get validation issues for a collection. */\n getIssues(collection: string): Map<string, ValidationIssue[]> {\n const result = new Map<string, ValidationIssue[]>()\n const entries = this.cache.get(collection)\n if (!entries) return result\n for (const [slug, cached] of entries) {\n if (cached.issues.length > 0) {\n result.set(slug, cached.issues)\n }\n }\n return result\n }\n\n /** Invalidate a single entry and reload it without reloading the entire collection. */\n async invalidate(collection: string, slug: string): Promise<void> {\n const def = this.config.collections[collection]\n if (!def) return\n\n const collectionCache = this.cache.get(collection)\n if (!collectionCache) return\n\n const existing = collectionCache.get(slug)\n if (!existing) return\n\n const loader = resolveLoader(def.loader)\n const directory = resolve(this.rootDir, def.directory)\n\n try {\n const result = await this.loadEntry(\n collection,\n existing.entry.filePath,\n directory,\n loader,\n def,\n )\n if (result) {\n collectionCache.set(slug, result)\n } else {\n // Entry was filtered out after reload\n collectionCache.delete(slug)\n }\n } catch {\n // File may have been deleted; remove from cache\n collectionCache.delete(slug)\n }\n }\n\n /** Invalidate an entire collection. */\n async invalidateCollection(collection: string): Promise<void> {\n this.cache.delete(collection)\n this.loaded.delete(collection)\n }\n\n /** Invalidate all collections. */\n invalidateAll(): void {\n this.cache.clear()\n this.loaded.clear()\n }\n\n /** Get cache statistics for debugging and monitoring. */\n getCacheStats(): { collections: number; entries: Record<string, number>; totalEntries: number } {\n const entries: Record<string, number> = {}\n let totalEntries = 0\n for (const [name, cache] of this.cache) {\n entries[name] = cache.size\n totalEntries += cache.size\n }\n return { collections: this.loaded.size, entries, totalEntries }\n }\n\n /** Invalidate entries matching a predicate without reloading the entire collection. */\n async invalidateWhere(\n collection: string,\n predicate: (entry: ContentEntry) => boolean,\n ): Promise<number> {\n const collectionCache = this.cache.get(collection)\n if (!collectionCache) return 0\n\n const def = this.config.collections[collection]\n if (!def) return 0\n\n const loader = resolveLoader(def.loader)\n const directory = resolve(this.rootDir, def.directory)\n let count = 0\n\n for (const [slug, cached] of collectionCache) {\n if (!predicate(cached.entry)) continue\n count++\n try {\n const result = await this.loadEntry(\n collection,\n cached.entry.filePath,\n directory,\n loader,\n def,\n )\n if (result) {\n collectionCache.set(slug, result)\n } else {\n collectionCache.delete(slug)\n }\n } catch {\n collectionCache.delete(slug)\n }\n }\n\n return count\n }\n\n /** Resolve the list of content validators for a collection. */\n private resolveValidators(def: CollectionDef<any>): ContentValidator[] {\n const builtin = def.disableBuiltinValidators ? [] : builtinMarkdownValidators\n const custom = def.validators ?? []\n return [...builtin, ...custom]\n }\n\n private createMarkdownConfig(): MarkdownConfig {\n const base = this.config.markdown ?? {}\n const remarkPlugins = [...(base.remarkPlugins ?? [])]\n const rehypePlugins = [...(base.rehypePlugins ?? [])]\n const plugins = this.config.plugins ?? []\n\n if (plugins.length > 0) {\n remarkPlugins.push(...collectRemarkPlugins(plugins))\n rehypePlugins.push(...collectRehypePlugins(plugins))\n }\n\n return {\n ...base,\n ...(remarkPlugins.length > 0 ? { remarkPlugins } : {}),\n ...(rehypePlugins.length > 0 ? { rehypePlugins } : {}),\n }\n }\n}\n","/**\n * ContentLayer — the main API for working with content collections.\n *\n * Created via createContentLayer(config). Provides methods to:\n * - Load and query collections (getCollection, getEntry)\n * - Convert markdown directly (convert)\n * - Invalidate cache (invalidate, invalidateCollection, invalidateAll)\n * - Validate all entries (validate)\n */\n\nimport { watch as fsWatch, type FSWatcher } from 'fs'\nimport { resolve as pathResolve } from 'path'\n\nimport { convert as coreConvert } from './convert'\nimport type { ConvertResult } from './convert'\nimport type { MarkdownConfig } from './schemas/markdown-config'\nimport type { ContentEntry } from './entry'\nimport type { CollectionDef, InferCollectionData } from './schemas/collection'\nimport type { ContentLayerConfig } from './schemas/content-config'\nimport { ContentStore } from './store'\nimport type { ValidationResult } from './validation'\n\nexport type WatchEvent = {\n collection: string\n event: string\n filename: string | null\n}\n\nexport type WatchHandle = {\n close(): void\n}\n\nexport type WatchCallback = (event: WatchEvent) => void\n\n/** Typed content layer that preserves collection schema types. */\nexport type TypedContentLayer<T extends Record<string, CollectionDef>> = ContentLayer & {\n getCollection<K extends keyof T & string>(\n name: K,\n ): Promise<ContentEntry<InferCollectionData<T[K]>>[]>\n getEntry<K extends keyof T & string>(\n collection: K,\n slug: string,\n ): Promise<ContentEntry<InferCollectionData<T[K]>> | undefined>\n}\n\nexport interface ContentLayer {\n /** Get all entries in a collection. */\n getCollection(name: string): Promise<ContentEntry[]>\n\n /** Get a single entry by collection name and slug. */\n getEntry(collection: string, slug: string): Promise<ContentEntry | undefined>\n\n /** Convert raw markdown to HTML (no collection, no validation). */\n convert(markdown: string, options?: LayerConvertOptions): Promise<ConvertResult>\n\n /** Invalidate a single entry's cache. */\n invalidate(collection: string, slug: string): Promise<void>\n\n /** Invalidate an entire collection's cache. */\n invalidateCollection(collection: string): Promise<void>\n\n /** Invalidate all cached data. */\n invalidateAll(): void\n\n /** Validate all entries in a collection (or all collections). */\n validate(collection?: string): Promise<ValidationResult[]>\n\n /** Get the names of all configured collections. */\n getCollectionNames(): string[]\n\n /** Get the definition of a collection. */\n getCollectionDef(name: string): CollectionDef | undefined\n\n /** Get all collection definitions. */\n getCollections(): Record<string, CollectionDef>\n\n /** Invalidate entries in a collection that match a predicate. Returns count of invalidated entries. */\n invalidateWhere(collection: string, predicate: (entry: ContentEntry) => boolean): Promise<number>\n\n /** Watch collection directories for changes. Returns a handle to stop watching. */\n watch(callback: WatchCallback): WatchHandle\n\n /** Get cache statistics for debugging and monitoring. */\n getCacheStats(): { collections: number; entries: Record<string, number>; totalEntries: number }\n}\n\nexport type LayerConvertOptions = {\n markdown?: MarkdownConfig\n}\n\nclass ContentLayerImpl implements ContentLayer {\n private store: ContentStore\n private config: ContentLayerConfig\n\n constructor(config: ContentLayerConfig) {\n this.config = config\n this.store = new ContentStore(config)\n }\n\n async getCollection(name: string): Promise<ContentEntry[]> {\n const def = this.config.collections[name]\n if (!def) {\n throw new Error(\n `Collection \"${name}\" not found. Available: ${Object.keys(this.config.collections).join(\n ', ',\n )}`,\n )\n }\n return this.store.loadCollection(name, def)\n }\n\n async getEntry(collection: string, slug: string): Promise<ContentEntry | undefined> {\n const cached = this.store.getEntry(collection, slug)\n if (cached) return cached\n\n // If the collection was already loaded and this slug wasn't found, short-circuit.\n if (this.store.isCollectionLoaded(collection)) return undefined\n\n // The first getEntry call loads the full collection and then serves from cache.\n // Single-entry loading would skip collection-level transforms and validation context.\n await this.getCollection(collection)\n return this.store.getEntry(collection, slug)\n }\n\n async convert(markdown: string, options?: LayerConvertOptions): Promise<ConvertResult> {\n return coreConvert(markdown, {\n markdown: options?.markdown ?? this.config.markdown,\n })\n }\n\n async invalidate(collection: string, slug: string): Promise<void> {\n await this.store.invalidate(collection, slug)\n }\n\n async invalidateCollection(collection: string): Promise<void> {\n await this.store.invalidateCollection(collection)\n }\n\n invalidateAll(): void {\n this.store.invalidateAll()\n }\n\n async invalidateWhere(\n collection: string,\n predicate: (entry: ContentEntry) => boolean,\n ): Promise<number> {\n return this.store.invalidateWhere(collection, predicate)\n }\n\n async validate(collection?: string): Promise<ValidationResult[]> {\n const names = collection ? [collection] : Object.keys(this.config.collections)\n const results: ValidationResult[] = []\n\n for (const name of names) {\n // Ensure loaded\n await this.getCollection(name)\n\n const issues = this.store.getIssues(name)\n const entries = Array.from(issues.entries()).map(([slug, entryIssues]) => {\n const entry = this.store.getEntry(name, slug)\n return {\n slug,\n filePath: entry?.filePath ?? '',\n issues: entryIssues,\n }\n })\n\n let errors = 0\n let warnings = 0\n for (const entry of entries) {\n for (const issue of entry.issues) {\n if (issue.severity === 'error') errors++\n else warnings++\n }\n }\n\n results.push({ collection: name, entries, errors, warnings })\n }\n\n return results\n }\n\n getCollectionNames(): string[] {\n return Object.keys(this.config.collections)\n }\n\n getCollectionDef(name: string): CollectionDef | undefined {\n return this.config.collections[name]\n }\n\n getCollections(): Record<string, CollectionDef> {\n return { ...this.config.collections }\n }\n\n watch(callback: WatchCallback): WatchHandle {\n const watchers: FSWatcher[] = []\n const root = this.config.root ?? process.cwd()\n const timers = new Map<string, ReturnType<typeof setTimeout>>()\n\n for (const [name, def] of Object.entries(this.config.collections)) {\n const dir = pathResolve(root, def.directory)\n try {\n const watcher = fsWatch(dir, { recursive: true }, (event, filename) => {\n // Debounce: coalesce rapid changes per collection within 100ms\n const existing = timers.get(name)\n if (existing) clearTimeout(existing)\n timers.set(\n name,\n setTimeout(() => {\n timers.delete(name)\n void this.store.invalidateCollection(name)\n callback({ collection: name, event, filename })\n }, 100),\n )\n })\n watchers.push(watcher)\n } catch {\n // Directory may not exist yet — skip silently\n }\n }\n\n return {\n close() {\n for (const w of watchers) w.close()\n for (const t of timers.values()) clearTimeout(t)\n timers.clear()\n },\n }\n }\n\n getCacheStats() {\n return this.store.getCacheStats()\n }\n}\n\n/** Create a new content layer from a configuration. */\nexport function createContentLayer<T extends Record<string, CollectionDef>>(\n config: ContentLayerConfig & { collections: T },\n): TypedContentLayer<T>\nexport function createContentLayer(config: ContentLayerConfig): ContentLayer\nexport function createContentLayer(config: ContentLayerConfig): ContentLayer {\n return new ContentLayerImpl(config)\n}\n"],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ import { d as ValidatorContext, i as CollectionDef, l as ContentValidator, m as ValidationResult, o as InferCollectionData, p as ValidationIssue, t as ContentLayerConfig } from "./content-config-DJXUOcNG.mjs";
2
+ import { r as MarkdownConfig, t as Heading } from "./heading-Dhvzlay-.mjs";
3
+
4
+ //#region src/convert.d.ts
5
+ type ConvertOptions = {
6
+ markdown?: MarkdownConfig;
7
+ };
8
+ type ConvertResult = {
9
+ html: string;
10
+ toc: Heading[];
11
+ frontmatter: Record<string, unknown>;
12
+ };
13
+ declare function convert(input: string, options?: ConvertOptions): Promise<ConvertResult>;
14
+ //#endregion
15
+ //#region src/entry.d.ts
16
+ type RenderedContent = {
17
+ /** Processed HTML */html: string; /** Extracted headings for TOC */
18
+ headings: Heading[]; /** Estimated read time in minutes */
19
+ readTime: number;
20
+ };
21
+ declare class ContentEntry<T = Record<string, any>> {
22
+ /** URL-friendly identifier */
23
+ readonly slug: string;
24
+ /** Collection this entry belongs to */
25
+ readonly collection: string;
26
+ /** Absolute path to source file */
27
+ readonly filePath: string;
28
+ /** Validated data (frontmatter or parsed data) */
29
+ readonly data: T;
30
+ /** Raw body content (markdown only) */
31
+ readonly rawContent?: string;
32
+ /** Cached render result */
33
+ private _rendered?;
34
+ /** Markdown config for rendering */
35
+ private _markdownConfig;
36
+ constructor(slug: string, collection: string, filePath: string, data: T, rawContent: string | undefined, markdownConfig: MarkdownConfig);
37
+ /** Render the entry content to HTML. Cached after first call. */
38
+ render(options?: {
39
+ force?: boolean;
40
+ }): Promise<RenderedContent>;
41
+ /** Clear cached render result. */
42
+ clearRenderCache(): void;
43
+ }
44
+ //#endregion
45
+ //#region src/validation/code-block-validator.d.ts
46
+ declare const codeBlockValidator: ContentValidator;
47
+ //#endregion
48
+ //#region src/validation/heading-validator.d.ts
49
+ declare const headingValidator: ContentValidator;
50
+ //#endregion
51
+ //#region src/validation/link-validator.d.ts
52
+ type LinkValidatorOptions = {
53
+ /** Glob patterns for internal links to skip file-existence checks on. */skipPatterns?: string[];
54
+ };
55
+ declare function createLinkValidator(options?: LinkValidatorOptions): ContentValidator;
56
+ declare const linkValidator: ContentValidator;
57
+ //#endregion
58
+ //#region src/validation/runner.d.ts
59
+ /** The built-in validators for markdown content. */
60
+ declare const builtinMarkdownValidators: ContentValidator[];
61
+ /** Run all validators on a single content entry. */
62
+ declare function runValidators(ctx: ValidatorContext, validators: ContentValidator[]): Promise<ValidationIssue[]>;
63
+ //#endregion
64
+ //#region src/content-layer.d.ts
65
+ type WatchEvent = {
66
+ collection: string;
67
+ event: string;
68
+ filename: string | null;
69
+ };
70
+ type WatchHandle = {
71
+ close(): void;
72
+ };
73
+ type WatchCallback = (event: WatchEvent) => void;
74
+ /** Typed content layer that preserves collection schema types. */
75
+ type TypedContentLayer<T extends Record<string, CollectionDef>> = ContentLayer & {
76
+ getCollection<K extends keyof T & string>(name: K): Promise<ContentEntry<InferCollectionData<T[K]>>[]>;
77
+ getEntry<K extends keyof T & string>(collection: K, slug: string): Promise<ContentEntry<InferCollectionData<T[K]>> | undefined>;
78
+ };
79
+ interface ContentLayer {
80
+ /** Get all entries in a collection. */
81
+ getCollection(name: string): Promise<ContentEntry[]>;
82
+ /** Get a single entry by collection name and slug. */
83
+ getEntry(collection: string, slug: string): Promise<ContentEntry | undefined>;
84
+ /** Convert raw markdown to HTML (no collection, no validation). */
85
+ convert(markdown: string, options?: LayerConvertOptions): Promise<ConvertResult>;
86
+ /** Invalidate a single entry's cache. */
87
+ invalidate(collection: string, slug: string): Promise<void>;
88
+ /** Invalidate an entire collection's cache. */
89
+ invalidateCollection(collection: string): Promise<void>;
90
+ /** Invalidate all cached data. */
91
+ invalidateAll(): void;
92
+ /** Validate all entries in a collection (or all collections). */
93
+ validate(collection?: string): Promise<ValidationResult[]>;
94
+ /** Get the names of all configured collections. */
95
+ getCollectionNames(): string[];
96
+ /** Get the definition of a collection. */
97
+ getCollectionDef(name: string): CollectionDef | undefined;
98
+ /** Get all collection definitions. */
99
+ getCollections(): Record<string, CollectionDef>;
100
+ /** Invalidate entries in a collection that match a predicate. Returns count of invalidated entries. */
101
+ invalidateWhere(collection: string, predicate: (entry: ContentEntry) => boolean): Promise<number>;
102
+ /** Watch collection directories for changes. Returns a handle to stop watching. */
103
+ watch(callback: WatchCallback): WatchHandle;
104
+ /** Get cache statistics for debugging and monitoring. */
105
+ getCacheStats(): {
106
+ collections: number;
107
+ entries: Record<string, number>;
108
+ totalEntries: number;
109
+ };
110
+ }
111
+ type LayerConvertOptions = {
112
+ markdown?: MarkdownConfig;
113
+ };
114
+ /** Create a new content layer from a configuration. */
115
+ declare function createContentLayer<T extends Record<string, CollectionDef>>(config: ContentLayerConfig & {
116
+ collections: T;
117
+ }): TypedContentLayer<T>;
118
+ declare function createContentLayer(config: ContentLayerConfig): ContentLayer;
119
+ //#endregion
120
+ export { ConvertResult as _, WatchEvent as a, builtinMarkdownValidators as c, linkValidator as d, headingValidator as f, ConvertOptions as g, RenderedContent as h, WatchCallback as i, runValidators as l, ContentEntry as m, LayerConvertOptions as n, WatchHandle as o, codeBlockValidator as p, TypedContentLayer as r, createContentLayer as s, ContentLayer as t, createLinkValidator as u, convert as v };
121
+ //# sourceMappingURL=content-layer-CpHYUYNN.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-layer-CpHYUYNN.d.mts","names":[],"sources":["../src/convert.ts","../src/entry.ts","../src/validation/code-block-validator.ts","../src/validation/heading-validator.ts","../src/validation/link-validator.ts","../src/validation/runner.ts","../src/content-layer.ts"],"mappings":"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"}