@glw907/cairn-cms 0.10.0 → 0.14.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 (95) hide show
  1. package/dist/components/ComponentForm.svelte +33 -10
  2. package/dist/components/ComponentForm.svelte.d.ts.map +1 -1
  3. package/dist/components/IconPicker.svelte +53 -7
  4. package/dist/components/IconPicker.svelte.d.ts +7 -3
  5. package/dist/components/IconPicker.svelte.d.ts.map +1 -1
  6. package/dist/content/adapter.d.ts +4 -0
  7. package/dist/content/adapter.d.ts.map +1 -0
  8. package/dist/content/adapter.js +4 -0
  9. package/dist/content/concepts.js +2 -2
  10. package/dist/content/schema.d.ts +75 -0
  11. package/dist/content/schema.d.ts.map +1 -0
  12. package/dist/content/schema.js +72 -0
  13. package/dist/content/types.d.ts +30 -7
  14. package/dist/content/types.d.ts.map +1 -1
  15. package/dist/content/validate.d.ts +5 -3
  16. package/dist/content/validate.d.ts.map +1 -1
  17. package/dist/content/validate.js +14 -7
  18. package/dist/delivery/CairnHead.svelte +36 -0
  19. package/dist/delivery/CairnHead.svelte.d.ts +15 -0
  20. package/dist/delivery/CairnHead.svelte.d.ts.map +1 -0
  21. package/dist/delivery/content-index.d.ts +16 -6
  22. package/dist/delivery/content-index.d.ts.map +1 -1
  23. package/dist/delivery/content-index.js +17 -8
  24. package/dist/delivery/index.d.ts +26 -0
  25. package/dist/delivery/index.d.ts.map +1 -0
  26. package/dist/delivery/index.js +21 -0
  27. package/dist/delivery/json-ld.d.ts +2 -0
  28. package/dist/delivery/json-ld.d.ts.map +1 -0
  29. package/dist/delivery/json-ld.js +16 -0
  30. package/dist/delivery/responses.d.ts +14 -0
  31. package/dist/delivery/responses.d.ts.map +1 -0
  32. package/dist/delivery/responses.js +30 -0
  33. package/dist/delivery/seo-fields.d.ts +22 -0
  34. package/dist/delivery/seo-fields.d.ts.map +1 -0
  35. package/dist/delivery/seo-fields.js +32 -0
  36. package/dist/delivery/seo.d.ts +4 -0
  37. package/dist/delivery/seo.d.ts.map +1 -1
  38. package/dist/delivery/seo.js +11 -0
  39. package/dist/delivery/site-descriptors.d.ts +5 -0
  40. package/dist/delivery/site-descriptors.d.ts.map +1 -0
  41. package/dist/delivery/site-descriptors.js +9 -0
  42. package/dist/delivery/site-index.d.ts +8 -2
  43. package/dist/delivery/site-index.d.ts.map +1 -1
  44. package/dist/delivery/site-index.js +26 -2
  45. package/dist/delivery/site-indexes.d.ts +26 -0
  46. package/dist/delivery/site-indexes.d.ts.map +1 -0
  47. package/dist/delivery/site-indexes.js +22 -0
  48. package/dist/index.d.ts +9 -3
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +5 -2
  51. package/dist/render/component-grammar.d.ts +7 -0
  52. package/dist/render/component-grammar.d.ts.map +1 -1
  53. package/dist/render/component-grammar.js +27 -8
  54. package/dist/render/component-validate.js +3 -3
  55. package/dist/render/glyph.d.ts +4 -1
  56. package/dist/render/glyph.d.ts.map +1 -1
  57. package/dist/render/glyph.js +6 -2
  58. package/dist/render/registry.d.ts +23 -5
  59. package/dist/render/registry.d.ts.map +1 -1
  60. package/dist/render/registry.js +6 -0
  61. package/dist/render/rehype-dispatch.d.ts +1 -5
  62. package/dist/render/rehype-dispatch.d.ts.map +1 -1
  63. package/dist/render/rehype-dispatch.js +71 -19
  64. package/dist/render/remark-directives.d.ts +1 -1
  65. package/dist/render/remark-directives.d.ts.map +1 -1
  66. package/dist/render/remark-directives.js +37 -0
  67. package/dist/sveltekit/public-routes.d.ts +14 -0
  68. package/dist/sveltekit/public-routes.d.ts.map +1 -1
  69. package/dist/sveltekit/public-routes.js +22 -2
  70. package/package.json +6 -1
  71. package/src/lib/components/ComponentForm.svelte +33 -10
  72. package/src/lib/components/IconPicker.svelte +53 -7
  73. package/src/lib/content/adapter.ts +10 -0
  74. package/src/lib/content/concepts.ts +2 -2
  75. package/src/lib/content/schema.ts +133 -0
  76. package/src/lib/content/types.ts +30 -7
  77. package/src/lib/content/validate.ts +10 -7
  78. package/src/lib/delivery/CairnHead.svelte +36 -0
  79. package/src/lib/delivery/content-index.ts +39 -17
  80. package/src/lib/delivery/index.ts +36 -0
  81. package/src/lib/delivery/json-ld.ts +16 -0
  82. package/src/lib/delivery/responses.ts +34 -0
  83. package/src/lib/delivery/seo-fields.ts +43 -0
  84. package/src/lib/delivery/seo.ts +13 -0
  85. package/src/lib/delivery/site-descriptors.ts +12 -0
  86. package/src/lib/delivery/site-index.ts +26 -2
  87. package/src/lib/delivery/site-indexes.ts +52 -0
  88. package/src/lib/index.ts +8 -2
  89. package/src/lib/render/component-grammar.ts +34 -10
  90. package/src/lib/render/component-validate.ts +3 -3
  91. package/src/lib/render/glyph.ts +6 -2
  92. package/src/lib/render/registry.ts +27 -5
  93. package/src/lib/render/rehype-dispatch.ts +67 -20
  94. package/src/lib/render/remark-directives.ts +39 -1
  95. package/src/lib/sveltekit/public-routes.ts +33 -2
