@refrakt-md/runes 0.43.0 → 0.44.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 (47) hide show
  1. package/dist/composed-rune.d.ts +9 -1
  2. package/dist/composed-rune.d.ts.map +1 -1
  3. package/dist/composed-rune.js +14 -2
  4. package/dist/composed-rune.js.map +1 -1
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/composition-errors.d.ts +7 -0
  9. package/dist/lib/composition-errors.d.ts.map +1 -1
  10. package/dist/lib/composition-errors.js +7 -0
  11. package/dist/lib/composition-errors.js.map +1 -1
  12. package/dist/lib/composition-placement.d.ts +88 -0
  13. package/dist/lib/composition-placement.d.ts.map +1 -0
  14. package/dist/lib/composition-placement.js +180 -0
  15. package/dist/lib/composition-placement.js.map +1 -0
  16. package/dist/lib/composition.d.ts +54 -3
  17. package/dist/lib/composition.d.ts.map +1 -1
  18. package/dist/lib/composition.js +259 -38
  19. package/dist/lib/composition.js.map +1 -1
  20. package/dist/lib/index.d.ts +1 -1
  21. package/dist/lib/index.d.ts.map +1 -1
  22. package/dist/lib/index.js +123 -243
  23. package/dist/lib/index.js.map +1 -1
  24. package/dist/lib/resolver.d.ts +5 -0
  25. package/dist/lib/resolver.d.ts.map +1 -1
  26. package/dist/lib/resolver.js +25 -3
  27. package/dist/lib/resolver.js.map +1 -1
  28. package/dist/lib/slot-markers.js +9 -1
  29. package/dist/lib/slot-markers.js.map +1 -1
  30. package/dist/lib/universal-attribute-defs.d.ts +12 -0
  31. package/dist/lib/universal-attribute-defs.d.ts.map +1 -0
  32. package/dist/lib/universal-attribute-defs.js +225 -0
  33. package/dist/lib/universal-attribute-defs.js.map +1 -0
  34. package/dist/plugins.d.ts.map +1 -1
  35. package/dist/plugins.js +36 -1
  36. package/dist/plugins.js.map +1 -1
  37. package/dist/project-runes.d.ts +9 -2
  38. package/dist/project-runes.d.ts.map +1 -1
  39. package/dist/project-runes.js +5 -5
  40. package/dist/project-runes.js.map +1 -1
  41. package/dist/reference.d.ts.map +1 -1
  42. package/dist/reference.js +9 -0
  43. package/dist/reference.js.map +1 -1
  44. package/dist/tags/section.d.ts.map +1 -1
  45. package/dist/tags/section.js +10 -0
  46. package/dist/tags/section.js.map +1 -1
  47. package/package.json +3 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composition-placement.js","sourceRoot":"","sources":["../../src/lib/composition-placement.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ import type { RuneConfig } from '@refrakt-md/transform';
4
4
  import type { SchemaTable } from './schema-table.js';
5
5
  import { SchemaSideTable } from './schema-side-table.js';
6
6
  import type { RegistersDeclaration } from './registers.js';
7
+ import type { AxisTarget, CompositionPlacements } from './composition-placement.js';
7
8
  /**
8
9
  * A composition template, as `createContentModelSchema` receives it — the third
9
10
  * of D5's three mutually exclusive emit paths, beside `transform` and `emits`.
@@ -19,6 +20,8 @@ export interface CompositionTemplate {
19
20
  body: string;
20
21
  /** The `blocks` the rune declares — what `{% metablock %}` may name (D7). */
21
22
  blocks?: readonly string[];
23
+ /** D34 — the definition's defaults for universal axes, by attribute. */
24
+ defaults?: Readonly<Record<string, string>>;
22
25
  }
23
26
  /** One node of a composition's expansion, as the contract records it (D6). */
