@glw907/cairn-cms 0.21.0 → 0.24.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/dist/content/concepts.d.ts.map +1 -1
- package/dist/content/concepts.js +7 -0
- package/dist/content/frontmatter.d.ts +8 -0
- package/dist/content/frontmatter.d.ts.map +1 -1
- package/dist/content/frontmatter.js +19 -0
- package/dist/content/types.d.ts +6 -0
- package/dist/content/types.d.ts.map +1 -1
- package/dist/content/validate.d.ts.map +1 -1
- package/dist/content/validate.js +8 -1
- package/dist/delivery/content-index.d.ts +7 -0
- package/dist/delivery/content-index.d.ts.map +1 -1
- package/dist/delivery/content-index.js +7 -0
- package/dist/delivery/head.d.ts +2 -0
- package/dist/delivery/head.d.ts.map +1 -0
- package/dist/delivery/head.js +4 -0
- package/dist/delivery/index.d.ts +0 -1
- package/dist/delivery/index.d.ts.map +1 -1
- package/dist/delivery/index.js +0 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/render/pipeline.d.ts +4 -0
- package/dist/render/pipeline.d.ts.map +1 -1
- package/dist/render/pipeline.js +3 -1
- package/dist/render/registry.d.ts +1 -1
- package/dist/render/registry.d.ts.map +1 -1
- package/dist/render/rehype-dispatch.d.ts +5 -0
- package/dist/render/rehype-dispatch.d.ts.map +1 -1
- package/dist/render/rehype-dispatch.js +12 -1
- package/dist/render/remark-directives.d.ts.map +1 -1
- package/dist/render/remark-directives.js +15 -6
- package/dist/render/sanitize-schema.d.ts +4 -3
- package/dist/render/sanitize-schema.d.ts.map +1 -1
- package/dist/render/sanitize-schema.js +6 -5
- package/dist/sveltekit/public-routes.d.ts +1 -0
- package/dist/sveltekit/public-routes.d.ts.map +1 -1
- package/dist/sveltekit/public-routes.js +1 -1
- package/package.json +6 -1
- package/src/lib/content/concepts.ts +9 -0
- package/src/lib/content/frontmatter.ts +21 -0
- package/src/lib/content/types.ts +6 -0
- package/src/lib/content/validate.ts +6 -1
- package/src/lib/delivery/content-index.ts +13 -0
- package/src/lib/delivery/head.ts +4 -0
- package/src/lib/delivery/index.ts +0 -1
- package/src/lib/index.ts +7 -0
- package/src/lib/render/pipeline.ts +6 -1
- package/src/lib/render/registry.ts +1 -1
- package/src/lib/render/rehype-dispatch.ts +12 -1
- package/src/lib/render/remark-directives.ts +16 -5
- package/src/lib/render/sanitize-schema.ts +6 -5
- package/src/lib/sveltekit/public-routes.ts +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"concepts.d.ts","sourceRoot":"","sources":["../../src/lib/content/concepts.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAElG;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAGjE,CAAC;AAeF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC,EAClD,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAM,EAC5D,OAAO,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAmB,GAC/D,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"concepts.d.ts","sourceRoot":"","sources":["../../src/lib/content/concepts.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAElG;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAGjE,CAAC;AAeF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC,EAClD,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAM,EAC5D,OAAO,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAmB,GAC/D,iBAAiB,EAAE,CA0BrB;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CACzB,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,EAAE,EAAE,MAAM,GACT,iBAAiB,GAAG,SAAS,CAE/B"}
|
package/dist/content/concepts.js
CHANGED
|
@@ -29,6 +29,12 @@ export function normalizeConcepts(content, urlPolicy = {}, routing = CONCEPT_ROU
|
|
|
29
29
|
for (const [id, config] of Object.entries(content)) {
|
|
30
30
|
if (!config)
|
|
31
31
|
continue;
|
|
32
|
+
const summaryFields = config.summaryFields ?? [];
|
|
33
|
+
const declared = new Set(config.schema.fields.map((field) => field.name));
|
|
34
|
+
const undeclared = summaryFields.find((key) => !declared.has(key));
|
|
35
|
+
if (undeclared !== undefined) {
|
|
36
|
+
throw new Error(`cairn: concept "${id}" summaryFields key "${undeclared}" is not a declared field`);
|
|
37
|
+
}
|
|
32
38
|
const policy = urlPolicy[id] ?? {};
|
|
33
39
|
descriptors.push({
|
|
34
40
|
id,
|
|
@@ -38,6 +44,7 @@ export function normalizeConcepts(content, urlPolicy = {}, routing = CONCEPT_ROU
|
|
|
38
44
|
permalink: policy.permalink ?? defaultPermalink(id),
|
|
39
45
|
datePrefix: policy.datePrefix ?? 'day',
|
|
40
46
|
fields: config.schema.fields,
|
|
47
|
+
summaryFields,
|
|
41
48
|
validate: config.schema.validate,
|
|
42
49
|
});
|
|
43
50
|
}
|
|
@@ -8,6 +8,14 @@ export declare function frontmatterFromForm(fields: FrontmatterField[], form: Fo
|
|
|
8
8
|
* slicing the ISO string avoids a local-timezone shift.
|
|
9
9
|
*/
|
|
10
10
|
export declare function dateInputValue(value: unknown): string;
|
|
11
|
+
/**
|
|
12
|
+
* True when `s` is a canonical zero-padded `YYYY-MM-DD` string naming a real calendar date.
|
|
13
|
+
* Rejects a wrong format, an impossible month or day, and a JS date-rollover such as
|
|
14
|
+
* `2026-02-30` (which `Date` would silently roll forward to March 2). The committed form a
|
|
15
|
+
* date field carries is exactly this canonical shape, which is what the form and
|
|
16
|
+
* `dateInputValue` emit, so a value outside it is a hand-edit or odd-YAML error.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isCalendarDate(s: string): boolean;
|
|
11
19
|
/** Reassemble a markdown file from frontmatter and body for committing. */
|
|
12
20
|
export declare function serializeMarkdown(frontmatter: object, body: string): string;
|
|
13
21
|
/** Parse a markdown file into its frontmatter and body: the read-side inverse of serialize. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/lib/content/frontmatter.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,gFAAgF;AAChF,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,gBAAgB,EAAE,EAC1B,IAAI,EAAE,QAAQ,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4BzB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASrD;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,+FAA+F;AAC/F,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG;IAC7C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;CACd,CAGA"}
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/lib/content/frontmatter.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,gFAAgF;AAChF,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,gBAAgB,EAAE,EAC1B,IAAI,EAAE,QAAQ,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4BzB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASrD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAYjD;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,+FAA+F;AAC/F,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG;IAC7C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;CACd,CAGA"}
|
|
@@ -47,6 +47,25 @@ export function dateInputValue(value) {
|
|
|
47
47
|
}
|
|
48
48
|
return '';
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* True when `s` is a canonical zero-padded `YYYY-MM-DD` string naming a real calendar date.
|
|
52
|
+
* Rejects a wrong format, an impossible month or day, and a JS date-rollover such as
|
|
53
|
+
* `2026-02-30` (which `Date` would silently roll forward to March 2). The committed form a
|
|
54
|
+
* date field carries is exactly this canonical shape, which is what the form and
|
|
55
|
+
* `dateInputValue` emit, so a value outside it is a hand-edit or odd-YAML error.
|
|
56
|
+
*/
|
|
57
|
+
export function isCalendarDate(s) {
|
|
58
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
|
|
59
|
+
if (!match)
|
|
60
|
+
return false;
|
|
61
|
+
const year = Number(match[1]);
|
|
62
|
+
const month = Number(match[2]);
|
|
63
|
+
const day = Number(match[3]);
|
|
64
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
65
|
+
return (date.getUTCFullYear() === year &&
|
|
66
|
+
date.getUTCMonth() === month - 1 &&
|
|
67
|
+
date.getUTCDate() === day);
|
|
68
|
+
}
|
|
50
69
|
/** Reassemble a markdown file from frontmatter and body for committing. */
|
|
51
70
|
export function serializeMarkdown(frontmatter, body) {
|
|
52
71
|
return matter.stringify(body, frontmatter);
|
package/dist/content/types.d.ts
CHANGED
|
@@ -94,6 +94,9 @@ export interface ConceptConfig<S extends ConceptSchema = ConceptSchema> {
|
|
|
94
94
|
label?: string;
|
|
95
95
|
/** The concept's schema: the form projection, the generated validator, and the inferred type. */
|
|
96
96
|
schema: S;
|
|
97
|
+
/** Frontmatter keys to surface on each `ContentSummary.fields`, so a list card reads an authored
|
|
98
|
+
* field without a per-entry detail read. Each key should also be declared in `schema`. */
|
|
99
|
+
summaryFields?: string[];
|
|
97
100
|
}
|
|
98
101
|
/**
|
|
99
102
|
* A concept's URL policy, set per concept in the YAML site-config (not the adapter). `permalink` is
|
|
@@ -194,6 +197,9 @@ export interface ConceptDescriptor {
|
|
|
194
197
|
/** Filename date-prefix granularity for a dated concept; resolved by `normalizeConcepts`. */
|
|
195
198
|
datePrefix: DatePrefix;
|
|
196
199
|
fields: FrontmatterField[];
|
|
200
|
+
/** Frontmatter keys the index copies onto each summary's `fields` record. `normalizeConcepts`
|
|
201
|
+
* resolves it to `[]` when a concept omits `summaryFields`. */
|
|
202
|
+
summaryFields: string[];
|
|
197
203
|
validate(frontmatter: Record<string, unknown>, body: string): ValidationResult;
|
|
198
204
|
}
|
|
199
205
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/content/types.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,0GAA0G;AAC1G,UAAU,SAAS;IACjB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,gCAAgC;AAChC,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;yEACqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,+BAA+B;AAC/B,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,IAAI,EAAE,UAAU,CAAC;IACjB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,iCAAiC;AACjC,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AACD,sCAAsC;AACtC,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,IAAI,EAAE,SAAS,CAAC;CACjB;AACD,sEAAsE;AACtE,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AACD,iEAAiE;AACjE,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,aAAa,GACb,SAAS,GACT,YAAY,GACZ,SAAS,GACT,aAAa,CAAC;AAElB;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC3C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa;IACpE,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iGAAiG;IACjG,MAAM,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/content/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/lib/content/validate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGrE;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,gBAAgB,EAAE,EAC1B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/lib/content/validate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGrE;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,gBAAgB,EAAE,EAC1B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,gBAAgB,CAoClB"}
|
package/dist/content/validate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dateInputValue } from './frontmatter.js';
|
|
1
|
+
import { dateInputValue, isCalendarDate } from './frontmatter.js';
|
|
2
2
|
/**
|
|
3
3
|
* Validate raw frontmatter against a field list. Required text and date fields must be
|
|
4
4
|
* non-empty; required tag fields must be non-empty lists. A present boolean coerces to `true`
|
|
@@ -27,6 +27,11 @@ export function validateFields(fields, frontmatter) {
|
|
|
27
27
|
const list = Array.isArray(value) ? value.map(String) : [];
|
|
28
28
|
if (field.required && list.length === 0)
|
|
29
29
|
errors[field.name] = `${field.label} is required`;
|
|
30
|
+
else if (field.type === 'tags') {
|
|
31
|
+
const unknown = list.find((tag) => !field.options.includes(tag));
|
|
32
|
+
if (unknown !== undefined)
|
|
33
|
+
errors[field.name] = `${field.label} contains an unknown value: ${unknown}`;
|
|
34
|
+
}
|
|
30
35
|
if (list.length > 0)
|
|
31
36
|
data[field.name] = list;
|
|
32
37
|
break;
|
|
@@ -35,6 +40,8 @@ export function validateFields(fields, frontmatter) {
|
|
|
35
40
|
const text = value instanceof Date ? dateInputValue(value) : typeof value === 'string' ? value.trim() : '';
|
|
36
41
|
if (field.required && text === '')
|
|
37
42
|
errors[field.name] = `${field.label} is required`;
|
|
43
|
+
else if (text !== '' && !isCalendarDate(text))
|
|
44
|
+
errors[field.name] = `${field.label} must be a valid date (YYYY-MM-DD)`;
|
|
38
45
|
if (text !== '')
|
|
39
46
|
data[field.name] = text;
|
|
40
47
|
break;
|
|
@@ -6,6 +6,9 @@ export interface RawFile {
|
|
|
6
6
|
}
|
|
7
7
|
/** The cheap, plain-data view of one entry, for lists, feeds, and the sitemap. */
|
|
8
8
|
export interface ContentSummary {
|
|
9
|
+
/** The descriptor id this entry belongs to, e.g. "posts". Lets a list or page branch per
|
|
10
|
+
* concept without re-deriving it from a proxy like `entry.date`. */
|
|
11
|
+
concept: string;
|
|
9
12
|
id: string;
|
|
10
13
|
slug: string;
|
|
11
14
|
permalink: string;
|
|
@@ -16,6 +19,10 @@ export interface ContentSummary {
|
|
|
16
19
|
excerpt: string;
|
|
17
20
|
wordCount: number;
|
|
18
21
|
draft: boolean;
|
|
22
|
+
/** The frontmatter keys the descriptor nominated via `summaryFields`, read off the validated,
|
|
23
|
+
* normalized frontmatter. Held in a separate record so a nominated key cannot collide with a
|
|
24
|
+
* typed summary field. Empty when the concept declares no `summaryFields`. */
|
|
25
|
+
fields: Record<string, unknown>;
|
|
19
26
|
}
|
|
20
27
|
/** The detail view: a summary plus the frontmatter and the body to render. The frontmatter
|
|
21
28
|
* type defaults to `Record<string, unknown>`; the typed-reads pass infers it from the concept
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-index.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/content-index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,yFAAyF;AACzF,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"content-index.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/content-index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,yFAAyF;AACzF,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B;yEACqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf;;mFAE+E;IAC/E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;wEAEwE;AACxE,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,SAAQ,cAAc;IAC/E,WAAW,EAAE,CAAC,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,qCAAqC;AACrC,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACvD,GAAG,CAAC,IAAI,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,cAAc,EAAE,CAAC;IAC1D,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAC9C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,cAAc,EAAE,CAAC;IACzE,OAAO,IAAI;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG;QAAE,KAAK,CAAC,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,cAAc,CAAA;KAAE,CAAC;IACzE,sFAAsF;IACtF,QAAQ,IAAI,cAAc,EAAE,CAAC;CAC9B;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,EAAE,CAElE;AAqBD,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5D,KAAK,EAAE,OAAO,EAAE,EAChB,UAAU,EAAE,iBAAiB,GAC5B,YAAY,CAAC,CAAC,CAAC,CA4EjB"}
|
|
@@ -44,7 +44,13 @@ export function createContentIndex(files, descriptor) {
|
|
|
44
44
|
problems.push({ id, draft, errors: result.errors });
|
|
45
45
|
continue;
|
|
46
46
|
}
|
|
47
|
+
const summaryFieldValues = {};
|
|
48
|
+
for (const key of descriptor.summaryFields) {
|
|
49
|
+
if (key in result.data)
|
|
50
|
+
summaryFieldValues[key] = result.data[key];
|
|
51
|
+
}
|
|
47
52
|
entries.push({
|
|
53
|
+
concept: descriptor.id,
|
|
48
54
|
id,
|
|
49
55
|
slug,
|
|
50
56
|
permalink: permalink(descriptor, { id, slug, date }),
|
|
@@ -55,6 +61,7 @@ export function createContentIndex(files, descriptor) {
|
|
|
55
61
|
excerpt: deriveExcerpt(body, { description: asString(raw.description) }),
|
|
56
62
|
wordCount: wordCount(body),
|
|
57
63
|
draft,
|
|
64
|
+
fields: summaryFieldValues,
|
|
58
65
|
frontmatter: result.data,
|
|
59
66
|
body,
|
|
60
67
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/head.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// cairn-cms: the delivery head component entry (@glw907/cairn-cms/delivery/head). CairnHead lives
|
|
2
|
+
// behind its own export so importing a delivery data helper from /delivery never pulls a .svelte
|
|
3
|
+
// module into the graph. A node-environment data import then needs no Svelte plugin.
|
|
4
|
+
export { default as CairnHead } from './CairnHead.svelte';
|
package/dist/delivery/index.d.ts
CHANGED
|
@@ -23,5 +23,4 @@ export { permalink } from '../content/permalink.js';
|
|
|
23
23
|
export { buildSiteManifest, buildLinkResolver } from './manifest.js';
|
|
24
24
|
export { createPublicRoutes } from '../sveltekit/public-routes.js';
|
|
25
25
|
export type { PublicRoutesDeps, ListData, TagData, TagIndexData, EntryData, } from '../sveltekit/public-routes.js';
|
|
26
|
-
export { default as CairnHead } from './CairnHead.svelte';
|
|
27
26
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC9G,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,YAAY,EACV,gBAAgB,EAChB,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,SAAS,GACV,MAAM,+BAA+B,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/delivery/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC9G,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,YAAY,EACV,gBAAgB,EAChB,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,SAAS,GACV,MAAM,+BAA+B,CAAC"}
|
package/dist/delivery/index.js
CHANGED
|
@@ -19,4 +19,3 @@ export { jsonLdScript } from './json-ld.js';
|
|
|
19
19
|
export { permalink } from '../content/permalink.js';
|
|
20
20
|
export { buildSiteManifest, buildLinkResolver } from './manifest.js';
|
|
21
21
|
export { createPublicRoutes } from '../sveltekit/public-routes.js';
|
|
22
|
-
export { default as CairnHead } from './CairnHead.svelte';
|
package/dist/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export type { ReferenceOptions } from './render/component-reference.js';
|
|
|
26
26
|
export { glyph } from './render/glyph.js';
|
|
27
27
|
export type { IconSet } from './render/glyph.js';
|
|
28
28
|
export { remarkDirectiveStamp } from './render/remark-directives.js';
|
|
29
|
-
export { rehypeDispatch, isElement, strProp, iconSpan, cardShell, markFirstList, } from './render/rehype-dispatch.js';
|
|
29
|
+
export { rehypeDispatch, isElement, strProp, iconSpan, cardShell, headRow, markFirstList, } from './render/rehype-dispatch.js';
|
|
30
30
|
export type { MakeIcon } from './render/rehype-dispatch.js';
|
|
31
31
|
export { createRenderer } from './render/pipeline.js';
|
|
32
32
|
export type { RendererOptions } from './render/pipeline.js';
|
|
@@ -57,4 +57,7 @@ export { readSeoFields, resolveImageUrl } from './delivery/seo-fields.js';
|
|
|
57
57
|
export type { SeoFields } from './delivery/seo-fields.js';
|
|
58
58
|
export { paginate } from './delivery/paginate.js';
|
|
59
59
|
export type { Page } from './delivery/paginate.js';
|
|
60
|
+
export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from './delivery/responses.js';
|
|
61
|
+
export { createPublicRoutes } from './sveltekit/public-routes.js';
|
|
62
|
+
export type { PublicRoutesDeps, ListData, TagData, TagIndexData, EntryData } from './sveltekit/public-routes.js';
|
|
60
63
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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
|
@@ -21,7 +21,7 @@ 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
|
-
export { rehypeDispatch, isElement, strProp, iconSpan, cardShell, markFirstList, } from './render/rehype-dispatch.js';
|
|
24
|
+
export { rehypeDispatch, isElement, strProp, iconSpan, cardShell, headRow, markFirstList, } from './render/rehype-dispatch.js';
|
|
25
25
|
export { createRenderer } from './render/pipeline.js';
|
|
26
26
|
export { CommitConflictError } from './github/types.js';
|
|
27
27
|
export { appJwt, installationToken, signingSelfTest } from './github/signing.js';
|
|
@@ -43,3 +43,8 @@ export { buildRobots } from './delivery/robots.js';
|
|
|
43
43
|
export { buildSeoMeta } from './delivery/seo.js';
|
|
44
44
|
export { readSeoFields, resolveImageUrl } from './delivery/seo-fields.js';
|
|
45
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';
|
|
@@ -16,6 +16,10 @@ export interface RendererOptions {
|
|
|
16
16
|
* vector the floor closes, so it is only for a site whose content is fully developer-controlled.
|
|
17
17
|
* It is a code-level adapter decision, never an editor-facing setting. */
|
|
18
18
|
unsafeDisableSanitize?: boolean;
|
|
19
|
+
/** The `rel` value forced on every `target="_blank"` anchor, applied last so it also covers
|
|
20
|
+
* component-built anchors. Defaults to `'noopener noreferrer'`. Set a different string to change
|
|
21
|
+
* it, or `false` to disable the injection (a site that owns its own anchor hardening). */
|
|
22
|
+
anchorRel?: string | false;
|
|
19
23
|
}
|
|
20
24
|
/** Compose a site's render pipeline from its component registry: directive syntax to
|
|
21
25
|
* stamped markers to registry-built hast. Returns `renderMarkdown` plus the remark/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/lib/render/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAStD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAMjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC9B;;0FAEsF;IACtF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;wCAGoC;IACpC,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C;;+EAE2E;IAC3E,qBAAqB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/lib/render/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAStD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAMjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC9B;;0FAEsF;IACtF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;wCAGoC;IACpC,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C;;+EAE2E;IAC3E,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;+FAE2F;IAC3F,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC5B;AAED;;uFAEuF;AACvF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,GAAE,eAAoB;;;8BA0BrD,MAAM,SAAQ;QAAE,OAAO,CAAC,EAAE,WAAW,CAAA;KAAE,KAAQ,OAAO,CAAC,MAAM,CAAC;EAKjG"}
|
package/dist/render/pipeline.js
CHANGED
|
@@ -23,13 +23,15 @@ export function createRenderer(registry, options = {}) {
|
|
|
23
23
|
const floor = options.unsafeDisableSanitize
|
|
24
24
|
? []
|
|
25
25
|
: [[rehypeSanitize, buildSanitizeSchema(registry, options.sanitizeSchema)]];
|
|
26
|
+
const rel = options.anchorRel ?? 'noopener noreferrer';
|
|
26
27
|
const rehypePlugins = [
|
|
27
28
|
rehypeRaw,
|
|
28
29
|
...floor,
|
|
29
30
|
[rehypeDispatch, registry, options.stagger],
|
|
30
31
|
rehypeSlug,
|
|
31
|
-
rehypeAnchorRel,
|
|
32
32
|
];
|
|
33
|
+
if (rel !== false)
|
|
34
|
+
rehypePlugins.push([rehypeAnchorRel, rel]);
|
|
33
35
|
const processor = unified()
|
|
34
36
|
.use(remarkParse)
|
|
35
37
|
.use(remarkGfm)
|
|
@@ -12,7 +12,7 @@ export interface AttributeField {
|
|
|
12
12
|
/** Initial value; a string for text/select/icon, a boolean for boolean. */
|
|
13
13
|
default?: string | boolean;
|
|
14
14
|
/** Allowed values for `type: 'select'`. */
|
|
15
|
-
options?: string[];
|
|
15
|
+
options?: readonly string[];
|
|
16
16
|
/** Helper text shown under the field. */
|
|
17
17
|
help?: string;
|
|
18
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/lib/render/registry.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAEpD,+EAA+E;AAC/E,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE/D,iGAAiG;AACjG,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/lib/render/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,11 @@ export declare function iconSpan(glyphEl: Element, role?: string): Element;
|
|
|
8
8
|
export type MakeIcon = (name: string, role?: string) => Element;
|
|
9
9
|
/** Section wrapper: `<section class=…><div class="card-body">…</div></section>`. */
|
|
10
10
|
export declare function cardShell(classes: string[], body: ElementContent[]): Element;
|
|
11
|
+
/** Card head row: `<div class="ec-head">[icon]<h2 class="card-title">{title}</h2></div>`.
|
|
12
|
+
* Pass the title's inline children and an optional pre-built icon element, the way `cardShell`
|
|
13
|
+
* takes already-built body content. This factors the icon-plus-heading head that a titled
|
|
14
|
+
* component build would otherwise rebuild by hand (the shape the removed `splitHead` produced). */
|
|
15
|
+
export declare function headRow(title: ElementContent[], icon?: Element): Element;
|
|
11
16
|
/** Tag the first <ul> among children with `ec-grid` and strip its whitespace-only
|
|
12
17
|
* text nodes so the bare list serializes without newlines. Returns that <ul>. */
|
|
13
18
|
export declare function markFirstList(children: ElementContent[]): Element | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rehype-dispatch.d.ts","sourceRoot":"","sources":["../../src/lib/render/rehype-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAE1D,OAAO,EAA0D,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE/G,wBAAgB,SAAS,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,IAAI,OAAO,CAE3E;AAKD,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGvE;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAGjE;AAED,kFAAkF;AAClF,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;AAEhE,oFAAoF;AACpF,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,OAAO,CAE5E;AAED;kFACkF;AAClF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,GAAG,SAAS,CAS7E;AAqFD;;;;;mFAKmF;AACnF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,OAAO,IACnE,MAAM,IAAI,UAYnB"}
|
|
1
|
+
{"version":3,"file":"rehype-dispatch.d.ts","sourceRoot":"","sources":["../../src/lib/render/rehype-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAE1D,OAAO,EAA0D,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE/G,wBAAgB,SAAS,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,IAAI,OAAO,CAE3E;AAKD,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGvE;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAGjE;AAED,kFAAkF;AAClF,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;AAEhE,oFAAoF;AACpF,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,OAAO,CAE5E;AAED;;;oGAGoG;AACpG,wBAAgB,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAKxE;AAED;kFACkF;AAClF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,GAAG,SAAS,CAS7E;AAqFD;;;;;mFAKmF;AACnF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,OAAO,IACnE,MAAM,IAAI,UAYnB"}
|
|
@@ -4,7 +4,7 @@ export function isElement(node) {
|
|
|
4
4
|
return !!node && node.type === 'element';
|
|
5
5
|
}
|
|
6
6
|
// hast Properties values are PropertyValue (string | number | boolean | array | null).
|
|
7
|
-
// Directive markers (
|
|
7
|
+
// Directive markers (dataPrimitive/dataRole/dataAttr<Key>) are always stamped as strings;
|
|
8
8
|
// this reads them back with that guarantee instead of casting at each call site.
|
|
9
9
|
export function strProp(node, name) {
|
|
10
10
|
const value = node.properties?.[name];
|
|
@@ -19,6 +19,17 @@ export function iconSpan(glyphEl, role) {
|
|
|
19
19
|
export function cardShell(classes, body) {
|
|
20
20
|
return h('section', { className: classes }, [h('div', { className: ['card-body'] }, body)]);
|
|
21
21
|
}
|
|
22
|
+
/** Card head row: `<div class="ec-head">[icon]<h2 class="card-title">{title}</h2></div>`.
|
|
23
|
+
* Pass the title's inline children and an optional pre-built icon element, the way `cardShell`
|
|
24
|
+
* takes already-built body content. This factors the icon-plus-heading head that a titled
|
|
25
|
+
* component build would otherwise rebuild by hand (the shape the removed `splitHead` produced). */
|
|
26
|
+
export function headRow(title, icon) {
|
|
27
|
+
const children = [];
|
|
28
|
+
if (icon)
|
|
29
|
+
children.push(icon);
|
|
30
|
+
children.push(h('h2', { className: ['card-title'] }, title));
|
|
31
|
+
return h('div', { className: ['ec-head'] }, children);
|
|
32
|
+
}
|
|
22
33
|
/** Tag the first <ul> among children with `ec-grid` and strip its whitespace-only
|
|
23
34
|
* text nodes so the bare list serializes without newlines. Returns that <ul>. */
|
|
24
35
|
export function markFirstList(children) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remark-directives.d.ts","sourceRoot":"","sources":["../../src/lib/render/remark-directives.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8B,IAAI,EAAQ,MAAM,OAAO,CAAC;AAGpE,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAkDrE,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,IAEtD,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"remark-directives.d.ts","sourceRoot":"","sources":["../../src/lib/render/remark-directives.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8B,IAAI,EAAQ,MAAM,OAAO,CAAC;AAGpE,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAkDrE,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,IAEtD,MAAM,IAAI,UAgEnB"}
|
|
@@ -60,19 +60,23 @@ 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
|
-
|
|
63
|
+
const iconField = def?.attributes?.find((field) => field.type === 'icon');
|
|
64
|
+
const iconKey = iconField?.key ?? 'icon';
|
|
65
|
+
let icon = attrs[iconKey] || undefined;
|
|
64
66
|
if (!icon && role)
|
|
65
67
|
icon = registry.defaultIcon(node.name, role);
|
|
66
68
|
const properties = { dataPrimitive: node.name };
|
|
67
|
-
if (icon)
|
|
68
|
-
properties.dataIcon = icon;
|
|
69
69
|
if (role)
|
|
70
70
|
properties.dataRole = role;
|
|
71
71
|
// Carry every declared attribute to hast so the dispatch partitioner can build the
|
|
72
|
-
// component context.
|
|
73
|
-
//
|
|
72
|
+
// component context. The icon attribute uses the already-resolved `icon` (the author value
|
|
73
|
+
// coerced through the same empty-is-absent rule above, or the defaultIconByRole default), so
|
|
74
|
+
// a role default reaches the build through the one declared path and a blank `icon=` falls
|
|
75
|
+
// back to that default the same way a missing one does. data-attr-<key> survives to the
|
|
76
|
+
// element; build() consumes it and returns a fresh element, so the marker never reaches the
|
|
77
|
+
// published DOM.
|
|
74
78
|
for (const field of def?.attributes ?? []) {
|
|
75
|
-
const raw = attrs[field.key];
|
|
79
|
+
const raw = field === iconField ? icon : attrs[field.key];
|
|
76
80
|
if (raw != null)
|
|
77
81
|
properties[dataAttrProp(field.key)] = raw;
|
|
78
82
|
}
|
|
@@ -92,6 +96,11 @@ export function remarkDirectiveStamp(registry) {
|
|
|
92
96
|
markSlot(child, child.name);
|
|
93
97
|
}
|
|
94
98
|
}
|
|
99
|
+
// A directive [label] that the component has no `title` slot to claim would otherwise fall
|
|
100
|
+
// through as body content and render as a stray paragraph. Drop it.
|
|
101
|
+
if (!slotNames.has('title')) {
|
|
102
|
+
node.children = node.children.filter((child) => !isDirectiveLabel(child));
|
|
103
|
+
}
|
|
95
104
|
});
|
|
96
105
|
visit(tree, ['textDirective', 'leafDirective'], (node, index, parent) => {
|
|
97
106
|
if (!parent || index == null)
|
|
@@ -12,9 +12,10 @@ import { type ComponentRegistry } from './registry.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function buildSanitizeSchema(registry: ComponentRegistry, extend?: (defaults: Schema) => Schema): Schema;
|
|
14
14
|
/**
|
|
15
|
-
* Force rel
|
|
15
|
+
* Force a `rel` value on every target="_blank" anchor, to prevent reverse-tabnabbing.
|
|
16
16
|
* hast-util-sanitize runs no per-node hook, so this small transform carries the behavior the old
|
|
17
|
-
* DOMPurify preview pass enforced, now on the delivered output as well.
|
|
17
|
+
* DOMPurify preview pass enforced, now on the delivered output as well. The value is the renderer's
|
|
18
|
+
* `anchorRel` option (default `noopener noreferrer`); a site can override it or disable it entirely.
|
|
18
19
|
*/
|
|
19
|
-
export declare function rehypeAnchorRel(): (tree: Root) => void;
|
|
20
|
+
export declare function rehypeAnchorRel(rel: string): (tree: Root) => void;
|
|
20
21
|
//# sourceMappingURL=sanitize-schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize-schema.d.ts","sourceRoot":"","sources":["../../src/lib/render/sanitize-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EAAE,IAAI,EAAW,MAAM,MAAM,CAAC;AAE1C,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAMrE;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,iBAAiB,EAC3B,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GACpC,MAAM,CA6BR;AAED
|
|
1
|
+
{"version":3,"file":"sanitize-schema.d.ts","sourceRoot":"","sources":["../../src/lib/render/sanitize-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EAAE,IAAI,EAAW,MAAM,MAAM,CAAC;AAE1C,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAMrE;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,iBAAiB,EAC3B,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GACpC,MAAM,CA6BR;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,IACjC,MAAM,IAAI,UAOnB"}
|
|
@@ -3,7 +3,7 @@ import { visit } from 'unist-util-visit';
|
|
|
3
3
|
import { dataAttrProp } from './registry.js';
|
|
4
4
|
// The fixed directive markers the stamp writes and the dispatch reads. They are inert data
|
|
5
5
|
// attributes, never a script vector, and must survive the floor so the dispatch still runs.
|
|
6
|
-
const FIXED_MARKERS = ['dataPrimitive', 'dataSlot', '
|
|
6
|
+
const FIXED_MARKERS = ['dataPrimitive', 'dataSlot', 'dataRole', 'dataRise'];
|
|
7
7
|
/**
|
|
8
8
|
* Build the delivery sanitize schema. Starts from hast-util-sanitize's defaultSchema, the
|
|
9
9
|
* GitHub-lineage allowlist that strips scripts, inline event handlers, and javascript:/data: URLs,
|
|
@@ -42,15 +42,16 @@ export function buildSanitizeSchema(registry, extend) {
|
|
|
42
42
|
return extend ? extend(schema) : schema;
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
|
-
* Force rel
|
|
45
|
+
* Force a `rel` value on every target="_blank" anchor, to prevent reverse-tabnabbing.
|
|
46
46
|
* hast-util-sanitize runs no per-node hook, so this small transform carries the behavior the old
|
|
47
|
-
* DOMPurify preview pass enforced, now on the delivered output as well.
|
|
47
|
+
* DOMPurify preview pass enforced, now on the delivered output as well. The value is the renderer's
|
|
48
|
+
* `anchorRel` option (default `noopener noreferrer`); a site can override it or disable it entirely.
|
|
48
49
|
*/
|
|
49
|
-
export function rehypeAnchorRel() {
|
|
50
|
+
export function rehypeAnchorRel(rel) {
|
|
50
51
|
return (tree) => {
|
|
51
52
|
visit(tree, 'element', (node) => {
|
|
52
53
|
if (node.tagName === 'a' && node.properties?.target === '_blank') {
|
|
53
|
-
node.properties.rel =
|
|
54
|
+
node.properties.rel = rel;
|
|
54
55
|
}
|
|
55
56
|
});
|
|
56
57
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-routes.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/public-routes.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,WAAW,CAAA;KAAE,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtG,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,KAAK,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC;6EACyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qEAAqE;AACrE,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,uDAAuD;AACvD,MAAM,WAAW,OAAQ,SAAQ,QAAQ;IACvC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACxC;AAED,oFAAoF;AACpF,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,2DAA2D;AAC3D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB;uBAWvB;QAAE,GAAG,EAAE,GAAG,CAAA;KAAE,KAAG,OAAO,CAAC,SAAS,CAAC;6BA0BjC,MAAM,KAAG,QAAQ;8BAKhB,MAAM,KAAG,YAAY;yBAK1B,MAAM,SAAS;QAAE,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,KAAG,OAAO;mBAO5D;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE;EAKvC"}
|
|
1
|
+
{"version":3,"file":"public-routes.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/public-routes.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,WAAW,CAAA;KAAE,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtG,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,KAAK,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC;6EACyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qEAAqE;AACrE,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,uDAAuD;AACvD,MAAM,WAAW,OAAQ,SAAQ,QAAQ;IACvC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACxC;AAED,oFAAoF;AACpF,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,2DAA2D;AAC3D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB;uBAWvB;QAAE,GAAG,EAAE,GAAG,CAAA;KAAE,KAAG,OAAO,CAAC,SAAS,CAAC;6BA0BjC,MAAM,KAAG,QAAQ;8BAKhB,MAAM,KAAG,YAAY;yBAK1B,MAAM,SAAS;QAAE,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,KAAG,OAAO;mBAO5D;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE;EAKvC"}
|
|
@@ -41,7 +41,7 @@ export function createPublicRoutes(deps) {
|
|
|
41
41
|
...(fields.author ? { author: fields.author } : {}),
|
|
42
42
|
...(entry.date ? { feeds } : {}),
|
|
43
43
|
});
|
|
44
|
-
return { entry, html: await render(entry.body, { stagger: true, resolve: buildLinkResolver(site) }), canonicalUrl, seo, newer, older };
|
|
44
|
+
return { concept: entry.concept, entry, html: await render(entry.body, { stagger: true, resolve: buildLinkResolver(site) }), canonicalUrl, seo, newer, older };
|
|
45
45
|
}
|
|
46
46
|
/** The chronological archive for one concept: every non-draft summary, newest-first. */
|
|
47
47
|
function archiveLoad(conceptId) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glw907/cairn-cms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Embedded, magic-link, GitHub-committing CMS for SvelteKit/Cloudflare sites.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -53,6 +53,11 @@
|
|
|
53
53
|
"svelte": "./dist/delivery/index.js",
|
|
54
54
|
"default": "./dist/delivery/index.js"
|
|
55
55
|
},
|
|
56
|
+
"./delivery/head": {
|
|
57
|
+
"types": "./dist/delivery/head.d.ts",
|
|
58
|
+
"svelte": "./dist/delivery/head.js",
|
|
59
|
+
"default": "./dist/delivery/head.js"
|
|
60
|
+
},
|
|
56
61
|
"./package.json": "./package.json"
|
|
57
62
|
},
|
|
58
63
|
"files": [
|
|
@@ -43,6 +43,14 @@ export function normalizeConcepts(
|
|
|
43
43
|
const descriptors: ConceptDescriptor[] = [];
|
|
44
44
|
for (const [id, config] of Object.entries(content)) {
|
|
45
45
|
if (!config) continue;
|
|
46
|
+
const summaryFields = config.summaryFields ?? [];
|
|
47
|
+
const declared = new Set(config.schema.fields.map((field) => field.name));
|
|
48
|
+
const undeclared = summaryFields.find((key) => !declared.has(key));
|
|
49
|
+
if (undeclared !== undefined) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`cairn: concept "${id}" summaryFields key "${undeclared}" is not a declared field`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
46
54
|
const policy = urlPolicy[id] ?? {};
|
|
47
55
|
descriptors.push({
|
|
48
56
|
id,
|
|
@@ -52,6 +60,7 @@ export function normalizeConcepts(
|
|
|
52
60
|
permalink: policy.permalink ?? defaultPermalink(id),
|
|
53
61
|
datePrefix: policy.datePrefix ?? 'day',
|
|
54
62
|
fields: config.schema.fields,
|
|
63
|
+
summaryFields,
|
|
55
64
|
validate: config.schema.validate,
|
|
56
65
|
});
|
|
57
66
|
}
|
|
@@ -56,6 +56,27 @@ export function dateInputValue(value: unknown): string {
|
|
|
56
56
|
return '';
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* True when `s` is a canonical zero-padded `YYYY-MM-DD` string naming a real calendar date.
|
|
61
|
+
* Rejects a wrong format, an impossible month or day, and a JS date-rollover such as
|
|
62
|
+
* `2026-02-30` (which `Date` would silently roll forward to March 2). The committed form a
|
|
63
|
+
* date field carries is exactly this canonical shape, which is what the form and
|
|
64
|
+
* `dateInputValue` emit, so a value outside it is a hand-edit or odd-YAML error.
|
|
65
|
+
*/
|
|
66
|
+
export function isCalendarDate(s: string): boolean {
|
|
67
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
|
|
68
|
+
if (!match) return false;
|
|
69
|
+
const year = Number(match[1]);
|
|
70
|
+
const month = Number(match[2]);
|
|
71
|
+
const day = Number(match[3]);
|
|
72
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
73
|
+
return (
|
|
74
|
+
date.getUTCFullYear() === year &&
|
|
75
|
+
date.getUTCMonth() === month - 1 &&
|
|
76
|
+
date.getUTCDate() === day
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
59
80
|
/** Reassemble a markdown file from frontmatter and body for committing. */
|
|
60
81
|
export function serializeMarkdown(frontmatter: object, body: string): string {
|
|
61
82
|
return matter.stringify(body, frontmatter);
|
package/src/lib/content/types.ts
CHANGED
|
@@ -110,6 +110,9 @@ export interface ConceptConfig<S extends ConceptSchema = ConceptSchema> {
|
|
|
110
110
|
label?: string;
|
|
111
111
|
/** The concept's schema: the form projection, the generated validator, and the inferred type. */
|
|
112
112
|
schema: S;
|
|
113
|
+
/** Frontmatter keys to surface on each `ContentSummary.fields`, so a list card reads an authored
|
|
114
|
+
* field without a per-entry detail read. Each key should also be declared in `schema`. */
|
|
115
|
+
summaryFields?: string[];
|
|
113
116
|
}
|
|
114
117
|
|
|
115
118
|
/**
|
|
@@ -215,6 +218,9 @@ export interface ConceptDescriptor {
|
|
|
215
218
|
/** Filename date-prefix granularity for a dated concept; resolved by `normalizeConcepts`. */
|
|
216
219
|
datePrefix: DatePrefix;
|
|
217
220
|
fields: FrontmatterField[];
|
|
221
|
+
/** Frontmatter keys the index copies onto each summary's `fields` record. `normalizeConcepts`
|
|
222
|
+
* resolves it to `[]` when a concept omits `summaryFields`. */
|
|
223
|
+
summaryFields: string[];
|
|
218
224
|
validate(frontmatter: Record<string, unknown>, body: string): ValidationResult;
|
|
219
225
|
}
|
|
220
226
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// validator stays thin (engine-fat rule). Saving runs the concept's validator on the
|
|
4
4
|
// server before any commit; invalid input bounces to the form (spec §7.4).
|
|
5
5
|
import type { FrontmatterField, ValidationResult } from './types.js';
|
|
6
|
-
import { dateInputValue } from './frontmatter.js';
|
|
6
|
+
import { dateInputValue, isCalendarDate } from './frontmatter.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Validate raw frontmatter against a field list. Required text and date fields must be
|
|
@@ -34,12 +34,17 @@ export function validateFields(
|
|
|
34
34
|
case 'freetags': {
|
|
35
35
|
const list = Array.isArray(value) ? value.map(String) : [];
|
|
36
36
|
if (field.required && list.length === 0) errors[field.name] = `${field.label} is required`;
|
|
37
|
+
else if (field.type === 'tags') {
|
|
38
|
+
const unknown = list.find((tag) => !field.options.includes(tag));
|
|
39
|
+
if (unknown !== undefined) errors[field.name] = `${field.label} contains an unknown value: ${unknown}`;
|
|
40
|
+
}
|
|
37
41
|
if (list.length > 0) data[field.name] = list;
|
|
38
42
|
break;
|
|
39
43
|
}
|
|
40
44
|
case 'date': {
|
|
41
45
|
const text = value instanceof Date ? dateInputValue(value) : typeof value === 'string' ? value.trim() : '';
|
|
42
46
|
if (field.required && text === '') errors[field.name] = `${field.label} is required`;
|
|
47
|
+
else if (text !== '' && !isCalendarDate(text)) errors[field.name] = `${field.label} must be a valid date (YYYY-MM-DD)`;
|
|
43
48
|
if (text !== '') data[field.name] = text;
|
|
44
49
|
break;
|
|
45
50
|
}
|
|
@@ -16,6 +16,9 @@ export interface RawFile {
|
|
|
16
16
|
|
|
17
17
|
/** The cheap, plain-data view of one entry, for lists, feeds, and the sitemap. */
|
|
18
18
|
export interface ContentSummary {
|
|
19
|
+
/** The descriptor id this entry belongs to, e.g. "posts". Lets a list or page branch per
|
|
20
|
+
* concept without re-deriving it from a proxy like `entry.date`. */
|
|
21
|
+
concept: string;
|
|
19
22
|
id: string;
|
|
20
23
|
slug: string;
|
|
21
24
|
permalink: string;
|
|
@@ -26,6 +29,10 @@ export interface ContentSummary {
|
|
|
26
29
|
excerpt: string;
|
|
27
30
|
wordCount: number;
|
|
28
31
|
draft: boolean;
|
|
32
|
+
/** The frontmatter keys the descriptor nominated via `summaryFields`, read off the validated,
|
|
33
|
+
* normalized frontmatter. Held in a separate record so a nominated key cannot collide with a
|
|
34
|
+
* typed summary field. Empty when the concept declares no `summaryFields`. */
|
|
35
|
+
fields: Record<string, unknown>;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
/** The detail view: a summary plus the frontmatter and the body to render. The frontmatter
|
|
@@ -98,7 +105,12 @@ export function createContentIndex<F = Record<string, unknown>>(
|
|
|
98
105
|
problems.push({ id, draft, errors: result.errors });
|
|
99
106
|
continue;
|
|
100
107
|
}
|
|
108
|
+
const summaryFieldValues: Record<string, unknown> = {};
|
|
109
|
+
for (const key of descriptor.summaryFields) {
|
|
110
|
+
if (key in result.data) summaryFieldValues[key] = result.data[key];
|
|
111
|
+
}
|
|
101
112
|
entries.push({
|
|
113
|
+
concept: descriptor.id,
|
|
102
114
|
id,
|
|
103
115
|
slug,
|
|
104
116
|
permalink: permalink(descriptor, { id, slug, date }),
|
|
@@ -109,6 +121,7 @@ export function createContentIndex<F = Record<string, unknown>>(
|
|
|
109
121
|
excerpt: deriveExcerpt(body, { description: asString(raw.description) }),
|
|
110
122
|
wordCount: wordCount(body),
|
|
111
123
|
draft,
|
|
124
|
+
fields: summaryFieldValues,
|
|
112
125
|
frontmatter: result.data as F,
|
|
113
126
|
body,
|
|
114
127
|
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// cairn-cms: the delivery head component entry (@glw907/cairn-cms/delivery/head). CairnHead lives
|
|
2
|
+
// behind its own export so importing a delivery data helper from /delivery never pulls a .svelte
|
|
3
|
+
// module into the graph. A node-environment data import then needs no Svelte plugin.
|
|
4
|
+
export { default as CairnHead } from './CairnHead.svelte';
|
package/src/lib/index.ts
CHANGED
|
@@ -92,6 +92,7 @@ export {
|
|
|
92
92
|
strProp,
|
|
93
93
|
iconSpan,
|
|
94
94
|
cardShell,
|
|
95
|
+
headRow,
|
|
95
96
|
markFirstList,
|
|
96
97
|
} from './render/rehype-dispatch.js';
|
|
97
98
|
export type { MakeIcon } from './render/rehype-dispatch.js';
|
|
@@ -155,3 +156,9 @@ export { readSeoFields, resolveImageUrl } from './delivery/seo-fields.js';
|
|
|
155
156
|
export type { SeoFields } from './delivery/seo-fields.js';
|
|
156
157
|
export { paginate } from './delivery/paginate.js';
|
|
157
158
|
export type { Page } from './delivery/paginate.js';
|
|
159
|
+
// Root superset of the delivery route surface: a wrong guess from root for a route loader or a
|
|
160
|
+
// response helper now resolves. The CairnHead component stays out of root so the root barrel stays
|
|
161
|
+
// node-importable for the unit suite; it resolves from @glw907/cairn-cms/delivery/head.
|
|
162
|
+
export { rssResponse, jsonFeedResponse, sitemapResponse, robotsResponse } from './delivery/responses.js';
|
|
163
|
+
export { createPublicRoutes } from './sveltekit/public-routes.js';
|
|
164
|
+
export type { PublicRoutesDeps, ListData, TagData, TagIndexData, EntryData } from './sveltekit/public-routes.js';
|
|
@@ -30,6 +30,10 @@ export interface RendererOptions {
|
|
|
30
30
|
* vector the floor closes, so it is only for a site whose content is fully developer-controlled.
|
|
31
31
|
* It is a code-level adapter decision, never an editor-facing setting. */
|
|
32
32
|
unsafeDisableSanitize?: boolean;
|
|
33
|
+
/** The `rel` value forced on every `target="_blank"` anchor, applied last so it also covers
|
|
34
|
+
* component-built anchors. Defaults to `'noopener noreferrer'`. Set a different string to change
|
|
35
|
+
* it, or `false` to disable the injection (a site that owns its own anchor hardening). */
|
|
36
|
+
anchorRel?: string | false;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
/** Compose a site's render pipeline from its component registry: directive syntax to
|
|
@@ -43,13 +47,14 @@ export function createRenderer(registry: ComponentRegistry, options: RendererOpt
|
|
|
43
47
|
const floor: PluggableList = options.unsafeDisableSanitize
|
|
44
48
|
? []
|
|
45
49
|
: [[rehypeSanitize, buildSanitizeSchema(registry, options.sanitizeSchema)]];
|
|
50
|
+
const rel = options.anchorRel ?? 'noopener noreferrer';
|
|
46
51
|
const rehypePlugins: PluggableList = [
|
|
47
52
|
rehypeRaw,
|
|
48
53
|
...floor,
|
|
49
54
|
[rehypeDispatch, registry, options.stagger],
|
|
50
55
|
rehypeSlug,
|
|
51
|
-
rehypeAnchorRel,
|
|
52
56
|
];
|
|
57
|
+
if (rel !== false) rehypePlugins.push([rehypeAnchorRel, rel]);
|
|
53
58
|
const processor = unified()
|
|
54
59
|
.use(remarkParse)
|
|
55
60
|
.use(remarkGfm)
|
|
@@ -19,7 +19,7 @@ export interface AttributeField {
|
|
|
19
19
|
/** Initial value; a string for text/select/icon, a boolean for boolean. */
|
|
20
20
|
default?: string | boolean;
|
|
21
21
|
/** Allowed values for `type: 'select'`. */
|
|
22
|
-
options?: string[];
|
|
22
|
+
options?: readonly string[];
|
|
23
23
|
/** Helper text shown under the field. */
|
|
24
24
|
help?: string;
|
|
25
25
|
}
|
|
@@ -7,7 +7,7 @@ export function isElement(node: ElementContent | undefined): node is Element {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
// hast Properties values are PropertyValue (string | number | boolean | array | null).
|
|
10
|
-
// Directive markers (
|
|
10
|
+
// Directive markers (dataPrimitive/dataRole/dataAttr<Key>) are always stamped as strings;
|
|
11
11
|
// this reads them back with that guarantee instead of casting at each call site.
|
|
12
12
|
export function strProp(node: Element, name: string): string | undefined {
|
|
13
13
|
const value = node.properties?.[name];
|
|
@@ -28,6 +28,17 @@ export function cardShell(classes: string[], body: ElementContent[]): Element {
|
|
|
28
28
|
return h('section', { className: classes }, [h('div', { className: ['card-body'] }, body)]);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/** Card head row: `<div class="ec-head">[icon]<h2 class="card-title">{title}</h2></div>`.
|
|
32
|
+
* Pass the title's inline children and an optional pre-built icon element, the way `cardShell`
|
|
33
|
+
* takes already-built body content. This factors the icon-plus-heading head that a titled
|
|
34
|
+
* component build would otherwise rebuild by hand (the shape the removed `splitHead` produced). */
|
|
35
|
+
export function headRow(title: ElementContent[], icon?: Element): Element {
|
|
36
|
+
const children: ElementContent[] = [];
|
|
37
|
+
if (icon) children.push(icon);
|
|
38
|
+
children.push(h('h2', { className: ['card-title'] }, title));
|
|
39
|
+
return h('div', { className: ['ec-head'] }, children);
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
/** Tag the first <ul> among children with `ec-grid` and strip its whitespace-only
|
|
32
43
|
* text nodes so the bare list serializes without newlines. Returns that <ul>. */
|
|
33
44
|
export function markFirstList(children: ElementContent[]): Element | undefined {
|
|
@@ -59,17 +59,22 @@ export function remarkDirectiveStamp(registry: ComponentRegistry) {
|
|
|
59
59
|
const def = registry.get(node.name);
|
|
60
60
|
const attrs = node.attributes ?? {};
|
|
61
61
|
const role = attrs.role || undefined;
|
|
62
|
-
|
|
62
|
+
const iconField = def?.attributes?.find((field) => field.type === 'icon');
|
|
63
|
+
const iconKey = iconField?.key ?? 'icon';
|
|
64
|
+
let icon = attrs[iconKey] || undefined;
|
|
63
65
|
if (!icon && role) icon = registry.defaultIcon(node.name, role);
|
|
64
66
|
|
|
65
67
|
const properties: Record<string, string> = { dataPrimitive: node.name };
|
|
66
|
-
if (icon) properties.dataIcon = icon;
|
|
67
68
|
if (role) properties.dataRole = role;
|
|
68
69
|
// Carry every declared attribute to hast so the dispatch partitioner can build the
|
|
69
|
-
// component context.
|
|
70
|
-
//
|
|
70
|
+
// component context. The icon attribute uses the already-resolved `icon` (the author value
|
|
71
|
+
// coerced through the same empty-is-absent rule above, or the defaultIconByRole default), so
|
|
72
|
+
// a role default reaches the build through the one declared path and a blank `icon=` falls
|
|
73
|
+
// back to that default the same way a missing one does. data-attr-<key> survives to the
|
|
74
|
+
// element; build() consumes it and returns a fresh element, so the marker never reaches the
|
|
75
|
+
// published DOM.
|
|
71
76
|
for (const field of def?.attributes ?? []) {
|
|
72
|
-
const raw = attrs[field.key];
|
|
77
|
+
const raw = field === iconField ? icon : attrs[field.key];
|
|
73
78
|
if (raw != null) properties[dataAttrProp(field.key)] = raw;
|
|
74
79
|
}
|
|
75
80
|
|
|
@@ -91,6 +96,12 @@ export function remarkDirectiveStamp(registry: ComponentRegistry) {
|
|
|
91
96
|
markSlot(child, (child as { name: string }).name);
|
|
92
97
|
}
|
|
93
98
|
}
|
|
99
|
+
|
|
100
|
+
// A directive [label] that the component has no `title` slot to claim would otherwise fall
|
|
101
|
+
// through as body content and render as a stray paragraph. Drop it.
|
|
102
|
+
if (!slotNames.has('title')) {
|
|
103
|
+
node.children = node.children.filter((child) => !isDirectiveLabel(child)) as typeof node.children;
|
|
104
|
+
}
|
|
94
105
|
});
|
|
95
106
|
|
|
96
107
|
visit(tree, ['textDirective', 'leafDirective'], (node, index, parent) => {
|
|
@@ -5,7 +5,7 @@ import { dataAttrProp, type ComponentRegistry } from './registry.js';
|
|
|
5
5
|
|
|
6
6
|
// The fixed directive markers the stamp writes and the dispatch reads. They are inert data
|
|
7
7
|
// attributes, never a script vector, and must survive the floor so the dispatch still runs.
|
|
8
|
-
const FIXED_MARKERS = ['dataPrimitive', 'dataSlot', '
|
|
8
|
+
const FIXED_MARKERS = ['dataPrimitive', 'dataSlot', 'dataRole', 'dataRise'];
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Build the delivery sanitize schema. Starts from hast-util-sanitize's defaultSchema, the
|
|
@@ -51,15 +51,16 @@ export function buildSanitizeSchema(
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
* Force rel
|
|
54
|
+
* Force a `rel` value on every target="_blank" anchor, to prevent reverse-tabnabbing.
|
|
55
55
|
* hast-util-sanitize runs no per-node hook, so this small transform carries the behavior the old
|
|
56
|
-
* DOMPurify preview pass enforced, now on the delivered output as well.
|
|
56
|
+
* DOMPurify preview pass enforced, now on the delivered output as well. The value is the renderer's
|
|
57
|
+
* `anchorRel` option (default `noopener noreferrer`); a site can override it or disable it entirely.
|
|
57
58
|
*/
|
|
58
|
-
export function rehypeAnchorRel() {
|
|
59
|
+
export function rehypeAnchorRel(rel: string) {
|
|
59
60
|
return (tree: Root) => {
|
|
60
61
|
visit(tree, 'element', (node: Element) => {
|
|
61
62
|
if (node.tagName === 'a' && node.properties?.target === '_blank') {
|
|
62
|
-
node.properties.rel =
|
|
63
|
+
node.properties.rel = rel;
|
|
63
64
|
}
|
|
64
65
|
});
|
|
65
66
|
};
|
|
@@ -45,6 +45,7 @@ export interface TagIndexData {
|
|
|
45
45
|
|
|
46
46
|
/** One entry's data: the detail entry, its rendered html, and its canonical URL. */
|
|
47
47
|
export interface EntryData {
|
|
48
|
+
concept: string;
|
|
48
49
|
entry: ContentEntry;
|
|
49
50
|
html: string;
|
|
50
51
|
canonicalUrl: string;
|
|
@@ -87,7 +88,7 @@ export function createPublicRoutes(deps: PublicRoutesDeps) {
|
|
|
87
88
|
...(fields.author ? { author: fields.author } : {}),
|
|
88
89
|
...(entry.date ? { feeds } : {}),
|
|
89
90
|
});
|
|
90
|
-
return { entry, html: await render(entry.body, { stagger: true, resolve: buildLinkResolver(site) }), canonicalUrl, seo, newer, older };
|
|
91
|
+
return { concept: entry.concept, entry, html: await render(entry.body, { stagger: true, resolve: buildLinkResolver(site) }), canonicalUrl, seo, newer, older };
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
/** The chronological archive for one concept: every non-draft summary, newest-first. */
|