@@ -0,0 +1,15 @@
1
+ import type { SeoMeta } from './seo.js';
2
+ type $$ComponentProps = {
3
+ seo: SeoMeta;
4
+ title?: string | false;
5
+ };
6
+ /**
7
+ * Renders a page's SEO head from a SeoMeta object into <svelte:head>: a title, meta tags, link
8
+ * tags, and one escaped JSON-LD script. The title renders from seo.title by default; title={false}
9
+ * lets the site own the <title>, and a string overrides it. It carries no CSS, so it pulls in no
10
+ * admin styles.
11
+ */
12
+ declare const CairnHead: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type CairnHead = ReturnType<typeof CairnHead>;
14
+ export default CairnHead;
15
+ //# sourceMappingURL=CairnHead.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CairnHead.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/CairnHead.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAGvC,KAAK,gBAAgB,GAAI;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;CAAE,CAAC;AAkCnE;;;;;GAKG;AACH,QAAA,MAAM,SAAS,sDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -17,17 +17,25 @@ export interface ContentSummary {
17
17
  wordCount: number;
18
18
  draft: boolean;
19
19
  }
20
- /** The detail view: a summary plus the frontmatter and the body to render. */
21
- export interface ContentEntry extends ContentSummary {
22
- frontmatter: Record<string, unknown>;
20
+ /** The detail view: a summary plus the frontmatter and the body to render. The frontmatter
21
+ * type defaults to `Record<string, unknown>`; the typed-reads pass infers it from the concept
22
+ * fields. Generic now so that change does not break this signature. */
23
+ export interface ContentEntry<F = Record<string, unknown>> extends ContentSummary {
24
+ frontmatter: F;
23
25
  body: string;
24
26
  }
27
+ /** One entry's validation failure, recorded at build for the site aggregator's gate. */
28
+ export interface ContentProblem {
29
+ id: string;
30
+ draft: boolean;
31
+ errors: Record<string, string>;
32
+ }
25
33
  /** The per-concept query surface. */
26
- export interface ContentIndex {
34
+ export interface ContentIndex<F = Record<string, unknown>> {
27
35
  all(opts?: {
28
36
  includeDrafts?: boolean;
29
37
  }): ContentSummary[];
30
- byId(id: string): ContentEntry | undefined;
38
+ byId(id: string): ContentEntry<F> | undefined;
31
39
  byTag(tag: string, opts?: {
32
40
  includeDrafts?: boolean;
33
41
  }): ContentSummary[];
@@ -39,9 +47,11 @@ export interface ContentIndex {
39
47
  newer?: ContentSummary;
40
48
  older?: ContentSummary;
41
49
  };
50
+ /** Per-entry validation failures recorded at build, for the site-level build gate. */
51
+ problems(): ContentProblem[];
42
52
  }
43
53
  /** Map a Vite eager `?raw` glob record (`{ path: raw }`) to `RawFile[]`. */
44
54
  export declare function fromGlob(record: Record<string, string>): RawFile[];
45
55
  /** Build a concept's index from its raw files and normalized descriptor. */
46
- export declare function createContentIndex(files: RawFile[], descriptor: ConceptDescriptor): ContentIndex;
56
+ export declare function createContentIndex<F = Record<string, unknown>>(files: RawFile[], descriptor: ConceptDescriptor): ContentIndex<F>;
47
57
  //# sourceMappingURL=content-index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"content-index.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/content-index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,yFAAyF;AACzF,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,8EAA8E;AAC9E,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qCAAqC;AACrC,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,IAAI,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,cAAc,EAAE,CAAC;IAC1D,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IAC3C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,cAAc,EAAE,CAAC;IACzE,OAAO,IAAI;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG;QAAE,KAAK,CAAC,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,cAAc,CAAA;KAAE,CAAC;CAC1E;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,EAAE,CAElE;AAqBD,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,iBAAiB,GAAG,YAAY,CA2DhG"}
1
+ {"version":3,"file":"content-index.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/content-index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,yFAAyF;AACzF,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;wEAEwE;AACxE,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,SAAQ,cAAc;IAC/E,WAAW,EAAE,CAAC,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,qCAAqC;AACrC,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACvD,GAAG,CAAC,IAAI,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,cAAc,EAAE,CAAC;IAC1D,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAC9C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,cAAc,EAAE,CAAC;IACzE,OAAO,IAAI;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG;QAAE,KAAK,CAAC,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,cAAc,CAAA;KAAE,CAAC;IACzE,sFAAsF;IACtF,QAAQ,IAAI,cAAc,EAAE,CAAC;CAC9B;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,EAAE,CAElE;AAqBD,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5D,KAAK,EAAE,OAAO,EAAE,EAChB,UAAU,EAAE,iBAAiB,GAC5B,YAAY,CAAC,CAAC,CAAC,CAmEjB"}
@@ -29,23 +29,31 @@ function asTags(value) {
29
29
  }
30
30
  /** Build a concept's index from its raw files and normalized descriptor. */
31
31
  export function createContentIndex(files, descriptor) {
32
+ const problems = [];
32
33
  const entries = files.map((file) => {
33
34
  const id = idFromFilename(basename(file.path));
34
35
  const slug = slugFromId(id, descriptor.routing.dated ? descriptor.datePrefix : null);
35
- const { frontmatter, body } = parseMarkdown(file.raw);
36
- const date = asDate(frontmatter.date);
36
+ const { frontmatter: raw, body } = parseMarkdown(file.raw);
37
+ const date = asDate(raw.date);
38
+ const draft = raw.draft === true;
39
+ // Validate once at build. The cheap summary stays raw-derived and robust; the typed detail
40
+ // frontmatter carries the normalized data on success, the raw frontmatter on failure. A
41
+ // failure is recorded, not thrown, so the query surface does not explode on construction.
42
+ const result = descriptor.validate(raw, body);
43
+ if (!result.ok)
44
+ problems.push({ id, draft, errors: result.errors });
37
45
  return {
38
46
  id,
39
47
  slug,
40
48
  permalink: permalink(descriptor, { id, slug, date }),
41
- title: asString(frontmatter.title) ?? id,
49
+ title: asString(raw.title) ?? id,
42
50
  date,
43
- updated: asDate(frontmatter.updated),
44
- tags: asTags(frontmatter.tags),
45
- excerpt: deriveExcerpt(body, { description: asString(frontmatter.description) }),
51
+ updated: asDate(raw.updated),
52
+ tags: asTags(raw.tags),
53
+ excerpt: deriveExcerpt(body, { description: asString(raw.description) }),
46
54
  wordCount: wordCount(body),
47
- draft: frontmatter.draft === true,
48
- frontmatter,
55
+ draft,
56
+ frontmatter: (result.ok ? result.data : raw),
49
57
  body,
50
58
  };
51
59
  });
@@ -82,5 +90,6 @@ export function createContentIndex(files, descriptor) {
82
90
  older: i < list.length - 1 ? summarize(list[i + 1]) : undefined,
83
91
  };
84
92
  },
93
+ problems: () => problems,
85
94
  };
86
95
  }
