@refrakt-md/runes 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/config.d.ts.map +1 -1
  2. package/dist/config.js +27 -7
  3. package/dist/config.js.map +1 -1
  4. package/dist/index.d.ts +4 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -10
  7. package/dist/index.js.map +1 -1
  8. package/dist/layouts/grid.d.ts +1 -1
  9. package/dist/lib/index.d.ts +28 -0
  10. package/dist/lib/index.d.ts.map +1 -1
  11. package/dist/lib/index.js +47 -1
  12. package/dist/lib/index.js.map +1 -1
  13. package/dist/lib/resolver.d.ts.map +1 -1
  14. package/dist/lib/resolver.js +16 -1
  15. package/dist/lib/resolver.js.map +1 -1
  16. package/dist/lib/schema-table.d.ts +176 -0
  17. package/dist/lib/schema-table.d.ts.map +1 -0
  18. package/dist/lib/schema-table.js +427 -0
  19. package/dist/lib/schema-table.js.map +1 -0
  20. package/dist/reference.d.ts +33 -0
  21. package/dist/reference.d.ts.map +1 -1
  22. package/dist/reference.js +32 -1
  23. package/dist/reference.js.map +1 -1
  24. package/dist/rune.d.ts +0 -2
  25. package/dist/rune.d.ts.map +1 -1
  26. package/dist/rune.js +0 -1
  27. package/dist/rune.js.map +1 -1
  28. package/dist/schema-row.d.ts +89 -0
  29. package/dist/schema-row.d.ts.map +1 -0
  30. package/dist/schema-row.js +78 -0
  31. package/dist/schema-row.js.map +1 -0
  32. package/dist/seo.d.ts +9 -0
  33. package/dist/seo.d.ts.map +1 -1
  34. package/dist/seo.js +84 -7
  35. package/dist/seo.js.map +1 -1
  36. package/dist/tags/accordion.d.ts +58 -0
  37. package/dist/tags/accordion.d.ts.map +1 -1
  38. package/dist/tags/accordion.js +47 -15
  39. package/dist/tags/accordion.js.map +1 -1
  40. package/dist/tags/blog.d.ts.map +1 -1
  41. package/dist/tags/blog.js +9 -1
  42. package/dist/tags/blog.js.map +1 -1
  43. package/dist/tags/breadcrumb.d.ts +30 -0
  44. package/dist/tags/breadcrumb.d.ts.map +1 -1
  45. package/dist/tags/breadcrumb.js +28 -21
  46. package/dist/tags/breadcrumb.js.map +1 -1
  47. package/dist/tags/budget.d.ts.map +1 -1
  48. package/dist/tags/budget.js +7 -1
  49. package/dist/tags/budget.js.map +1 -1
  50. package/dist/tags/datatable.d.ts.map +1 -1
  51. package/dist/tags/datatable.js +6 -1
  52. package/dist/tags/datatable.js.map +1 -1
  53. package/dist/tags/embed.d.ts +8 -0
  54. package/dist/tags/embed.d.ts.map +1 -1
  55. package/dist/tags/embed.js +19 -6
  56. package/dist/tags/embed.js.map +1 -1
  57. package/dist/tags/figure.d.ts +7 -0
  58. package/dist/tags/figure.d.ts.map +1 -1
  59. package/dist/tags/figure.js +14 -5
  60. package/dist/tags/figure.js.map +1 -1
  61. package/dist/tags/gallery.d.ts.map +1 -1
  62. package/dist/tags/gallery.js +8 -1
  63. package/dist/tags/gallery.js.map +1 -1
  64. package/package.json +3 -3
