@glw907/cairn-cms 0.18.0 → 0.21.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 (59) hide show
  1. package/dist/components/DeleteDialog.svelte +81 -0
  2. package/dist/components/DeleteDialog.svelte.d.ts +21 -0
  3. package/dist/components/DeleteDialog.svelte.d.ts.map +1 -0
  4. package/dist/components/EditPage.svelte +127 -8
  5. package/dist/components/EditPage.svelte.d.ts +8 -0
  6. package/dist/components/EditPage.svelte.d.ts.map +1 -1
  7. package/dist/components/LinkPicker.svelte +109 -0
  8. package/dist/components/LinkPicker.svelte.d.ts +18 -0
  9. package/dist/components/LinkPicker.svelte.d.ts.map +1 -0
  10. package/dist/components/MarkdownEditor.svelte +33 -3
  11. package/dist/components/MarkdownEditor.svelte.d.ts +5 -0
  12. package/dist/components/MarkdownEditor.svelte.d.ts.map +1 -1
  13. package/dist/components/RenameDialog.svelte +72 -0
  14. package/dist/components/RenameDialog.svelte.d.ts +20 -0
  15. package/dist/components/RenameDialog.svelte.d.ts.map +1 -0
  16. package/dist/components/index.d.ts +3 -0
  17. package/dist/components/index.d.ts.map +1 -1
  18. package/dist/components/index.js +3 -0
  19. package/dist/components/link-completion.d.ts +16 -0
  20. package/dist/components/link-completion.d.ts.map +1 -0
  21. package/dist/components/link-completion.js +48 -0
  22. package/dist/components/markdown-format.d.ts +25 -5
  23. package/dist/components/markdown-format.d.ts.map +1 -1
  24. package/dist/components/markdown-format.js +85 -0
  25. package/dist/content/ids.d.ts +7 -0
  26. package/dist/content/ids.d.ts.map +1 -1
  27. package/dist/content/ids.js +11 -0
  28. package/dist/content/links.d.ts +7 -0
  29. package/dist/content/links.d.ts.map +1 -1
  30. package/dist/content/links.js +11 -0
  31. package/dist/content/manifest.d.ts +15 -1
  32. package/dist/content/manifest.d.ts.map +1 -1
  33. package/dist/content/manifest.js +45 -3
  34. package/dist/delivery/manifest.d.ts.map +1 -1
  35. package/dist/delivery/manifest.js +7 -0
  36. package/dist/github/repo.d.ts.map +1 -1
  37. package/dist/github/repo.js +8 -1
  38. package/dist/index.d.ts +3 -3
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +2 -2
  41. package/dist/sveltekit/content-routes.d.ts +11 -2
  42. package/dist/sveltekit/content-routes.d.ts.map +1 -1
  43. package/dist/sveltekit/content-routes.js +157 -9
  44. package/package.json +2 -1
  45. package/src/lib/components/DeleteDialog.svelte +81 -0
  46. package/src/lib/components/EditPage.svelte +127 -8
  47. package/src/lib/components/LinkPicker.svelte +109 -0
  48. package/src/lib/components/MarkdownEditor.svelte +33 -3
  49. package/src/lib/components/RenameDialog.svelte +72 -0
  50. package/src/lib/components/index.ts +3 -0
  51. package/src/lib/components/link-completion.ts +57 -0
  52. package/src/lib/components/markdown-format.ts +82 -0
  53. package/src/lib/content/ids.ts +12 -0
  54. package/src/lib/content/links.ts +13 -0
  55. package/src/lib/content/manifest.ts +55 -3
  56. package/src/lib/delivery/manifest.ts +6 -0
  57. package/src/lib/github/repo.ts +8 -1
  58. package/src/lib/index.ts +3 -2
  59. package/src/lib/sveltekit/content-routes.ts +178 -11
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/components/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/components/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC"}
@@ -11,3 +11,6 @@ export { default as ComponentInsertDialog } from './ComponentInsertDialog.svelte
11
11
  export { default as ComponentForm } from './ComponentForm.svelte';
12
12
  export { default as IconPicker } from './IconPicker.svelte';
13
13
  export { default as NavTree } from './NavTree.svelte';