@@ -0,0 +1,26 @@
1
+ export { createContentIndex, fromGlob } from './content-index.js';
2
+ export type { RawFile, ContentSummary, ContentEntry, ContentIndex, ContentProblem } from './content-index.js';
3
+ export { createSiteIndex } from './site-index.js';
4
+ export type { SiteIndex, ConceptIndex } from './site-index.js';
5
+ export { createSiteIndexes } from './site-indexes.js';
6
+ export type { SiteIndexes, SiteGlobs } from './site-indexes.js';
7
+ export { siteDescriptors } from './site-descriptors.js';
8
+ export { deriveExcerpt, wordCount } from './excerpt.js';
9
+ export { buildRssFeed, buildJsonFeed } from './feeds.js';
10
+ export type { FeedChannel, FeedItem } from './feeds.js';
11
+ export { buildSitemap } from './sitemap.js';
12
+ export type { SitemapUrl } from './sitemap.js';
13
+ export { buildRobots } from './robots.js';
14
+ export { buildSeoMeta } from './seo.js';
15
+ export type { SeoInput, SeoMeta } from './seo.js';
16
+ export { readSeoFields, resolveImageUrl } from './seo-fields.js';
17
+ export type { SeoFields } from './seo-fields.js';
18
+ export { paginate } from './paginate.js';
19
+ export type { Page } from './paginate.js';
20
+ export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from './responses.js';
21
+ export { jsonLdScript } from './json-ld.js';
22
+ export { permalink } from '../content/permalink.js';
23
+ export { createPublicRoutes } from '../sveltekit/public-routes.js';
24
+ export type { PublicRoutesDeps, ListData, TagData, TagIndexData, EntryData, } from '../sveltekit/public-routes.js';
25
+ export { default as CairnHead } from './CairnHead.svelte';
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC9G,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,YAAY,EACV,gBAAgB,EAChB,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,SAAS,GACV,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,21 @@
1
+ // cairn-cms: the public delivery entry (@glw907/cairn-cms/delivery). The complete, canonical,
2
+ // backend-free toolkit a SvelteKit site wires its public pages with: the content index and the
3
+ // site resolver, the descriptor helper, the syndication and SEO builders, the endpoint response
4
+ // helpers, the catch-all route loaders, and the head component. It imports nothing from auth,
5
+ // github, or email, so importing it does not pull the server backend into a public bundle.
6
+ export { createContentIndex, fromGlob } from './content-index.js';
7
+ export { createSiteIndex } from './site-index.js';
8
+ export { createSiteIndexes } from './site-indexes.js';
9
+ export { siteDescriptors } from './site-descriptors.js';
10
+ export { deriveExcerpt, wordCount } from './excerpt.js';
11
+ export { buildRssFeed, buildJsonFeed } from './feeds.js';
12
+ export { buildSitemap } from './sitemap.js';
13
+ export { buildRobots } from './robots.js';
14
+ export { buildSeoMeta } from './seo.js';
15
+ export { readSeoFields, resolveImageUrl } from './seo-fields.js';
16
+ export { paginate } from './paginate.js';
17
+ export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from './responses.js';
18
+ export { jsonLdScript } from './json-ld.js';
19
+ export { permalink } from '../content/permalink.js';
20
+ export { createPublicRoutes } from '../sveltekit/public-routes.js';
21
+ export { default as CairnHead } from './CairnHead.svelte';
@@ -0,0 +1,2 @@
1
+ export declare function jsonLdScript(data: Record<string, unknown>): string;
2
+ //# sourceMappingURL=json-ld.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-ld.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/json-ld.ts"],"names":[],"mappings":"AAOA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAQlE"}
@@ -0,0 +1,16 @@
1
+ // cairn-cms: serialize a JSON-LD object into a safe inline script string. JSON.stringify does
2
+ // not escape <, >, or &, so a value containing "</script>" would close the element and inject
3
+ // markup. Escaping the three characters to their JSON unicode forms keeps the structured data
4
+ // identical for a parser while making the bytes unable to break out of the script element.
5
+ // The line separator U+2028 and paragraph separator U+2029 get the same treatment: they are
6
+ // legal inside a JSON string but unsafe in inline script text, where some parsers read them as
7
+ // line terminators, so an author pasting one into frontmatter would corrupt the JSON-LD block.
8
+ export function jsonLdScript(data) {
9
+ const json = JSON.stringify(data)
10
+ .replace(/</g, '\\u003c')
11
+ .replace(/>/g, '\\u003e')
12
+ .replace(/&/g, '\\u0026')
13
+ .replace(/\u2028/g, '\\u2028')
14
+ .replace(/\u2029/g, '\\u2029');
15
+ return `<script type="application/ld+json">${json}</script>`;
16
+ }
@@ -0,0 +1,14 @@
1
+ import { type FeedChannel, type FeedItem } from './feeds.js';
2
+ import { type SitemapUrl } from './sitemap.js';
3
+ /** An RSS 2.0 feed response. */
4
+ export declare function rssResponse(channel: FeedChannel, items: FeedItem[]): Response;
5
+ /** A JSON Feed 1.1 response. */
6
+ export declare function jsonFeedResponse(channel: FeedChannel, items: FeedItem[]): Response;
7
+ /** A sitemap response. */
8
+ export declare function sitemapResponse(urls: SitemapUrl[]): Response;
9
+ /** A robots.txt response. */
10
+ export declare function robotsResponse(opts: {
11
+ sitemapUrl: string;
12
+ disallow?: string[];
13
+ }): Response;
14
+ //# sourceMappingURL=responses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/responses.ts"],"names":[],"mappings":"AAGA,OAAO,EAA+B,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAG7D,gCAAgC;AAChC,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAI7E;AAED,gCAAgC;AAChC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAIlF;AAED,0BAA0B;AAC1B,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,CAI5D;AAED,6BAA6B;AAC7B,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,QAAQ,CAI1F"}
@@ -0,0 +1,30 @@
1
+ // cairn-cms: response helpers for the public delivery endpoints. Each wraps a builder in a
2
+ // Response with the correct Content-Type, so a site's +server.ts GET is a single call. The
3
+ // content type is the one detail every site otherwise copies and occasionally gets wrong.
4
+ import { buildRssFeed, buildJsonFeed } from './feeds.js';
5
+ import { buildSitemap } from './sitemap.js';
6
+ import { buildRobots } from './robots.js';
7
+ /** An RSS 2.0 feed response. */
8
+ export function rssResponse(channel, items) {
9
+ return new Response(buildRssFeed(channel, items), {
10
+ headers: { 'Content-Type': 'application/rss+xml; charset=utf-8' },
11
+ });
12
+ }
13
+ /** A JSON Feed 1.1 response. */
14
+ export function jsonFeedResponse(channel, items) {
15
+ return new Response(buildJsonFeed(channel, items), {
16
+ headers: { 'Content-Type': 'application/feed+json; charset=utf-8' },
17
+ });
18
+ }
19
+ /** A sitemap response. */
20
+ export function sitemapResponse(urls) {
21
+ return new Response(buildSitemap(urls), {
22
+ headers: { 'Content-Type': 'application/xml; charset=utf-8' },
23
+ });
24
+ }
25
+ /** A robots.txt response. */
26
+ export function robotsResponse(opts) {
27
+ return new Response(buildRobots(opts), {
28
+ headers: { 'Content-Type': 'text/plain; charset=utf-8' },
29
+ });
30
+ }
@@ -0,0 +1,22 @@
1
+ /** The head fields a concept can carry in frontmatter. Each is optional and omitted when absent.
2
+ * `author` is article-scoped downstream: the head builder emits `article:author` only for a dated
3
+ * entry, so an `author` on an undated Page is read here but not rendered. */
4
+ export interface SeoFields {
5
+ description?: string;
6
+ image?: string;
7
+ robots?: string;
8
+ author?: string;
9
+ }
10
+ /** Read the known SEO head fields off an entry's normalized frontmatter. Keeps a present string,
11
+ * trimmed, and omits an absent, empty, or non-string value. Trimming the stored value keeps a stray
12
+ * `robots: " noindex "` from reaching the head tag with surrounding whitespace. The field must be
13
+ * declared in the concept's schema to survive the validate-once read; an undeclared key is not on the
14
+ * normalized frontmatter. */
15
+ export declare function readSeoFields(frontmatter: Record<string, unknown>): SeoFields;
16
+ /** Resolve an author-supplied image path to an absolute URL against the site origin. An absolute or
17
+ * protocol-relative URL passes through; a root-relative path anchors to the origin; a malformed
18
+ * string returns undefined rather than throwing at build. The sites use a bare-domain origin, so a
19
+ * bare path also anchors to the origin root; against a sub-path origin it would resolve relative to
20
+ * that path, per the WHATWG URL rules. */
21
+ export declare function resolveImageUrl(image: string, origin: string): string | undefined;
22
+ //# sourceMappingURL=seo-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-fields.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/seo-fields.ts"],"names":[],"mappings":"AAKA;;8EAE8E;AAC9E,MAAM,WAAW,SAAS;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID;;;;8BAI8B;AAC9B,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAO7E;AAED;;;;2CAI2C;AAC3C,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMjF"}
@@ -0,0 +1,32 @@
1
+ // cairn-cms: the SEO head fields read at the cross-concept boundary (schema-source-of-truth design,
2
+ // Plan 3). The catch-all route resolves any concept by request path, so the entry's frontmatter is
3
+ // typed Record<string, unknown>; this reads the known head fields by name and coerces. Kept apart
4
+ // from seo.ts (the head builder) so reading frontmatter and building the head stay distinct concerns.
5
+ const KEYS = ['description', 'image', 'robots', 'author'];
6
+ /** Read the known SEO head fields off an entry's normalized frontmatter. Keeps a present string,
7
+ * trimmed, and omits an absent, empty, or non-string value. Trimming the stored value keeps a stray
8
+ * `robots: " noindex "` from reaching the head tag with surrounding whitespace. The field must be
9
+ * declared in the concept's schema to survive the validate-once read; an undeclared key is not on the
10
+ * normalized frontmatter. */
11
+ export function readSeoFields(frontmatter) {
12
+ const fields = {};
13
+ for (const key of KEYS) {
14
+ const value = frontmatter[key];
15
+ if (typeof value === 'string' && value.trim() !== '')
16
+ fields[key] = value.trim();
17
+ }
18
+ return fields;
19
+ }
20
+ /** Resolve an author-supplied image path to an absolute URL against the site origin. An absolute or
21
+ * protocol-relative URL passes through; a root-relative path anchors to the origin; a malformed
22
+ * string returns undefined rather than throwing at build. The sites use a bare-domain origin, so a
23
+ * bare path also anchors to the origin root; against a sub-path origin it would resolve relative to
24
+ * that path, per the WHATWG URL rules. */
25
+ export function resolveImageUrl(image, origin) {
26
+ try {
27
+ return new URL(image, origin).href;
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
@@ -12,6 +12,10 @@ export interface SeoInput {
12
12
  json?: string;
13
13
  };
14
14
  image?: string;
15
+ /** A robots meta directive, e.g. "noindex, nofollow". Omitted from the head when absent. */
16
+ robots?: string;
17
+ /** Author name, emitted as article:author for the article type. */
18
+ author?: string;
15
19
  }
16
20
  /** Plain-data head: a title, meta tags, link tags, and one JSON-LD object. */
17
21
  export interface SeoMeta {
@@ -1 +1 @@
1
- {"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/seo.ts"],"names":[],"mappings":"AAIA,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,8EAA8E;AAC9E,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9D,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,sCAAsC;AACtC,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CA6CrD"}
1
+ {"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/seo.ts"],"names":[],"mappings":"AAIA,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,8EAA8E;AAC9E,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9D,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,sCAAsC;AACtC,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAsDrD"}
@@ -17,6 +17,17 @@ export function buildSeoMeta(input) {
17
17
  meta.push({ property: 'og:image', content: input.image });
18
18
  meta.push({ name: 'twitter:image', content: input.image });
19
19
  }