@@ -0,0 +1,176 @@
1
+ import Markdoc from '@markdoc/markdoc';
2
+ import type { RenderableTreeNode } from '@markdoc/markdoc';
3
+ declare const Tag: typeof Markdoc.Tag;
4
+ /**
5
+ * The declarative schema.org table, and the applier that realises it
6
+ * (SPEC-130 / WORK-565).
7
+ *
8
+ * ## Why this is data consulted at transform time, not engine config
9
+ *
10
+ * The obvious home is the identity transform engine, and it does not work.
11
+ * `packages/content/src/site.ts` never applies the engine — it runs
12
+ * `Markdoc.transform`, then `extractSeo`, and `collectJsonLd` *derives* the
13
+ * JSON-LD by walking that tree for `typeof`. The engine runs later, at render
14
+ * time, so schema emitted there would reach the HTML and never reach the
15
+ * JSON-LD.
16
+ *
17
+ * So the table is an option to `createContentModelSchema`, sitting beside the
18
+ * rune's other self-declarations (`sections`, `mediaSlots`, `provides`, `base`),
19
+ * and the applier runs on the rune's own output.
20
+ *
21
+ * ## Three resolution strategies, and why the third exists
22
+ *
23
+ * `createComponentRenderable` drops a property `<meta>` that no `schema:` map
24
+ * names — it is classified pure data and its value is already in the bag. Once
25
+ * runes stop declaring `schema:` themselves, those nodes are gone by the time
26
+ * the applier runs. The bag is the recovery:
27
+ *
28
+ * 1. **Stamp in place** — a named node that survives into the output.
29
+ * 2. **Rebuild from the bag** — a value-only source whose carrier was dropped.
30
+ * 3. **Synthesise an entity** — a nested span built from either of the above.
31
+ *
32
+ * That is why `createComponentRenderable` needs no knowledge of schema at all:
33
+ * it simply always treats property metas as pure data, which is what it already
34
+ * does when `schema:` is absent.
35
+ *
36
+ * ## Name resolution is attribute-agnostic, deliberately
37
+ *
38
+ * A source resolves whether it surfaces as `data-name` or `data-field`, and the
39
+ * applier never branches on which. SPEC-133 moves ~97 nodes between `properties`
40
+ * and `refs`; under this rule every one of those moves is a no-op for every
41
+ * table, because the table keys on a name in the rune's flat namespace
42
+ * (ADR-008), not on the attribute that happens to carry it.
43
+ */
44
+ /**
45
+ * Marks a `typeof` the **author** stated, which a parent may not overrule.
46
+ *
47
+ * D9's default stands: a parent's `children` row retypes its children, and a
48
+ * child that says nothing about its type gets the parent's. This is the narrow
49
+ * opt-out — a `{% track type="song" %}` inside a `{% playlist type="podcast" %}`
50
+ * stays a song, because saying so and being overruled anyway would make the
51
+ * attribute a lie.
52
+ *
53
+ * It has to be a marker because "no type stated" and "this exact type stated"
54
+ * are the same value by transform time, and Markdoc transforms bottom-up so a
55
+ * child cannot ask its parent. An own-property on the Tag rather than an
56
+ * attribute, so `JSON.parse(JSON.stringify(…))` at the serialize boundary drops
57
+ * it and it can never reach the HTML; the applier deletes it once read.
58
+ */
59
+ export declare const SCHEMA_TYPE_EXPLICIT: unique symbol;
60
+ /** A source name in the rune's flat namespace, mapped to a schema.org property. */
61
+ export type PropertyMap = Record<string, string>;
62
+ /** A nested entity: its own type, the property that holds it, its own map. */
63
+ export interface EntityRow {
64
+ type: string;
65
+ /** The property on the parent that holds this entity. Required — see below. */
66
+ property: string;
67
+ properties?: PropertyMap;
68
+ /** Properties taking a node's own content, via an RDFa-conformant wrapper. */
69
+ text?: PropertyMap;
70
+ /** The wrapper element `text` emits. Defaults to `div`. */
71
+ textTag?: string;
72
+ /** Properties whose value is generated rather than read from content. */
73
+ generated?: Record<string, 'index'>;
74
+ }
75
+ /** One resolvable schema row: a type plus what it maps. */
76
+ export interface SchemaRow {
77
+ type?: string;
78
+ properties?: PropertyMap;
79
+ /**
80
+ * Properties taking a node's own content. The applier emits the
81
+ * RDFa-conformant wrapper; no rune hand-writes one.
82
+ *
83
+ * The wrapper is conformant, not a workaround. RDFa Core 1.1 §7.5 step 11:
84
+ * an element carrying both `property` and `typeof` has its object fixed to
85
+ * the typed resource, so its text is unreachable as a literal. Since
86
+ * SPEC-082 renders the SEO carriers inline, refrakt publishes RDFa *and*
87
+ * JSON-LD on the same page — dropping the wrapper would have the two
88
+ * channels assert different graphs on every accordion, recipe and how-to.
89
+ */
90
+ text?: PropertyMap;
91
+ /**
92
+ * The wrapper element `text` emits. Defaults to `div`.
93
+ *
94
+ * It is a rendering choice, not a schema one — `how-to` and `recipe` wrap an
95
+ * `<li>`'s content in a `<p>`, and a `<div>` there would change the page's
96
+ * margins. Declared rather than fixed so the applier can reproduce what each
97
+ * rune already renders, byte for byte.
98
+ */
99
+ textTag?: string;
100
+ entities?: Record<string, EntityRow>;
101
+ /** Properties whose value is generated. `index` is the only generator. */
102
+ generated?: Record<string, 'index'>;
103
+ /**
104
+ * Properties that always serialise as an array, however many items (D6).
105
+ *
106
+ * `readonly` so a table written the way every other one is — a single
107
+ * `as const` literal — type-checks; nothing here mutates it.
108
+ */
109
+ lists?: readonly string[];
110
+ /** Per-child-rune type and property remapping. */
111
+ children?: Record<string, EntityRow>;
112
+ }
113
+ /** A rune's schema table: one row, or a row chosen by an attribute value. */
114
+ export interface SchemaTable extends SchemaRow {
115
+ /**
116
+ * Selects a row by the value of the named **attribute**.
117
+ *
118
+ * It names an attribute, not a modifier. `modifiers` is read by the engine,
119
+ * which has no part in this path — the row is picked here, from `attrs`.
120
+ * `validateSchemaTable` enforces that the named attribute is declared, so
121
+ * the ambiguity cannot ship silently.
122
+ */
123
+ by?: string;
124
+ /** Rows keyed by attribute value, used with `by`. */
125
+ rows?: Record<string, SchemaRow>;
126
+ /** The row for when the `by` attribute is absent or unmatched. */
127
+ fallback?: SchemaRow;
128
+ }
129
+ /** A problem with a table, found at build time. */
130
+ export interface SchemaTableIssue {
131
+ path: string;
132
+ message: string;
133
+ }
134
+ /**
135
+ * Check a table's internal consistency.
136
+ *
137
+ * Curation, not validation: nothing here checks a mapping against schema.org,
138
+ * because refrakt ships no ontology (D5). What it does check is the structural
139
+ * rule that cannot be got wrong safely — `collectJsonLd` nests a child entity
140
+ * only when the same node carries both `typeof` and `property`, so declaring a
141
+ * child's type without the property that holds it produces a detached
142
+ * top-level entity related to nothing. The table rejects that rather than
143
+ * emitting it.
144
+ */
145
+ export declare function validateSchemaTable(table: SchemaTable, declaredAttributes?: string[]): SchemaTableIssue[];
146
+ /** Pick the row a set of attributes selects. */
147
+ export declare function selectRow(table: SchemaTable, attrs: Record<string, unknown>): SchemaRow;
148
+ type AnyTag = InstanceType<typeof Tag>;
149
+ /**
150
+ * Find a node by name, whichever attribute carries it.
151
+ *
152
+ * `data-name` and `data-field` are the two surfaces a name can appear on today,
153
+ * and SPEC-133 moves nodes between them. Checking both, in one pass, with no
154
+ * branch on which matched, is what makes those moves a no-op here.
155
+ */
156
+ /**
157
+ * Every node bearing a name, in document order, within one rune.
158
+ *
159
+ * **The search stops at another rune's node.** ADR-008's flat namespace is
160
+ * unique *per rune*, so the same name means different things in a parent and in
161
+ * a child it contains: `character` names its title span `name`, and so does
162
+ * every `character-section` inside it. Reaching across that boundary published a
163
+ * character whose `name` was the character plus each of its section headings.
164
+ */
165
+ export declare function findAllByName(root: RenderableTreeNode | RenderableTreeNode[], name: string): AnyTag[];
166
+ /** The first node bearing a name, within one rune — see `findAllByName`. */
167
+ export declare function findByName(root: RenderableTreeNode | RenderableTreeNode[], name: string): AnyTag | undefined;
168
+ /**
169
+ * Apply a rune's schema table to its transform output.
170
+ *
171
+ * Runs on the rune's own root, after its transform has built the tree and
172
+ * `createComponentRenderable` has written the field bag.
173
+ */
174
+ export declare function applySchemaTable(output: RenderableTreeNode, table: SchemaTable, attrs: Record<string, unknown>): RenderableTreeNode;
175
+ export {};
176
+ //# sourceMappingURL=schema-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-table.d.ts","sourceRoot":"","sources":["../../src/lib/schema-table.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3D,QAAA,MAAQ,GAAG,oBAAY,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,eAA2C,CAAC;AAE7E,mFAAmF;AACnF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,8EAA8E;IAC9E,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,2DAA2D;AAC3D,MAAM,WAAW,SAAS;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrC,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACrC;AAED,6EAA6E;AAC7E,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC7C;;;;;;;OAOG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,WAAW,EAClB,kBAAkB,GAAE,MAAM,EAAO,GAC/B,gBAAgB,EAAE,CA6CpB;AAED,gDAAgD;AAChD,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAKvF;AAID,KAAK,MAAM,GAAG,YAAY,CAAC,OAAO,GAAG,CAAC,CAAC;AAIvC;;;;;;GAMG;AACH;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC5B,IAAI,EAAE,kBAAkB,GAAG,kBAAkB,EAAE,EAC/C,IAAI,EAAE,MAAM,GACV,MAAM,EAAE,CA0BV;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CACzB,IAAI,EAAE,kBAAkB,GAAG,kBAAkB,EAAE,EAC/C,IAAI,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAEpB;AAyLD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,kBAAkB,CAkDpB"}
@@ -0,0 +1,427 @@
1
+ import Markdoc from '@markdoc/markdoc';
2
+ import { SCHEMA_LISTS_ATTR } from '../seo.js';
3
+ const { Tag } = Markdoc;
4
+ /**
5
+ * The declarative schema.org table, and the applier that realises it
6
+ * (SPEC-130 / WORK-565).
7
+ *
8
+ * ## Why this is data consulted at transform time, not engine config
9
+ *
10
+ * The obvious home is the identity transform engine, and it does not work.
11
+ * `packages/content/src/site.ts` never applies the engine — it runs
12
+ * `Markdoc.transform`, then `extractSeo`, and `collectJsonLd` *derives* the
13
+ * JSON-LD by walking that tree for `typeof`. The engine runs later, at render
14
+ * time, so schema emitted there would reach the HTML and never reach the
15
+ * JSON-LD.
16
+ *
17
+ * So the table is an option to `createContentModelSchema`, sitting beside the
18
+ * rune's other self-declarations (`sections`, `mediaSlots`, `provides`, `base`),
19
+ * and the applier runs on the rune's own output.
20
+ *
21
+ * ## Three resolution strategies, and why the third exists
22
+ *
23
+ * `createComponentRenderable` drops a property `<meta>` that no `schema:` map
24
+ * names — it is classified pure data and its value is already in the bag. Once
25
+ * runes stop declaring `schema:` themselves, those nodes are gone by the time
26
+ * the applier runs. The bag is the recovery:
27
+ *
28
+ * 1. **Stamp in place** — a named node that survives into the output.
29
+ * 2. **Rebuild from the bag** — a value-only source whose carrier was dropped.
30
+ * 3. **Synthesise an entity** — a nested span built from either of the above.
31
+ *
32
+ * That is why `createComponentRenderable` needs no knowledge of schema at all:
33
+ * it simply always treats property metas as pure data, which is what it already
34
+ * does when `schema:` is absent.
35
+ *
36
+ * ## Name resolution is attribute-agnostic, deliberately
37
+ *
38
+ * A source resolves whether it surfaces as `data-name` or `data-field`, and the
39
+ * applier never branches on which. SPEC-133 moves ~97 nodes between `properties`
40
+ * and `refs`; under this rule every one of those moves is a no-op for every
41
+ * table, because the table keys on a name in the rune's flat namespace
42
+ * (ADR-008), not on the attribute that happens to carry it.
43
+ */
44
+ /**
45
+ * Marks a `typeof` the **author** stated, which a parent may not overrule.
46
+ *
47
+ * D9's default stands: a parent's `children` row retypes its children, and a
48
+ * child that says nothing about its type gets the parent's. This is the narrow
49
+ * opt-out — a `{% track type="song" %}` inside a `{% playlist type="podcast" %}`
50
+ * stays a song, because saying so and being overruled anyway would make the
51
+ * attribute a lie.
52
+ *
53
+ * It has to be a marker because "no type stated" and "this exact type stated"
54
+ * are the same value by transform time, and Markdoc transforms bottom-up so a
55
+ * child cannot ask its parent. An own-property on the Tag rather than an
56
+ * attribute, so `JSON.parse(JSON.stringify(…))` at the serialize boundary drops
57
+ * it and it can never reach the HTML; the applier deletes it once read.
58
+ */
59
+ export const SCHEMA_TYPE_EXPLICIT = Symbol.for('refrakt.schemaTypeExplicit');
60
+ /**
61
+ * Check a table's internal consistency.
62
+ *
63
+ * Curation, not validation: nothing here checks a mapping against schema.org,
64
+ * because refrakt ships no ontology (D5). What it does check is the structural
65
+ * rule that cannot be got wrong safely — `collectJsonLd` nests a child entity
66
+ * only when the same node carries both `typeof` and `property`, so declaring a
67
+ * child's type without the property that holds it produces a detached
68
+ * top-level entity related to nothing. The table rejects that rather than
69
+ * emitting it.
70
+ */
71
+ export function validateSchemaTable(table, declaredAttributes = []) {
72
+ const issues = [];
73
+ if (table.by !== undefined) {
74
+ if (declaredAttributes.length > 0 && !declaredAttributes.includes(table.by)) {
75
+ issues.push({
76
+ path: 'by',
77
+ message: `\`by: '${table.by}'\` names an attribute the rune does not declare. \`by\` selects a row from \`attrs\`, not from a modifier.`,
78
+ });
79
+ }
80
+ if (!table.rows || Object.keys(table.rows).length === 0) {
81
+ issues.push({ path: 'rows', message: '`by` needs `rows` to select from.' });
82
+ }
83
+ if (!table.fallback && !table.type) {
84
+ issues.push({
85
+ path: 'fallback',
86
+ message: '`by` needs an explicit `fallback` row for the absent or unmatched case.',
87
+ });
88
+ }
89
+ }
90
+ const checkEntities = (rows, path) => {
91
+ for (const [name, row] of Object.entries(rows ?? {})) {
92
+ if (!row.property) {
93
+ issues.push({
94
+ path: `${path}.${name}`,
95
+ message: `A child entity needs the \`property\` that holds it. Without one it floats up as a detached top-level entity: \`collectJsonLd\` nests a typed node only when it also carries \`property\`.`,
96
+ });
97
+ }
98
+ if (!row.type) {
99
+ issues.push({ path: `${path}.${name}`, message: 'A child entity needs a `type`.' });
100
+ }
101
+ }
102
+ };
103
+ const checkRow = (row, path) => {
104
+ checkEntities(row.entities, `${path}entities`);
105
+ checkEntities(row.children, `${path}children`);
106
+ };
107
+ checkRow(table, '');
108
+ for (const [value, row] of Object.entries(table.rows ?? {}))
109
+ checkRow(row, `rows.${value}.`);
110
+ if (table.fallback)
111
+ checkRow(table.fallback, 'fallback.');
112
+ return issues;
113
+ }
114
+ /** Pick the row a set of attributes selects. */
115
+ export function selectRow(table, attrs) {
116
+ if (!table.by)
117
+ return table;
118
+ const value = attrs[table.by];
119
+ const row = value === undefined ? undefined : table.rows?.[String(value)];
120
+ return row ?? table.fallback ?? table;
121
+ }
122
+ const isTag = (n) => Markdoc.Tag.isTag(n);
123
+ /**
124
+ * Find a node by name, whichever attribute carries it.
125
+ *
126
+ * `data-name` and `data-field` are the two surfaces a name can appear on today,
127
+ * and SPEC-133 moves nodes between them. Checking both, in one pass, with no
128
+ * branch on which matched, is what makes those moves a no-op here.
129
+ */
130
+ /**
131
+ * Every node bearing a name, in document order, within one rune.
132
+ *
133
+ * **The search stops at another rune's node.** ADR-008's flat namespace is
134
+ * unique *per rune*, so the same name means different things in a parent and in
135
+ * a child it contains: `character` names its title span `name`, and so does
136
+ * every `character-section` inside it. Reaching across that boundary published a
137
+ * character whose `name` was the character plus each of its section headings.
138
+ */
139
+ export function findAllByName(root, name) {
140
+ const kebab = name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
141
+ const out = [];
142
+ const visit = (node, top) => {
143
+ if (Array.isArray(node)) {
144
+ for (const c of node)
145
+ visit(c, top);
146
+ return;
147
+ }
148
+ if (!isTag(node))
149
+ return;
150
+ const attrs = node.attributes ?? {};
151
+ if (!top && attrs['data-rune'] !== undefined)
152
+ return;
153
+ const named = attrs['data-name'] === name ||
154
+ attrs['data-name'] === kebab ||
155
+ attrs['data-field'] === name ||
156
+ attrs['data-field'] === kebab;
157
+ if (named) {
158
+ out.push(node);
159
+ // A name marks one node, not a subtree: descending into a match would
160
+ // find nothing new and risks a nested re-use of the same name.
161
+ return;
162
+ }
163
+ for (const c of node.children ?? [])
164
+ visit(c, false);
165
+ };
166
+ visit(root, true);
167
+ return out;
168
+ }
169
+ /** The first node bearing a name, within one rune — see `findAllByName`. */
170
+ export function findByName(root, name) {
171
+ return findAllByName(root, name)[0];
172
+ }
173
+ /** The rune's field bag, as written by `createComponentRenderable`. */
174
+ function readBag(root) {
175
+ const raw = root.attributes?.['data-rune-fields'];
176
+ if (typeof raw !== 'string')
177
+ return {};
178
+ try {
179
+ return JSON.parse(raw);
180
+ }
181
+ catch {
182
+ return {};
183
+ }
184
+ }
185
+ /**
186
+ * Resolve one source to a carrier and stamp `property` on it.
187
+ *
188
+ * Returns the node that ended up carrying the property, or `undefined` when the
189
+ * source resolves to nothing — which is not an error: a rune legitimately omits
190
+ * optional content, and a row for absent content should emit nothing rather
191
+ * than an empty carrier.
192
+ */
193
+ function stamp(root, bag, source, property, sink) {
194
+ // Every node bearing the name, not just the first. A name in the rune's flat
195
+ // namespace can be worn by a whole collection — `recipe` gives each of its
196
+ // ingredient `<li>`s `data-name="ingredient"` — and stamping one of six would
197
+ // publish a single ingredient and silently drop the rest.
198
+ const nodes = findAllByName(root, source);
199
+ if (nodes.length > 0) {
200
+ for (const node of nodes)
201
+ node.attributes.property = property;
202
+ return nodes[0];
203
+ }
204
+ const value = bag[source];
205
+ if (value === undefined || value === '')
206
+ return undefined;
207
+ const meta = new Tag('meta', { property, content: carry(value) });
208
+ sink.push(meta);
209
+ return meta;
210
+ }
211
+ /**
212
+ * Wrap a node's own content so its text is readable as a literal.
213
+ *
214
+ * See the note on `SchemaRow.text` — an element carrying both `property` and
215
+ * `typeof` cannot also contribute its text, so the value needs its own element.
216
+ */
217
+ function applyText(root, source, property, tag = 'div') {
218
+ const node = findByName(root, source);
219
+ if (!node)
220
+ return;
221
+ const wrapper = new Tag(tag, { property }, node.children ?? []);
222
+ node.children = [wrapper];
223
+ }
224
+ /** Apply a row's own properties, entities and generated values to one node. */
225
+ function applyRow(node, row, bag, index) {
226
+ if (row.type)
227
+ node.attributes.typeof = row.type;
228
+ // D6 — travel the list declaration to `collectJsonLd`, which derives the
229
+ // graph and has no other way to know a one-item collection is still a list.
230
+ if (row.lists && row.lists.length > 0) {
231
+ node.attributes[SCHEMA_LISTS_ATTR] = JSON.stringify(row.lists);
232
+ }
233
+ const appended = [];
234
+ for (const [source, property] of Object.entries(row.properties ?? {})) {
235
+ stamp(node, bag, source, property, appended);
236
+ }
237
+ for (const [source, property] of Object.entries(row.text ?? {})) {
238
+ applyText(node, source, property, row.textTag);
239
+ }
240
+ for (const [property, generator] of Object.entries(row.generated ?? {})) {
241
+ // `index` is the one generator: a position exists nowhere in the content,
242
+ // so it cannot be resolved from a node or the bag.
243
+ if (generator === 'index' && index !== undefined) {
244
+ appended.push(new Tag('meta', { property, content: String(index + 1) }));
245
+ }
246
+ }
247
+ for (const [name, entity] of Object.entries(row.entities ?? {})) {
248
+ const span = buildEntity(node, entity, bag);
249
+ if (span)
250
+ appended.push(span);
251
+ }
252
+ if (appended.length > 0)
253
+ node.children = [...(node.children ?? []), ...appended];
254
+ }
255
+ /**
256
+ * Build a nested entity span, or nothing.
257
+ *
258
+ * D4 — an entity resolving to a bare `@type` with no properties emits nothing.
259
+ * Seven runes in the catalog assert a type and describe nothing; the table
260
+ * makes that a decision rather than an accident.
261
+ */
262
+ function buildEntity(root, entity, bag) {
263
+ const carriers = [];
264
+ for (const [source, property] of Object.entries(entity.properties ?? {})) {
265
+ const node = findByName(root, source);
266
+ if (node) {
267
+ // The source survives in the output, so the entity takes a copy of its
268
+ // value rather than relocating the node — moving it would change the
269
+ // rendered HTML, which this mechanism must not do.
270
+ const text = textOf(node);
271
+ if (text)
272
+ carriers.push(new Tag('meta', { property, content: text }));
273
+ continue;
274
+ }
275
+ const value = bag[source];
276
+ if (value !== undefined && value !== '') {
277
+ carriers.push(new Tag('meta', { property, content: carry(value) }));
278
+ }
279
+ }
280
+ if (carriers.length === 0)
281
+ return undefined;
282
+ return new Tag('span', { typeof: entity.type, property: entity.property }, carriers);
283
+ }
284
+ /**
285
+ * Carry a bag value onto a `content` attribute without changing its type.
286
+ *
287
+ * `data-rune-fields` stores values typed, and `collectJsonLd` reads `content`
288
+ * straight through — so coercing here would turn a `ratingValue` of `5` into
289
+ * `"5"` in the published JSON-LD. Caught by the baseline: it was the only
290
+ * difference between the hand-built spans and the table.
291
+ */
292
+ function carry(value) {
293
+ if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'string') {
294
+ return value;
295
+ }
296
+ return String(value);
297
+ }
298
+ /**
299
+ * Strip every `property=` stamp inside a child the parent is about to retype.
300
+ *
301
+ * Stops at a nested `typeof`: that subtree is its own entity and its properties
302
+ * belong to it, not to the child being retyped.
303
+ */
304
+ function clearProperties(node) {
305
+ const kept = [];
306
+ for (const child of node.children ?? []) {
307
+ if (!isTag(child)) {
308
+ kept.push(child);
309
+ continue;
310
+ }
311
+ const tag = child;
312
+ const attrs = tag.attributes ?? {};
313
+ // A rebuilt carrier — a `<meta property=… content=…>` with no name — exists
314
+ // only to carry a schema value. Stripping its property would leave an inert
315
+ // `<meta>` in the markup, so it goes with the mapping that created it.
316
+ const isCarrier = tag.name === 'meta' &&
317
+ attrs.property !== undefined &&
318
+ attrs['data-name'] === undefined &&
319
+ attrs['data-field'] === undefined;
320
+ if (isCarrier)
321
+ continue;
322
+ if (attrs.property !== undefined)
323
+ delete tag.attributes.property;
324
+ if (attrs.typeof === undefined)
325
+ clearProperties(tag);
326
+ kept.push(child);
327
+ }
328
+ node.children = kept;
329
+ }
330
+ function textOf(node) {
331
+ if (typeof node === 'string')
332
+ return node;
333
+ if (Array.isArray(node))
334
+ return node.map(textOf).join('');
335
+ if (!isTag(node))
336
+ return '';
337
+ const content = node.attributes?.content;
338
+ if (content !== undefined)
339
+ return String(content);
340
+ return (node.children ?? []).map(textOf).join('');
341
+ }
342
+ /**
343
+ * Apply a rune's schema table to its transform output.
344
+ *
345
+ * Runs on the rune's own root, after its transform has built the tree and
346
+ * `createComponentRenderable` has written the field bag.
347
+ */
348
+ export function applySchemaTable(output, table, attrs) {
349
+ if (!isTag(output))
350
+ return output;
351
+ // `schema="none"` suppresses the whole subtree, not just the root: a child
352
+ // rune declares its own type independently, so stripping only the root would
353
+ // leave orphan typed nodes with no container (WORK-552).
354
+ if (attrs.schema === 'none')
355
+ return output;
356
+ const root = output;
357
+ const row = selectRow(table, attrs);
358
+ const bag = readBag(root);
359
+ applyRow(root, row, bag);
360
+ // Children are retyped by the parent, never by themselves (D9). Markdoc
361
+ // transforms bottom-up, so by now the children carry their own `typeof` and
362
+ // this rewrites them — and only the parent can supply the `property` that
363
+ // nests them.
364
+ for (const [childName, childRow] of Object.entries(row.children ?? {})) {
365
+ const items = findChildren(root, childName);
366
+ items.forEach((item, i) => {
367
+ // A type the *author* stated is the child's own and survives: a
368
+ // `{% track type="song" %}` inside a podcast stays a song, because
369
+ // saying so and being overruled would make the attribute a lie. A type
370
+ // that came from the child's fallback row is marked implicit, and the
371
+ // parent is the better authority on what an unlabelled child is.
372
+ const marked = item;
373
+ const ownType = Boolean(marked[SCHEMA_TYPE_EXPLICIT]);
374
+ delete marked[SCHEMA_TYPE_EXPLICIT];
375
+ if (ownType) {
376
+ // A child that kept its own type keeps its own property map with it —
377
+ // the two are one statement, and re-stamping the parent's over it
378
+ // would duplicate every value it already carries. What the parent
379
+ // still supplies is the containing property and anything positional,
380
+ // which only a parent can know.
381
+ applyRow(item, { type: undefined, generated: childRow.generated }, readBag(item), i);
382
+ }
383
+ else {
384
+ // The parent retypes, so the parent's map is the whole truth for this
385
+ // child. Clearing first is what makes a property that does not belong
386
+ // to the new type disappear rather than linger: `byArtist` on a
387
+ // `PodcastEpisode` is worse than the `MusicRecording` it replaced,
388
+ // which was at least coherently wrong.
389
+ if (childRow.properties)
390
+ clearProperties(item);
391
+ applyRow(item, childRow, readBag(item), i);
392
+ }
393
+ item.attributes.property = childRow.property;
394
+ });
395
+ }
396
+ return output;
397
+ }
398
+ /**
399
+ * Every node the named child occupies, in document order.
400
+ *
401
+ * Matched on `data-rune` *or* a name, for the same reason `findByName` is
402
+ * attribute-agnostic: a parent may build some of a collection itself and receive
403
+ * the rest as authored child tags — `playlist` does exactly that, with markdown
404
+ * list items beside `{% track %}` children. Both populations belong to one
405
+ * collection and must take one row, or half of it would go unmapped.
406
+ */
407
+ function findChildren(root, name) {
408
+ const out = [];
409
+ const visit = (node) => {
410
+ if (Array.isArray(node)) {
411
+ for (const c of node)
412
+ visit(c);
413
+ return;
414
+ }
415
+ if (!isTag(node))
416
+ return;
417
+ const attrs = node.attributes ?? {};
418
+ if (attrs['data-rune'] === name || attrs['data-name'] === name || attrs['data-field'] === name)
419
+ out.push(node);
420
+ for (const c of node.children ?? [])
421
+ visit(c);
422
+ };
423
+ for (const c of root.children ?? [])
424
+ visit(c);
425
+ return out;
426
+ }
427
+ //# sourceMappingURL=schema-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-table.js","sourceRoot":"","sources":["../../src/lib/schema-table.ts"],"names":[],"mappings":"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"}
@@ -185,12 +185,45 @@ export interface SerializedAttribute {
185
185
  matches?: string[];
186
186
  description?: string;
187
187
  }
