@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
|
@@ -3,47 +3,30 @@
|
|
|
3
3
|
// returns cheap plain-data summaries plus an on-demand detail lookup. It is concept-generic:
|
|
4
4
|
// every operation reads the descriptor and its routing rule, never a hardcoded concept id.
|
|
5
5
|
import { parseMarkdown } from '../content/frontmatter.js';
|
|
6
|
-
import {
|
|
7
|
-
import { permalink } from '../content/permalink.js';
|
|
6
|
+
import { entryId, entryIdentity, asDate, asString, asTags } from '../content/identity.js';
|
|
8
7
|
import { deriveExcerpt, wordCount } from './excerpt.js';
|
|
9
8
|
/** Map a Vite eager `?raw` glob record (`{ path: raw }`) to `RawFile[]`. */
|
|
10
9
|
export function fromGlob(record) {
|
|
11
10
|
return Object.entries(record).map(([path, raw]) => ({ path, raw }));
|
|
12
11
|
}
|
|
13
|
-
function basename(path) {
|
|
14
|
-
const slash = path.lastIndexOf('/');
|
|
15
|
-
return slash >= 0 ? path.slice(slash + 1) : path;
|
|
16
|
-
}
|
|
17
|
-
function asString(value) {
|
|
18
|
-
return typeof value === 'string' && value.trim() ? value : undefined;
|
|
19
|
-
}
|
|
20
|
-
function asDate(value) {
|
|
21
|
-
if (value instanceof Date)
|
|
22
|
-
return Number.isNaN(value.getTime()) ? undefined : value.toISOString().slice(0, 10);
|
|
23
|
-
if (typeof value === 'string')
|
|
24
|
-
return value.match(/^\d{4}-\d{2}-\d{2}/)?.[0];
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
|
-
function asTags(value) {
|
|
28
|
-
return Array.isArray(value) ? value.map(String) : [];
|
|
29
|
-
}
|
|
30
12
|
/** Build a concept's index from its raw files and normalized descriptor. */
|
|
31
13
|
export function createContentIndex(files, descriptor) {
|
|
32
14
|
const problems = [];
|
|
33
15
|
const entries = [];
|
|
34
16
|
for (const file of files) {
|
|
35
|
-
const id = idFromFilename(basename(file.path));
|
|
36
|
-
const slug = slugFromId(id, descriptor.routing.dated ? descriptor.datePrefix : null);
|
|
37
17
|
const { frontmatter: raw, body } = parseMarkdown(file.raw);
|
|
38
|
-
const
|
|
18
|
+
const id = entryId(file.path);
|
|
39
19
|
const draft = raw.draft === true;
|
|
40
|
-
// Validate
|
|
41
|
-
//
|
|
20
|
+
// Validate before resolving the permalink. A date-token permalink throws on an entry with no
|
|
21
|
+
// valid date; the validate gate records that as a content problem rather than aborting the whole
|
|
22
|
+
// index build, so one bad entry degrades to a skip, not a crash. A failure is also excluded from
|
|
23
|
+
// the typed read, so every readable entry's frontmatter is the validator's normalized output.
|
|
42
24
|
const result = descriptor.validate(raw, body);
|
|
43
25
|
if (!result.ok) {
|
|
44
26
|
problems.push({ id, draft, errors: result.errors });
|
|
45
27
|
continue;
|
|
46
28
|
}
|
|
29
|
+
const { slug, date, permalink } = entryIdentity(descriptor, file.path, raw);
|
|
47
30
|
const summaryFieldValues = {};
|
|
48
31
|
for (const key of descriptor.summaryFields) {
|
|
49
32
|
if (key in result.data)
|
|
@@ -53,7 +36,7 @@ export function createContentIndex(files, descriptor) {
|
|
|
53
36
|
concept: descriptor.id,
|
|
54
37
|
id,
|
|
55
38
|
slug,
|
|
56
|
-
permalink
|
|
39
|
+
permalink,
|
|
57
40
|
title: asString(raw.title) ?? id,
|
|
58
41
|
date,
|
|
59
42
|
updated: asDate(raw.updated),
|
package/dist/delivery/data.d.ts
CHANGED
|
@@ -21,4 +21,3 @@ export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from '
|
|
|
21
21
|
export { jsonLdScript } from './json-ld.js';
|
|
22
22
|
export { permalink } from '../content/permalink.js';
|
|
23
23
|
export { buildSiteManifest, buildLinkResolver } from './manifest.js';
|
|
24
|
-
//# sourceMappingURL=data.d.ts.map
|
package/dist/delivery/feeds.d.ts
CHANGED
|
@@ -24,4 +24,3 @@ export interface FeedItem {
|
|
|
24
24
|
export declare function buildRssFeed(channel: FeedChannel, items: FeedItem[]): string;
|
|
25
25
|
/** Build a JSON Feed 1.1 document. */
|
|
26
26
|
export declare function buildJsonFeed(channel: FeedChannel, items: FeedItem[]): string;
|
|
27
|
-
//# sourceMappingURL=feeds.d.ts.map
|
package/dist/delivery/head.d.ts
CHANGED
package/dist/delivery/index.d.ts
CHANGED
|
@@ -10,4 +10,3 @@ export declare function buildSiteManifest<A extends CairnAdapter>(adapter: A, co
|
|
|
10
10
|
/** A resolver backed by the site index, for the build. A miss throws, so a dangling cairn: token
|
|
11
11
|
* fails the prerender (the build backstop). The preview uses manifestLinkResolver, which marks. */
|
|
12
12
|
export declare function buildLinkResolver(site: SiteIndex): LinkResolve;
|
|
13
|
-
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -19,4 +19,3 @@ export declare function readSeoFields(frontmatter: Record<string, unknown>): Seo
|
|
|
19
19
|
* bare path also anchors to the origin root; against a sub-path origin it would resolve relative to
|
|
20
20
|
* that path, per the WHATWG URL rules. */
|
|
21
21
|
export declare function resolveImageUrl(image: string, origin: string): string | undefined;
|
|
22
|
-
//# sourceMappingURL=seo-fields.d.ts.map
|
package/dist/delivery/seo.d.ts
CHANGED
|
@@ -2,4 +2,3 @@ import type { CairnAdapter, ConceptDescriptor } from '../content/types.js';
|
|
|
2
2
|
import type { SiteConfig } from '../nav/site-config.js';
|
|
3
3
|
/** Per-concept descriptors for a site, from its adapter content and its parsed site config. */
|
|
4
4
|
export declare function siteDescriptors(adapter: CairnAdapter, siteConfig: SiteConfig): ConceptDescriptor[];
|
|
5
|
-
//# sourceMappingURL=site-descriptors.d.ts.map
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
// cairn-cms: the one-call descriptor helper. A delivery site needs the same per-concept
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
import {
|
|
5
|
-
import { urlPolicyFrom } from '../nav/site-config.js';
|
|
1
|
+
// cairn-cms: the one-call descriptor helper. A delivery site needs the same per-concept descriptors
|
|
2
|
+
// the admin runtime uses; this delegates to the shared resolveConcepts so the pairing is one path, not
|
|
3
|
+
// tribal knowledge. The YAML URL policy stays the single source of truth.
|
|
4
|
+
import { resolveConcepts } from '../content/concepts.js';
|
|
6
5
|
/** Per-concept descriptors for a site, from its adapter content and its parsed site config. */
|
|
7
6
|
export function siteDescriptors(adapter, siteConfig) {
|
|
8
|
-
return
|
|
7
|
+
return resolveConcepts(adapter.content, siteConfig);
|
|
9
8
|
}
|
|
@@ -23,4 +23,3 @@ export type SiteIndexes<A extends CairnAdapter> = {
|
|
|
23
23
|
export declare function createSiteIndexes<const A extends CairnAdapter>(adapter: A, config: SiteConfig, globs: SiteGlobs<A>, opts?: {
|
|
24
24
|
validate?: boolean;
|
|
25
25
|
}): SiteIndexes<A>;
|
|
26
|
-
//# sourceMappingURL=site-indexes.d.ts.map
|
package/dist/email.d.ts
CHANGED
package/dist/env.d.ts
CHANGED
|
@@ -9,4 +9,3 @@ export interface GithubKeyEnv {
|
|
|
9
9
|
* installation) and the Worker's private-key secret. Throws when the secret is unset.
|
|
10
10
|
*/
|
|
11
11
|
export declare function appCredentials(backend: Pick<BackendConfig, 'appId' | 'installationId'>, env: GithubKeyEnv): AppCredentials;
|
|
12
|
-
//# sourceMappingURL=credentials.d.ts.map
|
package/dist/github/repo.d.ts
CHANGED
package/dist/github/signing.d.ts
CHANGED
package/dist/github/types.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -27,38 +27,9 @@ export type { ReferenceOptions } from './render/component-reference.js';
|
|
|
27
27
|
export { glyph } from './render/glyph.js';
|
|
28
28
|
export type { IconSet } from './render/glyph.js';
|
|
29
29
|
export { remarkDirectiveStamp } from './render/remark-directives.js';
|
|
30
|
-
export { rehypeDispatch, isElement, strProp, iconSpan, cardShell, headRow, markFirstList, } from './render/rehype-dispatch.js';
|
|
31
|
-
export type { MakeIcon } from './render/rehype-dispatch.js';
|
|
32
30
|
export { createRenderer } from './render/pipeline.js';
|
|
33
31
|
export type { RendererOptions } from './render/pipeline.js';
|
|
34
32
|
export type { RepoRef, RepoFile, CommitAuthor, AppCredentials } from './github/types.js';
|
|
35
33
|
export { CommitConflictError } from './github/types.js';
|
|
36
|
-
export { appJwt, installationToken, signingSelfTest } from './github/signing.js';
|
|
37
|
-
export { treeUrl, markdownFilesIn, listMarkdown, contentsUrl, readRaw, fileSha, commitFile, } from './github/repo.js';
|
|
38
|
-
export { appCredentials } from './github/credentials.js';
|
|
39
|
-
export type { GithubKeyEnv } from './github/credentials.js';
|
|
40
34
|
export { parseSiteConfig, urlPolicyFrom, extractMenu, setMenu, validateNavTree, MAX_NAV_NODES, NavValidationError, SiteConfigError, } from './nav/site-config.js';
|
|
41
35
|
export type { NavNode, SiteConfig } from './nav/site-config.js';
|
|
42
|
-
export { permalink } from './content/permalink.js';
|
|
43
|
-
export { createContentIndex, fromGlob } from './delivery/content-index.js';
|
|
44
|
-
export type { RawFile, ContentSummary, ContentEntry, ContentIndex, ContentProblem, } from './delivery/content-index.js';
|
|
45
|
-
export { createSiteIndex } from './delivery/site-index.js';
|
|
46
|
-
export type { SiteIndex, ConceptIndex } from './delivery/site-index.js';
|
|
47
|
-
export { createSiteIndexes } from './delivery/site-indexes.js';
|
|
48
|
-
export type { SiteIndexes, SiteGlobs } from './delivery/site-indexes.js';
|
|
49
|
-
export { deriveExcerpt, wordCount } from './delivery/excerpt.js';
|
|
50
|
-
export { buildRssFeed, buildJsonFeed } from './delivery/feeds.js';
|
|
51
|
-
export type { FeedChannel, FeedItem } from './delivery/feeds.js';
|
|
52
|
-
export { buildSitemap } from './delivery/sitemap.js';
|
|
53
|
-
export type { SitemapUrl } from './delivery/sitemap.js';
|
|
54
|
-
export { buildRobots } from './delivery/robots.js';
|
|
55
|
-
export { buildSeoMeta } from './delivery/seo.js';
|
|
56
|
-
export type { SeoInput, SeoMeta } from './delivery/seo.js';
|
|
57
|
-
export { readSeoFields, resolveImageUrl } from './delivery/seo-fields.js';
|
|
58
|
-
export type { SeoFields } from './delivery/seo-fields.js';
|
|
59
|
-
export { paginate } from './delivery/paginate.js';
|
|
60
|
-
export type { Page } from './delivery/paginate.js';
|
|
61
|
-
export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from './delivery/responses.js';
|
|
62
|
-
export { createPublicRoutes } from './sveltekit/public-routes.js';
|
|
63
|
-
export type { PublicRoutesDeps, ListData, TagData, TagIndexData, EntryData } from './sveltekit/public-routes.js';
|
|
64
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -21,30 +21,11 @@ export { buildComponentInsert } from './render/component-insert.js';
|
|
|
21
21
|
export { generateComponentReference } from './render/component-reference.js';
|
|
22
22
|
export { glyph } from './render/glyph.js';
|
|
23
23
|
export { remarkDirectiveStamp } from './render/remark-directives.js';
|
|
24
|
-
|
|
24
|
+
// The component-authoring helpers (iconSpan, cardShell, headRow, isElement, strAttr) live on the
|
|
25
|
+
// @glw907/cairn-cms/render subpath, not the root barrel. rehypeDispatch is deliberately not public:
|
|
26
|
+
// createRenderer is the one public render pipeline, so the safe plugin ordering is the only public
|
|
27
|
+
// path. See docs/superpowers/specs/2026-06-05-cairn-render-authoring-surface-design.md.
|
|
25
28
|
export { createRenderer } from './render/pipeline.js';
|
|
26
29
|
export { CommitConflictError } from './github/types.js';
|
|
27
|
-
export { appJwt, installationToken, signingSelfTest } from './github/signing.js';
|
|
28
|
-
export { treeUrl, markdownFilesIn, listMarkdown, contentsUrl, readRaw, fileSha, commitFile, } from './github/repo.js';
|
|
29
|
-
export { appCredentials } from './github/credentials.js';
|
|
30
30
|
// Nav tree and site-config helpers (Plan 06).
|
|
31
31
|
export { parseSiteConfig, urlPolicyFrom, extractMenu, setMenu, validateNavTree, MAX_NAV_NODES, NavValidationError, SiteConfigError, } from './nav/site-config.js';
|
|
32
|
-
// Public content delivery (public-delivery design): the query index, syndication, and
|
|
33
|
-
// discovery surface that sites read. Pure builders plus the one permalink resolver; the
|
|
34
|
-
// SvelteKit loaders live under the /sveltekit subpath.
|
|
35
|
-
export { permalink } from './content/permalink.js';
|
|
36
|
-
export { createContentIndex, fromGlob } from './delivery/content-index.js';
|
|
37
|
-
export { createSiteIndex } from './delivery/site-index.js';
|
|
38
|
-
export { createSiteIndexes } from './delivery/site-indexes.js';
|
|
39
|
-
export { deriveExcerpt, wordCount } from './delivery/excerpt.js';
|
|
40
|
-
export { buildRssFeed, buildJsonFeed } from './delivery/feeds.js';
|
|
41
|
-
export { buildSitemap } from './delivery/sitemap.js';
|
|
42
|
-
export { buildRobots } from './delivery/robots.js';
|
|
43
|
-
export { buildSeoMeta } from './delivery/seo.js';
|
|
44
|
-
export { readSeoFields, resolveImageUrl } from './delivery/seo-fields.js';
|
|
45
|
-
export { paginate } from './delivery/paginate.js';
|
|
46
|
-
// Root superset of the delivery route surface: a wrong guess from root for a route loader or a
|
|
47
|
-
// response helper now resolves. The CairnHead component stays out of root so the root barrel stays
|
|
48
|
-
// node-importable for the unit suite; it resolves from @glw907/cairn-cms/delivery/head.
|
|
49
|
-
export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from './delivery/responses.js';
|
|
50
|
-
export { createPublicRoutes } from './sveltekit/public-routes.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// cairn-cms: the component-authoring toolkit (@glw907/cairn-cms/render). A site authoring components
|
|
2
|
+
// through build(ctx) reaches for these hast builders and the string-attribute reader. Curated on
|
|
3
|
+
// purpose: the internal hast helpers (strProp, markFirstList, dataAttrProp) stay internal, and
|
|
4
|
+
// rehypeDispatch is deliberately omitted (createRenderer is the one public render pipeline).
|
|
5
|
+
export { iconSpan, cardShell, headRow, isElement, strAttr } from './rehype-dispatch.js';
|
|
@@ -11,4 +11,3 @@ export type ComponentInsert = {
|
|
|
11
11
|
/** Serialize a component's form values, then validate the result against its schema. Returns the
|
|
12
12
|
* markdown to insert at the cursor, or the field errors keyed by attribute key or slot name. */
|
|
13
13
|
export declare function buildComponentInsert(def: ComponentDef, values: ComponentValues): Promise<ComponentInsert>;
|
|
14
|
-
//# sourceMappingURL=component-insert.d.ts.map
|
|
@@ -8,4 +8,3 @@ export interface ReferenceOptions {
|
|
|
8
8
|
/** Build a self-contained markdown reference (the llms-full.txt shape) for a component registry, for
|
|
9
9
|
* authors and for pointing an LLM at one curated file. */
|
|
10
10
|
export declare function generateComponentReference(registry: ComponentRegistry, opts: ReferenceOptions): string;
|
|
11
|
-
//# sourceMappingURL=component-reference.d.ts.map
|
package/dist/render/glyph.d.ts
CHANGED
package/dist/render/index.d.ts
CHANGED
package/dist/render/pipeline.js
CHANGED
|
@@ -8,7 +8,7 @@ import rehypeSlug from 'rehype-slug';
|
|
|
8
8
|
import rehypeStringify from 'rehype-stringify';
|
|
9
9
|
import rehypeSanitize from 'rehype-sanitize';
|
|
10
10
|
import { VFile } from 'vfile';
|
|
11
|
-
import { buildSanitizeSchema, rehypeAnchorRel } from './sanitize-schema.js';
|
|
11
|
+
import { buildSanitizeSchema, rehypeAnchorRel, rehypeSinkGuard } from './sanitize-schema.js';
|
|
12
12
|
import { remarkDirectiveStamp } from './remark-directives.js';
|
|
13
13
|
import { remarkResolveCairnLinks, CAIRN_RESOLVE } from './resolve-links.js';
|
|
14
14
|
import { rehypeDispatch } from './rehype-dispatch.js';
|
|
@@ -33,6 +33,10 @@ export function createRenderer(registry = defineRegistry({ components: [] }), op
|
|
|
33
33
|
];
|
|
34
34
|
if (rel !== false)
|
|
35
35
|
rehypePlugins.push([rehypeAnchorRel, rel]);
|
|
36
|
+
// The sink guard runs last, over the fully-built tree, so it neutralizes a sink a component
|
|
37
|
+
// build() emitted after the floor. Gated by the same switch as the floor.
|
|
38
|
+
if (!options.unsafeDisableSanitize)
|
|
39
|
+
rehypePlugins.push(rehypeSinkGuard);
|
|
36
40
|
const processor = unified()
|
|
37
41
|
.use(remarkParse)
|
|
38
42
|
.use(remarkGfm)
|
|
@@ -70,6 +70,8 @@ export interface ComponentRegistry {
|
|
|
70
70
|
names: string[];
|
|
71
71
|
get(name: string): ComponentDef | undefined;
|
|
72
72
|
defaultIcon(name: string, role?: string): string | undefined;
|
|
73
|
+
/** The component's first `type:'icon'` attribute, or undefined when it declares none. */
|
|
74
|
+
iconField(name: string): AttributeField | undefined;
|
|
73
75
|
}
|
|
74
76
|
/** The hast property name carrying one declared attribute from stamp to dispatch, e.g. `tone`
|
|
75
77
|
* becomes `dataAttrTone`. The directive stamp writes it and the rehype dispatch reads it, so both
|
|
@@ -91,4 +93,3 @@ export interface ComponentValues {
|
|
|
91
93
|
/** Seed an empty {@link ComponentValues} from a component's schema: attribute defaults (or '' / false)
|
|
92
94
|
* and empty slot values ([] for repeatable, '' otherwise). */
|
|
93
95
|
export declare function emptyValues(def: ComponentDef): ComponentValues;
|
|
94
|
-
//# sourceMappingURL=registry.d.ts.map
|
package/dist/render/registry.js
CHANGED
|
@@ -4,17 +4,32 @@
|
|
|
4
4
|
export function dataAttrProp(key) {
|
|
5
5
|
return `dataAttr${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
6
6
|
}
|
|
7
|
+
/** A component's first `type:'icon'` attribute, or undefined when it declares none. Both the
|
|
8
|
+
* construction-time guard and the registry's `iconField` derive the icon field from this one
|
|
9
|
+
* predicate rather than spelling the `type === 'icon'` find twice. */
|
|
10
|
+
function findIconField(def) {
|
|
11
|
+
return def.attributes?.find((field) => field.type === 'icon');
|
|
12
|
+
}
|
|
7
13
|
/**
|
|
8
14
|
* Build a registry from a site's component definitions. The single source the render
|
|
9
15
|
* pipeline (directive stamp plus rehype dispatch) and the editor palette both read.
|
|
10
16
|
*/
|
|
11
17
|
export function defineRegistry({ components }) {
|
|
18
|
+
for (const c of components) {
|
|
19
|
+
if (c.defaultIconByRole && Object.keys(c.defaultIconByRole).length > 0 && !findIconField(c)) {
|
|
20
|
+
throw new Error(`cairn: component "${c.name}" sets defaultIconByRole but declares no type:'icon' attribute, so the default icon can never render`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
12
23
|
const byName = new Map(components.map((c) => [c.name, c]));
|
|
13
24
|
return {
|
|
14
25
|
defs: components,
|
|
15
26
|
names: components.map((c) => c.name),
|
|
16
27
|
get: (name) => byName.get(name),
|
|
17
28
|
defaultIcon: (name, role) => (role ? byName.get(name)?.defaultIconByRole?.[role] : undefined),
|
|
29
|
+
iconField: (name) => {
|
|
30
|
+
const def = byName.get(name);
|
|
31
|
+
return def ? findIconField(def) : undefined;
|
|
32
|
+
},
|
|
18
33
|
};
|
|
19
34
|
}
|
|
20
35
|
/** Seed an empty {@link ComponentValues} from a component's schema: attribute defaults (or '' / false)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Root, Element, ElementContent } from 'hast';
|
|
2
|
-
import { type ComponentRegistry } from './registry.js';
|
|
2
|
+
import { type ComponentContext, type ComponentRegistry } from './registry.js';
|
|
3
3
|
export declare function isElement(node: ElementContent | undefined): node is Element;
|
|
4
|
+
/** Read a declared string attribute off the component context, returning undefined for a boolean or
|
|
5
|
+
* absent value. Replaces the `typeof ctx.attributes[key] === 'string'` narrowing a build repeats. */
|
|
6
|
+
export declare function strAttr(ctx: ComponentContext, key: string): string | undefined;
|
|
4
7
|
export declare function strProp(node: Element, name: string): string | undefined;
|
|
5
8
|
/** Wrap a pre-built glyph in an ec-icon span; secondary role adds the modifier. */
|
|
6
9
|
export declare function iconSpan(glyphEl: Element, role?: string): Element;
|
|
@@ -8,11 +11,11 @@ export declare function iconSpan(glyphEl: Element, role?: string): Element;
|
|
|
8
11
|
export type MakeIcon = (name: string, role?: string) => Element;
|
|
9
12
|
/** Section wrapper: `<section class=…><div class="card-body">…</div></section>`. */
|
|
10
13
|
export declare function cardShell(classes: string[], body: ElementContent[]): Element;
|
|
11
|
-
/** Card head row: `<div class="ec-head">[icon]<
|
|
12
|
-
* Pass the title's inline children
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
export declare function headRow(title: ElementContent[], icon?: Element): Element;
|
|
14
|
+
/** Card head row: `<div class="ec-head">[icon]<hN class="card-title">{title}</hN></div>`.
|
|
15
|
+
* Pass the title's inline children, an optional pre-built icon element, and an optional heading
|
|
16
|
+
* level (default 2). This factors the icon-plus-heading head that a titled component build would
|
|
17
|
+
* otherwise rebuild by hand (the shape the removed `splitHead` produced). */
|
|
18
|
+
export declare function headRow(title: ElementContent[], icon?: Element, level?: number): Element;
|
|
16
19
|
/** Tag the first <ul> among children with `ec-grid` and strip its whitespace-only
|
|
17
20
|
* text nodes so the bare list serializes without newlines. Returns that <ul>. */
|
|
18
21
|
export declare function markFirstList(children: ElementContent[]): Element | undefined;
|
|
@@ -23,4 +26,3 @@ export declare function markFirstList(children: ElementContent[]): Element | und
|
|
|
23
26
|
* `data-rise` while dropping `style`. Nested primitives never get it. Non-primitive
|
|
24
27
|
* content (lede, intro paragraphs, the page-toc nav) passes through untouched. */
|
|
25
28
|
export declare function rehypeDispatch(registry: ComponentRegistry, stagger?: boolean): (tree: Root) => void;
|
|
26
|
-
//# sourceMappingURL=rehype-dispatch.d.ts.map
|
|
@@ -3,6 +3,12 @@ import { dataAttrProp } from './registry.js';
|
|
|
3
3
|
export function isElement(node) {
|
|
4
4
|
return !!node && node.type === 'element';
|
|
5
5
|
}
|
|
6
|
+
/** Read a declared string attribute off the component context, returning undefined for a boolean or
|
|
7
|
+
* absent value. Replaces the `typeof ctx.attributes[key] === 'string'` narrowing a build repeats. */
|
|
8
|
+
export function strAttr(ctx, key) {
|
|
9
|
+
const value = ctx.attributes[key];
|
|
10
|
+
return typeof value === 'string' ? value : undefined;
|
|
11
|
+
}
|
|
6
12
|
// hast Properties values are PropertyValue (string | number | boolean | array | null).
|
|
7
13
|
// Directive markers (dataPrimitive/dataRole/dataAttr<Key>) are always stamped as strings;
|
|
8
14
|
// this reads them back with that guarantee instead of casting at each call site.
|
|
@@ -19,15 +25,15 @@ export function iconSpan(glyphEl, role) {
|
|
|
19
25
|
export function cardShell(classes, body) {
|
|
20
26
|
return h('section', { className: classes }, [h('div', { className: ['card-body'] }, body)]);
|
|
21
27
|
}
|
|
22
|
-
/** Card head row: `<div class="ec-head">[icon]<
|
|
23
|
-
* Pass the title's inline children
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
export function headRow(title, icon) {
|
|
28
|
+
/** Card head row: `<div class="ec-head">[icon]<hN class="card-title">{title}</hN></div>`.
|
|
29
|
+
* Pass the title's inline children, an optional pre-built icon element, and an optional heading
|
|
30
|
+
* level (default 2). This factors the icon-plus-heading head that a titled component build would
|
|
31
|
+
* otherwise rebuild by hand (the shape the removed `splitHead` produced). */
|
|
32
|
+
export function headRow(title, icon, level = 2) {
|
|
27
33
|
const children = [];
|
|
28
34
|
if (icon)
|
|
29
35
|
children.push(icon);
|
|
30
|
-
children.push(h(
|
|
36
|
+
children.push(h(`h${level}`, { className: ['card-title'] }, title));
|
|
31
37
|
return h('div', { className: ['ec-head'] }, children);
|
|
32
38
|
}
|
|
33
39
|
/** Tag the first <ul> among children with `ec-grid` and strip its whitespace-only
|
|
@@ -60,7 +60,7 @@ export function remarkDirectiveStamp(registry) {
|
|
|
60
60
|
const def = registry.get(node.name);
|
|
61
61
|
const attrs = node.attributes ?? {};
|
|
62
62
|
const role = attrs.role || undefined;
|
|
63
|
-
const iconField =
|
|
63
|
+
const iconField = registry.iconField(node.name);
|
|
64
64
|
const iconKey = iconField?.key ?? 'icon';
|
|
65
65
|
let icon = attrs[iconKey] || undefined;
|
|
66
66
|
if (!icon && role)
|
|
@@ -5,4 +5,3 @@ export declare const CAIRN_RESOLVE = "cairnResolve";
|
|
|
5
5
|
* pass through. A missing target is marked with the cairn-broken-link class (the resolver returns
|
|
6
6
|
* undefined) or, when the resolver throws, the error propagates and fails the build. */
|
|
7
7
|
export declare function remarkResolveCairnLinks(): (tree: unknown, file: VFile) => void;
|
|
8
|
-
//# sourceMappingURL=resolve-links.d.ts.map
|
|
@@ -18,4 +18,17 @@ export declare function buildSanitizeSchema(registry: ComponentRegistry, extend?
|
|
|
18
18
|
* `anchorRel` option (default `noopener noreferrer`); a site can override it or disable it entirely.
|
|
19
19
|
*/
|
|
20
20
|
export declare function rehypeAnchorRel(rel: string): (tree: Root) => void;
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Post-dispatch safety floor over the fully-built tree. The pre-dispatch rehype-sanitize floor
|
|
23
|
+
* cleans author content, but a component build() runs after it and can route a raw author
|
|
24
|
+
* attribute value into a sink. This guard runs last and neutralizes those sinks on every element
|
|
25
|
+
* no matter which plugin or which build() produced it: an unsafe URL scheme in a URL-bearing
|
|
26
|
+
* attribute, an inline on* event handler, or an inline style (stripped wholesale, matching the
|
|
27
|
+
* floor and cairn's class-driven styling). It is gated by the same unsafeDisableSanitize switch as
|
|
28
|
+
* the floor.
|
|
29
|
+
*
|
|
30
|
+
* The guard's boundary is the URL scheme check plus the on* and style strip. It does not remove a
|
|
31
|
+
* build()-emitted raw script, style, or iframe srcdoc element node. A build() that emits those is
|
|
32
|
+
* running site-developer code, and author markdown is cleaned by the pre-dispatch floor.
|
|
33
|
+
*/
|
|
34
|
+
export declare function rehypeSinkGuard(): (tree: Root) => void;
|