14
+ export { default as LinkPicker } from './LinkPicker.svelte';
15
+ export { default as DeleteDialog } from './DeleteDialog.svelte';
16
+ export { default as RenameDialog } from './RenameDialog.svelte';
@@ -0,0 +1,16 @@
1
+ import type { Completion, CompletionSource } from '@codemirror/autocomplete';
2
+ import type { LinkTarget } from '../content/manifest.js';
3
+ /** The open `[[query` before the cursor, or null. The query stops at a closing bracket or a newline,
4
+ * so a finished `[[x]]` link and ordinary prose never trigger. `from` is the index of the `[[`. */
5
+ export declare function matchCairnTrigger(before: string): {
6
+ query: string;
7
+ from: number;
8
+ } | null;
9
+ /** The completion options for a query: a case-insensitive title substring match, each option grouped
10
+ * by concept, a draft marked and a post date shown in the detail, and the apply text the full link. */
11
+ export declare function linkCompletions(targets: LinkTarget[], query: string): Completion[];
12
+ /** A CodeMirror CompletionSource over the site's link targets, triggered by `[[`. It replaces the
13
+ * whole `[[query` with the chosen link, and sets filter:false because linkCompletions already
14
+ * filtered by the query (CodeMirror would otherwise re-filter against the literal `[[query`). */
15
+ export declare function cairnLinkCompletionSource(targets: LinkTarget[]): CompletionSource;
16
+ //# sourceMappingURL=link-completion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-completion.d.ts","sourceRoot":"","sources":["../../src/lib/components/link-completion.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAuC,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAElH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAazD;oGACoG;AACpG,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGxF;AAED;wGACwG;AACxG,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,CASlF;AAED;;kGAEkG;AAClG,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,gBAAgB,CAcjF"}
@@ -0,0 +1,48 @@
1
+ import { syntaxTree } from '@codemirror/language';
2
+ import { formatCairnToken, escapeLinkText } from '../content/links.js';
3
+ /** The known concepts in display order; an unlisted concept sorts after these under its own name. */
4
+ const CONCEPT_SECTIONS = {
5
+ pages: { name: 'Pages', rank: 0 },
6
+ posts: { name: 'Posts', rank: 1 },
7
+ };
8
+ function sectionFor(concept) {
9
+ return CONCEPT_SECTIONS[concept] ?? { name: concept.charAt(0).toUpperCase() + concept.slice(1), rank: 2 };
10
+ }
11
+ /** The open `[[query` before the cursor, or null. The query stops at a closing bracket or a newline,
12
+ * so a finished `[[x]]` link and ordinary prose never trigger. `from` is the index of the `[[`. */
13
+ export function matchCairnTrigger(before) {
14
+ const match = /\[\[([^[\]\n]*)$/.exec(before);
15
+ return match ? { query: match[1], from: match.index } : null;
16
+ }
17
+ /** The completion options for a query: a case-insensitive title substring match, each option grouped
18
+ * by concept, a draft marked and a post date shown in the detail, and the apply text the full link. */
19
+ export function linkCompletions(targets, query) {
20
+ const q = query.trim().toLowerCase();
21
+ const matched = q ? targets.filter((t) => t.title.toLowerCase().includes(q)) : targets;
22
+ return matched.map((t) => ({
23
+ label: t.title,
24
+ section: sectionFor(t.concept),
25
+ detail: t.draft ? 'Draft' : t.date,
26
+ apply: `[${escapeLinkText(t.title)}](${formatCairnToken(t)})`,
27
+ }));
28
+ }
29
+ /** A CodeMirror CompletionSource over the site's link targets, triggered by `[[`. It replaces the
30
+ * whole `[[query` with the chosen link, and sets filter:false because linkCompletions already
31
+ * filtered by the query (CodeMirror would otherwise re-filter against the literal `[[query`). */
32
+ export function cairnLinkCompletionSource(targets) {
33
+ return (context) => {
34
+ const line = context.state.doc.lineAt(context.pos);
35
+ const before = context.state.sliceDoc(line.from, context.pos);
36
+ const trigger = matchCairnTrigger(before);
37
+ if (!trigger)
38
+ return null;
39
+ // Skip a [[ inside a fenced or inline code node: a cairn link there would be literal text, and
40
+ // the build resolver does not look inside code. The node name carries "Code" for both forms.
41
+ const node = syntaxTree(context.state).resolveInner(context.pos, -1);
42
+ for (let n = node; n; n = n.parent) {
43
+ if (/Code/.test(n.name))
44
+ return null;
45
+ }
46
+ return { from: line.from + trigger.from, options: linkCompletions(targets, trigger.query), filter: false };
47
+ };
48
+ }
@@ -1,8 +1,3 @@
1
- /**
2
- * Pure markdown selection transforms for the editor toolbar. Each call maps a document and a
3
- * selection range to a new document and a new selection, with no DOM. The MarkdownEditor view
4
- * dispatches the result; keeping the logic here lets it unit-test without a browser.
5
- */
6
1
  export type FormatKind = 'bold' | 'italic' | 'code' | 'heading' | 'quote' | 'ul' | 'link';
7
2
  export interface FormatResult {
8
3
  doc: string;
@@ -10,4 +5,29 @@ export interface FormatResult {
10
5
  to: number;
11
6
  }
12
7
  export declare function applyMarkdownFormat(doc: string, from: number, to: number, kind: FormatKind): FormatResult;
8
+ /**
9
+ * Insert an inline markdown link at the selection. With a non-empty selection the selected text
10
+ * becomes the display text; with an empty selection the title is the display text. The cursor
11
+ * collapses just after the inserted link. Unlike the block insert, this adds no surrounding
12
+ * blank lines, since a link is inline. Pure, so the editor dispatches the result.
13
+ */
14
+ export declare function insertInlineLink(doc: string, from: number, to: number, href: string, title: string): FormatResult;
15
+ /**
16
+ * Unwrap every cairn: link whose href is exactly `href`, replacing it with its plain display text.
17
+ * The save guard's one-click fix calls this to drop a broken link while keeping the words. The
18
+ * document is parsed with the same remark pipeline extractCairnLinks uses, so the two agree on what
19
+ * a link is. Each matching link node is located by its source offsets and spliced out from last to
20
+ * first, which leaves the rest of the document exact and unescapes the display text. A token inside
21
+ * a code span or fence is not a link node, so it is never touched, and a link with a different url
22
+ * is left in place.
23
+ */
24
+ export declare function unwrapCairnLink(doc: string, href: string): string;
25
+ /**
26
+ * Rewrite every cairn: link whose href is exactly `oldHref` so its href becomes `newHref`, keeping
27
+ * the display text and any link title byte-for-byte. Rename calls this to repoint a renamed entry's
28
+ * inbound tokens. Parsed with the same remark pipeline as extractCairnLinks, so a token inside a code
29
+ * span is not a link node and is never touched. Each matching node's source span is rewritten from
30
+ * last to first, replacing only the `](oldHref` run so the label and title stay exact.
31
+ */
32
+ export declare function rewriteCairnLink(doc: string, oldHref: string, newHref: string): string;
13
33
  //# sourceMappingURL=markdown-format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-format.d.ts","sourceRoot":"","sources":["../../src/lib/components/markdown-format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;AAE1F,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAKD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,YAAY,CAsBzG"}
1
+ {"version":3,"file":"markdown-format.d.ts","sourceRoot":"","sources":["../../src/lib/components/markdown-format.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;AAE1F,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAKD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,YAAY,CAsBzG;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAKjH;AAUD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAgBjE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAkBtF"}
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Pure markdown selection transforms for the editor toolbar. Each call maps a document and a
3
+ * selection range to a new document and a new selection, with no DOM. The MarkdownEditor view
4
+ * dispatches the result; keeping the logic here lets it unit-test without a browser.
5
+ */
6
+ import { unified } from 'unified';
7
+ import remarkParse from 'remark-parse';
8
+ import remarkGfm from 'remark-gfm';
9
+ import { visit } from 'unist-util-visit';
10
+ import { escapeLinkText } from '../content/links.js';
1
11
  const WRAP = { bold: '**', italic: '_', code: '`' };
2
12
  const LINE_PREFIX = { heading: '# ', quote: '> ', ul: '- ' };
3
13
  export function applyMarkdownFormat(doc, from, to, kind) {
@@ -21,3 +31,78 @@ export function applyMarkdownFormat(doc, from, to, kind) {
21
31
  const added = prefixed.length - region.length;
22
32
  return { doc: doc.slice(0, lineStart) + prefixed + doc.slice(to), from: from + prefix.length, to: to + added };
23
33
  }
34
+ /**
35
+ * Insert an inline markdown link at the selection. With a non-empty selection the selected text
36
+ * becomes the display text; with an empty selection the title is the display text. The cursor
37
+ * collapses just after the inserted link. Unlike the block insert, this adds no surrounding
38
+ * blank lines, since a link is inline. Pure, so the editor dispatches the result.
39
+ */
40
+ export function insertInlineLink(doc, from, to, href, title) {
41
+ const text = from < to ? doc.slice(from, to) : escapeLinkText(title);
42
+ const inserted = `[${text}](${href})`;
43
+ const end = from + inserted.length;
44
+ return { doc: doc.slice(0, from) + inserted + doc.slice(to), from: end, to: end };
45
+ }
46
+ /** Concatenate a link node's text-child values. The parser has already unescaped them, so a source
47
+ * `Notes \[draft\]` yields `Notes [draft]`. Used instead of mdast-util-to-string, which is not a
48
+ * direct dependency. Non-text children (a nested emphasis, say) contribute no value, which is fine
49
+ * for the picker-produced links this fix targets. */
50
+ function linkText(node) {
51
+ return node.children.map((c) => ('value' in c ? c.value : '')).join('');
52
+ }
53
+ /**
54
+ * Unwrap every cairn: link whose href is exactly `href`, replacing it with its plain display text.
55
+ * The save guard's one-click fix calls this to drop a broken link while keeping the words. The
56
+ * document is parsed with the same remark pipeline extractCairnLinks uses, so the two agree on what
57
+ * a link is. Each matching link node is located by its source offsets and spliced out from last to
58
+ * first, which leaves the rest of the document exact and unescapes the display text. A token inside
59
+ * a code span or fence is not a link node, so it is never touched, and a link with a different url
60
+ * is left in place.
61
+ */
62
+ export function unwrapCairnLink(doc, href) {
63
+ const tree = unified().use(remarkParse).use(remarkGfm).parse(doc);
64
+ const spans = [];
65
+ visit(tree, 'link', (node) => {
66
+ if (node.url !== href)
67
+ return;
68
+ const start = node.position?.start?.offset;
69
+ const end = node.position?.end?.offset;
70
+ if (start == null || end == null)
71
+ return;
72
+ spans.push({ start, end, text: linkText(node) });
73
+ });
74
+ spans.sort((a, b) => b.start - a.start);
75
+ let out = doc;
76
+ for (const span of spans) {
77
+ out = out.slice(0, span.start) + span.text + out.slice(span.end);
78
+ }
79
+ return out;
80
+ }
81
+ /**
82
+ * Rewrite every cairn: link whose href is exactly `oldHref` so its href becomes `newHref`, keeping
83
+ * the display text and any link title byte-for-byte. Rename calls this to repoint a renamed entry's
84
+ * inbound tokens. Parsed with the same remark pipeline as extractCairnLinks, so a token inside a code
85
+ * span is not a link node and is never touched. Each matching node's source span is rewritten from
86
+ * last to first, replacing only the `](oldHref` run so the label and title stay exact.
87
+ */
88
+ export function rewriteCairnLink(doc, oldHref, newHref) {
89
+ const tree = unified().use(remarkParse).use(remarkGfm).parse(doc);
90
+ const spans = [];
91
+ visit(tree, 'link', (node) => {
92
+ if (node.url !== oldHref)
93
+ return;
94
+ const start = node.position?.start?.offset;
95
+ const end = node.position?.end?.offset;
96
+ if (start == null || end == null)
97
+ return;
98
+ spans.push({ start, end });
99
+ });
100
+ spans.sort((a, b) => b.start - a.start);
101
+ let out = doc;
102
+ for (const span of spans) {
103
+ const src = out.slice(span.start, span.end);
104
+ const rewritten = src.replace(`](${oldHref}`, `](${newHref}`);
105
+ out = out.slice(0, span.start) + rewritten + out.slice(span.end);
106
+ }
107
+ return out;
108
+ }
@@ -28,4 +28,11 @@ export declare function slugFromId(id: string, datePrefix: DatePrefix | null): s
28
28
  * malformed date so a bad create fails before touching git.
29
29
  */
30
30
  export declare function composeDatedId(date: string, slug: string, datePrefix: DatePrefix): string;
31
+ /**
32
+ * Rename an id by swapping its slug, keeping any date prefix. slugFromId strips only the leading
33
+ * date prefix, so the id is exactly its prefix followed by its slug; this replaces the slug suffix
34
+ * with newSlug. A non-dated concept passes null, so the whole id is the slug and the id becomes
35
+ * newSlug. The caller validates newSlug with isValidId first.
36
+ */
37
+ export declare function renameId(oldId: string, newSlug: string, datePrefix: DatePrefix | null): string;
31
38
  //# sourceMappingURL=ids.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/lib/content/ids.ts"],"names":[],"mappings":"AAOA,qGAAqG;AACrG,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,yDAAyD;AACzD,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM7C;AAED,uGAAuG;AACvG,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AASlD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI,GAAG,MAAM,CAG5E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAiBzF"}
1
+ {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/lib/content/ids.ts"],"names":[],"mappings":"AAOA,qGAAqG;AACrG,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,yDAAyD;AACzD,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM7C;AAED,uGAAuG;AACvG,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AASlD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI,GAAG,MAAM,CAG5E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAiBzF;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI,GAAG,MAAM,CAI9F"}
@@ -71,3 +71,14 @@ export function composeDatedId(date, slug, datePrefix) {
71
71
  }
72
72
  return `${prefix}-${slug}`;
73
73
  }
74
+ /**
75
+ * Rename an id by swapping its slug, keeping any date prefix. slugFromId strips only the leading
76
+ * date prefix, so the id is exactly its prefix followed by its slug; this replaces the slug suffix
77
+ * with newSlug. A non-dated concept passes null, so the whole id is the slug and the id becomes
78
+ * newSlug. The caller validates newSlug with isValidId first.
79
+ */
80
+ export function renameId(oldId, newSlug, datePrefix) {
81
+ const oldSlug = slugFromId(oldId, datePrefix);
82
+ const prefix = oldId.slice(0, oldId.length - oldSlug.length);
83
+ return prefix + newSlug;
84
+ }
@@ -8,6 +8,13 @@ export interface CairnRef {
8
8
  export type LinkResolve = (ref: CairnRef) => string | undefined;
9
9
  /** Parse a `cairn:<concept>/<id>` href, or null for any other href or a malformed token. */
10
10
  export declare function parseCairnToken(href: string): CairnRef | null;
11
+ /** Write the `cairn:<concept>/<id>` token for a ref. The inverse of parseCairnToken, so the editor
12
+ * link picker and the autocomplete write exactly the form the resolver reads back. */
13
+ export declare function formatCairnToken(ref: CairnRef): string;
14
+ /** Escape the characters that would break a markdown link's display text: a backslash and the
15
+ * square brackets that delimit the text. Used where a content title becomes link display text,
16
+ * so an unbalanced bracket in a title cannot truncate the generated link. */
17
+ export declare function escapeLinkText(text: string): string;
11
18
  /** The cairn links a markdown body points at, in first-occurrence order, deduped by concept/id.
12
19
  * Parses the body as mdast, so a token inside a code span or fence is never matched. */
13
20
  export declare function extractCairnLinks(body: string): CairnRef[];
@@ -1 +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"}
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;uFACuF;AACvF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAEtD;AAED;;8EAE8E;AAC9E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;yFACyF;AACzF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAa1D"}
@@ -21,6 +21,17 @@ export function parseCairnToken(href) {
21
21
  return null;
22
22
  return { concept, id };
23
23
  }
24
+ /** Write the `cairn:<concept>/<id>` token for a ref. The inverse of parseCairnToken, so the editor
25
+ * link picker and the autocomplete write exactly the form the resolver reads back. */
26
+ export function formatCairnToken(ref) {
27
+ return `cairn:${ref.concept}/${ref.id}`;
28
+ }
29
+ /** Escape the characters that would break a markdown link's display text: a backslash and the
30
+ * square brackets that delimit the text. Used where a content title becomes link display text,
31
+ * so an unbalanced bracket in a title cannot truncate the generated link. */
32
+ export function escapeLinkText(text) {
33
+ return text.replace(/[\\[\]]/g, (ch) => `\\${ch}`);
34
+ }
24
35
  /** The cairn links a markdown body points at, in first-occurrence order, deduped by concept/id.
25
36
  * Parses the body as mdast, so a token inside a code span or fence is never matched. */
26
37
  export function extractCairnLinks(body) {
@@ -34,7 +34,10 @@ export declare function emptyManifest(): Manifest;
34
34
  /** Serialize canonically: entries sorted by concept then id, links sorted and deduped, a fixed key
35
35
  * order, two-space pretty, and a trailing newline, so the committed file diffs cleanly in a PR. */
36
36
  export declare function serializeManifest(manifest: Manifest): string;
37
- /** Parse a committed manifest. Throws on malformed JSON or the wrong shape. */
37
+ /** Parse a committed manifest. Throws on malformed JSON, a wrong version, or a malformed entry, so
38
+ * every reader (the save guard, the delete path, the preview) sees a well-formed graph or a clear
39
+ * error. The build regenerates the manifest, so a real file is always canonical; this guards a
40
+ * hand-edited or truncated one. */
38
41
  export declare function parseManifest(raw: string): Manifest;
39
42
  /** Throw if the committed manifest drifts from what the corpus says. Both sides are compared in the
40
43
  * canonical serialized form, so semantic equality never spuriously fails. The build calls this so a
@@ -45,6 +48,17 @@ export declare function verifyManifest(built: Manifest, committedRaw: string): v
45
48
  export declare function upsertEntry(manifest: Manifest, entry: ManifestEntry): Manifest;
46
49
  /** Drop the entry with the given concept and id, if present. The delete path's patch. */
47
50
  export declare function removeEntry(manifest: Manifest, concept: string, id: string): Manifest;
51
+ /** One inbound linker: enough to name it and link to its edit page in the delete guard. */
52
+ export interface InboundLink {
53
+ concept: string;
54
+ id: string;
55
+ title: string;
56
+ permalink: string;
57
+ }
58
+ /** Every entry whose outbound edges point at the target, excluding the target itself. The delete
59
+ * guard reads this to name "what links here"; the backlinks panel will reuse it. Pure over the
60
+ * manifest, so the request-time delete path and a unit test call it the same way. */
61
+ export declare function inboundLinks(manifest: Manifest, concept: string, id: string): InboundLink[];
48
62
  /** A resolver backed by manifest targets, for the admin preview. A miss returns undefined, so the
49
63
  * render step marks the link broken rather than throwing. The build resolver throws instead. */
50
64
  export declare function manifestLinkResolver(targets: {
@@ -1 +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"}
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;;;oCAGoC;AACpC,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAqCnD;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,2FAA2F;AAC3F,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;sFAEsF;AACtF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,EAAE,CAK3F;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"}
@@ -63,13 +63,46 @@ export function serializeManifest(manifest) {
63
63
  }));
64
64
  return `${JSON.stringify({ version: 1, entries }, null, 2)}\n`;
65
65
  }
66
- /** Parse a committed manifest. Throws on malformed JSON or the wrong shape. */
66
+ /** Parse a committed manifest. Throws on malformed JSON, a wrong version, or a malformed entry, so
67
+ * every reader (the save guard, the delete path, the preview) sees a well-formed graph or a clear
68
+ * error. The build regenerates the manifest, so a real file is always canonical; this guards a
69
+ * hand-edited or truncated one. */
67
70
  export function parseManifest(raw) {
68
71
  const data = JSON.parse(raw);
69
- if (!data || typeof data !== 'object' || !Array.isArray(data.entries)) {
72
+ if (!data || typeof data !== 'object') {
70
73
  throw new Error('content manifest: malformed file, expected { version, entries: [] }');
71
74
  }
72
- return { version: 1, entries: data.entries };
75
+ const obj = data;
76
+ if (obj.version !== 1) {
77
+ throw new Error(`content manifest: unsupported version ${String(obj.version)}, expected 1`);
78
+ }
79
+ if (!Array.isArray(obj.entries)) {
80
+ throw new Error('content manifest: malformed file, expected { version, entries: [] }');
81
+ }
82
+ for (const entry of obj.entries) {
83
+ const e = entry;
84
+ const ok = e &&
85
+ typeof e.id === 'string' &&
86
+ typeof e.concept === 'string' &&
87
+ typeof e.title === 'string' &&
88
+ typeof e.permalink === 'string' &&
89
+ typeof e.draft === 'boolean' &&
90
+ (e.date === undefined || typeof e.date === 'string') &&
91
+ Array.isArray(e.links);
92
+ if (!ok) {
93
+ throw new Error(`content manifest: malformed entry ${JSON.stringify(e)}`);
94
+ }
95
+ // Validate each link element's shape, not just that links is an array. inboundLinks and the
96
+ // delete guard read l.concept and l.id, so a string, null, or id-less element would read as
97
+ // undefined and silently drop a real inbound linker. Reject it here instead.
98
+ for (const link of e.links) {
99
+ const l = link;
100
+ if (!l || typeof l !== 'object' || typeof l.concept !== 'string' || typeof l.id !== 'string') {
101
+ throw new Error(`content manifest: malformed link ${JSON.stringify(link)} in entry ${JSON.stringify(e)}`);
102
+ }
103
+ }
104
+ }
105
+ return { version: 1, entries: obj.entries };
73
106
  }
74
107
  /** Throw if the committed manifest drifts from what the corpus says. Both sides are compared in the
75
108
  * canonical serialized form, so semantic equality never spuriously fails. The build calls this so a
@@ -90,6 +123,15 @@ export function upsertEntry(manifest, entry) {
90
123
  export function removeEntry(manifest, concept, id) {
91
124
  return { version: 1, entries: manifest.entries.filter((e) => !(e.concept === concept && e.id === id)) };
92
125
  }
126
+ /** Every entry whose outbound edges point at the target, excluding the target itself. The delete
127
+ * guard reads this to name "what links here"; the backlinks panel will reuse it. Pure over the
128
+ * manifest, so the request-time delete path and a unit test call it the same way. */
129
+ export function inboundLinks(manifest, concept, id) {
130
+ return manifest.entries
131
+ .filter((e) => !(e.concept === concept && e.id === id))
132
+ .filter((e) => e.links.some((l) => l.concept === concept && l.id === id))
133
+ .map((e) => ({ concept: e.concept, id: e.id, title: e.title, permalink: e.permalink }));
134
+ }
93
135
  /** A resolver backed by manifest targets, for the admin preview. A miss returns undefined, so the
94
136
  * render step marks the link broken rather than throwing. The build resolver throws instead. */
95
137
  export function manifestLinkResolver(targets) {
@@ -1 +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"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/manifest.ts"],"names":[],"mappings":"AASA,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,CAevH;AAED;oGACoG;AACpG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,WAAW,CAM9D"}
@@ -5,6 +5,7 @@
5
5
  // the build (the backstop). The admin preview uses manifestLinkResolver instead.
6
6
  import { siteDescriptors } from './site-descriptors.js';
7
7
  import { fromGlob } from './content-index.js';
8
+ import { parseMarkdown } from '../content/frontmatter.js';
8
9
  import { emptyManifest, manifestEntryFromFile } from '../content/manifest.js';
9
10
  /** Build the whole-corpus manifest from a site's adapter, config, and per-concept globs. Drafts are
10
11
  * included and flagged, so the admin picker and the guards see the full graph. */
@@ -14,6 +15,12 @@ export function buildSiteManifest(adapter, config, globs) {
14
15
  for (const descriptor of siteDescriptors(adapter, config)) {
15
16
  const record = globRecord[descriptor.id] ?? {};
16
17
  for (const file of fromGlob(record)) {
18
+ // Validate the same way createContentIndex does, so the manifest and the site index agree on
19
+ // which entries exist. A validation failure is excluded from both; otherwise the preview would
20
+ // resolve a link the build then rejects as a missing target.
21
+ const { frontmatter, body } = parseMarkdown(file.raw);
22
+ if (!descriptor.validate(frontmatter, body).ok)
23
+ continue;
17
24
  manifest.entries.push(manifestEntryFromFile(descriptor, file));
18
25
  }
19
26
  }
@@ -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;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"}
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,CAyCjB"}
@@ -175,8 +175,15 @@ export async function commitFiles(repo, changes, opts, token) {
175
175
  headers: { ...ghHeaders('application/vnd.github+json', token), 'Content-Type': 'application/json' },
176
176
  body: JSON.stringify({ base_tree: baseTree, tree }),
177
177
  });
178
- if (!treeRes.ok)
178
+ if (!treeRes.ok) {
179
+ // A 422 means an entry is unprocessable against the base tree, which a delete of an
180
+ // already-removed path produces (a concurrent delete or rename got there first). Treat it as
181
+ // the same non-fast-forward conflict the ref PATCH surfaces, so the caller fails safe with the
182
+ // reload-and-retry path instead of a raw 500.
183
+ if (treeRes.status === 422)
184
+ throw new CommitConflictError(`${repo.branch} (tree create)`);
179
185
  throw new Error(`GitHub tree create failed: ${treeRes.status} ${await treeRes.text()}`);
186
+ }
180
187
  const newTree = (await treeRes.json()).sha;
181
188
  const commitRes = await fetch(gitUrl(repo, 'commits'), {
182
189
  method: 'POST',
package/dist/index.d.ts CHANGED
@@ -11,10 +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';
14
+ export { parseCairnToken, extractCairnLinks, formatCairnToken, escapeLinkText } from './content/links.js';
15
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';
16
+ export { serializeManifest, parseManifest, emptyManifest, verifyManifest, upsertEntry, removeEntry, manifestEntryFromFile, manifestLinkResolver, inboundLinks, } from './content/manifest.js';
17
+ export type { Manifest, ManifestEntry, LinkTarget, InboundLink } from './content/manifest.js';
18
18
  export { defineRegistry, emptyValues } from './render/registry.js';
19
19
  export type { ComponentDef, ComponentRegistry, FieldType, AttributeField, SlotKind, SlotDef, ComponentValues, } from './render/registry.js';
20
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
@@ -11,8 +11,8 @@ export { isValidId, idFromFilename, filenameFromId, slugify, slugFromId, compose
11
11
  // Internal-link token and the committed content manifest (content-graph design). The corpus
12
12
  // builder and the request-time resolver ship from the delivery entry; this surface is the
13
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';
14
+ export { parseCairnToken, extractCairnLinks, formatCairnToken, escapeLinkText } from './content/links.js';
15
+ export { serializeManifest, parseManifest, emptyManifest, verifyManifest, upsertEntry, removeEntry, manifestEntryFromFile, manifestLinkResolver, inboundLinks, } from './content/manifest.js';
16
16
  // Render engine (Plan 04): generic directive pipeline; sites own the component registry.
17
17
  export { defineRegistry, emptyValues } from './render/registry.js';
18
18
  export { serializeComponent, parseComponent } from './render/component-grammar.js';
@@ -1,5 +1,6 @@
1
+ import { fail } from '@sveltejs/kit';
1
2
  import { type GithubKeyEnv } from '../github/credentials.js';
2
- import { type LinkTarget } from '../content/manifest.js';
3
+ import { type LinkTarget, type InboundLink } from '../content/manifest.js';
3
4
  import type { CairnRuntime, FrontmatterField } from '../content/types.js';
4
5
  import type { Editor, Role } from '../auth/types.js';
5
6
  /** A sidebar concept entry: just enough to render the nav without shipping validators to the client. */
@@ -50,9 +51,15 @@ export interface EditData {
50
51
  title: string;
51
52
  isNew: boolean;
52
53
  saved: boolean;
54
+ /** True after a successful rename redirect (`?renamed=1`), to confirm the new URL to the author. */
55
+ renamed: boolean;
53
56
  error: string | null;
57
+ /** The current URL slug (the date-stripped id for a dated concept), for the rename dialog prefill. */
58
+ slug: string;
54
59
  /** The site's link targets, for the preview resolver and the link picker; from the committed manifest. */
55
60
  linkTargets: LinkTarget[];
61
+ /** The entries that link to this one, for the delete guard. Empty when nothing links here. */
62
+ inboundLinks: InboundLink[];
56
63
  }
57
64
  /** The structural event the content routes read; a real SvelteKit RequestEvent satisfies it. */
58
65
  export interface ContentEvent {
@@ -77,7 +84,9 @@ export declare function createContentRoutes(runtime: CairnRuntime, deps?: Conten
77
84
  listLoad: (event: ContentEvent) => Promise<ListData>;
78
85
  createAction: (event: ContentEvent) => Promise<never>;
79
86
  editLoad: (event: ContentEvent) => Promise<EditData>;
80
- saveAction: (event: ContentEvent) => Promise<never>;
87
+ saveAction: (event: ContentEvent) => Promise<ReturnType<typeof fail> | never>;
88
+ deleteAction: (event: ContentEvent) => Promise<ReturnType<typeof fail> | never>;
89
+ renameAction: (event: ContentEvent) => Promise<ReturnType<typeof fail> | never>;
81
90
  mintToken: (env: GithubKeyEnv) => Promise<string>;
82
91
  };
83
92
  //# sourceMappingURL=content-routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"content-routes.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/content-routes.ts"],"names":[],"mappings":"AAQA,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7E,OAAO,EAAuF,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE9I,OAAO,KAAK,EAAE,YAAY,EAAqB,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC7F,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAErD,wGAAwG;AACxG,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,gGAAgG;AAChG,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC;IAC1C,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,yGAAyG;IACzG,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,oCAAoC;AACpC,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,gFAAgF;IAChF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,qDAAqD;IACrD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,6FAA6F;AAC7F,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,0GAA0G;IAC1G,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACnC,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC;CACnC;AAED,sFAAsF;AACtF,MAAM,WAAW,iBAAiB;IAChC,6FAA6F;IAC7F,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACpD;AAgBD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,GAAE,iBAAsB;wBAK1D,YAAY,KAAG,UAAU;yBAa1B,KAAK;sBAqBA,YAAY,KAAG,OAAO,CAAC,QAAQ,CAAC;0BAqB5B,YAAY,KAAG,OAAO,CAAC,KAAK,CAAC;sBAyCjC,YAAY,KAAG,OAAO,CAAC,QAAQ,CAAC;wBA+C9B,YAAY,KAAG,OAAO,CAAC,KAAK,CAAC;qBArK5C,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC;EA4NnD"}
1
+ {"version":3,"file":"content-routes.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/content-routes.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,IAAI,EAAE,MAAM,eAAe,CAAC;AAMtD,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7E,OAAO,EAAkH,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE3L,OAAO,KAAK,EAAE,YAAY,EAAqB,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC7F,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAErD,wGAAwG;AACxG,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,gGAAgG;AAChG,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC;IAC1C,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,yGAAyG;IACzG,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,oCAAoC;AACpC,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,gFAAgF;IAChF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,qDAAqD;IACrD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,6FAA6F;AAC7F,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,oGAAoG;IACpG,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,sGAAsG;IACtG,IAAI,EAAE,MAAM,CAAC;IACb,0GAA0G;IAC1G,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,8FAA8F;IAC9F,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAED,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACnC,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC;CACnC;AAED,sFAAsF;AACtF,MAAM,WAAW,iBAAiB;IAChC,6FAA6F;IAC7F,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACpD;AAgBD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,GAAE,iBAAsB;wBAK1D,YAAY,KAAG,UAAU;yBAa1B,KAAK;sBAqBA,YAAY,KAAG,OAAO,CAAC,QAAQ,CAAC;0BAqB5B,YAAY,KAAG,OAAO,CAAC,KAAK,CAAC;sBAyCjC,YAAY,KAAG,OAAO,CAAC,QAAQ,CAAC;wBAyD9B,YAAY,KAAG,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,GAAG,KAAK,CAAC;0BA8ErD,YAAY,KAAG,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,GAAG,KAAK,CAAC;0BA0CvD,YAAY,KAAG,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,GAAG,KAAK,CAAC;qBAvSxE,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC;EA2XnD"}