24
27
  export interface CompositionOutlineNode {
@@ -56,6 +59,31 @@ export interface CompositionInfo {
56
59
  config?: RuneConfig;
57
60
  /** The config key the engine finds it under. */
58
61
  typeName?: string;
62
+ /** Where each universal axis lands (D33): its placement, the rune it lands
63
+ * on, or why it is not offered. For `inspect`, `contracts` and `reference`. */
64
+ axes: Record<string, AxisPlacementInfo>;
65
+ /** D34 — the definition's defaults, each with where it lands and any literal
66
+ * the template writes there, which wins over it. */
67
+ defaults?: Record<string, CompositionDefaultInfo>;
68
+ }
69
+ /** One `defaults` entry, as tooling reads it (D34). */
70
+ export interface CompositionDefaultInfo {
71
+ value: string;
72
+ placement: AxisTarget['placement'];
73
+ /** The placed rune it lands on; absent for a root-placed axis. */
74
+ rune?: string;
75
+ /** A literal the template writes on that rune, which the default yields to. */
76
+ literal?: string;
77
+ }
78
+ /** One axis's placement on a composed rune, as tooling reads it. */
79
+ export interface AxisPlacementInfo {
80
+ placement: AxisTarget['placement'];
81
+ /** The placed rune it lands on. */
82
+ rune?: string;
83
+ /** Offered, but dropped with a warning: there is no carrier (D24). */
84
+ dropped?: string;
85
+ /** Not offered, and why (D33.3). */
86
+ unavailable?: string;
59
87
  }
60
88
  /** Compositions by schema — the same pattern as `schemaTables`: tooling has only
61
89
  * the schema to read from. */
@@ -77,9 +105,10 @@ export declare function frontmatterKeyLine(frontmatter: string, path: readonly s
77
105
  * that becomes a property of the outer entity, not a peer of it (D9). */
78
106
  export declare const SUBORDINATE_SCHEMA_TYPES: ReadonlySet<string>;
79
107
  /**
80
- * `{% slot name="x" each %}` is the spec's spelling, and Markdoc's tag grammar
81
- * has no bare boolean attribute. Rewrite it to `each=true` inside slot tags
82
- * only, outside quoted values, before parsing.
108
+ * `{% slot name="x" each %}` and `{% section chrome %}` are the spec's
109
+ * spellings (D26, D31), and Markdoc's tag grammar has no bare boolean
110
+ * attribute. Rewrite `each` inside slot tags, and `chrome` inside any tag, to
111
+ * `=true`, outside quoted values, before parsing.
83
112
  */
84
113
  export declare function normaliseEach(body: string): string;
85
114
  export interface CompiledComposition {
@@ -92,6 +121,19 @@ export interface CompiledComposition {
92
121
  * placed node came from (ADR-046 decision 3).
93
122
  */
94
123
  place: (resolved: ResolvedContent, attrs: Record<string, any>) => Node[];
124
+ /** Where the universal attributes land (D14, D31, D33). */
125
+ placements: CompositionPlacements;
126
+ /** One instance's attributes less the universal ones that moved off the root
127
+ * (or were dropped for want of a carrier). What the root keeps. */
128
+ rootAttributes: (attrs: Record<string, any>) => Record<string, any>;
129
+ /** The parsed template, as construction checked it. */
130
+ templateNodes: readonly Node[];
131
+ /** SPEC-182 D4 — the filled attributes the author also wrote, for the
132
+ * `fill-overridden` warning. */
133
+ overriddenFills: (resolved: ResolvedContent, written: Record<string, unknown>) => Array<{
134
+ attribute: string;
135
+ field: string;
136
+ }>;
95
137
  }
96
138
  export interface CompileCompositionOptions {
97
139
  contentModel: ContentModel | ((attrs: Record<string, any>) => ContentModel);
@@ -125,6 +167,8 @@ export interface CompositionDefinition {
125
167
  metaFields?: Record<string, unknown>;
126
168
  blocks?: Record<string, unknown>;
127
169
  provides?: string[];
170
+ /** D34 — defaults for universal axes, by attribute name. */
171
+ defaults?: Record<string, string>;
128
172
  template: string;
129
173
  /** 1-based line of the source on which the template body starts. */
130
174
  templateLine: number;
@@ -149,6 +193,13 @@ export declare function parseCompositionDefinition(rune: string, source: string)
149
193
  * resolves a field against the rune's modifier values, exactly as it does for a
150
194
  * tree-owning rune — which must declare the same attributes as modifiers for
151
195
  * its own blocks to fill.
196
+ *
197
+ * An enum attribute the template only forwards to a placed rune
198
+ * (`{% section media-position=$attrs["media-position"] %}`) is that rune's
199
+ * input, not a modifier of the composition: as a `data-*` on the empty root it
200
+ * would match the dimension rules meant for the rune it is forwarded to — a
201
+ * `data-media-position="cover"` root is laid out as a cover well. It stays in
202
+ * the field bag, which the schema row and registration read.
152
203
  */
153
204
  export declare function composedRuneConfig(def: CompositionDefinition): RuneConfig;
154
205
  /** One rune as the catalog-wide checks see it. */
@@ -1 +1 @@
1
- {"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../../src/lib/composition.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../../src/lib/composition.ts"],"names":[],"mappings":"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"}
@@ -68,6 +68,8 @@ import { SchemaSideTable } from './schema-side-table.js';
68
68
  import { declareSlotMarkers, declareSlotMarkersOnNodes } from './slot-markers.js';
69
69
  import { registersSources } from './registers.js';
70
70
  import { parse } from '../parse.js';
71
+ import { universalAttributeDefinition } from './universal-attribute-defs.js';
72
+ import { moveAttributes, resolvePlacements } from './composition-placement.js';
71
73
  const { Ast, Tag: MarkdocTag } = Markdoc;
72
74
  /** Compositions by schema — the same pattern as `schemaTables`: tooling has only
73
75
  * the schema to read from. */
@@ -212,6 +214,8 @@ function slotFields(model, fail) {
212
214
  refs,
213
215
  ...(listItems ? { listItems } : {}),
214
216
  ...(ambiguousList ? { ambiguousList } : {}),
217
+ ...(f.fills ? { fills: f.fills } : {}),
218
+ ...(f.itemModel ? { itemFields: f.itemModel.fields.map((i) => i.name) } : {}),
215
219
  });
216
220
  };
217
221
  if (s.type === 'sequence') {
@@ -259,47 +263,56 @@ function slotValues(model, resolved) {
259
263
  // Template parsing and the construction-time checks
260
264
  // ---------------------------------------------------------------------------
261
265
  /**
262
- * `{% slot name="x" each %}` is the spec's spelling, and Markdoc's tag grammar
263
- * has no bare boolean attribute. Rewrite it to `each=true` inside slot tags
264
- * only, outside quoted values, before parsing.
266
+ * `{% slot name="x" each %}` and `{% section chrome %}` are the spec's
267
+ * spellings (D26, D31), and Markdoc's tag grammar has no bare boolean
268
+ * attribute. Rewrite `each` inside slot tags, and `chrome` inside any tag, to
269
+ * `=true`, outside quoted values, before parsing.
265
270
  */
266
271
  export function normaliseEach(body) {
267
- return body.replace(/\{%-?\s*slot\b([^%]*?)(-?%\})/g, (whole, inner, close) => {
268
- let out = '';
269
- let quote = null;
270
- let i = 0;
271
- while (i < inner.length) {
272
- const ch = inner[i];
273
- if (quote) {
274
- if (ch === '\\') {
275
- out += inner.slice(i, i + 2);
276
- i += 2;
277
- continue;
278
- }
279
- if (ch === quote)
280
- quote = null;
281
- out += ch;
282
- i++;
283
- continue;
284
- }
285
- if (ch === '"' || ch === "'") {
286
- quote = ch;
287
- out += ch;
288
- i++;
272
+ return body.replace(/\{%-?\s*([a-z][\w-]*)\b([^%]*?)(-?%\})/g, (whole, tag, inner, close) => {
273
+ const bare = tag === 'slot' ? ['each', 'chrome'] : ['chrome'];
274
+ const out = bareToTrue(inner, bare);
275
+ return out === inner ? whole : whole.replace(inner + close, out + close);
276
+ });
277
+ }
278
+ /** Rewrite each bare word in `words` to `word=true`, outside quoted values. */
279
+ function bareToTrue(inner, words) {
280
+ let out = '';
281
+ let quote = null;
282
+ let i = 0;
283
+ while (i < inner.length) {
284
+ const ch = inner[i];
285
+ if (quote) {
286
+ if (ch === '\\') {
287
+ out += inner.slice(i, i + 2);
288
+ i += 2;
289
289
  continue;
290
290
  }
291
+ if (ch === quote)
292
+ quote = null;
293
+ out += ch;
294
+ i++;
295
+ continue;
296
+ }
297
+ if (ch === '"' || ch === "'") {
298
+ quote = ch;
299
+ out += ch;
300
+ i++;
301
+ continue;
302
+ }
303
+ if (i === 0 || /\s/.test(inner[i - 1])) {
291
304
  const rest = inner.slice(i);
292
- const m = /^each(?![\w=-])/.exec(rest);
293
- if (m && (i === 0 || /\s/.test(inner[i - 1]))) {
294
- out += 'each=true';
295
- i += m[0].length;
305
+ const word = words.find((w) => new RegExp(`^${w}(?![\\w=-])`).test(rest));
306
+ if (word) {
307
+ out += `${word}=true`;
308
+ i += word.length;
296
309
  continue;
297
310
  }
298
- out += ch;
299
- i++;
300
311
  }
301
- return whole.replace(inner + close, out + close);
302
- });
312
+ out += ch;
313
+ i++;
314
+ }
315
+ return out;
303
316
  }
304
317
  /** Variables a template node reads, by path, in attribute or content position. */
305
318
  function variablePaths(value, out = []) {
@@ -370,6 +383,8 @@ export function compileComposition(template, options) {
370
383
  const placedSlots = new Map();
371
384
  const slotOrder = [];
372
385
  const placements = [];
386
+ /** Every rune the template places, for the paint carrier and role owners. */
387
+ const templateRunes = [];
373
388
  const checkVariables = (n, scope) => {
374
389
  for (const path of variablePaths(n.attributes)) {
375
390
  const [root, name] = path;
@@ -438,6 +453,7 @@ export function compileComposition(template, options) {
438
453
  fail('composition-cycle', `it places \`{% ${n.tag} %}\`, which is the rune itself — a composition cycle.`);
439
454
  }
440
455
  placements.push({ rune: n.tag, ...(scope.parentRune ? { parent: scope.parentRune } : {}) });
456
+ templateRunes.push({ node: n, top: !scope.parentRune, inEach: !!scope.each });
441
457
  const children = outlineOf(n.children, { ...scope, parentRune: n.tag });
442
458
  return [{ rune: n.tag, ...(children.length > 0 ? { children } : {}) }];
443
459
  }
@@ -476,6 +492,11 @@ export function compileComposition(template, options) {
476
492
  if (!field) {
477
493
  fail('slot-unknown-field', `slot \`${name}\` names no field of the content model (fields: ${[...fields.keys()].join(', ') || 'none'}) (D26).`);
478
494
  }
495
+ if (field?.fills) {
496
+ fail('fill-placed', `slot \`${name}\` places field \`${name}\`, which fills ${Object.keys(field.fills)
497
+ .map((a) => `\`${a}\``)
498
+ .join(', ')}. A filling field is placed into the rune's values, which the template places through a meta block or \`$attrs\`; a slot as well would render it twice. Remove the slot (SPEC-182 D5).`);
499
+ }
479
500
  if (placedSlots.has(name)) {
480
501
  fail('slot-placed-twice', `slot \`${name}\` is placed twice. A slot is placed exactly once; two positions are two declared fields, or a theme's concern (D26).`);
481
502
  }
@@ -538,8 +559,63 @@ export function compileComposition(template, options) {
538
559
  return { metablock: name };
539
560
  }
540
561
  const outline = outlineOf(ast.children, {});
541
- // D11 — a field no slot places parses and then silently does not render.
542
- const unplaced = [...fields.keys()].filter((f) => !placedSlots.has(f));
562
+ // D14, D31, D33 — the paint carrier and every axis's target.
563
+ const plan = resolvePlacements(rune, templateRunes, (code, msg, at) => {
564
+ current = at ?? current;
565
+ return fail(code, msg);
566
+ });
567
+ current = undefined;
568
+ // D34 — each default lands where the author's attribute would. One that
569
+ // lands nowhere is a definition error, not a silent no-op.
570
+ const defaultsByNode = new Map();
571
+ const defaultInfo = {};
572
+ for (const [name, value] of Object.entries(template.defaults ?? {})) {
573
+ const target = name === 'density'
574
+ ? plan.carrier
575
+ ? { placement: 'carrier', node: plan.carrier, rune: plan.carrier.tag }
576
+ : { placement: 'carrier', dropped: `\`${rune}\` places no rune to carry it` }
577
+ : plan.axes[plan.axisOf[name]];
578
+ if (!target)
579
+ continue;
580
+ if (target.placement === 'root') {
581
+ defaultInfo[name] = { value, placement: 'root' };
582
+ continue;
583
+ }
584
+ if (!('node' in target)) {
585
+ const why = 'dropped' in target ? target.dropped : target.unavailable;
586
+ throw new CompositionError('defaults-invalid', `Rune "${rune}": \`defaults.${name}\` lands nowhere: ${why} (D34).`);
587
+ }
588
+ const into = defaultsByNode.get(target.node) ?? {};
589
+ into[name] = value;
590
+ defaultsByNode.set(target.node, into);
591
+ const literal = target.node.attributes[name];
592
+ defaultInfo[name] = {
593
+ value,
594
+ placement: target.placement,
595
+ rune: target.rune,
596
+ ...(literal !== undefined && typeof literal !== 'object' ? { literal: String(literal) } : {}),
597
+ };
598
+ }
599
+ // SPEC-182 D2 — `fills` names declared attributes and item-model fields.
600
+ for (const [name, field] of fields) {
601
+ if (!field.fills)
602
+ continue;
603
+ if (!field.itemFields) {
604
+ fail('fills-invalid', `field \`${name}\` declares \`fills\` but no \`itemModel\`: there is nothing to fill from. Give it an item model that reads the values (SPEC-182 D1).`);
605
+ }
606
+ for (const [attr, ref] of Object.entries(field.fills)) {
607
+ if (!declared.has(attr)) {
608
+ fail('fills-invalid', `field \`${name}\` fills \`${attr}\`, which the rune does not declare (declared: ${[...declared].join(', ') || 'none'}). Declare it under \`attributes\` (SPEC-182 D2).`);
609
+ }
610
+ const source = typeof ref === 'string' && ref.startsWith('$') ? ref.slice(1) : undefined;
611
+ if (!source || !field.itemFields.includes(source)) {
612
+ fail('fills-invalid', `field \`${name}\` fills \`${attr}\` from \`${ref}\`, which names no field of its item model (fields: ${field.itemFields.join(', ')}). Write it as \`$<field>\` (SPEC-182 D2).`);
613
+ }
614
+ }
615
+ }
616
+ // D11 — a field no slot places parses and then silently does not render. A
617
+ // filling field is placed into the rune's values (SPEC-182 D5).
618
+ const unplaced = [...fields.keys()].filter((f) => !placedSlots.has(f) && !fields.get(f).fills);
543
619
  if (unplaced.length > 0) {
544
620
  fail('field-unplaced', `content-model field${unplaced.length > 1 ? 's' : ''} ${unplaced.map((f) => `\`${f}\``).join(', ')} ${unplaced.length > 1 ? 'are' : 'is'} placed by no slot, so authored content would be dropped. Place ${unplaced.length > 1 ? 'each' : 'it'} with \`{% slot name="…" /%}\` (D11).`);
545
621
  }
@@ -548,11 +624,22 @@ export function compileComposition(template, options) {
548
624
  options.schema?.fallback?.type ??
549
625
  Object.values(options.schema?.rows ?? {}).find((r) => r.type)?.type;
550
626
  const nodeSources = registersNodeSources(options.registers, fields, declared);
627
+ const axes = {};
628
+ for (const [axis, target] of Object.entries(plan.axes)) {
629
+ axes[axis] = {
630
+ placement: target.placement,
631
+ ...('rune' in target ? { rune: target.rune } : {}),
632
+ ...('dropped' in target ? { dropped: target.dropped } : {}),
633
+ ...('unavailable' in target ? { unavailable: target.unavailable } : {}),
634
+ };
635
+ }
551
636
  const info = {
552
637
  rune,
553
638
  slots: slotOrder,
554
639
  placements,
555
640
  outline,
641
+ axes,
642
+ ...(Object.keys(defaultInfo).length > 0 ? { defaults: defaultInfo } : {}),
556
643
  ...(schemaType ? { schemaType } : {}),
557
644
  };
558
645
  const attributeNames = [...declared];
@@ -565,6 +652,8 @@ export function compileComposition(template, options) {
565
652
  resolved: slotValues(structural, resolved),
566
653
  attrs: bound,
567
654
  fields,
655
+ moves: moveAttributes(plan, attrs).moves,
656
+ defaults: defaultsByNode,
568
657
  };
569
658
  const placed = expand(ast.children, ctx);
570
659
  // Top-level template nodes are the composed rune's own (D10). A slot
@@ -575,7 +664,11 @@ export function compileComposition(template, options) {
575
664
  }
576
665
  return placed;
577
666
  };
578
- const transform = (resolved, attrs, config) => {
667
+ const transform = (resolved, attrs, config, node) => {
668
+ // SPEC-182 D2 — values a paragraph fills become the rune's own, before
669
+ // anything reads them. `attrs` is the caller's object, filled in place, so
670
+ // the schema table applied after this transform reads them too.
671
+ Object.assign(attrs, filledAttributes(resolved, node?.attributes ?? {}));
579
672
  const placed = place(resolved, attrs);
580
673
  const children = Markdoc.transform(placed, templateConfig(config));
581
674
  // The field bag holds every attribute the author set (or that defaulted):
@@ -597,7 +690,61 @@ export function compileComposition(template, options) {
597
690
  copyRegisteredNodes(root, nodeSources);
598
691
  return root;
599
692
  };
600
- return { transform, info, place };
693
+ /** SPEC-182 D2, D4 — what the filling fields supply, for the attributes the
694
+ * author did not write. The author's attribute always wins. */
695
+ const filledAttributes = (resolved, written) => {
696
+ const out = {};
697
+ const values = slotValues(structural, resolved);
698
+ for (const [name, field] of fields) {
699
+ if (!field.fills)
700
+ continue;
701
+ const record = values[`${name}Data`];
702
+ if (!record)
703
+ continue;
704
+ for (const [attr, ref] of Object.entries(field.fills)) {
705
+ if (written[attr] !== undefined)
706
+ continue;
707
+ const value = record[ref.slice(1)];
708
+ if (value !== undefined && value !== '')
709
+ out[attr] = value;
710
+ }
711
+ }
712
+ return out;
713
+ };
714
+ /** SPEC-182 D4 — attributes the author wrote that a paragraph would also have
715
+ * filled: the paragraph is consumed and does not render. */
716
+ const overriddenFills = (resolved, written) => {
717
+ const out = [];
718
+ const values = slotValues(structural, resolved);
719
+ for (const [name, field] of fields) {
720
+ if (!field.fills || !values[`${name}Data`])
721
+ continue;
722
+ for (const attr of Object.keys(field.fills)) {
723
+ if (written[attr] !== undefined)
724
+ out.push({ attribute: attr, field: name });
725
+ }
726
+ }
727
+ return out;
728
+ };
729
+ const rootAttributes = (attrs) => {
730
+ const { moved } = moveAttributes(plan, attrs);
731
+ if (moved.size === 0)
732
+ return attrs;
733
+ const out = {};
734
+ for (const [name, value] of Object.entries(attrs))
735
+ if (!moved.has(name))
736
+ out[name] = value;
737
+ return out;
738
+ };
739
+ return {
740
+ transform,
741
+ info,
742
+ place,
743
+ placements: plan,
744
+ rootAttributes,
745
+ overriddenFills,
746
+ templateNodes: ast.children,
747
+ };
601
748
  }
602
749
  function asNodes(value) {
603
750
  if (Array.isArray(value))
@@ -667,6 +814,19 @@ function substituteNode(node, ctx) {
667
814
  }
668
815
  attributes[key] = resolved;
669
816
  }
817
+ if (node.type === 'tag') {
818
+ // D31 — the marker is the template's, consumed here: it reaches neither
819
+ // the placed rune's attributes nor the HTML.
820
+ delete attributes.chrome;
821
+ // D34.3 — author > template literal > definition default.
822
+ for (const [name, value] of Object.entries(ctx.defaults?.get(node) ?? {})) {
823
+ if (attributes[name] === undefined)
824
+ attributes[name] = value;
825
+ }
826
+ const moved = ctx.moves?.get(node);
827
+ if (moved)
828
+ Object.assign(attributes, moved);
829
+ }
670
830
  return cloneNode(node, attributes, expand(node.children, ctx));
671
831
  }
672
832
  /** The items a list field resolved to, each with its `$each` binding and content. */
@@ -862,6 +1022,7 @@ const DEFINITION_KEYS = new Set([
862
1022
  'metaFields',
863
1023
  'blocks',
864
1024
  'provides',
1025
+ 'defaults',
865
1026
  ]);
866
1027
  /** Keys that would give a composed rune styles of its own (D2). */
867
1028
  const STYLE_KEYS = new Set(['css', 'style', 'styles', 'stylesheet', 'block', 'class']);
@@ -974,6 +1135,7 @@ export function parseCompositionDefinition(rune, source) {
974
1135
  (!Array.isArray(fm.provides) || !fm.provides.every((p) => typeof p === 'string'))) {
975
1136
  fail('invalid-value', '`provides` must be a list of names.', ['provides']);
976
1137
  }
1138
+ const defaults = parseDefaults(fm.defaults, (code, msg, at) => fail(code, msg, ['defaults', ...(at ?? [])]));
977
1139
  return {
978
1140
  rune,
979
1141
  tag,
@@ -986,12 +1148,46 @@ export function parseCompositionDefinition(rune, source) {
986
1148
  ...(fm.metaFields ? { metaFields: fm.metaFields } : {}),
987
1149
  ...(fm.blocks ? { blocks: fm.blocks } : {}),
988
1150
  ...(fm.provides ? { provides: fm.provides } : {}),
1151
+ ...(defaults ? { defaults } : {}),
989
1152
  template: match[2],
990
1153
  // `---`, the frontmatter's lines, `---`: the body starts on the next.
991
1154
  templateLine: frontmatter.split(/\r?\n/).length + 3,
992
1155
  frontmatter,
993
1156
  };
994
1157
  }
1158
+ /** D34.6 — `density` has no author-facing attribute, so its scale is stated here. */
1159
+ const DENSITY_VALUES = ['full', 'compact', 'minimal'];
1160
+ /**
1161
+ * D34 — `defaults`: universal axes only, each with a value the axis accepts.
1162
+ * A rune's own attribute takes its default where it is declared.
1163
+ */
1164
+ function parseDefaults(raw, fail) {
1165
+ if (raw === undefined)
1166
+ return undefined;
1167
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
1168
+ fail('defaults-invalid', '`defaults` must be a mapping of universal attributes to values.');
1169
+ }
1170
+ const out = {};
1171
+ for (const [key, value] of Object.entries(raw)) {
1172
+ if (value === null || typeof value === 'object') {
1173
+ fail('defaults-invalid', `\`defaults.${key}\` must be a single value.`, [key]);
1174
+ }
1175
+ const text = String(value);
1176
+ const accepted = key === 'density'
1177
+ ? DENSITY_VALUES
1178
+ : universalAttributeDefinition(key) === undefined
1179
+ ? undefined
1180
+ : universalAttributeDefinition(key).matches;
1181
+ if (key !== 'density' && universalAttributeDefinition(key) === undefined) {
1182
+ fail('defaults-invalid', `\`defaults\` takes universal attributes only, and \`${key}\` is not one. A rune's own attribute takes its default where it is declared, under \`attributes\` (D34).`, [key]);
1183
+ }
1184
+ if (Array.isArray(accepted) && !accepted.includes(text)) {
1185
+ fail('defaults-invalid', `\`defaults.${key}\` is \`${text}\`, which \`${key}\` does not accept (${accepted.join(', ')}).`, [key]);
1186
+ }
1187
+ out[key] = text;
1188
+ }
1189
+ return out;
1190
+ }
995
1191
  function parseAttributes(raw, sectionFail) {
996
1192
  const fail = (code, msg) => sectionFail(code, msg);
997
1193
  if (raw === undefined)
@@ -1042,6 +1238,7 @@ const FIELD_KEYS = new Set([
1042
1238
  'itemModel',
1043
1239
  'emitTag',
1044
1240
  'emitAttributes',
1241
+ 'fills',
1045
1242
  ]);
1046
1243
  /** A field list as YAML writes it: a mapping keyed by name, or a list with `name`. */
1047
1244
  function parseFields(raw, where, fail) {
@@ -1306,6 +1503,13 @@ function parseDelimitedModel(c, fail) {
1306
1503
  * resolves a field against the rune's modifier values, exactly as it does for a
1307
1504
  * tree-owning rune — which must declare the same attributes as modifiers for
1308
1505
  * its own blocks to fill.
1506
+ *
1507
+ * An enum attribute the template only forwards to a placed rune
1508
+ * (`{% section media-position=$attrs["media-position"] %}`) is that rune's
1509
+ * input, not a modifier of the composition: as a `data-*` on the empty root it
1510
+ * would match the dimension rules meant for the rune it is forwarded to — a
1511
+ * `data-media-position="cover"` root is laid out as a cover well. It stays in
1512
+ * the field bag, which the schema row and registration read.
1309
1513
  */
1310
1514
  export function composedRuneConfig(def) {
1311
1515
  const read = new Set();
@@ -1317,22 +1521,39 @@ export function composedRuneConfig(def) {
1317
1521
  read.add(ref);
1318
1522
  }
1319
1523
  }
1524
+ const forwarded = forwardedAttributes(def.template);
1320
1525
  const modifiers = {};
1321
1526
  for (const [name, attr] of Object.entries(def.attributes)) {
1322
- if (!attr.matches && !read.has(name))
1527
+ if (!read.has(name) && (!attr.matches || forwarded.has(name)))
1323
1528
  continue;
1324
1529
  modifiers[name] = {
1325
1530
  source: 'meta',
1326
1531
  ...(attr.default !== undefined ? { default: String(attr.default) } : {}),
1327
1532
  };
1328
1533
  }
1534
+ // D34 — a root-placed axis's default is the generated config's, since a
1535
+ // template never writes on the root.
1536
+ const d = def.defaults ?? {};
1329
1537
  return {
1538
+ composed: true,
1539
+ ...(d.width ? { defaultWidth: d.width } : {}),
1540
+ ...(d.spacing ? { defaultSpacing: d.spacing } : {}),
1541
+ ...(d.inset ? { defaultInset: d.inset } : {}),
1330
1542
  ...(Object.keys(modifiers).length > 0 ? { modifiers } : {}),
1331
1543
  ...(def.metaFields ? { metaFields: def.metaFields } : {}),
1332
1544
  ...(def.blocks ? { blocks: def.blocks } : {}),
1333
1545
  ...(def.provides ? { provides: def.provides } : {}),
1334
1546
  };
1335
1547
  }
1548
+ /** The attributes a template passes to a placed rune as an attribute value:
1549
+ * `name=$attrs.x` or `name=$attrs["x"]`. */
1550
+ function forwardedAttributes(template) {
1551
+ const out = new Set();
1552
+ for (const m of template.matchAll(/[\w-]+\s*=\s*\$attrs(?:\.([\w-]+)|\[\s*["']([^"']+)["']\s*\])/g)) {
1553
+ out.add(m[1] ?? m[2]);
1554
+ }
1555
+ return out;
1556
+ }
1336
1557
  /** The schema types a rune can emit at its root — its table's, and its `seoType`. */
1337
1558
  function emittedTypes(entry, table) {
1338
1559
  return [