@glw907/cairn-cms 0.26.0 → 0.33.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.
- package/CHANGELOG.md +143 -0
- package/dist/auth/crypto.d.ts +0 -1
- package/dist/auth/store.d.ts +0 -1
- package/dist/auth/types.d.ts +0 -1
- package/dist/components/AdminLayout.svelte +372 -44
- package/dist/components/AdminLayout.svelte.d.ts +5 -5
- package/dist/components/CairnLogo.svelte +28 -0
- package/dist/components/CairnLogo.svelte.d.ts +15 -0
- package/dist/components/ComponentForm.svelte +1 -1
- package/dist/components/ComponentForm.svelte.d.ts +0 -1
- package/dist/components/ComponentInsertDialog.svelte.d.ts +0 -1
- package/dist/components/ConceptList.svelte +240 -45
- package/dist/components/ConceptList.svelte.d.ts +12 -3
- package/dist/components/ConfirmPage.svelte +20 -3
- package/dist/components/ConfirmPage.svelte.d.ts +0 -1
- package/dist/components/DeleteDialog.svelte.d.ts +0 -1
- package/dist/components/EditPage.svelte +12 -7
- package/dist/components/EditPage.svelte.d.ts +0 -1
- package/dist/components/EditorToolbar.svelte.d.ts +0 -1
- package/dist/components/IconPicker.svelte.d.ts +0 -1
- package/dist/components/LinkPicker.svelte.d.ts +0 -1
- package/dist/components/LoginPage.svelte +27 -5
- package/dist/components/LoginPage.svelte.d.ts +0 -1
- package/dist/components/ManageEditors.svelte +8 -5
- package/dist/components/ManageEditors.svelte.d.ts +0 -1
- package/dist/components/MarkdownEditor.svelte.d.ts +0 -1
- package/dist/components/NavTree.svelte +2 -2
- package/dist/components/NavTree.svelte.d.ts +0 -1
- package/dist/components/RenameDialog.svelte.d.ts +0 -1
- package/dist/components/admin-icons.d.ts +13 -0
- package/dist/components/admin-icons.js +15 -0
- package/dist/components/cairn-admin.css +5516 -37
- package/dist/components/cairn-favicon.d.ts +2 -0
- package/dist/components/cairn-favicon.js +7 -0
- package/dist/components/chrome-guard.d.ts +9 -0
- package/dist/components/chrome-guard.js +55 -0
- package/dist/components/fonts/BricolageGrotesque-OFL.txt +93 -0
- package/dist/components/fonts/Figtree-OFL.txt +93 -0
- package/dist/components/fonts/bricolage-grotesque.woff2 +0 -0
- package/dist/components/fonts/figtree.woff2 +0 -0
- package/dist/components/index.d.ts +0 -1
- package/dist/components/link-completion.d.ts +0 -1
- package/dist/components/markdown-format.d.ts +0 -1
- package/dist/content/adapter.d.ts +0 -1
- package/dist/content/compose.d.ts +1 -2
- package/dist/content/compose.js +2 -3
- package/dist/content/concepts.d.ts +7 -1
- package/dist/content/concepts.js +49 -1
- package/dist/content/frontmatter.d.ts +0 -1
- package/dist/content/identity.d.ts +23 -0
- package/dist/content/identity.js +43 -0
- package/dist/content/ids.d.ts +0 -1
- package/dist/content/links.d.ts +0 -1
- package/dist/content/manifest.d.ts +3 -2
- package/dist/content/manifest.js +6 -26
- package/dist/content/permalink.d.ts +0 -1
- package/dist/content/schema.d.ts +0 -1
- package/dist/content/types.d.ts +0 -1
- package/dist/content/validate.d.ts +0 -1
- package/dist/delivery/CairnHead.svelte.d.ts +0 -1
- package/dist/delivery/content-index.d.ts +0 -1
- package/dist/delivery/content-index.js +8 -25
- package/dist/delivery/data.d.ts +0 -1
- package/dist/delivery/excerpt.d.ts +0 -1
- package/dist/delivery/feeds.d.ts +0 -1
- package/dist/delivery/head.d.ts +0 -1
- package/dist/delivery/index.d.ts +0 -1
- package/dist/delivery/json-ld.d.ts +0 -1
- package/dist/delivery/manifest.d.ts +0 -1
- package/dist/delivery/paginate.d.ts +0 -1
- package/dist/delivery/responses.d.ts +0 -1
- package/dist/delivery/robots.d.ts +0 -1
- package/dist/delivery/seo-fields.d.ts +0 -1
- package/dist/delivery/seo.d.ts +0 -1
- package/dist/delivery/site-descriptors.d.ts +0 -1
- package/dist/delivery/site-descriptors.js +5 -6
- package/dist/delivery/site-index.d.ts +0 -1
- package/dist/delivery/site-indexes.d.ts +0 -1
- package/dist/delivery/sitemap.d.ts +0 -1
- package/dist/email.d.ts +0 -1
- package/dist/env.d.ts +0 -1
- package/dist/github/credentials.d.ts +0 -1
- package/dist/github/repo.d.ts +0 -1
- package/dist/github/signing.d.ts +0 -1
- package/dist/github/types.d.ts +0 -1
- package/dist/index.d.ts +0 -29
- package/dist/index.js +4 -23
- package/dist/nav/site-config.d.ts +0 -1
- package/dist/render/authoring.d.ts +3 -0
- package/dist/render/authoring.js +5 -0
- package/dist/render/component-grammar.d.ts +0 -1
- package/dist/render/component-insert.d.ts +0 -1
- package/dist/render/component-reference.d.ts +0 -1
- package/dist/render/component-validate.d.ts +0 -1
- package/dist/render/glyph.d.ts +0 -1
- package/dist/render/index.d.ts +0 -1
- package/dist/render/pipeline.d.ts +0 -1
- package/dist/render/pipeline.js +5 -1
- package/dist/render/registry.d.ts +2 -1
- package/dist/render/registry.js +15 -0
- package/dist/render/rehype-dispatch.d.ts +9 -7
- package/dist/render/rehype-dispatch.js +12 -6
- package/dist/render/remark-directives.d.ts +0 -1
- package/dist/render/remark-directives.js +1 -1
- package/dist/render/resolve-links.d.ts +0 -1
- package/dist/render/sanitize-schema.d.ts +14 -1
- package/dist/render/sanitize-schema.js +96 -0
- package/dist/sveltekit/auth-routes.d.ts +0 -1
- package/dist/sveltekit/content-routes.d.ts +12 -2
- package/dist/sveltekit/content-routes.js +37 -13
- package/dist/sveltekit/editors-routes.d.ts +0 -1
- package/dist/sveltekit/guard.d.ts +0 -1
- package/dist/sveltekit/health.d.ts +0 -1
- package/dist/sveltekit/index.d.ts +1 -3
- package/dist/sveltekit/index.js +0 -1
- package/dist/sveltekit/nav-routes.d.ts +0 -1
- package/dist/sveltekit/public-routes.d.ts +0 -1
- package/dist/sveltekit/types.d.ts +0 -1
- package/dist/vite/bin.d.ts +0 -1
- package/dist/vite/index.d.ts +0 -1
- package/package.json +16 -2
- package/src/lib/components/AdminLayout.svelte +372 -44
- package/src/lib/components/CairnLogo.svelte +28 -0
- package/src/lib/components/ComponentForm.svelte +1 -1
- package/src/lib/components/ConceptList.svelte +240 -45
- package/src/lib/components/ConfirmPage.svelte +20 -3
- package/src/lib/components/EditPage.svelte +12 -7
- package/src/lib/components/LoginPage.svelte +27 -5
- package/src/lib/components/ManageEditors.svelte +8 -5
- package/src/lib/components/NavTree.svelte +2 -2
- package/src/lib/components/admin-icons.ts +15 -0
- package/src/lib/components/cairn-admin.css +162 -7
- package/src/lib/components/cairn-favicon.ts +9 -0
- package/src/lib/components/chrome-guard.ts +62 -0
- package/src/lib/components/fonts/BricolageGrotesque-OFL.txt +93 -0
- package/src/lib/components/fonts/Figtree-OFL.txt +93 -0
- package/src/lib/components/fonts/bricolage-grotesque.woff2 +0 -0
- package/src/lib/components/fonts/figtree.woff2 +0 -0
- package/src/lib/content/compose.ts +3 -3
- package/src/lib/content/concepts.ts +61 -1
- package/src/lib/content/identity.ts +60 -0
- package/src/lib/content/manifest.ts +6 -27
- package/src/lib/delivery/content-index.ts +8 -27
- package/src/lib/delivery/site-descriptors.ts +5 -6
- package/src/lib/index.ts +4 -57
- package/src/lib/render/authoring.ts +7 -0
- package/src/lib/render/pipeline.ts +4 -1
- package/src/lib/render/registry.ts +20 -0
- package/src/lib/render/rehype-dispatch.ts +13 -6
- package/src/lib/render/remark-directives.ts +1 -1
- package/src/lib/render/sanitize-schema.ts +97 -0
- package/src/lib/sveltekit/content-routes.ts +51 -14
- package/src/lib/sveltekit/index.ts +2 -8
- package/dist/auth/crypto.d.ts.map +0 -1
- package/dist/auth/store.d.ts.map +0 -1
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/components/AdminLayout.svelte.d.ts.map +0 -1
- package/dist/components/ComponentForm.svelte.d.ts.map +0 -1
- package/dist/components/ComponentInsertDialog.svelte.d.ts.map +0 -1
- package/dist/components/ConceptList.svelte.d.ts.map +0 -1
- package/dist/components/ConfirmPage.svelte.d.ts.map +0 -1
- package/dist/components/DeleteDialog.svelte.d.ts.map +0 -1
- package/dist/components/EditPage.svelte.d.ts.map +0 -1
- package/dist/components/EditorToolbar.svelte.d.ts.map +0 -1
- package/dist/components/IconPicker.svelte.d.ts.map +0 -1
- package/dist/components/LinkPicker.svelte.d.ts.map +0 -1
- package/dist/components/LoginPage.svelte.d.ts.map +0 -1
- package/dist/components/ManageEditors.svelte.d.ts.map +0 -1
- package/dist/components/MarkdownEditor.svelte.d.ts.map +0 -1
- package/dist/components/NavTree.svelte.d.ts.map +0 -1
- package/dist/components/RenameDialog.svelte.d.ts.map +0 -1
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/link-completion.d.ts.map +0 -1
- package/dist/components/markdown-format.d.ts.map +0 -1
- package/dist/content/adapter.d.ts.map +0 -1
- package/dist/content/compose.d.ts.map +0 -1
- package/dist/content/concepts.d.ts.map +0 -1
- package/dist/content/frontmatter.d.ts.map +0 -1
- package/dist/content/ids.d.ts.map +0 -1
- package/dist/content/links.d.ts.map +0 -1
- package/dist/content/manifest.d.ts.map +0 -1
- package/dist/content/permalink.d.ts.map +0 -1
- package/dist/content/schema.d.ts.map +0 -1
- package/dist/content/types.d.ts.map +0 -1
- package/dist/content/validate.d.ts.map +0 -1
- package/dist/delivery/CairnHead.svelte.d.ts.map +0 -1
- package/dist/delivery/content-index.d.ts.map +0 -1
- package/dist/delivery/data.d.ts.map +0 -1
- package/dist/delivery/excerpt.d.ts.map +0 -1
- package/dist/delivery/feeds.d.ts.map +0 -1
- package/dist/delivery/head.d.ts.map +0 -1
- package/dist/delivery/index.d.ts.map +0 -1
- package/dist/delivery/json-ld.d.ts.map +0 -1
- package/dist/delivery/manifest.d.ts.map +0 -1
- package/dist/delivery/paginate.d.ts.map +0 -1
- package/dist/delivery/responses.d.ts.map +0 -1
- package/dist/delivery/robots.d.ts.map +0 -1
- package/dist/delivery/seo-fields.d.ts.map +0 -1
- package/dist/delivery/seo.d.ts.map +0 -1
- package/dist/delivery/site-descriptors.d.ts.map +0 -1
- package/dist/delivery/site-index.d.ts.map +0 -1
- package/dist/delivery/site-indexes.d.ts.map +0 -1
- package/dist/delivery/sitemap.d.ts.map +0 -1
- package/dist/email.d.ts.map +0 -1
- package/dist/env.d.ts.map +0 -1
- package/dist/github/credentials.d.ts.map +0 -1
- package/dist/github/repo.d.ts.map +0 -1
- package/dist/github/signing.d.ts.map +0 -1
- package/dist/github/types.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/nav/site-config.d.ts.map +0 -1
- package/dist/render/component-grammar.d.ts.map +0 -1
- package/dist/render/component-insert.d.ts.map +0 -1
- package/dist/render/component-reference.d.ts.map +0 -1
- package/dist/render/component-validate.d.ts.map +0 -1
- package/dist/render/glyph.d.ts.map +0 -1
- package/dist/render/index.d.ts.map +0 -1
- package/dist/render/pipeline.d.ts.map +0 -1
- package/dist/render/registry.d.ts.map +0 -1
- package/dist/render/rehype-dispatch.d.ts.map +0 -1
- package/dist/render/remark-directives.d.ts.map +0 -1
- package/dist/render/resolve-links.d.ts.map +0 -1
- package/dist/render/sanitize-schema.d.ts.map +0 -1
- package/dist/sveltekit/auth-routes.d.ts.map +0 -1
- package/dist/sveltekit/content-routes.d.ts.map +0 -1
- package/dist/sveltekit/editors-routes.d.ts.map +0 -1
- package/dist/sveltekit/guard.d.ts.map +0 -1
- package/dist/sveltekit/health.d.ts.map +0 -1
- package/dist/sveltekit/index.d.ts.map +0 -1
- package/dist/sveltekit/nav-routes.d.ts.map +0 -1
- package/dist/sveltekit/public-routes.d.ts.map +0 -1
- package/dist/sveltekit/types.d.ts.map +0 -1
- package/dist/vite/bin.d.ts.map +0 -1
- package/dist/vite/index.d.ts.map +0 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// The cairn mark as an inline SVG data URL, so an admin browser tab carries Cairn's brand. The fill
|
|
2
|
+
// is a fixed violet near the admin primary, since a favicon cannot read a CSS variable. The path is
|
|
3
|
+
// the same public-domain Temaki cairn used by CairnLogo.svelte.
|
|
4
|
+
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" fill="#7c3aed">' +
|
|
5
|
+
'<path d="M6.28 14C5.56 14 1 13.89 1 12.91C1 11.46 2.16 11.07 3.2 10.81C4.36 10.51 13.18 9.77 13.76 10.07C14.46 10.43 13.52 12.49 12.44 12.77C11.28 13.07 10.21 14 8.48 14C7.05 14 9.69 14 6.28 14ZM6.92 4.5C6.67 4.5 5 4.43 5 3.88C5 3.07 5.75 2.51 5.96 2.35C6.36 2.03 6.32 1.62 6.54 1.27C6.84 0.79 7.61 0.5 7.88 0.5C8.1 0.5 8.75 0.9 9.23 1.42C9.45 1.66 10 2.77 10 3.12C10 4.22 9.36 4.5 8.85 4.5C8.33 4.5 8.15 4.5 6.92 4.5ZM3.68 8.22C3 7.73 3.67 6.86 4.57 6.21C5.38 5.63 5.92 5.96 6.79 5.7C8.33 5.24 9.02 5.72 9.02 5.72L10.9 6.82C12.03 7.63 10.99 7.67 10.38 8.56C9.79 9.42 8.18 9.11 7.42 9.33C6.78 9.53 5.75 9.71 4.62 8.9L3.68 8.22Z"/></svg>';
|
|
6
|
+
/** The cairn mark as a `data:image/svg+xml` URL, for a `<link rel="icon">` on the admin pages. */
|
|
7
|
+
export const cairnFaviconHref = `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inspect the admin root's ancestor chain for host chrome. Returns a diagnostic when a
|
|
3
|
+
* width-constraining ancestor sits between the root and <body>, else null. Pure over the DOM so a
|
|
4
|
+
* test can build either shape. The sibling signal (host elements outside the admin subtree) is folded
|
|
5
|
+
* into the message as context rather than raised on its own, because it is the noisier of the two.
|
|
6
|
+
*/
|
|
7
|
+
export declare function detectChromeWrap(root: HTMLElement): string | null;
|
|
8
|
+
/** Run the check in dev and log one error when host chrome is detected. A no-op in production. */
|
|
9
|
+
export declare function warnIfChromeWrapped(root: HTMLElement): void;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Dev-only structural check that catches a host mounting the admin inside its own chrome. Every admin
|
|
2
|
+
// rule is scoped and the admin self-styles, but a host whose root layout wraps the admin in a
|
|
3
|
+
// width-constraining container (a `<main class="container">`) or renders its nav and footer around it
|
|
4
|
+
// breaks the full-bleed admin shell. The engine cannot prevent that layout mistake, so it names it.
|
|
5
|
+
// The check walks the ancestor chain once on mount and emits one console.error that points at the
|
|
6
|
+
// route-structure doc. The public entry runs only under import.meta.env.DEV, never throws, and changes
|
|
7
|
+
// no rendering.
|
|
8
|
+
const DOC = 'docs/admin-route-structure.md';
|
|
9
|
+
// max-width values that do not actually constrain the admin below the viewport. A host that sets a
|
|
10
|
+
// defensive `max-width: 100%` or `100vw` on a wrapper is not chrome, so skip those to avoid a spurious
|
|
11
|
+
// dev error. A real constraining container uses an absolute length (`64rem`, `1280px`) or a sub-100
|
|
12
|
+
// percentage, both of which still trip the guard.
|
|
13
|
+
const NON_CONSTRAINING = new Set(['none', '100%', '100vw']);
|
|
14
|
+
function describe(el) {
|
|
15
|
+
const tag = el.tagName.toLowerCase();
|
|
16
|
+
const cls = el.getAttribute('class');
|
|
17
|
+
return cls ? `<${tag} class="${cls}">` : `<${tag}>`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Inspect the admin root's ancestor chain for host chrome. Returns a diagnostic when a
|
|
21
|
+
* width-constraining ancestor sits between the root and <body>, else null. Pure over the DOM so a
|
|
22
|
+
* test can build either shape. The sibling signal (host elements outside the admin subtree) is folded
|
|
23
|
+
* into the message as context rather than raised on its own, because it is the noisier of the two.
|
|
24
|
+
*/
|
|
25
|
+
export function detectChromeWrap(root) {
|
|
26
|
+
const body = root.ownerDocument.body;
|
|
27
|
+
let constrainer = null;
|
|
28
|
+
let maxWidth = '';
|
|
29
|
+
for (let el = root.parentElement; el && el !== body; el = el.parentElement) {
|
|
30
|
+
const elMaxWidth = getComputedStyle(el).maxWidth;
|
|
31
|
+
if (elMaxWidth && !NON_CONSTRAINING.has(elMaxWidth)) {
|
|
32
|
+
constrainer = el;
|
|
33
|
+
maxWidth = elMaxWidth;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (!constrainer)
|
|
38
|
+
return null;
|
|
39
|
+
const siblings = [...body.children].filter((el) => !el.contains(root) && !root.contains(el) && el !== root);
|
|
40
|
+
const siblingNote = siblings.length
|
|
41
|
+
? ` Host elements also sit beside the admin in <body> (${siblings.map(describe).join(', ')}).`
|
|
42
|
+
: '';
|
|
43
|
+
return (`[cairn-cms] The admin is rendering inside host chrome. A width-constraining ancestor ` +
|
|
44
|
+
`${describe(constrainer)} (max-width: ${maxWidth}) sits between the admin root and <body>, so the ` +
|
|
45
|
+
`admin shell cannot fill the viewport.${siblingNote} Keep the host root layout chrome-free and move ` +
|
|
46
|
+
`your nav, footer, and app.css into a (site) route group. See ${DOC}.`);
|
|
47
|
+
}
|
|
48
|
+
/** Run the check in dev and log one error when host chrome is detected. A no-op in production. */
|
|
49
|
+
export function warnIfChromeWrapped(root) {
|
|
50
|
+
if (!import.meta.env.DEV)
|
|
51
|
+
return;
|
|
52
|
+
const problem = detectChromeWrap(root);
|
|
53
|
+
if (problem)
|
|
54
|
+
console.error(problem);
|
|
55
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2022 The Bricolage Grotesque Project Authors (https://github.com/ateliertriay/bricolage)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
https://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2022 The Figtree Project Authors (https://github.com/erikdkennedy/figtree)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file
|
|
Binary file
|
|
@@ -12,4 +12,3 @@ export { default as NavTree } from './NavTree.svelte';
|
|
|
12
12
|
export { default as LinkPicker } from './LinkPicker.svelte';
|
|
13
13
|
export { default as DeleteDialog } from './DeleteDialog.svelte';
|
|
14
14
|
export { default as RenameDialog } from './RenameDialog.svelte';
|
|
15
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -13,4 +13,3 @@ export declare function linkCompletions(targets: LinkTarget[], query: string): C
|
|
|
13
13
|
* whole `[[query` with the chosen link, and sets filter:false because linkCompletions already
|
|
14
14
|
* filtered by the query (CodeMirror would otherwise re-filter against the literal `[[query`). */
|
|
15
15
|
export declare function cairnLinkCompletionSource(targets: LinkTarget[]): CompletionSource;
|
|
16
|
-
//# sourceMappingURL=link-completion.d.ts.map
|
|
@@ -30,4 +30,3 @@ export declare function unwrapCairnLink(doc: string, href: string): string;
|
|
|
30
30
|
* last to first, replacing only the `](oldHref` run so the label and title stay exact.
|
|
31
31
|
*/
|
|
32
32
|
export declare function rewriteCairnLink(doc: string, oldHref: string, newHref: string): string;
|
|
33
|
-
//# sourceMappingURL=markdown-format.d.ts.map
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import type { CairnAdapter } from './types.js';
|
|
2
2
|
/** Declare a site's adapter while preserving each concept's concrete schema type for typed reads. */
|
|
3
3
|
export declare function defineAdapter<const A extends CairnAdapter>(adapter: A): A;
|
|
4
|
-
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CairnAdapter, CairnExtension, CairnRuntime } from './types.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { SiteConfig } from '../nav/site-config.js';
|
|
3
3
|
/** The input to {@link composeRuntime}. `siteConfig` is required so the per-concept URL policy is
|
|
4
4
|
* always derived from one source and can never be silently dropped. `extensions` fold in after the
|
|
5
5
|
* adapter's concepts. */
|
|
@@ -15,4 +15,3 @@ export interface ComposeInput {
|
|
|
15
15
|
* (seam 4) passes through untouched.
|
|
16
16
|
*/
|
|
17
17
|
export declare function composeRuntime({ adapter, siteConfig, extensions }: ComposeInput): CairnRuntime;
|
|
18
|
-
//# sourceMappingURL=compose.d.ts.map
|
package/dist/content/compose.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { urlPolicyFrom } from '../nav/site-config.js';
|
|
1
|
+
import { resolveConcepts } from './concepts.js';
|
|
3
2
|
/**
|
|
4
3
|
* Fold an adapter and any extensions into the composed runtime (seam 2). The per-concept URL policy
|
|
5
4
|
* is derived from the site config, the same source the delivery path uses, so the runtime and
|
|
@@ -24,7 +23,7 @@ export function composeRuntime({ adapter, siteConfig, extensions = [] }) {
|
|
|
24
23
|
}
|
|
25
24
|
return {
|
|
26
25
|
siteName: adapter.siteName,
|
|
27
|
-
concepts:
|
|
26
|
+
concepts: resolveConcepts(content, siteConfig),
|
|
28
27
|
backend: adapter.backend,
|
|
29
28
|
sender: adapter.sender,
|
|
30
29
|
render: adapter.render,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ConceptConfig, ConceptDescriptor, ConceptUrlPolicy, RoutingRule } from './types.js';
|
|
2
|
+
import { type SiteConfig } from '../nav/site-config.js';
|
|
2
3
|
/**
|
|
3
4
|
* Concept-fixed routing, keyed by concept id (spec §7.2). Posts are dated feed entries;
|
|
4
5
|
* pages are plain navigable structure. Not in adapter config. A future Fragments adds one
|
|
@@ -13,6 +14,11 @@ export declare const CONCEPT_ROUTING: Readonly<Record<string, RoutingRule>>;
|
|
|
13
14
|
* a new concept attaches additively; production passes the default `CONCEPT_ROUTING`.
|
|
14
15
|
*/
|
|
15
16
|
export declare function normalizeConcepts(content: Record<string, ConceptConfig | undefined>, urlPolicy?: Record<string, ConceptUrlPolicy | undefined>, routing?: Readonly<Record<string, RoutingRule>>): ConceptDescriptor[];
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a site's concept descriptors from its content map and parsed site config. The admin runtime
|
|
19
|
+
* (composeRuntime) and the delivery layer (siteDescriptors) both call this, so the per-concept URL
|
|
20
|
+
* policy is derived once from the YAML and the runtime and delivery permalinks cannot diverge.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveConcepts(content: Record<string, ConceptConfig | undefined>, siteConfig: SiteConfig): ConceptDescriptor[];
|
|
16
23
|
/** Look up a normalized concept by id, or undefined when the site does not enable it. */
|
|
17
24
|
export declare function findConcept(concepts: ConceptDescriptor[], id: string): ConceptDescriptor | undefined;
|
|
18
|
-
//# sourceMappingURL=concepts.d.ts.map
|
package/dist/content/concepts.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { urlPolicyFrom } from '../nav/site-config.js';
|
|
1
2
|
/**
|
|
2
3
|
* Concept-fixed routing, keyed by concept id (spec §7.2). Posts are dated feed entries;
|
|
3
4
|
* pages are plain navigable structure. Not in adapter config. A future Fragments adds one
|
|
@@ -17,6 +18,37 @@ function defaultLabel(id) {
|
|
|
17
18
|
function defaultPermalink(id) {
|
|
18
19
|
return id === 'pages' ? '/:slug' : `/${id}/:slug`;
|
|
19
20
|
}
|
|
21
|
+
/** Permalink tokens the resolver understands. */
|
|
22
|
+
const KNOWN_TOKENS = new Set(['slug', 'year', 'month', 'day']);
|
|
23
|
+
/** The date-bearing tokens; valid only for a dated concept. */
|
|
24
|
+
const DATE_TOKENS = new Set(['year', 'month', 'day']);
|
|
25
|
+
/** The valid date-prefix granularities. A runtime check, since the YAML is untyped. */
|
|
26
|
+
const DATE_PREFIXES = new Set(['year', 'month', 'day']);
|
|
27
|
+
/**
|
|
28
|
+
* Validate one concept's URL policy at build, so a misconfigured permalink or datePrefix fails loudly
|
|
29
|
+
* here rather than emitting a wrong or defaulted URL at render. The permalink must be root-relative and
|
|
30
|
+
* use only known tokens, a date token requires a dated concept, and the datePrefix must be in range.
|
|
31
|
+
*/
|
|
32
|
+
function validateUrlPolicy(id, policy, dated) {
|
|
33
|
+
if (policy.permalink !== undefined) {
|
|
34
|
+
const pattern = policy.permalink;
|
|
35
|
+
if (!pattern.startsWith('/')) {
|
|
36
|
+
throw new Error(`cairn: concept "${id}" permalink "${pattern}" must start with "/"`);
|
|
37
|
+
}
|
|
38
|
+
for (const match of pattern.matchAll(/:(\w+)/g)) {
|
|
39
|
+
const token = match[1];
|
|
40
|
+
if (!KNOWN_TOKENS.has(token)) {
|
|
41
|
+
throw new Error(`cairn: concept "${id}" permalink "${pattern}" uses unknown token ":${token}"`);
|
|
42
|
+
}
|
|
43
|
+
if (DATE_TOKENS.has(token) && !dated) {
|
|
44
|
+
throw new Error(`cairn: concept "${id}" is not dated, so permalink "${pattern}" cannot use the date token ":${token}"`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (policy.datePrefix !== undefined && !DATE_PREFIXES.has(policy.datePrefix)) {
|
|
49
|
+
throw new Error(`cairn: concept "${id}" datePrefix "${policy.datePrefix}" must be one of year, month, day`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
20
52
|
/**
|
|
21
53
|
* Normalize an adapter's declared concepts into uniform descriptors (seam 1). URL policy
|
|
22
54
|
* (`permalink`, `datePrefix`) comes from the YAML site-config, passed here as `urlPolicy` keyed by
|
|
@@ -26,6 +58,12 @@ function defaultPermalink(id) {
|
|
|
26
58
|
*/
|
|
27
59
|
export function normalizeConcepts(content, urlPolicy = {}, routing = CONCEPT_ROUTING) {
|
|
28
60
|
const descriptors = [];
|
|
61
|
+
const declaredConcepts = new Set(Object.keys(content).filter((key) => content[key] !== undefined));
|
|
62
|
+
for (const key of Object.keys(urlPolicy)) {
|
|
63
|
+
if (!declaredConcepts.has(key)) {
|
|
64
|
+
throw new Error(`cairn: URL policy names concept "${key}", which is not declared under content`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
29
67
|
for (const [id, config] of Object.entries(content)) {
|
|
30
68
|
if (!config)
|
|
31
69
|
continue;
|
|
@@ -35,12 +73,14 @@ export function normalizeConcepts(content, urlPolicy = {}, routing = CONCEPT_ROU
|
|
|
35
73
|
if (undeclared !== undefined) {
|
|
36
74
|
throw new Error(`cairn: concept "${id}" summaryFields key "${undeclared}" is not a declared field`);
|
|
37
75
|
}
|
|
76
|
+
const conceptRouting = routing[id] ?? DEFAULT_ROUTING;
|
|
38
77
|
const policy = urlPolicy[id] ?? {};
|
|
78
|
+
validateUrlPolicy(id, policy, conceptRouting.dated);
|
|
39
79
|
descriptors.push({
|
|
40
80
|
id,
|
|
41
81
|
label: config.label ?? defaultLabel(id),
|
|
42
82
|
dir: config.dir,
|
|
43
|
-
routing:
|
|
83
|
+
routing: conceptRouting,
|
|
44
84
|
permalink: policy.permalink ?? defaultPermalink(id),
|
|
45
85
|
datePrefix: policy.datePrefix ?? 'day',
|
|
46
86
|
fields: config.schema.fields,
|
|
@@ -50,6 +90,14 @@ export function normalizeConcepts(content, urlPolicy = {}, routing = CONCEPT_ROU
|
|
|
50
90
|
}
|
|
51
91
|
return descriptors;
|
|
52
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolve a site's concept descriptors from its content map and parsed site config. The admin runtime
|
|
95
|
+
* (composeRuntime) and the delivery layer (siteDescriptors) both call this, so the per-concept URL
|
|
96
|
+
* policy is derived once from the YAML and the runtime and delivery permalinks cannot diverge.
|
|
97
|
+
*/
|
|
98
|
+
export function resolveConcepts(content, siteConfig) {
|
|
99
|
+
return normalizeConcepts(content, urlPolicyFrom(siteConfig));
|
|
100
|
+
}
|
|
53
101
|
/** Look up a normalized concept by id, or undefined when the site does not enable it. */
|
|
54
102
|
export function findConcept(concepts, id) {
|
|
55
103
|
return concepts.find((concept) => concept.id === id);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ConceptDescriptor } from './types.js';
|
|
2
|
+
/** A content entry's resolved URL identity. */
|
|
3
|
+
export interface EntryIdentity {
|
|
4
|
+
id: string;
|
|
5
|
+
slug: string;
|
|
6
|
+
date?: string;
|
|
7
|
+
permalink: string;
|
|
8
|
+
}
|
|
9
|
+
/** A present, non-empty string, else undefined. The read-model string coercion. */
|
|
10
|
+
export declare function asString(value: unknown): string | undefined;
|
|
11
|
+
/** A YYYY-MM-DD date. An unquoted YAML date parses as a JS Date; a string is sliced to its date head. */
|
|
12
|
+
export declare function asDate(value: unknown): string | undefined;
|
|
13
|
+
/** Tags as an array, empty when the file declares none. */
|
|
14
|
+
export declare function asTags(value: unknown): string[];
|
|
15
|
+
/** A content entry's id: its filename stem (the date prefix is part of a dated id). */
|
|
16
|
+
export declare function entryId(path: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a content entry's URL identity from its concept descriptor, its file path, and its parsed
|
|
19
|
+
* frontmatter. The slug strips the leading date prefix for a dated concept and is the id verbatim for
|
|
20
|
+
* an undated one. The permalink is the one resolver every reader shares. The caller parses the markdown
|
|
21
|
+
* once and passes the frontmatter, so there is no second parse here.
|
|
22
|
+
*/
|
|
23
|
+
export declare function entryIdentity(descriptor: ConceptDescriptor, path: string, frontmatter: Record<string, unknown>): EntryIdentity;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// cairn-cms: a content entry's URL identity in one place (engine-hardening pass 3). The id, the
|
|
2
|
+
// slug, the date, and the permalink are computed here, so the content index and the manifest cannot
|
|
3
|
+
// drift on what an entry's URL is. A cairn: link resolves through the manifest in the admin preview
|
|
4
|
+
// and through the content index in the public build, so the two must agree by construction.
|
|
5
|
+
import { idFromFilename, slugFromId } from './ids.js';
|
|
6
|
+
import { permalink } from './permalink.js';
|
|
7
|
+
/** The basename of a glob path: the segment after the last slash, or the whole path. */
|
|
8
|
+
function basename(path) {
|
|
9
|
+
const slash = path.lastIndexOf('/');
|
|
10
|
+
return slash >= 0 ? path.slice(slash + 1) : path;
|
|
11
|
+
}
|
|
12
|
+
/** A present, non-empty string, else undefined. The read-model string coercion. */
|
|
13
|
+
export function asString(value) {
|
|
14
|
+
return typeof value === 'string' && value.trim() ? value : undefined;
|
|
15
|
+
}
|
|
16
|
+
/** A YYYY-MM-DD date. An unquoted YAML date parses as a JS Date; a string is sliced to its date head. */
|
|
17
|
+
export function asDate(value) {
|
|
18
|
+
if (value instanceof Date)
|
|
19
|
+
return Number.isNaN(value.getTime()) ? undefined : value.toISOString().slice(0, 10);
|
|
20
|
+
if (typeof value === 'string')
|
|
21
|
+
return value.match(/^\d{4}-\d{2}-\d{2}/)?.[0];
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
/** Tags as an array, empty when the file declares none. */
|
|
25
|
+
export function asTags(value) {
|
|
26
|
+
return Array.isArray(value) ? value.map(String) : [];
|
|
27
|
+
}
|
|
28
|
+
/** A content entry's id: its filename stem (the date prefix is part of a dated id). */
|
|
29
|
+
export function entryId(path) {
|
|
30
|
+
return idFromFilename(basename(path));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resolve a content entry's URL identity from its concept descriptor, its file path, and its parsed
|
|
34
|
+
* frontmatter. The slug strips the leading date prefix for a dated concept and is the id verbatim for
|
|
35
|
+
* an undated one. The permalink is the one resolver every reader shares. The caller parses the markdown
|
|
36
|
+
* once and passes the frontmatter, so there is no second parse here.
|
|
37
|
+
*/
|
|
38
|
+
export function entryIdentity(descriptor, path, frontmatter) {
|
|
39
|
+
const id = entryId(path);
|
|
40
|
+
const slug = slugFromId(id, descriptor.routing.dated ? descriptor.datePrefix : null);
|
|
41
|
+
const date = asDate(frontmatter.date);
|
|
42
|
+
return { id, slug, date, permalink: permalink(descriptor, { id, slug, date }) };
|
|
43
|
+
}
|
package/dist/content/ids.d.ts
CHANGED
|
@@ -35,4 +35,3 @@ export declare function composeDatedId(date: string, slug: string, datePrefix: D
|
|
|
35
35
|
* newSlug. The caller validates newSlug with isValidId first.
|
|
36
36
|
*/
|
|
37
37
|
export declare function renameId(oldId: string, newSlug: string, datePrefix: DatePrefix | null): string;
|
|
38
|
-
//# sourceMappingURL=ids.d.ts.map
|
package/dist/content/links.d.ts
CHANGED
|
@@ -18,4 +18,3 @@ export declare function escapeLinkText(text: string): string;
|
|
|
18
18
|
/** The cairn links a markdown body points at, in first-occurrence order, deduped by concept/id.
|
|
19
19
|
* Parses the body as mdast, so a token inside a code span or fence is never matched. */
|
|
20
20
|
export declare function extractCairnLinks(body: string): CairnRef[];
|
|
21
|
-
//# sourceMappingURL=links.d.ts.map
|
|
@@ -24,7 +24,9 @@ export interface LinkTarget {
|
|
|
24
24
|
date?: string;
|
|
25
25
|
draft: boolean;
|
|
26
26
|
}
|
|
27
|
-
/** Build one manifest entry from a content file. Drafts are included and flagged.
|
|
27
|
+
/** Build one manifest entry from a content file. Drafts are included and flagged. The id, date, and
|
|
28
|
+
* permalink come from entryIdentity, the same source content-index uses, so a cairn: link resolves to
|
|
29
|
+
* one URL whether the admin preview reads the manifest or the public build reads the content index. */
|
|
28
30
|
export declare function manifestEntryFromFile(descriptor: ConceptDescriptor, file: {
|
|
29
31
|
path: string;
|
|
30
32
|
raw: string;
|
|
@@ -83,4 +85,3 @@ export declare function manifestLinkResolver(targets: {
|
|
|
83
85
|
id: string;
|
|
84
86
|
permalink: string;
|
|
85
87
|
}[]): LinkResolve;
|
|
86
|
-
//# sourceMappingURL=manifest.d.ts.map
|
package/dist/content/manifest.js
CHANGED
|
@@ -3,41 +3,21 @@
|
|
|
3
3
|
// code reads the content graph without an N+1 GitHub crawl. The build regenerates and verifies
|
|
4
4
|
// it; the save path patches one entry and commits it with the content in one commit. Each entry
|
|
5
5
|
// carries its identity and its outbound cairn: edges, so the manifest is the link graph.
|
|
6
|
-
import { idFromFilename, slugFromId } from './ids.js';
|
|
7
6
|
import { parseMarkdown } from './frontmatter.js';
|
|
8
|
-
import {
|
|
7
|
+
import { entryIdentity, asString } from './identity.js';
|
|
9
8
|
import { extractCairnLinks } from './links.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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. */
|
|
9
|
+
/** Build one manifest entry from a content file. Drafts are included and flagged. The id, date, and
|
|
10
|
+
* permalink come from entryIdentity, the same source content-index uses, so a cairn: link resolves to
|
|
11
|
+
* one URL whether the admin preview reads the manifest or the public build reads the content index. */
|
|
27
12
|
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
13
|
const { frontmatter, body } = parseMarkdown(file.raw);
|
|
34
|
-
const date =
|
|
14
|
+
const { id, date, permalink } = entryIdentity(descriptor, file.path, frontmatter);
|
|
35
15
|
return {
|
|
36
16
|
id,
|
|
37
17
|
concept: descriptor.id,
|
|
38
18
|
title: asString(frontmatter.title) ?? id,
|
|
39
19
|
date,
|
|
40
|
-
permalink
|
|
20
|
+
permalink,
|
|
41
21
|
draft: frontmatter.draft === true,
|
|
42
22
|
links: extractCairnLinks(body),
|
|
43
23
|
};
|
package/dist/content/schema.d.ts
CHANGED
|
@@ -72,4 +72,3 @@ export interface DefineFieldsOptions<F extends readonly FrontmatterField[]> {
|
|
|
72
72
|
/** Declare a concept's fields once. Returns the schema's faces derived from that one declaration. */
|
|
73
73
|
export declare function defineFields<const F extends readonly FrontmatterField[]>(fields: F, options?: DefineFieldsOptions<F>): ConceptSchema<F>;
|
|
74
74
|
export {};
|
|
75
|
-
//# sourceMappingURL=schema.d.ts.map
|
package/dist/content/types.d.ts
CHANGED
|
@@ -15,4 +15,3 @@ import type { FrontmatterField, ValidationResult } from './types.js';
|
|
|
15
15
|
* `Date` to `YYYY-MM-DD` so a valid parsed date is not mistaken for an empty one.
|
|
16
16
|
*/
|
|
17
17
|
export declare function validateFields(fields: FrontmatterField[], frontmatter: Record<string, unknown>): ValidationResult;
|
|
18
|
-
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -65,4 +65,3 @@ export interface ContentIndex<F = Record<string, unknown>> {
|
|
|
65
65
|
export declare function fromGlob(record: Record<string, string>): RawFile[];
|
|
66
66
|
/** Build a concept's index from its raw files and normalized descriptor. */
|
|
67
67
|
export declare function createContentIndex<F = Record<string, unknown>>(files: RawFile[], descriptor: ConceptDescriptor): ContentIndex<F>;
|
|
68
|
-
//# sourceMappingURL=content-index.d.ts.map
|