188
+ /** A rune's structured-data claims, ordered by schema.org property. */
189
+ export interface SerializedSchemaOrg {
190
+ type?: string;
191
+ /** The attribute a multi-row table selects on. */
192
+ by?: string;
193
+ /** Every schema.org property, with the source name that supplies it. */
194
+ properties: Array<{
195
+ property: string;
196
+ source: string;
197
+ kind: string;
198
+ entity?: string;
199
+ }>;
200
+ entities: Array<{
201
+ name: string;
202
+ type: string;
203
+ property: string;
204
+ }>;
205
+ children: Array<{
206
+ rune: string;
207
+ type: string;
208
+ property: string;
209
+ }>;
210
+ lists: string[];
211
+ /** Types this rune can emit, when the table selects a row by attribute. */
212
+ types: string[];
213
+ }
188
214
  export interface SerializedRune {
189
215
  name: string;
190
216
  plugin: string;
191
217
  aliases: string[];
192
218
  description: string;
193
219
  authoringHints?: string;
220
+ /**
221
+ * What this rune publishes as structured data — SPEC-130 / WORK-566.
222
+ *
223
+ * Nothing validates it against schema.org: refrakt ships no ontology (D5).
224
+ * Documenting it *is* the control, which is why it is here.
225
+ */
226
+ schemaOrg?: SerializedSchemaOrg;
194
227
  attributes: {
195
228
  own: Record<string, SerializedAttribute>;
196
229
  base?: {