@refrakt-md/runes 0.14.4 → 0.16.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 (119) hide show
  1. package/dist/aggregate-resolve.d.ts +4 -0
  2. package/dist/aggregate-resolve.d.ts.map +1 -0
  3. package/dist/aggregate-resolve.js +209 -0
  4. package/dist/aggregate-resolve.js.map +1 -0
  5. package/dist/collection-helpers.d.ts +101 -0
  6. package/dist/collection-helpers.d.ts.map +1 -0
  7. package/dist/collection-helpers.js +206 -0
  8. package/dist/collection-helpers.js.map +1 -0
  9. package/dist/collection-resolve.d.ts +5 -0
  10. package/dist/collection-resolve.d.ts.map +1 -0
  11. package/dist/collection-resolve.js +209 -0
  12. package/dist/collection-resolve.js.map +1 -0
  13. package/dist/config.d.ts +46 -4
  14. package/dist/config.d.ts.map +1 -1
  15. package/dist/config.js +208 -88
  16. package/dist/config.js.map +1 -1
  17. package/dist/deferred-body.d.ts +19 -0
  18. package/dist/deferred-body.d.ts.map +1 -0
  19. package/dist/deferred-body.js +70 -0
  20. package/dist/deferred-body.js.map +1 -0
  21. package/dist/drawer-pipeline.d.ts +33 -0
  22. package/dist/drawer-pipeline.d.ts.map +1 -0
  23. package/dist/drawer-pipeline.js +206 -0
  24. package/dist/drawer-pipeline.js.map +1 -0
  25. package/dist/expand-pipeline.d.ts +51 -0
  26. package/dist/expand-pipeline.d.ts.map +1 -0
  27. package/dist/expand-pipeline.js +314 -0
  28. package/dist/expand-pipeline.js.map +1 -0
  29. package/dist/field-match.d.ts +43 -0
  30. package/dist/field-match.d.ts.map +1 -0
  31. package/dist/field-match.js +121 -0
  32. package/dist/field-match.js.map +1 -0
  33. package/dist/functions.d.ts +36 -0
  34. package/dist/functions.d.ts.map +1 -0
  35. package/dist/functions.js +74 -0
  36. package/dist/functions.js.map +1 -0
  37. package/dist/index.d.ts +34 -4
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +95 -4
  40. package/dist/index.js.map +1 -1
  41. package/dist/lang-map.d.ts +32 -0
  42. package/dist/lang-map.d.ts.map +1 -0
  43. package/dist/lang-map.js +76 -0
  44. package/dist/lang-map.js.map +1 -0
  45. package/dist/lib/index.d.ts +6 -0
  46. package/dist/lib/index.d.ts.map +1 -1
  47. package/dist/lib/index.js +9 -0
  48. package/dist/lib/index.js.map +1 -1
  49. package/dist/lib/read-file.d.ts +78 -0
  50. package/dist/lib/read-file.d.ts.map +1 -0
  51. package/dist/lib/read-file.js +166 -0
  52. package/dist/lib/read-file.js.map +1 -0
  53. package/dist/nodes.d.ts.map +1 -1
  54. package/dist/nodes.js +18 -2
  55. package/dist/nodes.js.map +1 -1
  56. package/dist/outline-scope.d.ts +46 -0
  57. package/dist/outline-scope.d.ts.map +1 -0
  58. package/dist/outline-scope.js +208 -0
  59. package/dist/outline-scope.js.map +1 -0
  60. package/dist/plugins.d.ts +13 -0
  61. package/dist/plugins.d.ts.map +1 -1
  62. package/dist/plugins.js +65 -1
  63. package/dist/plugins.js.map +1 -1
  64. package/dist/relationships-resolve.d.ts +4 -0
  65. package/dist/relationships-resolve.d.ts.map +1 -0
  66. package/dist/relationships-resolve.js +180 -0
  67. package/dist/relationships-resolve.js.map +1 -0
  68. package/dist/snippet-pipeline.d.ts +42 -0
  69. package/dist/snippet-pipeline.d.ts.map +1 -0
  70. package/dist/snippet-pipeline.js +219 -0
  71. package/dist/snippet-pipeline.js.map +1 -0
  72. package/dist/tags/aggregate.d.ts +19 -0
  73. package/dist/tags/aggregate.d.ts.map +1 -0
  74. package/dist/tags/aggregate.js +59 -0
  75. package/dist/tags/aggregate.js.map +1 -0
  76. package/dist/tags/card.d.ts +25 -0
  77. package/dist/tags/card.d.ts.map +1 -0
  78. package/dist/tags/card.js +138 -0
  79. package/dist/tags/card.js.map +1 -0
  80. package/dist/tags/collection.d.ts +10 -0
  81. package/dist/tags/collection.d.ts.map +1 -0
  82. package/dist/tags/collection.js +54 -0
  83. package/dist/tags/collection.js.map +1 -0
  84. package/dist/tags/conversation.d.ts.map +1 -1
  85. package/dist/tags/conversation.js +26 -6
  86. package/dist/tags/conversation.js.map +1 -1
  87. package/dist/tags/drawer.d.ts +26 -0
  88. package/dist/tags/drawer.d.ts.map +1 -0
  89. package/dist/tags/drawer.js +134 -0
  90. package/dist/tags/drawer.js.map +1 -0
  91. package/dist/tags/expand.d.ts +22 -0
  92. package/dist/tags/expand.d.ts.map +1 -0
  93. package/dist/tags/expand.js +76 -0
  94. package/dist/tags/expand.js.map +1 -0
  95. package/dist/tags/progress.d.ts +9 -0
  96. package/dist/tags/progress.d.ts.map +1 -0
  97. package/dist/tags/progress.js +96 -0
  98. package/dist/tags/progress.js.map +1 -0
  99. package/dist/tags/relationships.d.ts +12 -0
  100. package/dist/tags/relationships.d.ts.map +1 -0
  101. package/dist/tags/relationships.js +62 -0
  102. package/dist/tags/relationships.js.map +1 -0
  103. package/dist/tags/snippet.d.ts +19 -0
  104. package/dist/tags/snippet.d.ts.map +1 -0
  105. package/dist/tags/snippet.js +49 -0
  106. package/dist/tags/snippet.js.map +1 -0
  107. package/dist/util.d.ts +11 -0
  108. package/dist/util.d.ts.map +1 -1
  109. package/dist/util.js +35 -0
  110. package/dist/util.js.map +1 -1
  111. package/dist/xref-patterns.d.ts +57 -0
  112. package/dist/xref-patterns.d.ts.map +1 -0
  113. package/dist/xref-patterns.js +140 -0
  114. package/dist/xref-patterns.js.map +1 -0
  115. package/dist/xref-resolve.d.ts +22 -5
  116. package/dist/xref-resolve.d.ts.map +1 -1
  117. package/dist/xref-resolve.js +203 -79
  118. package/dist/xref-resolve.js.map +1 -1
  119. package/package.json +3 -3
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Drawer pipeline hooks (SPEC-060, WORK-257).
3
+ *
4
+ * - **Register** walks each page's renderable for drawer tags
5
+ * (`data-rune="drawer"`), and adds each as a page-scoped entity
6
+ * (`type: 'drawer'`, `scope: 'page'`, `sourceUrl: "${pageUrl}#drawer-${id}"`).
7
+ * Page scope means two pages can both declare `id="auth"` without
8
+ * colliding in the site-wide registry (WORK-256).
9
+ *
10
+ * - **postProcess** resolves drawer title heading levels for drawers that
11
+ * were authored without an explicit `headingLevel`. The title carries
12
+ * a `data-drawer-title-auto` marker emitted by the schema; the walk
13
+ * tracks document outline depth (the most recent `<h{n}>` seen) and
14
+ * rewrites the marked tag to `h{n+1}` (clamped 1..6, default h2 when
15
+ * no preceding heading exists).
16
+ */
17
+ import Markdoc from '@markdoc/markdoc';
18
+ import { DRAWER_TITLE_AUTO_MARKER } from './tags/drawer.js';
19
+ const { Tag } = Markdoc;
20
+ const HEADING_TAG_RE = /^h([1-6])$/;
21
+ /** Read a `data-*` attribute from a tag, treating undefined as undefined. */
22
+ function readDataAttr(tag, key) {
23
+ const v = tag.attributes?.[key];
24
+ return typeof v === 'string' ? v : undefined;
25
+ }
26
+ /** Read the `content` of a property meta tag (`<meta data-field="...">`)
27
+ * immediately under the drawer tag. The schema emits side/size/shortcut as
28
+ * meta tags; the identity-transform engine consumes them later and stamps
29
+ * them as `data-side` etc. on the wrapper. The register hook runs before
30
+ * the engine, so we have to read directly from the meta tags. */
31
+ function readPropertyMeta(drawerTag, field) {
32
+ for (const child of drawerTag.children ?? []) {
33
+ if (!Tag.isTag(child))
34
+ continue;
35
+ const c = child;
36
+ if (c.name !== 'meta')
37
+ continue;
38
+ if (c.attributes?.['data-field'] !== field)
39
+ continue;
40
+ const content = c.attributes?.['content'];
41
+ return typeof content === 'string' ? content : undefined;
42
+ }
43
+ return undefined;
44
+ }
45
+ /**
46
+ * Walk the renderable tree of every page and register each drawer rune
47
+ * as a page-scoped entity. Each drawer surfaces in the registry as
48
+ * `{ type: 'drawer', id, scope: 'page', sourceUrl: '<pageUrl>#drawer-<id>' }`
49
+ * with `data.title`, `data.side`, `data.size`, `data.shortcut` for
50
+ * downstream consumers (xref resolver, registry-driven tooling).
51
+ */
52
+ export function registerDrawers(pages, registry, ctx) {
53
+ for (const page of pages) {
54
+ const drawersOnPage = [];
55
+ const seenIds = new Set();
56
+ walkForDrawers(page.renderable, (tag) => {
57
+ const id = readDataAttr(tag, 'data-drawer-id');
58
+ if (!id) {
59
+ ctx.error('drawer rune is missing required `id` attribute', page.url);
60
+ return;
61
+ }
62
+ if (seenIds.has(id)) {
63
+ ctx.warn(`drawer id="${id}" is declared more than once on this page — last declaration wins`, page.url);
64
+ }
65
+ seenIds.add(id);
66
+ drawersOnPage.push({
67
+ id,
68
+ title: extractTitleText(tag),
69
+ side: readPropertyMeta(tag, 'side') ?? 'right',
70
+ size: readPropertyMeta(tag, 'size') ?? 'md',
71
+ shortcut: readPropertyMeta(tag, 'shortcut'),
72
+ });
73
+ });
74
+ for (const d of drawersOnPage) {
75
+ registry.register({
76
+ type: 'drawer',
77
+ id: d.id,
78
+ scope: 'page',
79
+ sourceUrl: `${page.url}#drawer-${d.id}`,
80
+ data: {
81
+ title: d.title,
82
+ side: d.side,
83
+ size: d.size,
84
+ shortcut: d.shortcut,
85
+ },
86
+ });
87
+ }
88
+ }
89
+ }
90
+ /** Walk the renderable looking for drawer-rune tags. Visits each drawer's
91
+ * shell exactly once (we don't recurse into a drawer's body, drawer-in-drawer
92
+ * is out of scope). */
93
+ function walkForDrawers(node, visit) {
94
+ if (Array.isArray(node)) {
95
+ for (const c of node)
96
+ walkForDrawers(c, visit);
97
+ return;
98
+ }
99
+ if (!Tag.isTag(node))
100
+ return;
101
+ const tag = node;
102
+ if (tag.attributes?.['data-rune'] === 'drawer') {
103
+ visit(tag);
104
+ // Don't recurse — drawer-in-drawer isn't supported and skipping the
105
+ // body avoids treating a future inner-drawer descendant as a sibling
106
+ // of the outer one.
107
+ return;
108
+ }
109
+ if (!tag.children)
110
+ return;
111
+ for (const c of tag.children)
112
+ walkForDrawers(c, visit);
113
+ }
114
+ /** Pull the drawer's title text out of its header → title element. Returns
115
+ * `undefined` when the drawer has no title (the rune renders headerless
116
+ * in that case). String concatenation of leaf text nodes is good enough —
117
+ * drawer titles are short. */
118
+ function extractTitleText(drawerTag) {
119
+ const titleTag = findByDataName(drawerTag, 'title');
120
+ if (!titleTag)
121
+ return undefined;
122
+ const text = collectText(titleTag);
123
+ return text.length > 0 ? text : undefined;
124
+ }
125
+ function findByDataName(root, name) {
126
+ if (root.attributes?.['data-name'] === name) {
127
+ return root;
128
+ }
129
+ for (const c of root.children ?? []) {
130
+ if (Tag.isTag(c)) {
131
+ const hit = findByDataName(c, name);
132
+ if (hit)
133
+ return hit;
134
+ }
135
+ }
136
+ return undefined;
137
+ }
138
+ function collectText(node) {
139
+ if (typeof node === 'string')
140
+ return node;
141
+ if (Array.isArray(node))
142
+ return node.map(collectText).join('');
143
+ if (Tag.isTag(node)) {
144
+ const tag = node;
145
+ return (tag.children ?? []).map(collectText).join('');
146
+ }
147
+ return '';
148
+ }
149
+ /**
150
+ * postProcess walk: rewrite each `data-drawer-title-auto` heading to the
151
+ * level computed from the outline depth at that position on the page.
152
+ * Returns the same renderable identity when no drawer titles needed
153
+ * rewriting, so downstream postProcess steps can skip a no-op pass.
154
+ */
155
+ export function resolveAutoDrawerTitleLevels(renderable) {
156
+ const state = { lastHeadingLevel: 0, mutated: false };
157
+ const next = walkAndRewriteTitles(renderable, state);
158
+ return state.mutated ? next : renderable;
159
+ }
160
+ function walkAndRewriteTitles(node, state) {
161
+ if (typeof node === 'string' || typeof node === 'number' || node == null)
162
+ return node;
163
+ if (Array.isArray(node)) {
164
+ let mutated = false;
165
+ const next = node.map((c) => {
166
+ const w = walkAndRewriteTitles(c, state);
167
+ if (w !== c)
168
+ mutated = true;
169
+ return w;
170
+ });
171
+ return mutated ? next : node;
172
+ }
173
+ if (!Tag.isTag(node))
174
+ return node;
175
+ const tag = node;
176
+ const headingMatch = HEADING_TAG_RE.exec(tag.name);
177
+ // Auto-title placeholder: rewrite this node's tag name based on the
178
+ // last heading level we saw. Default to h2 when no heading has been
179
+ // encountered yet (the page-title h1 lives in layout, so the next
180
+ // reasonable level inside the body is h2).
181
+ const isAutoTitle = tag.attributes?.[DRAWER_TITLE_AUTO_MARKER] === 'true';
182
+ if (isAutoTitle) {
183
+ const base = state.lastHeadingLevel === 0 ? 1 : state.lastHeadingLevel;
184
+ const targetLevel = Math.min(6, Math.max(1, base + 1));
185
+ state.mutated = true;
186
+ const newAttrs = { ...tag.attributes };
187
+ delete newAttrs[DRAWER_TITLE_AUTO_MARKER];
188
+ return new Tag(`h${targetLevel}`, newAttrs, tag.children);
189
+ }
190
+ if (headingMatch) {
191
+ state.lastHeadingLevel = Number(headingMatch[1]);
192
+ }
193
+ if (!tag.children || tag.children.length === 0)
194
+ return tag;
195
+ let mutated = false;
196
+ const newChildren = tag.children.map((c) => {
197
+ const w = walkAndRewriteTitles(c, state);
198
+ if (w !== c)
199
+ mutated = true;
200
+ return w;
201
+ });
202
+ if (!mutated)
203
+ return tag;
204
+ return new Tag(tag.name, tag.attributes, newChildren);
205
+ }
206
+ //# sourceMappingURL=drawer-pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawer-pipeline.js","sourceRoot":"","sources":["../src/drawer-pipeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Expand pipeline (SPEC-066, WORK-260).
3
+ *
4
+ * Post-process resolver that finds `{% expand %}` placeholder tags
5
+ * (`data-rune="expand-pending"`) in a page's renderable tree, looks
6
+ * each up in the registry, reads + parses the entity's `sourceFile`
7
+ * (cached per build), calls the plugin's `extract()` to grab the
8
+ * embeddable AST subtree, optionally shifts heading levels, transforms
9
+ * the subtree using the build's full tags+nodes config, and substitutes
10
+ * the result in place wrapped in `<section class="rf-expand"
11
+ * data-rune="expand" data-outline-scope="${id}">`.
12
+ *
13
+ * The wrapper's `data-outline-scope` attribute is picked up by the
14
+ * WORK-259 walkers — heading IDs inside get prefixed, TOC items
15
+ * pointing at them get dropped — without expand needing to coordinate.
16
+ *
17
+ * Per-build caches:
18
+ * - `sourceFile → parsedAst` (one parse per file regardless of how
19
+ * many pages embed it)
20
+ *
21
+ * Per-page state:
22
+ * - resolution stack (`(type, id)` tuples) for cycle detection
23
+ */
24
+ import type { Node } from '@markdoc/markdoc';
25
+ import type { EntityRegistry, PipelineContext } from '@refrakt-md/types';
26
+ import type { CompiledXrefPattern } from './xref-patterns.js';
27
+ export interface ExpandResolveContext {
28
+ pageUrl: string;
29
+ registry: Readonly<EntityRegistry>;
30
+ patterns: CompiledXrefPattern[];
31
+ embedConfig?: {
32
+ tags: Record<string, unknown>;
33
+ nodes: Record<string, unknown>;
34
+ projectRoot?: string;
35
+ };
36
+ ctx: PipelineContext;
37
+ /** Per-build source-file cache. Shared across calls within one build,
38
+ * so a file embedded on twenty pages is parsed once. */
39
+ parseCache: Map<string, Node>;
40
+ }
41
+ /** Reset the per-build cache. Tests call this between cases; production
42
+ * builds rely on the cache living for the build's lifetime (each build
43
+ * starts fresh because the runes package is imported anew). */
44
+ export declare function __resetExpandCache(): void;
45
+ /**
46
+ * Walk a renderable tree, replace every `expand-pending` placeholder
47
+ * with the substituted entity content. Returns the same renderable
48
+ * reference when no placeholders were found.
49
+ */
50
+ export declare function resolveExpands(renderable: unknown, pageUrl: string, registry: Readonly<EntityRegistry>, patterns: CompiledXrefPattern[], embedConfig: ExpandResolveContext['embedConfig'], ctx: PipelineContext): unknown;
51
+ //# sourceMappingURL=expand-pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expand-pipeline.d.ts","sourceRoot":"","sources":["../src/expand-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAsB,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAG7F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAI9D,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IACnC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,GAAG,EAAE,eAAe,CAAC;IACrB;6DACyD;IACzD,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;CAC9B;AAOD;;gEAEgE;AAChE,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC7B,UAAU,EAAE,OAAO,EACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,CAAC,cAAc,CAAC,EAClC,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,WAAW,EAAE,oBAAoB,CAAC,aAAa,CAAC,EAChD,GAAG,EAAE,eAAe,GAClB,OAAO,CAUT"}
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Expand pipeline (SPEC-066, WORK-260).
3
+ *
4
+ * Post-process resolver that finds `{% expand %}` placeholder tags
5
+ * (`data-rune="expand-pending"`) in a page's renderable tree, looks
6
+ * each up in the registry, reads + parses the entity's `sourceFile`
7
+ * (cached per build), calls the plugin's `extract()` to grab the
8
+ * embeddable AST subtree, optionally shifts heading levels, transforms
9
+ * the subtree using the build's full tags+nodes config, and substitutes
10
+ * the result in place wrapped in `<section class="rf-expand"
11
+ * data-rune="expand" data-outline-scope="${id}">`.
12
+ *
13
+ * The wrapper's `data-outline-scope` attribute is picked up by the
14
+ * WORK-259 walkers — heading IDs inside get prefixed, TOC items
15
+ * pointing at them get dropped — without expand needing to coordinate.
16
+ *
17
+ * Per-build caches:
18
+ * - `sourceFile → parsedAst` (one parse per file regardless of how
19
+ * many pages embed it)
20
+ *
21
+ * Per-page state:
22
+ * - resolution stack (`(type, id)` tuples) for cycle detection
23
+ */
24
+ import Markdoc from '@markdoc/markdoc';
25
+ import { EXPAND_PLACEHOLDER_MARKER } from './tags/expand.js';
26
+ import { readWholeSandboxedFile, SnippetSandboxError } from './lib/read-file.js';
27
+ const { Tag } = Markdoc;
28
+ /** Per-build source-file parse cache. Lives outside the resolve context
29
+ * so a single build's runs reuse the same instance — the resolver is
30
+ * invoked per page, but the cache is per build. */
31
+ const buildParseCache = new Map();
32
+ /** Reset the per-build cache. Tests call this between cases; production
33
+ * builds rely on the cache living for the build's lifetime (each build
34
+ * starts fresh because the runes package is imported anew). */
35
+ export function __resetExpandCache() {
36
+ buildParseCache.clear();
37
+ }
38
+ /**
39
+ * Walk a renderable tree, replace every `expand-pending` placeholder
40
+ * with the substituted entity content. Returns the same renderable
41
+ * reference when no placeholders were found.
42
+ */
43
+ export function resolveExpands(renderable, pageUrl, registry, patterns, embedConfig, ctx) {
44
+ const rc = {
45
+ pageUrl,
46
+ registry,
47
+ patterns,
48
+ embedConfig,
49
+ ctx,
50
+ parseCache: buildParseCache,
51
+ };
52
+ return walk(renderable, rc, []);
53
+ }
54
+ function walk(node, rc, stack) {
55
+ if (Array.isArray(node)) {
56
+ let mutated = false;
57
+ const next = node.map((c) => {
58
+ const w = walk(c, rc, stack);
59
+ if (w !== c)
60
+ mutated = true;
61
+ return w;
62
+ });
63
+ return mutated ? next : node;
64
+ }
65
+ if (!Tag.isTag(node))
66
+ return node;
67
+ const tag = node;
68
+ const dataRune = tag.attributes?.['data-rune'];
69
+ if (dataRune === EXPAND_PLACEHOLDER_MARKER) {
70
+ return resolveOnePlaceholder(tag, rc, stack);
71
+ }
72
+ if (!tag.children || tag.children.length === 0)
73
+ return tag;
74
+ let mutated = false;
75
+ const newChildren = tag.children.map((c) => {
76
+ const w = walk(c, rc, stack);
77
+ if (w !== c)
78
+ mutated = true;
79
+ return w;
80
+ });
81
+ if (!mutated)
82
+ return tag;
83
+ return new Tag(tag.name, tag.attributes, newChildren);
84
+ }
85
+ function resolveOnePlaceholder(tag, rc, stack) {
86
+ const attrs = tag.attributes;
87
+ const id = String(attrs['data-expand-id'] ?? '');
88
+ const typeHint = attrs['data-expand-type'];
89
+ const levelAttr = attrs['data-expand-level'];
90
+ const canonical = attrs['data-expand-canonical'] === 'true';
91
+ const authoredLabel = attrs['data-expand-label'];
92
+ if (!id) {
93
+ rc.ctx.error(`expand placeholder is missing \`primary\`/id attribute on ${rc.pageUrl}`, rc.pageUrl);
94
+ return errorNode(id, 'missing id attribute');
95
+ }
96
+ const level = levelAttr !== undefined ? Number(levelAttr) : undefined;
97
+ const entity = findEmbeddableEntity(rc.registry, id, rc.pageUrl, typeHint);
98
+ if (!entity) {
99
+ rc.ctx.error(`expand "${id}" on ${rc.pageUrl} — entity not found`, rc.pageUrl);
100
+ return errorNode(id, 'entity not found');
101
+ }
102
+ // Embeddable via embed() OR (sourceFile + extract) — SPEC-069.
103
+ if (!entity.embed && !(entity.sourceFile && entity.extract)) {
104
+ rc.ctx.error(`expand "${id}" on ${rc.pageUrl} — entity type "${entity.type}" does not support embedding (no embed() or sourceFile/extract)`, rc.pageUrl);
105
+ return errorNode(id, `entity type "${entity.type}" does not support embedding`);
106
+ }
107
+ // Cycle detection. The stack is per-page-render — embedding the same
108
+ // entity on two different pages is fine; embedding it inside itself
109
+ // transitively is the bug we catch.
110
+ if (stack.some(s => s.type === entity.type && s.id === entity.id)) {
111
+ const cyclePath = [...stack, { type: entity.type, id: entity.id }]
112
+ .map(s => `${s.id} (${s.type})`)
113
+ .join(' → ');
114
+ rc.ctx.error(`expand cycle detected on ${rc.pageUrl}. Cycle: ${cyclePath}`, rc.pageUrl);
115
+ return errorNode(id, 'cycle detected');
116
+ }
117
+ // Resolve the entity's content: embed() (in-memory) takes precedence; else
118
+ // read + extract from the source file (requires a project root).
119
+ let extracted;
120
+ if (entity.embed) {
121
+ extracted = entity.embed();
122
+ }
123
+ else {
124
+ const projectRoot = rc.embedConfig?.projectRoot;
125
+ if (!projectRoot) {
126
+ rc.ctx.error(`expand "${id}" — no project root configured (embedConfig.projectRoot is unset)`, rc.pageUrl);
127
+ return errorNode(id, 'no project root configured');
128
+ }
129
+ let parsed;
130
+ try {
131
+ parsed = parseSourceFile(entity.sourceFile, projectRoot, rc.parseCache);
132
+ }
133
+ catch (err) {
134
+ const msg = err instanceof SnippetSandboxError ? err.message : err.message;
135
+ rc.ctx.error(`expand "${id}" — failed to read source file "${entity.sourceFile}": ${msg}`, rc.pageUrl);
136
+ return errorNode(id, msg);
137
+ }
138
+ extracted = entity.extract(parsed);
139
+ }
140
+ if (!extracted) {
141
+ rc.ctx.error(`expand "${id}" — extractor returned no content; the entity may be out-of-sync with the registry`, rc.pageUrl);
142
+ return errorNode(id, 'extractor returned no content');
143
+ }
144
+ // Heading processing — `level=N` shifts headings by `N - 1`. Without
145
+ // `level=` the embed stays in peer-document mode; the outline-scope
146
+ // walkers (WORK-259) handle ID namespacing + TOC isolation downstream.
147
+ let processed = extracted;
148
+ if (level !== undefined && level !== 1) {
149
+ const shift = level - 1;
150
+ const clampedHeadings = [];
151
+ processed = shiftHeadings(extracted, shift, clampedHeadings);
152
+ if (clampedHeadings.length > 0) {
153
+ rc.ctx.warn(`expand "${id}" at ${rc.pageUrl} — heading demotion (level=${level}) would push ${clampedHeadings.length} heading(s) past H6. Clamped to H6: ${clampedHeadings.map(t => `"${t}"`).join(', ')}`, rc.pageUrl);
154
+ }
155
+ }
156
+ if (!rc.embedConfig) {
157
+ rc.ctx.error(`expand "${id}" — no embedConfig threaded through the pipeline`, rc.pageUrl);
158
+ return errorNode(id, 'no embed transform config');
159
+ }
160
+ // Transform the extracted subtree using the build's full tags+nodes
161
+ // config so embedded plan runes (or any other plugin-contributed
162
+ // schemas) execute normally. A nested walk handles any expand placeholders
163
+ // inside the embedded content (e.g. an embedded spec that references
164
+ // other specs).
165
+ const childStack = [...stack, { type: entity.type, id: entity.id }];
166
+ const transformed = Markdoc.transform(processed, rc.embedConfig);
167
+ const recursivelyResolved = walk(transformed, rc, childStack);
168
+ // Canonical-link affordance — resolve via the same chain xref uses.
169
+ const canonicalHref = resolveCanonicalHref(entity, rc.patterns);
170
+ // The engine's identity transform adds `class="rf-expand"` from the
171
+ // `Expand: { block: 'expand' }` config entry; don't set it here or it
172
+ // renders duplicated.
173
+ const wrapperAttrs = {
174
+ 'data-rune': 'expand',
175
+ 'data-entity-id': entity.id,
176
+ 'data-entity-type': entity.type,
177
+ 'data-source': 'registry',
178
+ };
179
+ // Outline-scope marker only when `level=` is unset (peer-document mode).
180
+ if (level === undefined) {
181
+ wrapperAttrs['data-outline-scope'] = entity.id;
182
+ }
183
+ if (canonicalHref) {
184
+ wrapperAttrs['data-canonical-href'] = canonicalHref;
185
+ }
186
+ const children = [];
187
+ if (Array.isArray(recursivelyResolved)) {
188
+ children.push(...recursivelyResolved);
189
+ }
190
+ else {
191
+ children.push(recursivelyResolved);
192
+ }
193
+ if (canonical) {
194
+ const label = authoredLabel || canonicalLinkDefault(entity);
195
+ const linkAttrs = {
196
+ class: canonicalHref
197
+ ? 'rf-expand__canonical-link'
198
+ : 'rf-expand__canonical-link rf-xref--unresolved',
199
+ };
200
+ if (canonicalHref)
201
+ linkAttrs.href = canonicalHref;
202
+ children.push(new Tag('a', linkAttrs, [label]));
203
+ }
204
+ return new Tag('section', wrapperAttrs, children);
205
+ }
206
+ /** Resolve an entity by id and (optional) type hint. Uses the same
207
+ * registry surface as xref. */
208
+ function findEmbeddableEntity(registry, id, pageUrl, typeHint) {
209
+ const types = typeHint ? [typeHint] : registry.getTypes();
210
+ for (const type of types) {
211
+ const e = registry.getById(type, id, pageUrl);
212
+ if (e)
213
+ return e;
214
+ }
215
+ // Name-match fallback (same algorithm as xref).
216
+ const idLower = id.toLowerCase();
217
+ for (const type of types) {
218
+ for (const e of registry.getAll(type)) {
219
+ const name = e.data.name ?? '';
220
+ const title = e.data.title ?? '';
221
+ if (name.toLowerCase() === idLower || title.toLowerCase() === idLower)
222
+ return e;
223
+ }
224
+ }
225
+ return undefined;
226
+ }
227
+ /** Resolve an entity's canonical URL using the same chain as xref: the
228
+ * registered `sourceUrl` first, then pattern matching against the id. */
229
+ function resolveCanonicalHref(entity, patterns) {
230
+ const sourceUrl = entity.sourceUrl;
231
+ const dataUrl = entity.data.url;
232
+ const baseUrl = dataUrl || sourceUrl;
233
+ if (baseUrl) {
234
+ const headingId = entity.data.headingId;
235
+ return headingId ? `${baseUrl}#${headingId}` : baseUrl;
236
+ }
237
+ for (const p of patterns) {
238
+ const m = p.match.exec(entity.id);
239
+ if (!m)
240
+ continue;
241
+ const groups = (m.groups ?? {});
242
+ const PLACEHOLDER_RE = /\{([a-zA-Z_$][a-zA-Z0-9_$]*)\}/g;
243
+ return p.template.replace(PLACEHOLDER_RE, (_match, name) => {
244
+ const value = name === 'id' ? entity.id : (groups[name] ?? '');
245
+ return value.split('/').map(encodeURIComponent).join('/');
246
+ });
247
+ }
248
+ return undefined;
249
+ }
250
+ function canonicalLinkDefault(entity) {
251
+ const title = entity.data.title;
252
+ const name = entity.data.name;
253
+ if (title)
254
+ return `View ${entity.type}: ${title}`;
255
+ if (name)
256
+ return `View ${entity.type}: ${name}`;
257
+ return `View ${entity.type} ${entity.id}`;
258
+ }
259
+ /** Parse a source file, caching per build. The path is resolved through
260
+ * the same sandbox as snippet so absolute paths and traversal escapes
261
+ * are rejected. File-system access lives behind a helper in
262
+ * `lib/read-file.ts` so the `node:fs` import stays out of this module
263
+ * and Vite can tree-shake it from browser bundles. */
264
+ function parseSourceFile(sourceFile, projectRoot, cache) {
265
+ const cached = cache.get(sourceFile);
266
+ if (cached)
267
+ return cached;
268
+ const raw = readWholeSandboxedFile({ relativePath: sourceFile, projectRoot });
269
+ const ast = Markdoc.parse(raw);
270
+ cache.set(sourceFile, ast);
271
+ return ast;
272
+ }
273
+ /** Recursively shift heading levels in a Markdoc AST subtree. Tracks
274
+ * headings that would push past H6 so the caller can warn. */
275
+ function shiftHeadings(node, shift, clampedHeadings) {
276
+ if (node.type === 'heading') {
277
+ const originalLevel = node.attributes.level;
278
+ const newLevel = originalLevel + shift;
279
+ if (newLevel > 6) {
280
+ const text = collectHeadingText(node);
281
+ clampedHeadings.push(text);
282
+ node.attributes.level = 6;
283
+ }
284
+ else {
285
+ node.attributes.level = newLevel;
286
+ }
287
+ }
288
+ for (const child of node.children ?? []) {
289
+ shiftHeadings(child, shift, clampedHeadings);
290
+ }
291
+ return node;
292
+ }
293
+ function collectHeadingText(node) {
294
+ const parts = [];
295
+ for (const child of node.walk()) {
296
+ if (child.type === 'text' && typeof child.attributes.content === 'string') {
297
+ parts.push(child.attributes.content);
298
+ }
299
+ }
300
+ return parts.join('');
301
+ }
302
+ /** Build a visible error placeholder for an expand resolution that
303
+ * failed. The build also surfaces the error via `ctx.error` — the
304
+ * inline rendering exists so the failure shows up on the page. */
305
+ function errorNode(id, message) {
306
+ // Engine adds `rf-expand` from the block config. Error variant gets a
307
+ // static modifier via the engine config; we only set the data attrs.
308
+ return new Tag('section', {
309
+ 'data-rune': 'expand',
310
+ 'data-entity-id': id,
311
+ 'data-expand-error': message,
312
+ }, [`expand "${id}" — ${message}`]);
313
+ }
314
+ //# sourceMappingURL=expand-pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expand-pipeline.js","sourceRoot":"","sources":["../src/expand-pipeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Shared field-match grammar (SPEC-070 canonical).
3
+ *
4
+ * One parser + matcher for the `field:value` selector used by `collection`'s
5
+ * `filter`, `entityRoutes`' `filter` (SPEC-069), and `backlog`.
6
+ *
7
+ * filter := clause (WS clause)*
8
+ * clause := field ":" value // split on the FIRST colon
9
+ * value := bareword | '"' .* '"' // double-quotes carry spaces
10
+ *
11
+ * Operator is selected by the value's shape:
12
+ * - exact (default): status:ready
13
+ * - glob: url:/blog/* ( * → any run of chars, anchored full-match )
14
+ * - regex: id:/^SPEC-\d+$/ ( wrapped in slashes + optional flags )
15
+ *
16
+ * Same field repeated → OR; different fields → AND. Matching is case-sensitive.
17
+ */
18
+ /** Minimal entity shape the matcher resolves fields from. */
19
+ export interface MatchableEntity {
20
+ id: string;
21
+ type: string;
22
+ sourceUrl?: string;
23
+ sourceFile?: string;
24
+ data: Record<string, unknown>;
25
+ }
26
+ export interface FieldMatchClause {
27
+ field: string;
28
+ /** Values OR'd together for this field. */
29
+ values: string[];
30
+ }
31
+ export interface ParsedFieldMatch {
32
+ clauses: FieldMatchClause[];
33
+ warnings: string[];
34
+ }
35
+ /** Parse a filter expression into clauses, collecting warnings for malformed input. */
36
+ export declare function parseFieldMatch(expr: string | undefined | null): ParsedFieldMatch;
37
+ /** Resolve a field name against an entity: top-level first, then `data`; `url` is an alias. */
38
+ export declare function resolveEntityField(entity: MatchableEntity, field: string): unknown;
39
+ /** Test whether an entity matches all clauses (AND across fields, OR within a field). */
40
+ export declare function matchesFieldMatch(entity: MatchableEntity, parsed: ParsedFieldMatch): boolean;
41
+ /** Convenience: parse + match in one call. */
42
+ export declare function matchesFilterExpr(entity: MatchableEntity, expr: string | undefined | null): boolean;
43
+ //# sourceMappingURL=field-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-match.d.ts","sourceRoot":"","sources":["../src/field-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,6DAA6D;AAC7D,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,MAAM,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AA2BD,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,gBAAgB,CA0BjF;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAIlF;AA+BD,yFAAyF;AACzF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAO5F;AAED,8CAA8C;AAC9C,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAEnG"}