@glw907/cairn-cms 0.17.0 → 0.18.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 (54) hide show
  1. package/dist/components/EditPage.svelte +9 -2
  2. package/dist/components/EditPage.svelte.d.ts +2 -0
  3. package/dist/components/EditPage.svelte.d.ts.map +1 -1
  4. package/dist/content/compose.d.ts.map +1 -1
  5. package/dist/content/compose.js +1 -0
  6. package/dist/content/links.d.ts +14 -0
  7. package/dist/content/links.d.ts.map +1 -0
  8. package/dist/content/links.js +41 -0
  9. package/dist/content/manifest.d.ts +55 -0
  10. package/dist/content/manifest.d.ts.map +1 -0
  11. package/dist/content/manifest.js +98 -0
  12. package/dist/content/types.d.ts +10 -1
  13. package/dist/content/types.d.ts.map +1 -1
  14. package/dist/delivery/index.d.ts +1 -0
  15. package/dist/delivery/index.d.ts.map +1 -1
  16. package/dist/delivery/index.js +1 -0
  17. package/dist/delivery/manifest.d.ts +13 -0
  18. package/dist/delivery/manifest.d.ts.map +1 -0
  19. package/dist/delivery/manifest.js +31 -0
  20. package/dist/github/repo.d.ts +21 -0
  21. package/dist/github/repo.d.ts.map +1 -1
  22. package/dist/github/repo.js +79 -0
  23. package/dist/index.d.ts +4 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +5 -0
  26. package/dist/render/pipeline.d.ts +4 -1
  27. package/dist/render/pipeline.d.ts.map +1 -1
  28. package/dist/render/pipeline.js +7 -2
  29. package/dist/render/resolve-links.d.ts +8 -0
  30. package/dist/render/resolve-links.d.ts.map +1 -0
  31. package/dist/render/resolve-links.js +36 -0
  32. package/dist/render/sanitize-schema.d.ts.map +1 -1
  33. package/dist/render/sanitize-schema.js +9 -0
  34. package/dist/sveltekit/content-routes.d.ts +3 -0
  35. package/dist/sveltekit/content-routes.d.ts.map +1 -1
  36. package/dist/sveltekit/content-routes.js +29 -2
  37. package/dist/sveltekit/public-routes.d.ts +2 -0
  38. package/dist/sveltekit/public-routes.d.ts.map +1 -1
  39. package/dist/sveltekit/public-routes.js +2 -1
  40. package/package.json +1 -1
  41. package/src/lib/components/EditPage.svelte +9 -2
  42. package/src/lib/content/compose.ts +1 -0
  43. package/src/lib/content/links.ts +48 -0
  44. package/src/lib/content/manifest.ts +138 -0
  45. package/src/lib/content/types.ts +10 -3
  46. package/src/lib/delivery/index.ts +1 -0
  47. package/src/lib/delivery/manifest.ts +38 -0
  48. package/src/lib/github/repo.ts +103 -0
  49. package/src/lib/index.ts +16 -0
  50. package/src/lib/render/pipeline.ts +8 -2
  51. package/src/lib/render/resolve-links.ts +42 -0
  52. package/src/lib/render/sanitize-schema.ts +9 -0
  53. package/src/lib/sveltekit/content-routes.ts +36 -4
  54. package/src/lib/sveltekit/public-routes.ts +4 -2
@@ -12,6 +12,8 @@ markdown editor and a live, design-accurate preview. The whole surface is one fo
12
12
  import type { IconSet } from '../render/glyph.js';
13
13
  import type { EditData } from '../sveltekit/content-routes.js';
14
14
  import type { TextareaField, TagsField, FreeTagsField } from '../content/types.js';
15
+ import type { LinkResolve } from '../content/links.js';
16
+ import { manifestLinkResolver } from '../content/manifest.js';
15
17
 
16
18
  interface Props {
17
19
  /** The edit load's data, plus the site name for the heading. */
@@ -19,7 +21,7 @@ markdown editor and a live, design-accurate preview. The whole surface is one fo
19
21
  /** The site's component registry, for the insert palette. */
20
22
  registry?: ComponentRegistry;
21
23
  /** The site's design-accurate render pipeline; the preview pane renders its output, which the floored pipeline already sanitized. */
22
- render?: (md: string, opts?: { stagger?: boolean }) => string | Promise<string>;
24
+ render?: (md: string, opts?: { stagger?: boolean; resolve?: LinkResolve }) => string | Promise<string>;
23
25
  /** The site's icon set, for the guided form's icon fields. */
24
26
  icons?: IconSet;
25
27
  }
@@ -33,6 +35,10 @@ markdown editor and a live, design-accurate preview. The whole surface is one fo
33
35
  let previewHtml = $state('');
34
36
  let insert = $state.raw<(text: string) => void>(() => {});
35
37
 