20
+ if (input.robots) {
21
+ meta.push({ name: 'robots', content: input.robots });
22
+ }
23
+ if (type === 'article') {
24
+ if (input.published)
25
+ meta.push({ property: 'article:published_time', content: input.published });
26
+ if (input.modified)
27
+ meta.push({ property: 'article:modified_time', content: input.modified });
28
+ if (input.author)
29
+ meta.push({ property: 'article:author', content: input.author });
30
+ }
20
31
  const links = [{ rel: 'canonical', href: input.canonicalUrl }];
21
32
  if (input.feeds?.rss) {
22
33
  links.push({ rel: 'alternate', type: 'application/rss+xml', href: input.feeds.rss, title: input.siteName });
@@ -0,0 +1,5 @@
1
+ import type { CairnAdapter, ConceptDescriptor } from '../content/types.js';
2
+ import type { SiteConfig } from '../nav/site-config.js';
3
+ /** Per-concept descriptors for a site, from its adapter content and its parsed site config. */
4
+ export declare function siteDescriptors(adapter: CairnAdapter, siteConfig: SiteConfig): ConceptDescriptor[];
5
+ //# sourceMappingURL=site-descriptors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-descriptors.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/site-descriptors.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,GAAG,iBAAiB,EAAE,CAElG"}
@@ -0,0 +1,9 @@
1
+ // cairn-cms: the one-call descriptor helper. A delivery site needs the same per-concept
2
+ // descriptors the admin runtime uses; this wraps the two calls that derive them so the
3
+ // pairing is not tribal knowledge. The YAML URL policy stays the single source of truth.
4
+ import { normalizeConcepts } from '../content/concepts.js';
5
+ import { urlPolicyFrom } from '../nav/site-config.js';
6
+ /** Per-concept descriptors for a site, from its adapter content and its parsed site config. */
7
+ export function siteDescriptors(adapter, siteConfig) {
8
+ return normalizeConcepts(adapter.content, urlPolicyFrom(siteConfig));
9
+ }
@@ -23,6 +23,12 @@ export interface SiteIndex {
23
23
  /** Every non-draft summary across concepts, for the site-wide sitemap. */
24
24
  all(): ContentSummary[];
25
25
  }
26
- /** Union per-concept indexes into a site-level resolver; throw on a duplicate permalink. */
27
- export declare function createSiteIndex(concepts: ConceptIndex[]): SiteIndex;
26
+ /**
27
+ * Union per-concept indexes into a site-level resolver. Throws on a duplicate permalink and,
28
+ * unless `validate` is `false`, on any non-draft entry whose frontmatter fails its concept's
29
+ * validator, so malformed content fails the build instead of shipping.
30
+ */
31
+ export declare function createSiteIndex(concepts: ConceptIndex[], opts?: {
32
+ validate?: boolean;
33
+ }): SiteIndex;
28
34
  //# sourceMappingURL=site-index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"site-index.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/site-index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAErF,4DAA4D;AAC5D,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACxB,4FAA4F;IAC5F,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IACpD,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,cAAc,CAAA;KAAE,CAAC;IACpF,uGAAuG;IACvG,OAAO,IAAI;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9B,mEAAmE;IACnE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IAC9C,0EAA0E;IAC1E,GAAG,IAAI,cAAc,EAAE,CAAC;CACzB;AAOD,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,SAAS,CAkCnE"}
1
+ {"version":3,"file":"site-index.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/site-index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAErF,4DAA4D;AAC5D,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACxB,4FAA4F;IAC5F,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IACpD,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,cAAc,CAAA;KAAE,CAAC;IACpF,uGAAuG;IACvG,OAAO,IAAI;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9B,mEAAmE;IACnE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IAC9C,0EAA0E;IAC1E,GAAG,IAAI,cAAc,EAAE,CAAC;CACzB;AAqBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,SAAS,CAwCtG"}
@@ -2,8 +2,32 @@
2
2
  function normalizePath(path) {
3
3
  return path.length > 1 ? path.replace(/\/+$/, '') : path;
4
4
  }
5
- /** Union per-concept indexes into a site-level resolver; throw on a duplicate permalink. */
6
- export function createSiteIndex(concepts) {
5
+ /** Collect non-draft validation failures across concepts from each index's recorded verdicts. */
6
+ function siteProblems(concepts) {
7
+ const problems = [];
8
+ for (const { descriptor, index } of concepts) {
9
+ for (const problem of index.problems()) {
10
+ if (problem.draft)
11
+ continue; // a half-finished draft never ships, so it does not fail the build
12
+ for (const [field, message] of Object.entries(problem.errors)) {
13
+ problems.push(`${descriptor.dir}/${problem.id}: ${field}: ${message}`);
14
+ }
15
+ }
16
+ }
17
+ return problems;
18
+ }
19
+ /**
20
+ * Union per-concept indexes into a site-level resolver. Throws on a duplicate permalink and,
21
+ * unless `validate` is `false`, on any non-draft entry whose frontmatter fails its concept's
22
+ * validator, so malformed content fails the build instead of shipping.
23
+ */
24
+ export function createSiteIndex(concepts, opts = {}) {
25
+ if (opts.validate !== false) {
26
+ const problems = siteProblems(concepts);
27
+ if (problems.length > 0) {
28
+ throw new Error(`site index: ${problems.length} invalid frontmatter field(s):\n ${problems.join('\n ')}`);
29
+ }
30
+ }
7
31
  const byPath = new Map();
8
32
  const byId = new Map();
9
33
  for (const { descriptor, index } of concepts) {
@@ -0,0 +1,26 @@
1
+ import type { CairnAdapter, ConceptConfig } from '../content/types.js';
2
+ import type { Infer } from '../content/schema.js';
3
+ import type { SiteConfig } from '../nav/site-config.js';
4
+ import type { ContentIndex } from './content-index.js';
5
+ import type { SiteIndex } from './site-index.js';
6
+ /** A per-concept raw glob record (`{ path: raw }`) keyed by concept id, from `import.meta.glob`. */
7
+ export type SiteGlobs<A extends CairnAdapter> = {
8
+ [K in keyof A['content']]?: Record<string, string>;
9
+ };
10
+ /** The typed per-concept indexes plus the cross-concept `site` resolver. A concept literally named
11
+ * `site` is not supported, since `site` is the reserved resolver key. */
12
+ export type SiteIndexes<A extends CairnAdapter> = {
13
+ [K in keyof A['content']]: ContentIndex<NonNullable<A['content'][K]> extends ConceptConfig<infer S> ? Infer<S> : Record<string, unknown>>;
14
+ } & {
15
+ readonly site: SiteIndex;
16
+ };
17
+ /**
18
+ * Build typed per-concept indexes and a site resolver from one adapter. Pass the per-concept raw
19
+ * globs as `{ posts: import.meta.glob('...?raw', { eager: true }), ... }`; Vite needs the literal
20
+ * glob at the call site, so the engine cannot glob on the site's behalf. `validate: false` opts out
21
+ * of the build gate, exactly as on `createSiteIndex`.
22
+ */
23
+ export declare function createSiteIndexes<const A extends CairnAdapter>(adapter: A, config: SiteConfig, globs: SiteGlobs<A>, opts?: {
24
+ validate?: boolean;
25
+ }): SiteIndexes<A>;
26
+ //# sourceMappingURL=site-indexes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-indexes.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/site-indexes.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAIxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE/D,oGAAoG;AACpG,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,YAAY,IAAI;KAC7C,CAAC,IAAI,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CACnD,CAAC;AAEF;0EAC0E;AAC1E,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,YAAY,IAAI;KAC/C,CAAC,IAAI,MAAM,CAAC,CAAC,SAAS,CAAC,GAAG,YAAY,CACrC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACjG;CACF,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAEjC;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,CAAC,SAAS,YAAY,EAC5D,OAAO,EAAE,CAAC,EACV,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EACnB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC,WAAW,CAAC,CAAC,CAAC,CAYhB"}
@@ -0,0 +1,22 @@
1
+ import { siteDescriptors } from './site-descriptors.js';
2
+ import { createContentIndex, fromGlob } from './content-index.js';
3
+ import { createSiteIndex } from './site-index.js';
4
+ /**
5
+ * Build typed per-concept indexes and a site resolver from one adapter. Pass the per-concept raw
6
+ * globs as `{ posts: import.meta.glob('...?raw', { eager: true }), ... }`; Vite needs the literal
7
+ * glob at the call site, so the engine cannot glob on the site's behalf. `validate: false` opts out
8
+ * of the build gate, exactly as on `createSiteIndex`.
9
+ */
10
+ export function createSiteIndexes(adapter, config, globs, opts = {}) {
11
+ const descriptors = siteDescriptors(adapter, config);
12
+ const byConcept = {};
13
+ const conceptIndexes = [];
14
+ for (const descriptor of descriptors) {
15
+ const record = globs[descriptor.id] ?? {};
16
+ const index = createContentIndex(fromGlob(record), descriptor);
17
+ byConcept[descriptor.id] = index;
18
+ conceptIndexes.push({ descriptor, index });
19
+ }
20
+ const site = createSiteIndex(conceptIndexes, opts);
21
+ return { ...byConcept, site };
22
+ }
package/dist/index.d.ts CHANGED
@@ -6,7 +6,9 @@ export type { CairnAdapter, ConceptConfig, FrontmatterField, TextField, Textarea
6
6
  export { CONCEPT_ROUTING, normalizeConcepts, findConcept } from './content/concepts.js';
7
7
  export { composeRuntime } from './content/compose.js';
8
8
  export { frontmatterFromForm, dateInputValue, serializeMarkdown, parseMarkdown, } from './content/frontmatter.js';
9
- export { validateFields } from './content/validate.js';
9
+ export { defineFields } from './content/schema.js';
10
+ export { defineAdapter } from './content/adapter.js';
11
+ export type { ConceptSchema, Infer, InferFields, DefineFieldsOptions, StandardInput, StandardSchemaV1 } from './content/schema.js';
10
12
  export { isValidId, idFromFilename, filenameFromId, slugify, slugFromId, composeDatedId, } from './content/ids.js';
11
13
  export type { DatePrefix } from './content/ids.js';
12
14
  export { defineRegistry, emptyValues } from './render/registry.js';
@@ -20,7 +22,7 @@ export type { ReferenceOptions } from './render/component-reference.js';
20
22
  export { glyph } from './render/glyph.js';
21
23
  export type { IconSet } from './render/glyph.js';
22
24
  export { remarkDirectiveStamp } from './render/remark-directives.js';
23
- export { rehypeDispatch, isElement, strProp, iconSpan, splitHead, cardShell, markFirstList, } from './render/rehype-dispatch.js';
25
+ export { rehypeDispatch, isElement, strProp, iconSpan, cardShell, markFirstList, } from './render/rehype-dispatch.js';
24
26
  export type { MakeIcon } from './render/rehype-dispatch.js';
25
27
  export { createRenderer } from './render/pipeline.js';
26
28
  export type { RendererOptions } from './render/pipeline.js';
@@ -34,9 +36,11 @@ export { parseSiteConfig, urlPolicyFrom, extractMenu, setMenu, validateNavTree,
34
36
  export type { NavNode, SiteConfig } from './nav/site-config.js';
35
37
  export { permalink } from './content/permalink.js';
36
38
  export { createContentIndex, fromGlob } from './delivery/content-index.js';
37
- export type { RawFile, ContentSummary, ContentEntry, ContentIndex, } from './delivery/content-index.js';
39
+ export type { RawFile, ContentSummary, ContentEntry, ContentIndex, ContentProblem, } from './delivery/content-index.js';
38
40
  export { createSiteIndex } from './delivery/site-index.js';
39
41
  export type { SiteIndex, ConceptIndex } from './delivery/site-index.js';
42
+ export { createSiteIndexes } from './delivery/site-indexes.js';
43
+ export type { SiteIndexes, SiteGlobs } from './delivery/site-indexes.js';
40
44
  export { deriveExcerpt, wordCount } from './delivery/excerpt.js';
41
45
  export { buildRssFeed, buildJsonFeed } from './delivery/feeds.js';
42
46
  export type { FeedChannel, FeedItem } from './delivery/feeds.js';
@@ -45,6 +49,8 @@ export type { SitemapUrl } from './delivery/sitemap.js';
45
49
  export { buildRobots } from './delivery/robots.js';
46
50
  export { buildSeoMeta } from './delivery/seo.js';
47
51
  export type { SeoInput, SeoMeta } from './delivery/seo.js';
52
+ export { readSeoFields, resolveImageUrl } from './delivery/seo-fields.js';
53
+ export type { SeoFields } from './delivery/seo-fields.js';
48
54
  export { paginate } from './delivery/paginate.js';
49
55
  export type { Page } from './delivery/paginate.js';
50
56
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -5,7 +5,8 @@ export { buildMagicLinkMessage, cloudflareSend } from './email.js';
5
5
  export { CONCEPT_ROUTING, normalizeConcepts, findConcept } from './content/concepts.js';
6
6
  export { composeRuntime } from './content/compose.js';
7
7
  export { frontmatterFromForm, dateInputValue, serializeMarkdown, parseMarkdown, } from './content/frontmatter.js';
8
- export { validateFields } from './content/validate.js';
8
+ export { defineFields } from './content/schema.js';
9
+ export { defineAdapter } from './content/adapter.js';
9
10
  export { isValidId, idFromFilename, filenameFromId, slugify, slugFromId, composeDatedId, } from './content/ids.js';
10
11
  // Render engine (Plan 04): generic directive pipeline; sites own the component registry.
11
12
  export { defineRegistry, emptyValues } from './render/registry.js';
@@ -15,7 +16,7 @@ export { buildComponentInsert } from './render/component-insert.js';
15
16
  export { generateComponentReference } from './render/component-reference.js';
16
17
  export { glyph } from './render/glyph.js';
17
18
  export { remarkDirectiveStamp } from './render/remark-directives.js';
18
- export { rehypeDispatch, isElement, strProp, iconSpan, splitHead, cardShell, markFirstList, } from './render/rehype-dispatch.js';
19
+ export { rehypeDispatch, isElement, strProp, iconSpan, cardShell, markFirstList, } from './render/rehype-dispatch.js';
19
20
  export { createRenderer } from './render/pipeline.js';
20
21
  export { CommitConflictError } from './github/types.js';
21
22
  export { appJwt, installationToken, signingSelfTest } from './github/signing.js';
@@ -29,9 +30,11 @@ export { parseSiteConfig, urlPolicyFrom, extractMenu, setMenu, validateNavTree,
29
30
  export { permalink } from './content/permalink.js';
30
31
  export { createContentIndex, fromGlob } from './delivery/content-index.js';
31
32
  export { createSiteIndex } from './delivery/site-index.js';
33
+ export { createSiteIndexes } from './delivery/site-indexes.js';
32
34
  export { deriveExcerpt, wordCount } from './delivery/excerpt.js';
33
35
  export { buildRssFeed, buildJsonFeed } from './delivery/feeds.js';
34
36
  export { buildSitemap } from './delivery/sitemap.js';
35
37
  export { buildRobots } from './delivery/robots.js';
36
38
  export { buildSeoMeta } from './delivery/seo.js';
39
+ export { readSeoFields, resolveImageUrl } from './delivery/seo-fields.js';
37
40
  export { paginate } from './delivery/paginate.js';
@@ -7,4 +7,11 @@ export declare function parseComponent(markdown: string, def: ComponentDef): Pro
7
7
  /** The raw attribute keys present on the component's opening directive, read from the parsed tree
8
8
  * (quote-aware, unlike a regex over the source). Used by validation to flag unknown keys. */
9
9
  export declare function parseRawAttributeKeys(markdown: string, def: ComponentDef): string[];
10
+ /** Parse the component once and derive both the guided-form values and the raw attribute keys.
11
+ * Validation needs both, so this seam spares it the double parse that calling
12
+ * {@link parseComponent} and {@link parseRawAttributeKeys} separately would cost. */
13
+ export declare function parseComponentWithRawKeys(markdown: string, def: ComponentDef): Promise<{
14
+ values: ComponentValues;
15
+ rawKeys: string[];
16
+ }>;
10
17
  //# sourceMappingURL=component-grammar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"component-grammar.d.ts","sourceRoot":"","sources":["../../src/lib/render/component-grammar.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAW,MAAM,eAAe,CAAC;AA8B5E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,GAAG,MAAM,CA2BrF;AAwBD;;wCAEwC;AACxC,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAqClG;AAED;8FAC8F;AAC9F,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,MAAM,EAAE,CAMnF"}
1
+ {"version":3,"file":"component-grammar.d.ts","sourceRoot":"","sources":["../../src/lib/render/component-grammar.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAW,MAAM,eAAe,CAAC;AA8B5E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,GAAG,MAAM,CA2BrF;AA4ED;;wCAEwC;AACxC,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAElG;AAED;8FAC8F;AAC9F,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,MAAM,EAAE,CAEnF;AAED;;sFAEsF;AACtF,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAGzD"}