38
+ // The manifest-backed resolver turns a cairn: link into its live permalink in the preview, and
39
+ // returns undefined for a missing target so the render step marks it cairn-broken-link.
40
+ const resolveLink = $derived(manifestLinkResolver(data.linkTargets));
41
+
36
42
  const PREVIEW_KEY = 'cairn-admin:preview';
37
43
 
38
44
  $effect(() => {
@@ -53,10 +59,11 @@ markdown editor and a live, design-accurate preview. The whole surface is one fo
53
59
  $effect(() => {
54
60
  if (!showPreview || !render) return;
55
61
  const md = body;
62
+ const resolve = resolveLink; // tracked read in the effect body
56
63
  const run = ++previewRun;
57
64
  const handle = setTimeout(async () => {
58
65
  try {
59
- const html = await render(md);
66
+ const html = await render(md, { resolve });
60
67
  if (run === previewRun) previewHtml = html;
61
68
  } catch {
62
69
  if (run === previewRun) previewHtml = '';
@@ -1,6 +1,7 @@
1
1
  import type { ComponentRegistry } from '../render/registry.js';
2
2
  import type { IconSet } from '../render/glyph.js';
3
3
  import type { EditData } from '../sveltekit/content-routes.js';
4
+ import type { LinkResolve } from '../content/links.js';
4
5
  interface Props {
5
6
  /** The edit load's data, plus the site name for the heading. */
6
7
  data: EditData & {
@@ -11,6 +12,7 @@ interface Props {
11
12
  /** The site's design-accurate render pipeline; the preview pane renders its output, which the floored pipeline already sanitized. */
12
13
  render?: (md: string, opts?: {
13
14
  stagger?: boolean;
15
+ resolve?: LinkResolve;
14
16
  }) => string | Promise<string>;
15
17
  /** The site's icon set, for the guided form's icon fields. */
16
18
  icons?: IconSet;
@@ -1 +1 @@
1
- {"version":3,"file":"EditPage.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/EditPage.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAI7D,UAAU,KAAK;IACb,gEAAgE;IAChE,IAAI,EAAE,QAAQ,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,qIAAqI;IACrI,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChF,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAoJH;;;;GAIG;AACH,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"EditPage.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/EditPage.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIrD,UAAU,KAAK;IACb,gEAAgE;IAChE,IAAI,EAAE,QAAQ,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,qIAAqI;IACrI,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,WAAW,CAAA;KAAE,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvG,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA2JH;;;;GAIG;AACH,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/lib/content/compose.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAc,YAAY,EAAE,cAAc,EAAE,YAAY,EAAiB,gBAAgB,EAAgB,MAAM,YAAY,CAAC;AAGxI;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,EACrB,UAAU,GAAE,cAAc,EAAO,EACjC,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAM,GAC3D,YAAY,CAwBd"}
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/lib/content/compose.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAc,YAAY,EAAE,cAAc,EAAE,YAAY,EAAiB,gBAAgB,EAAgB,MAAM,YAAY,CAAC;AAGxI;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,EACrB,UAAU,GAAE,cAAc,EAAO,EACjC,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAM,GAC3D,YAAY,CAyBd"}
@@ -23,6 +23,7 @@ export function composeRuntime(adapter, extensions = [], urlPolicy = {}) {
23
23
  backend: adapter.backend,
24
24
  sender: adapter.sender,
25
25
  render: adapter.render,
26
+ manifestPath: adapter.manifestPath ?? 'src/content/.cairn/index.json',
26
27
  registry: adapter.registry,
27
28
  icons: adapter.icons,
28
29
  navMenu: adapter.navMenu,
@@ -0,0 +1,14 @@
1
+ /** A resolved reference to a content entry by its concept and permanent id. */
2
+ export interface CairnRef {
3
+ concept: string;
4
+ id: string;
5
+ }
6
+ /** Resolve a reference to its live permalink. Returns undefined when the target is missing (the
7
+ * preview marks it); the build resolver throws instead, so a dangling token fails the build. */
8
+ export type LinkResolve = (ref: CairnRef) => string | undefined;
9
+ /** Parse a `cairn:<concept>/<id>` href, or null for any other href or a malformed token. */
10
+ export declare function parseCairnToken(href: string): CairnRef | null;
11
+ /** The cairn links a markdown body points at, in first-occurrence order, deduped by concept/id.
12
+ * Parses the body as mdast, so a token inside a code span or fence is never matched. */
13
+ export declare function extractCairnLinks(body: string): CairnRef[];
14
+ //# sourceMappingURL=links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../../src/lib/content/links.ts"],"names":[],"mappings":"AAUA,+EAA+E;AAC/E,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;iGACiG;AACjG,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,QAAQ,KAAK,MAAM,GAAG,SAAS,CAAC;AAEhE,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAS7D;AAED;yFACyF;AACzF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAa1D"}
@@ -0,0 +1,41 @@
1
+ // cairn-cms: the cairn: internal-link token. An internal link is a standard CommonMark link
2
+ // whose href is `cairn:<concept>/<id>`, keyed to the target's permanent filename stem so it
3
+ // survives a slug, date, or permalink change (content-graph design). This module owns the
4
+ // grammar; the render resolver (resolve-links.ts) reuses parseCairnToken.
5
+ import { unified } from 'unified';
6
+ import remarkParse from 'remark-parse';
7
+ import remarkGfm from 'remark-gfm';
8
+ import { visit } from 'unist-util-visit';
9
+ import { isValidId } from './ids.js';
10
+ /** Parse a `cairn:<concept>/<id>` href, or null for any other href or a malformed token. */
11
+ export function parseCairnToken(href) {
12
+ if (!href.startsWith('cairn:'))
13
+ return null;
14
+ const rest = href.slice('cairn:'.length);
15
+ const slash = rest.indexOf('/');
16
+ if (slash <= 0)
17
+ return null;
18
+ const concept = rest.slice(0, slash);
19
+ const id = rest.slice(slash + 1);
20
+ if (!concept || !isValidId(id))
21
+ return null;
22
+ return { concept, id };
23
+ }
24
+ /** The cairn links a markdown body points at, in first-occurrence order, deduped by concept/id.
25
+ * Parses the body as mdast, so a token inside a code span or fence is never matched. */
26
+ export function extractCairnLinks(body) {
27
+ const tree = unified().use(remarkParse).use(remarkGfm).parse(body);
28
+ const seen = new Set();
29
+ const refs = [];
30
+ visit(tree, 'link', (node) => {
31
+ const ref = node.url ? parseCairnToken(node.url) : null;
32
+ if (!ref)
33
+ return;
34
+ const key = `${ref.concept}/${ref.id}`;
35
+ if (seen.has(key))
36
+ return;
37
+ seen.add(key);
38
+ refs.push(ref);
39
+ });
40
+ return refs;
41
+ }
@@ -0,0 +1,55 @@
1
+ import { type CairnRef, type LinkResolve } from './links.js';
2
+ import type { ConceptDescriptor } from './types.js';
3
+ /** One entry's projection: its identity, routing, draft flag, and outbound cairn: edges. */
4
+ export interface ManifestEntry {
5
+ id: string;
6
+ concept: string;
7
+ title: string;
8
+ date?: string;
9
+ permalink: string;
10
+ draft: boolean;
11
+ links: CairnRef[];
12
+ }
13
+ /** The whole corpus as one committed file. `version` guards a future shape migration. */
14
+ export interface Manifest {
15
+ version: 1;
16
+ entries: ManifestEntry[];
17
+ }
18
+ /** The minimal entry view the preview resolver and (later) the picker read. */
19
+ export interface LinkTarget {
20
+ concept: string;
21
+ id: string;
22
+ permalink: string;
23
+ title: string;
24
+ date?: string;
25
+ draft: boolean;
26
+ }
27
+ /** Build one manifest entry from a content file. Drafts are included and flagged. */
28
+ export declare function manifestEntryFromFile(descriptor: ConceptDescriptor, file: {
29
+ path: string;
30
+ raw: string;
31
+ }): ManifestEntry;
32
+ /** An empty manifest, the starting point when no committed file exists yet. */
33
+ export declare function emptyManifest(): Manifest;
34
+ /** Serialize canonically: entries sorted by concept then id, links sorted and deduped, a fixed key
35
+ * order, two-space pretty, and a trailing newline, so the committed file diffs cleanly in a PR. */
36
+ export declare function serializeManifest(manifest: Manifest): string;
37
+ /** Parse a committed manifest. Throws on malformed JSON or the wrong shape. */
38
+ export declare function parseManifest(raw: string): Manifest;
39
+ /** Throw if the committed manifest drifts from what the corpus says. Both sides are compared in the
40
+ * canonical serialized form, so semantic equality never spuriously fails. The build calls this so a
41
+ * raw-git content edit, which leaves the committed manifest stale, fails the build loudly. */
42
+ export declare function verifyManifest(built: Manifest, committedRaw: string): void;
43
+ /** Replace the entry with the same concept and id, or add it. Order does not matter, since
44
+ * serializeManifest sorts. This is the save path's incremental patch. */
45
+ export declare function upsertEntry(manifest: Manifest, entry: ManifestEntry): Manifest;
46
+ /** Drop the entry with the given concept and id, if present. The delete path's patch. */
47
+ export declare function removeEntry(manifest: Manifest, concept: string, id: string): Manifest;
48
+ /** A resolver backed by manifest targets, for the admin preview. A miss returns undefined, so the
49
+ * render step marks the link broken rather than throwing. The build resolver throws instead. */
50
+ export declare function manifestLinkResolver(targets: {
51
+ concept: string;
52
+ id: string;
53
+ permalink: string;
54
+ }[]): LinkResolve;
55
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/lib/content/manifest.ts"],"names":[],"mappings":"AAQA,OAAO,EAAqB,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,4FAA4F;AAC5F,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,yFAAyF;AACzF,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB;AAmBD,qFAAqF;AACrF,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,iBAAiB,EAAE,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAiBvH;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,IAAI,QAAQ,CAExC;AAMD;oGACoG;AACpG,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAW5D;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAMnD;AAED;;+FAE+F;AAC/F,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAM1E;AAED;0EAC0E;AAC1E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,GAAG,QAAQ,CAI9E;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,QAAQ,CAErF;AAED;iGACiG;AACjG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,WAAW,CAG/G"}
@@ -0,0 +1,98 @@
1
+ // cairn-cms: the content manifest, a committed JSON projection of the corpus (content-graph
2
+ // design). The files in git stay the source of truth; the manifest exists so request-time admin
3
+ // code reads the content graph without an N+1 GitHub crawl. The build regenerates and verifies
4
+ // it; the save path patches one entry and commits it with the content in one commit. Each entry
5
+ // carries its identity and its outbound cairn: edges, so the manifest is the link graph.
6
+ import { idFromFilename, slugFromId } from './ids.js';
7
+ import { parseMarkdown } from './frontmatter.js';
8
+ import { permalink } from './permalink.js';
9
+ import { extractCairnLinks } from './links.js';
10
+ function basename(path) {
11
+ const slash = path.lastIndexOf('/');
12
+ return slash >= 0 ? path.slice(slash + 1) : path;
13
+ }
14
+ /** Mirror content-index's frontmatter coercion: a present non-empty string, else undefined. */
15
+ function asString(value) {
16
+ return typeof value === 'string' && value.trim() ? value : undefined;
17
+ }
18
+ /** Mirror content-index's date coercion: an unquoted YAML date is a JS Date, a string is sliced. */
19
+ function asDate(value) {
20
+ if (value instanceof Date)
21
+ return Number.isNaN(value.getTime()) ? undefined : value.toISOString().slice(0, 10);
22
+ if (typeof value === 'string')
23
+ return value.match(/^\d{4}-\d{2}-\d{2}/)?.[0];
24
+ return undefined;
25
+ }
26
+ /** Build one manifest entry from a content file. Drafts are included and flagged. */
27
+ export function manifestEntryFromFile(descriptor, file) {
28
+ const id = idFromFilename(basename(file.path));
29
+ // Use the same slug rule content-index uses, so the manifest's permalink for an entry always
30
+ // equals content-index's permalink for it. A cairn link must resolve to one URL whether the
31
+ // admin preview reads the manifest or the public build reads the content index.
32
+ const slug = slugFromId(id, descriptor.routing.dated ? descriptor.datePrefix : null);
33
+ const { frontmatter, body } = parseMarkdown(file.raw);
34
+ const date = asDate(frontmatter.date);
35
+ return {
36
+ id,
37
+ concept: descriptor.id,
38
+ title: asString(frontmatter.title) ?? id,
39
+ date,
40
+ permalink: permalink(descriptor, { id, slug, date }),
41
+ draft: frontmatter.draft === true,
42
+ links: extractCairnLinks(body),
43
+ };
44
+ }
45
+ /** An empty manifest, the starting point when no committed file exists yet. */
46
+ export function emptyManifest() {
47
+ return { version: 1, entries: [] };
48
+ }
49
+ function compareRef(a, b) {
50
+ return a.concept.localeCompare(b.concept) || a.id.localeCompare(b.id);
51
+ }
52
+ /** Serialize canonically: entries sorted by concept then id, links sorted and deduped, a fixed key
53
+ * order, two-space pretty, and a trailing newline, so the committed file diffs cleanly in a PR. */
54
+ export function serializeManifest(manifest) {
55
+ const entries = [...manifest.entries].sort(compareRef).map((e) => ({
56
+ id: e.id,
57
+ concept: e.concept,
58
+ title: e.title,
59
+ ...(e.date ? { date: e.date } : {}),
60
+ permalink: e.permalink,
61
+ draft: e.draft,
62
+ links: [...e.links].sort(compareRef).map((r) => ({ concept: r.concept, id: r.id })),
63
+ }));
64
+ return `${JSON.stringify({ version: 1, entries }, null, 2)}\n`;
65
+ }
66
+ /** Parse a committed manifest. Throws on malformed JSON or the wrong shape. */
67
+ export function parseManifest(raw) {
68
+ const data = JSON.parse(raw);
69
+ if (!data || typeof data !== 'object' || !Array.isArray(data.entries)) {
70
+ throw new Error('content manifest: malformed file, expected { version, entries: [] }');
71
+ }
72
+ return { version: 1, entries: data.entries };
73
+ }
74
+ /** Throw if the committed manifest drifts from what the corpus says. Both sides are compared in the
75
+ * canonical serialized form, so semantic equality never spuriously fails. The build calls this so a
76
+ * raw-git content edit, which leaves the committed manifest stale, fails the build loudly. */
77
+ export function verifyManifest(built, committedRaw) {
78
+ if (committedRaw !== serializeManifest(built)) {
79
+ throw new Error('content manifest is stale: the committed file does not match the corpus. Regenerate it (npm run cairn:manifest) and commit the result.');
80
+ }
81
+ }
82
+ /** Replace the entry with the same concept and id, or add it. Order does not matter, since
83
+ * serializeManifest sorts. This is the save path's incremental patch. */
84
+ export function upsertEntry(manifest, entry) {
85
+ const entries = manifest.entries.filter((e) => !(e.concept === entry.concept && e.id === entry.id));
86
+ entries.push(entry);
87
+ return { version: 1, entries };
88
+ }
89
+ /** Drop the entry with the given concept and id, if present. The delete path's patch. */
90
+ export function removeEntry(manifest, concept, id) {
91
+ return { version: 1, entries: manifest.entries.filter((e) => !(e.concept === concept && e.id === id)) };
92
+ }
93
+ /** A resolver backed by manifest targets, for the admin preview. A miss returns undefined, so the
94
+ * render step marks the link broken rather than throwing. The build resolver throws instead. */
95
+ export function manifestLinkResolver(targets) {
96
+ const byKey = new Map(targets.map((t) => [`${t.concept}/${t.id}`, t.permalink]));
97
+ return (ref) => byKey.get(`${ref.concept}/${ref.id}`);
98
+ }
@@ -2,6 +2,7 @@ import type { ComponentRegistry } from '../render/registry.js';
2
2
  import type { IconSet } from '../render/glyph.js';
3
3
  import type { DatePrefix } from './ids.js';
4
4
  import type { ConceptSchema } from './schema.js';
5
+ import type { LinkResolve } from './links.js';
5
6
  /** Common to every frontmatter field: the frontmatter key, the form label, and whether it is required. */
6
7
  interface FieldBase {
7
8
  /** Frontmatter key and form input name. */
@@ -149,10 +150,16 @@ export interface CairnAdapter {
149
150
  };
150
151
  backend: BackendConfig;
151
152
  sender: SenderConfig;
152
- /** The site's one renderer: the editor preview and every public page call it (design decision 4). */
153
+ /** The site's one renderer: the editor preview and every public page call it (design decision 4).
154
+ * `resolve` rewrites cairn: links to live permalinks; the build passes a site-index resolver, the
155
+ * preview a manifest one. */
153
156
  render(md: string, opts?: {
154
157
  stagger?: boolean;
158
+ resolve?: LinkResolve;
155
159
  }): string | Promise<string>;
160
+ /** Repo-relative path to the committed content manifest. Defaults to src/content/.cairn/index.json
161
+ * in composeRuntime. It sits outside any concept directory, so content enumeration never globs it. */
162
+ manifestPath?: string;
156
163
  /** Directive component registry; the renderer and the future palette derive from it (seam 3). */
157
164
  registry?: ComponentRegistry;
158
165
  /** The site's glyph name to SVG path-data map, for the admin icon picker and the renderer. */
@@ -243,7 +250,9 @@ export interface CairnRuntime {
243
250
  /** The site's one renderer: the editor preview and every public page call it (design decision 4). */
244
251
  render(md: string, opts?: {
245
252
  stagger?: boolean;
253
+ resolve?: LinkResolve;
246
254
  }): string | Promise<string>;
255
+ manifestPath: string;
247
256
  registry?: ComponentRegistry;
248
257
  /** The site's glyph name to SVG path-data map, for the admin icon picker and the renderer. */
249
258
  icons?: IconSet;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/content/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/content/types.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,0GAA0G;AAC1G,UAAU,SAAS;IACjB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,gCAAgC;AAChC,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;yEACqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,+BAA+B;AAC/B,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,IAAI,EAAE,UAAU,CAAC;IACjB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,iCAAiC;AACjC,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AACD,sCAAsC;AACtC,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,IAAI,EAAE,SAAS,CAAC;CACjB;AACD,sEAAsE;AACtE,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AACD,iEAAiE;AACjE,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,aAAa,GACb,SAAS,GACT,YAAY,GACZ,SAAS,GACT,aAAa,CAAC;AAElB;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC3C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa;IACpE,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iGAAiG;IACjG,MAAM,EAAE,CAAC,CAAC;CACX;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,0HAA0H;AAC1H,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,kHAAkH;AAClH,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,OAAO,EAAE;QACP,KAAK,CAAC,EAAE,aAAa,CAAC;QACtB,KAAK,CAAC,EAAE,aAAa,CAAC;KACvB,CAAC;IACF,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB;;kCAE8B;IAC9B,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClG;2GACuG;IACvG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,8FAA8F;IAC9F,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,0FAA0F;IAC1F,QAAQ,EAAE,OAAO,CAAC;IAClB,8BAA8B;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,WAAW,CAAC;IACrB,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAAC;CAChF;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uDAAuD;IACvD,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IACnC,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/D,sFAAsF;IACtF,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACxC,+FAA+F;IAC/F,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,wFAAwF;IACxF,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,qGAAqG;IACrG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClG,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,8FAA8F;IAC9F,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,qGAAqG;IACrG,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,mGAAmG;IACnG,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;CAC7B"}
@@ -20,6 +20,7 @@ export type { Page } from './paginate.js';
20
20
  export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from './responses.js';
21
21
  export { jsonLdScript } from './json-ld.js';
22
22
  export { permalink } from '../content/permalink.js';
23
+ export { buildSiteManifest, buildLinkResolver } from './manifest.js';
23
24
  export { createPublicRoutes } from '../sveltekit/public-routes.js';
24
25
  export type { PublicRoutesDeps, ListData, TagData, TagIndexData, EntryData, } from '../sveltekit/public-routes.js';
25
26
  export { default as CairnHead } from './CairnHead.svelte';
@@ -1 +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"}
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,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACrE,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"}
@@ -17,5 +17,6 @@ export { paginate } from './paginate.js';
17
17
  export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from './responses.js';
18
18
  export { jsonLdScript } from './json-ld.js';
19
19
  export { permalink } from '../content/permalink.js';
20
+ export { buildSiteManifest, buildLinkResolver } from './manifest.js';
20
21
  export { createPublicRoutes } from '../sveltekit/public-routes.js';
21
22
  export { default as CairnHead } from './CairnHead.svelte';
@@ -0,0 +1,13 @@
1
+ import type { Manifest } from '../content/manifest.js';
2
+ import type { LinkResolve } from '../content/links.js';
3
+ import type { SiteIndex } from './site-index.js';
4
+ import type { SiteConfig } from '../nav/site-config.js';
5
+ import type { CairnAdapter } from '../content/types.js';
6
+ import type { SiteGlobs } from './site-indexes.js';
7
+ /** Build the whole-corpus manifest from a site's adapter, config, and per-concept globs. Drafts are
8
+ * included and flagged, so the admin picker and the guards see the full graph. */
9
+ export declare function buildSiteManifest<A extends CairnAdapter>(adapter: A, config: SiteConfig, globs: SiteGlobs<A>): Manifest;
10
+ /** A resolver backed by the site index, for the build. A miss throws, so a dangling cairn: token
11
+ * fails the prerender (the build backstop). The preview uses manifestLinkResolver, which marks. */
12
+ export declare function buildLinkResolver(site: SiteIndex): LinkResolve;
13
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/manifest.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD;mFACmF;AACnF,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,YAAY,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,QAAQ,CAUvH;AAED;oGACoG;AACpG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,WAAW,CAM9D"}
@@ -0,0 +1,31 @@
1
+ // cairn-cms: the build-side manifest builder and the build link resolver (content-graph design).
2
+ // buildSiteManifest mirrors createSiteIndexes: it maps the site descriptors over the per-concept
3
+ // globs and projects each file to a manifest row. buildLinkResolver reads the site index, which is
4
+ // fresh from the files at build, and throws on a missing target so a dangling cairn: token fails
5
+ // the build (the backstop). The admin preview uses manifestLinkResolver instead.
6
+ import { siteDescriptors } from './site-descriptors.js';
7
+ import { fromGlob } from './content-index.js';
8
+ import { emptyManifest, manifestEntryFromFile } from '../content/manifest.js';
9
+ /** Build the whole-corpus manifest from a site's adapter, config, and per-concept globs. Drafts are
10
+ * included and flagged, so the admin picker and the guards see the full graph. */
11
+ export function buildSiteManifest(adapter, config, globs) {
12
+ const globRecord = globs;
13
+ const manifest = emptyManifest();
14
+ for (const descriptor of siteDescriptors(adapter, config)) {
15
+ const record = globRecord[descriptor.id] ?? {};
16
+ for (const file of fromGlob(record)) {
17
+ manifest.entries.push(manifestEntryFromFile(descriptor, file));
18
+ }
19
+ }
20
+ return manifest;
21
+ }
22
+ /** A resolver backed by the site index, for the build. A miss throws, so a dangling cairn: token
23
+ * fails the prerender (the build backstop). The preview uses manifestLinkResolver, which marks. */
24
+ export function buildLinkResolver(site) {
25
+ return (ref) => {
26
+ const url = site.concept(ref.concept)?.byId(ref.id)?.permalink;
27
+ if (!url)
28
+ throw new Error(`cairn link target not found: cairn:${ref.concept}/${ref.id}`);
29
+ return url;
30
+ };
31
+ }
@@ -45,5 +45,26 @@ export declare function commitFile(repo: RepoRef, path: string, content: string,
45
45
  message: string;
46
46
  author: CommitAuthor;
47
47
  }, token: string): Promise<string>;
48
+ /** A path change for an atomic commit: write `content`, or delete the path when `content` is null. */
49
+ export interface FileChange {
50
+ path: string;
51
+ content: string | null;
52
+ }
53
+ /**
54
+ * Commit several path changes in one commit over the Git Data API. The author is the editor; the
55
+ * committer is omitted, so GitHub attributes the commit to the App. Returns the new commit sha.
56
+ * Builds the new tree on the current head's tree, so paths not named here are preserved.
57
+ *
58
+ * Caller preconditions this layer cannot enforce (the save and lifecycle paths must): every
59
+ * `path` is confined to the site's content directories (the App token can write anywhere in the
60
+ * repo), and `author` is derived from the verified server-side session, never request input.
61
+ *
62
+ * An empty change set is rejected, since it would otherwise push an empty commit that triggers a
63
+ * site redeploy for no content change.
64
+ */
65
+ export declare function commitFiles(repo: RepoRef, changes: FileChange[], opts: {
66
+ message: string;
67
+ author: CommitAuthor;
68
+ }, token: string): Promise<string>;
48
69
  export {};
49
70
  //# sourceMappingURL=repo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/lib/github/repo.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAelE,iEAAiE;AACjE,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE7C;AAED,qFAAqF;AACrF,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAOD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,CAW1E;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAMlG;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKjG;AAOD,+EAA+E;AAC/E,wBAAsB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKhG;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,EAC/C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAiBjB"}
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/lib/github/repo.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAelE,iEAAiE;AACjE,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE7C;AAED,qFAAqF;AACrF,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAOD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,CAW1E;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAMlG;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKjG;AAOD,+EAA+E;AAC/E,wBAAsB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKhG;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,EAC/C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED,sGAAsG;AACtG,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AA8CD;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,UAAU,EAAE,EACrB,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,EAC/C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAkCjB"}
@@ -121,3 +121,82 @@ export async function commitFile(repo, path, content, opts, token) {
121
121
  throw new Error(`GitHub commit ${path} failed: ${res.status} ${await res.text()}`);
122
122
  return (await res.json()).commit.sha;
123
123
  }
124
+ /** A Git Data API URL under the repo's `git/` namespace. */
125
+ function gitUrl(repo, suffix) {
126
+ return `${API}/repos/${repo.owner}/${repo.repo}/git/${suffix}`;
127
+ }
128
+ /** The branch head commit sha, through the Git Data API single-ref read. */
129
+ async function headCommitSha(repo, token) {
130
+ const res = await fetch(gitUrl(repo, `ref/heads/${encodeURIComponent(repo.branch)}`), {
131
+ headers: ghHeaders('application/vnd.github+json', token),
132
+ });
133
+ if (!res.ok)
134
+ throw new Error(`GitHub ref ${repo.branch} failed: ${res.status}`);
135
+ return (await res.json()).object.sha;
136
+ }
137
+ /** The base tree sha of a commit. */
138
+ async function commitTreeSha(repo, commitSha, token) {
139
+ const res = await fetch(gitUrl(repo, `commits/${commitSha}`), {
140
+ headers: ghHeaders('application/vnd.github+json', token),
141
+ });
142
+ if (!res.ok)
143
+ throw new Error(`GitHub commit ${commitSha} failed: ${res.status}`);
144
+ return (await res.json()).tree.sha;
145
+ }
146
+ /** Map file changes to Git Trees API entries, encoding a null content as a delete. */
147
+ function treeChanges(changes) {
148
+ return changes.map((c) => c.content === null
149
+ ? { path: c.path, mode: '100644', type: 'blob', sha: null }
150
+ : { path: c.path, mode: '100644', type: 'blob', content: c.content });
151
+ }
152
+ /** Retries after the initial attempt when the branch moves under an atomic commit. */
153
+ const COMMIT_RETRIES = 3;
154
+ /**
155
+ * Commit several path changes in one commit over the Git Data API. The author is the editor; the
156
+ * committer is omitted, so GitHub attributes the commit to the App. Returns the new commit sha.
157
+ * Builds the new tree on the current head's tree, so paths not named here are preserved.
158
+ *
159
+ * Caller preconditions this layer cannot enforce (the save and lifecycle paths must): every
160
+ * `path` is confined to the site's content directories (the App token can write anywhere in the
161
+ * repo), and `author` is derived from the verified server-side session, never request input.
162
+ *
163
+ * An empty change set is rejected, since it would otherwise push an empty commit that triggers a
164
+ * site redeploy for no content change.
165
+ */
166
+ export async function commitFiles(repo, changes, opts, token) {
167
+ if (changes.length === 0)
168
+ throw new Error('commitFiles: no changes to commit');
169
+ const tree = treeChanges(changes);
170
+ for (let attempt = 0; attempt <= COMMIT_RETRIES; attempt++) {
171
+ const parent = await headCommitSha(repo, token);
172
+ const baseTree = await commitTreeSha(repo, parent, token);
173
+ const treeRes = await fetch(gitUrl(repo, 'trees'), {
174
+ method: 'POST',
175
+ headers: { ...ghHeaders('application/vnd.github+json', token), 'Content-Type': 'application/json' },
176
+ body: JSON.stringify({ base_tree: baseTree, tree }),
177
+ });
178
+ if (!treeRes.ok)
179
+ throw new Error(`GitHub tree create failed: ${treeRes.status} ${await treeRes.text()}`);
180
+ const newTree = (await treeRes.json()).sha;
181
+ const commitRes = await fetch(gitUrl(repo, 'commits'), {
182
+ method: 'POST',
183
+ headers: { ...ghHeaders('application/vnd.github+json', token), 'Content-Type': 'application/json' },
184
+ body: JSON.stringify({ message: opts.message, tree: newTree, parents: [parent], author: opts.author }),
185
+ });
186
+ if (!commitRes.ok)
187
+ throw new Error(`GitHub commit create failed: ${commitRes.status} ${await commitRes.text()}`);
188
+ const newCommit = (await commitRes.json()).sha;
189
+ const refRes = await fetch(gitUrl(repo, `refs/heads/${encodeURIComponent(repo.branch)}`), {
190
+ method: 'PATCH',
191
+ headers: { ...ghHeaders('application/vnd.github+json', token), 'Content-Type': 'application/json' },
192
+ body: JSON.stringify({ sha: newCommit, force: false }),
193
+ });
194
+ if (refRes.ok)
195
+ return newCommit;
196
+ // A non-fast-forward means the branch moved; retry on the new head so a concurrent commit
197
+ // is preserved. Any other failure is not a race, so surface it.
198
+ if (refRes.status !== 422)
199
+ throw new Error(`GitHub ref update failed: ${refRes.status} ${await refRes.text()}`);
200
+ }
201
+ throw new CommitConflictError(`${repo.branch} (atomic commit)`);
202
+ }
package/dist/index.d.ts CHANGED
@@ -11,6 +11,10 @@ export { defineAdapter } from './content/adapter.js';
11
11
  export type { ConceptSchema, Infer, InferFields, DefineFieldsOptions, StandardInput, StandardSchemaV1 } from './content/schema.js';
12
12
  export { isValidId, idFromFilename, filenameFromId, slugify, slugFromId, composeDatedId, } from './content/ids.js';
13
13
  export type { DatePrefix } from './content/ids.js';
14
+ export { parseCairnToken, extractCairnLinks } from './content/links.js';
15
+ export type { CairnRef, LinkResolve } from './content/links.js';
16
+ export { serializeManifest, parseManifest, emptyManifest, verifyManifest, upsertEntry, removeEntry, manifestEntryFromFile, manifestLinkResolver, } from './content/manifest.js';
17
+ export type { Manifest, ManifestEntry, LinkTarget } from './content/manifest.js';
14
18
  export { defineRegistry, emptyValues } from './render/registry.js';
15
19
  export type { ComponentDef, ComponentRegistry, FieldType, AttributeField, SlotKind, SlotDef, ComponentValues, } from './render/registry.js';
16
20
  export { serializeComponent, parseComponent } from './render/component-grammar.js';
@@ -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
@@ -8,6 +8,11 @@ export { frontmatterFromForm, dateInputValue, serializeMarkdown, parseMarkdown,
8
8
  export { defineFields } from './content/schema.js';
9
9
  export { defineAdapter } from './content/adapter.js';
10
10
  export { isValidId, idFromFilename, filenameFromId, slugify, slugFromId, composeDatedId, } from './content/ids.js';
11
+ // Internal-link token and the committed content manifest (content-graph design). The corpus
12
+ // builder and the request-time resolver ship from the delivery entry; this surface is the
13
+ // grammar, the manifest operations, and their types a migrating site adopts.
14
+ export { parseCairnToken, extractCairnLinks } from './content/links.js';
15
+ export { serializeManifest, parseManifest, emptyManifest, verifyManifest, upsertEntry, removeEntry, manifestEntryFromFile, manifestLinkResolver, } from './content/manifest.js';
11
16
  // Render engine (Plan 04): generic directive pipeline; sites own the component registry.
12
17
  export { defineRegistry, emptyValues } from './render/registry.js';
13
18
  export { serializeComponent, parseComponent } from './render/component-grammar.js';
@@ -1,6 +1,7 @@
1
1
  import { type PluggableList } from 'unified';
2
2
  import type { Schema } from 'hast-util-sanitize';
3
3
  import type { ComponentRegistry } from './registry.js';
4
+ import type { LinkResolve } from '../content/links.js';
4
5
  export interface RendererOptions {
5
6
  /** Stamp a `data-rise` ordinal (0, 1, 2, …) on each top-level component so a site's
6
7
  * CSS can drive an entrance-cascade delay off it. Omit for no stagger. The ordinal
@@ -22,6 +23,8 @@ export interface RendererOptions {
22
23
  export declare function createRenderer(registry: ComponentRegistry, options?: RendererOptions): {
23
24
  remarkPlugins: PluggableList;
24
25
  rehypePlugins: PluggableList;
25
- renderMarkdown: (content: string) => Promise<string>;
26
+ renderMarkdown: (content: string, opts?: {
27
+ resolve?: LinkResolve;
28
+ }) => Promise<string>;
26
29
  };
27
30
  //# sourceMappingURL=pipeline.d.ts.map