@astryxdesign/core 0.6.2-canary.bfee8a4 → 0.6.2-canary.c8d5849

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 (261) hide show
  1. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  2. package/dist/Chat/ChatComposerInput.js +2 -2
  3. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  4. package/dist/CheckboxInput/CheckboxInput.js +12 -2
  5. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  6. package/dist/Collapsible/Collapsible.js +6 -1
  7. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  8. package/dist/CommandPalette/CommandPaletteInput.js +1 -1
  9. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  10. package/dist/ComplexSelector/ComplexSelector.js +1 -1
  11. package/dist/DateInput/DateInput.d.ts.map +1 -1
  12. package/dist/DateInput/DateInput.js +4 -4
  13. package/dist/DateInput/NativeDateField.d.ts.map +1 -1
  14. package/dist/DateInput/NativeDateField.js +16 -16
  15. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  16. package/dist/DateInput/TouchDateField.js +2 -2
  17. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  18. package/dist/DateRangeInput/DateRangeInput.js +4 -4
  19. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  20. package/dist/DateTimeInput/DateTimeInput.js +8 -8
  21. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  22. package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
  23. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
  24. package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
  25. package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
  26. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  27. package/dist/Field/PanelSearchInput.js +1 -1
  28. package/dist/FileInput/FileInput.d.ts.map +1 -1
  29. package/dist/FileInput/FileInput.js +7 -7
  30. package/dist/Kbd/Kbd.d.ts +5 -3
  31. package/dist/Kbd/Kbd.d.ts.map +1 -1
  32. package/dist/Kbd/Kbd.js +36 -42
  33. package/dist/Link/Link.d.ts.map +1 -1
  34. package/dist/Link/Link.js +6 -2
  35. package/dist/Markdown/Markdown.d.ts +8 -2
  36. package/dist/Markdown/Markdown.d.ts.map +1 -1
  37. package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
  38. package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
  39. package/dist/Markdown/Markdown.fr23.bench.js +150 -0
  40. package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
  41. package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
  42. package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
  43. package/dist/Markdown/Markdown.js +262 -109
  44. package/dist/Markdown/ast.d.ts +162 -0
  45. package/dist/Markdown/ast.d.ts.map +1 -0
  46. package/dist/Markdown/ast.js +74 -0
  47. package/dist/Markdown/index.d.ts +5 -3
  48. package/dist/Markdown/index.d.ts.map +1 -1
  49. package/dist/Markdown/index.js +3 -2
  50. package/dist/Markdown/parser/index.d.ts +10 -0
  51. package/dist/Markdown/parser/index.d.ts.map +1 -0
  52. package/dist/Markdown/parser/index.js +10 -0
  53. package/dist/Markdown/parser.d.ts +126 -58
  54. package/dist/Markdown/parser.d.ts.map +1 -1
  55. package/dist/Markdown/parser.js +716 -167
  56. package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
  57. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
  58. package/dist/Markdown/plugins/frontmatter.js +132 -0
  59. package/dist/Markdown/plugins/index.d.ts +16 -0
  60. package/dist/Markdown/plugins/index.d.ts.map +1 -0
  61. package/dist/Markdown/plugins/index.js +13 -0
  62. package/dist/Markdown/plugins/protocol.d.ts +191 -0
  63. package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
  64. package/dist/Markdown/plugins/protocol.js +957 -0
  65. package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
  66. package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
  67. package/dist/Markdown/plugins/semanticFence.js +213 -0
  68. package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
  69. package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
  70. package/dist/Markdown/plugins/sourceDecoration.js +393 -0
  71. package/dist/Markdown/plugins/textTransform.d.ts +30 -0
  72. package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
  73. package/dist/Markdown/plugins/textTransform.js +374 -0
  74. package/dist/Markdown/remark.d.ts +196 -0
  75. package/dist/Markdown/remark.d.ts.map +1 -0
  76. package/dist/Markdown/remark.js +1062 -0
  77. package/dist/Markdown/url.d.ts +4 -0
  78. package/dist/Markdown/url.d.ts.map +1 -0
  79. package/dist/Markdown/url.js +25 -0
  80. package/dist/Markdown/utils.d.ts +4 -2
  81. package/dist/Markdown/utils.d.ts.map +1 -1
  82. package/dist/Markdown/utils.js +2 -1
  83. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  84. package/dist/NumberInput/NumberInput.js +4 -4
  85. package/dist/Outline/index.d.ts +1 -0
  86. package/dist/Outline/index.d.ts.map +1 -1
  87. package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
  88. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  89. package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
  90. package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
  91. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  92. package/dist/Outline/useOutlineFromMarkdown.js +8 -3
  93. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  94. package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
  95. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  96. package/dist/ProgressBar/ProgressBar.js +36 -31
  97. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  98. package/dist/RadioList/RadioListItem.js +13 -1
  99. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  100. package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
  101. package/dist/Selector/Selector.d.ts.map +1 -1
  102. package/dist/Selector/Selector.js +1 -1
  103. package/dist/SideNav/SideNav.d.ts +2 -1
  104. package/dist/SideNav/SideNav.d.ts.map +1 -1
  105. package/dist/SideNav/SideNav.js +9 -3
  106. package/dist/Slider/Slider.d.ts.map +1 -1
  107. package/dist/Slider/Slider.js +14 -8
  108. package/dist/Spinner/Spinner.d.ts.map +1 -1
  109. package/dist/Spinner/Spinner.js +23 -7
  110. package/dist/Switch/Switch.d.ts.map +1 -1
  111. package/dist/Switch/Switch.js +11 -0
  112. package/dist/TabList/Tab.d.ts +1 -1
  113. package/dist/TabList/Tab.d.ts.map +1 -1
  114. package/dist/TabList/Tab.js +20 -7
  115. package/dist/Table/Table.d.ts +1 -0
  116. package/dist/Table/Table.d.ts.map +1 -1
  117. package/dist/Table/Table.js +29 -13
  118. package/dist/TextArea/TextArea.d.ts.map +1 -1
  119. package/dist/TextArea/TextArea.js +1 -1
  120. package/dist/TextInput/TextInput.d.ts.map +1 -1
  121. package/dist/TextInput/TextInput.js +2 -2
  122. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  123. package/dist/TimeInput/TimeInput.js +4 -4
  124. package/dist/ToggleButton/ToggleButton.d.ts +2 -1
  125. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  126. package/dist/ToggleButton/ToggleButton.js +7 -1
  127. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  128. package/dist/Typeahead/BaseTypeahead.js +16 -7
  129. package/dist/astryx.css +14 -6
  130. package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
  131. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  132. package/dist/utils/interactionOverlay.stylex.js +9 -0
  133. package/package.json +18 -3
  134. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
  135. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
  136. package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
  137. package/src/Button/__tests__/Button.a11y.states.ts +9 -0
  138. package/src/Chat/ChatComposerInput.doc.mjs +3 -3
  139. package/src/Chat/ChatComposerInput.tsx +10 -2
  140. package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
  141. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  142. package/src/CheckboxInput/CheckboxInput.tsx +21 -1
  143. package/src/ClickableCard/ClickableCard.test.tsx +102 -5
  144. package/src/Collapsible/Collapsible.doc.mjs +11 -0
  145. package/src/Collapsible/Collapsible.test.tsx +21 -0
  146. package/src/Collapsible/Collapsible.tsx +5 -0
  147. package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
  148. package/src/ComplexSelector/ComplexSelector.tsx +6 -1
  149. package/src/DateInput/DateInput.tsx +6 -1
  150. package/src/DateInput/NativeDateField.tsx +24 -8
  151. package/src/DateInput/TouchDateField.tsx +6 -2
  152. package/src/DateRangeInput/DateRangeInput.tsx +6 -1
  153. package/src/DateTimeInput/DateTimeInput.tsx +6 -1
  154. package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
  155. package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
  156. package/src/Field/PanelSearchInput.tsx +5 -2
  157. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
  158. package/src/FileInput/FileInput.tsx +11 -2
  159. package/src/Kbd/Kbd.doc.mjs +3 -3
  160. package/src/Kbd/Kbd.test.tsx +57 -1
  161. package/src/Kbd/Kbd.tsx +57 -37
  162. package/src/Layout/LayoutFooter.doc.mjs +17 -0
  163. package/src/Link/Link.doc.mjs +11 -0
  164. package/src/Link/Link.test.tsx +24 -0
  165. package/src/Link/Link.tsx +5 -0
  166. package/src/Markdown/Markdown.doc.mjs +272 -0
  167. package/src/Markdown/Markdown.fr23.bench.ts +174 -0
  168. package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
  169. package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
  170. package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
  171. package/src/Markdown/Markdown.public.test.ts +126 -3
  172. package/src/Markdown/Markdown.spec.md +197 -44
  173. package/src/Markdown/Markdown.test.tsx +11 -0
  174. package/src/Markdown/Markdown.tsx +414 -135
  175. package/src/Markdown/ast.test.ts +307 -0
  176. package/src/Markdown/ast.ts +337 -0
  177. package/src/Markdown/incremental.test.ts +12 -0
  178. package/src/Markdown/index.ts +37 -1
  179. package/src/Markdown/modules/remark.spec.md +269 -0
  180. package/src/Markdown/parser/index.ts +64 -0
  181. package/src/Markdown/parser.public.test.ts +103 -0
  182. package/src/Markdown/parser.ts +1180 -235
  183. package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
  184. package/src/Markdown/plugins/frontmatter.ts +198 -0
  185. package/src/Markdown/plugins/index.ts +53 -0
  186. package/src/Markdown/plugins/protocol.test.tsx +1022 -0
  187. package/src/Markdown/plugins/protocol.ts +1618 -0
  188. package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
  189. package/src/Markdown/plugins/semanticFence.ts +424 -0
  190. package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
  191. package/src/Markdown/plugins/sourceDecoration.ts +539 -0
  192. package/src/Markdown/plugins/textTransform.test.ts +467 -0
  193. package/src/Markdown/plugins/textTransform.ts +554 -0
  194. package/src/Markdown/remark.test.tsx +1877 -0
  195. package/src/Markdown/remark.ts +1574 -0
  196. package/src/Markdown/url.ts +30 -0
  197. package/src/Markdown/utils.ts +13 -1
  198. package/src/MultiSelector/MultiSelector.test.tsx +1 -35
  199. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
  200. package/src/NumberInput/NumberInput.tsx +6 -1
  201. package/src/Outline/Outline.doc.mjs +7 -5
  202. package/src/Outline/Outline.spec.md +1 -1
  203. package/src/Outline/index.ts +1 -0
  204. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
  205. package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
  206. package/src/Outline/useOutlineFromMarkdown.ts +15 -3
  207. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  208. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
  209. package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
  210. package/src/ProgressBar/ProgressBar.test.tsx +29 -11
  211. package/src/ProgressBar/ProgressBar.tsx +59 -72
  212. package/src/RadioList/RadioList.doc.mjs +11 -0
  213. package/src/RadioList/RadioList.test.tsx +32 -0
  214. package/src/RadioList/RadioListItem.tsx +25 -1
  215. package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
  216. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  217. package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
  218. package/src/Selector/Selector.spec.md +12 -9
  219. package/src/Selector/Selector.tsx +6 -1
  220. package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
  221. package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
  222. package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
  223. package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
  224. package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
  225. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
  226. package/src/SideNav/SideNav.doc.mjs +1 -1
  227. package/src/SideNav/SideNav.test.tsx +10 -0
  228. package/src/SideNav/SideNav.tsx +14 -2
  229. package/src/Slider/Slider.doc.mjs +11 -0
  230. package/src/Slider/Slider.test.tsx +128 -0
  231. package/src/Slider/Slider.tsx +25 -7
  232. package/src/Spinner/Spinner.test.tsx +4 -0
  233. package/src/Spinner/Spinner.tsx +31 -18
  234. package/src/Switch/Switch.doc.mjs +11 -0
  235. package/src/Switch/Switch.test.tsx +16 -0
  236. package/src/Switch/Switch.tsx +28 -0
  237. package/src/TabList/Tab.tsx +35 -7
  238. package/src/TabList/TabList.doc.mjs +11 -0
  239. package/src/TabList/TabList.test.tsx +66 -0
  240. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
  241. package/src/Table/Table.doc.mjs +1 -1
  242. package/src/Table/Table.spec.md +54 -40
  243. package/src/Table/Table.test.tsx +83 -10
  244. package/src/Table/Table.tsx +30 -22
  245. package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
  246. package/src/TextArea/TextArea.tsx +6 -1
  247. package/src/TextInput/TextInput.test.tsx +20 -1
  248. package/src/TextInput/TextInput.tsx +6 -1
  249. package/src/TimeInput/TimeInput.tsx +6 -1
  250. package/src/ToggleButton/ToggleButton.test.tsx +133 -0
  251. package/src/ToggleButton/ToggleButton.tsx +9 -2
  252. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
  253. package/src/Tokenizer/Tokenizer.spec.md +142 -75
  254. package/src/TreeList/TreeList.spec.md +123 -75
  255. package/src/Typeahead/BaseTypeahead.spec.md +4 -3
  256. package/src/Typeahead/BaseTypeahead.tsx +21 -7
  257. package/src/Typeahead/Typeahead.test.tsx +53 -0
  258. package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
  259. package/src/__tests__/pressState.ts +93 -0
  260. package/src/inputFontFloor.test.ts +106 -0
  261. package/src/utils/interactionOverlay.stylex.ts +20 -0
@@ -0,0 +1,125 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file frontmatter.test.tsx
5
+ * @input Native document-start frontmatter in complete and streaming documents
6
+ * @output Typed metadata, non-rendering, ordering, and failure coverage
7
+ * @position Acceptance tests for first-party Markdown frontmatter
8
+ */
9
+
10
+ import {renderToString} from 'react-dom/server';
11
+ import {describe, expect, expectTypeOf, it} from 'vitest';
12
+ import {Markdown} from '../Markdown';
13
+ import {parseMarkdownAst, parseMarkdownAstInternal} from '../parser';
14
+ import {createMarkdownPlugin, type MarkdownTransform} from './protocol';
15
+ import {createMarkdownFrontmatter} from './frontmatter';
16
+
17
+ const documentMetadata = createMarkdownFrontmatter({
18
+ name: 'document-metadata',
19
+ parse(fields) {
20
+ return {title: fields.title ?? '', draft: fields.draft === 'true'};
21
+ },
22
+ });
23
+
24
+ const SOURCE = '---\ntitle: Release notes\ndraft: true\n---\n# Shipped\n\nBody';
25
+
26
+ describe('createMarkdownFrontmatter', () => {
27
+ it('removes document-start syntax and exposes typed metadata to later plugins', () => {
28
+ let seen: ReturnType<typeof documentMetadata.getMetadata>;
29
+ const observer = createMarkdownPlugin({
30
+ name: 'frontmatter-observer',
31
+ apiVersion: 1,
32
+ transform(root) {
33
+ seen = documentMetadata.getMetadata(root);
34
+ return root;
35
+ },
36
+ });
37
+ const root = parseMarkdownAst(SOURCE, {
38
+ plugins: [documentMetadata.plugin, observer],
39
+ });
40
+
41
+ expect(seen).toEqual({title: 'Release notes', draft: true});
42
+ expect(documentMetadata.parse(SOURCE)).toEqual({
43
+ status: 'match',
44
+ contentStart: SOURCE.indexOf('# Shipped'),
45
+ metadata: {title: 'Release notes', draft: true},
46
+ });
47
+ expect(documentMetadata.getMetadata(root)).toEqual(seen);
48
+ expect(root.children).toHaveLength(2);
49
+ expect(JSON.stringify(root)).not.toContain('title: Release notes');
50
+ expectTypeOf(documentMetadata.plugin).toMatchTypeOf<{
51
+ readonly name: string;
52
+ }>();
53
+ });
54
+
55
+ it('hides unfinished streaming frontmatter and exposes it once closed', () => {
56
+ const partial = parseMarkdownAstInternal(
57
+ '---\ntitle: Pending',
58
+ {plugins: [documentMetadata.plugin]},
59
+ false,
60
+ );
61
+ expect(documentMetadata.getMetadata(partial)).toBeUndefined();
62
+ expect(partial.children).toEqual([]);
63
+
64
+ const settled = parseMarkdownAstInternal(
65
+ SOURCE,
66
+ {plugins: [documentMetadata.plugin]},
67
+ false,
68
+ );
69
+ expect(documentMetadata.getMetadata(settled)).toEqual({
70
+ title: 'Release notes',
71
+ draft: true,
72
+ });
73
+ expect(settled.children[0]).toMatchObject({type: 'heading'});
74
+ expect(
75
+ documentMetadata.parse(
76
+ '---\r\ntitle: Windows\r\ndraft: false\r\n---\r\nBody',
77
+ ),
78
+ ).toMatchObject({
79
+ status: 'match',
80
+ metadata: {title: 'Windows', draft: false},
81
+ });
82
+ });
83
+
84
+ it('does not treat non-leading frontmatter syntax as metadata', () => {
85
+ expect(
86
+ documentMetadata.getMetadata(
87
+ parseMarkdownAst(`# Intro\n\n${SOURCE}`, {
88
+ plugins: [documentMetadata.plugin],
89
+ }),
90
+ ),
91
+ ).toBeUndefined();
92
+ });
93
+
94
+ it('keeps renderer and server output free of metadata syntax', () => {
95
+ const html = renderToString(
96
+ <Markdown plugins={[documentMetadata.plugin]}>{SOURCE}</Markdown>,
97
+ );
98
+ expect(html).toContain('Shipped');
99
+ expect(html).toContain('Body');
100
+ expect(html).not.toContain('Release notes');
101
+ expect(html).not.toContain('draft');
102
+ });
103
+
104
+ it('rejects duplicate fields and unrepresentable decoded metadata', () => {
105
+ const duplicate = parseMarkdownAst(
106
+ '---\ntitle: One\ntitle: Two\n---\nBody',
107
+ {plugins: [documentMetadata.plugin]},
108
+ );
109
+ expect(documentMetadata.getMetadata(duplicate)).toBeUndefined();
110
+
111
+ const invalid = createMarkdownFrontmatter({
112
+ name: 'invalid',
113
+ parse: () => new Date() as never,
114
+ });
115
+ expect(() =>
116
+ parseMarkdownAst(SOURCE, {plugins: [invalid.plugin]}),
117
+ ).not.toThrow();
118
+ });
119
+
120
+ it('returns a transform plugin that composes in the ordered pipeline', () => {
121
+ expectTypeOf<MarkdownTransform>().toBeFunction();
122
+ const root = parseMarkdownAst(SOURCE, {plugins: [documentMetadata.plugin]});
123
+ expect(root.children[0]).toMatchObject({type: 'heading', depth: 1});
124
+ });
125
+ });
@@ -0,0 +1,198 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file frontmatter.ts
5
+ * @input Document-start frontmatter delimiters and a typed metadata decoder
6
+ * @output A Markdown plugin plus a typed metadata reader for later transforms
7
+ * @position Optional first-party document metadata helper
8
+ */
9
+
10
+ import type {MarkdownAstRoot} from '../ast';
11
+ import {
12
+ createMarkdownPlugin,
13
+ freezeMarkdownPluginData,
14
+ isMarkdownPluginData,
15
+ markMarkdownTransformTrusted,
16
+ type MarkdownExtensionNode,
17
+ type MarkdownPluginData,
18
+ type MarkdownPluginEntry,
19
+ type MarkdownTransform,
20
+ } from './protocol';
21
+
22
+ const ENVELOPE_KEY = 'astryx:frontmatter';
23
+ const ENVELOPE_BRAND = 'astryx.markdown.frontmatter';
24
+ const ENVELOPE_VERSION = 1;
25
+
26
+ type FrontmatterRecord = Readonly<Record<string, string>>;
27
+ type Envelope = {
28
+ readonly brand: typeof ENVELOPE_BRAND;
29
+ readonly version: typeof ENVELOPE_VERSION;
30
+ readonly values: Readonly<Record<string, MarkdownPluginData>>;
31
+ };
32
+
33
+ export interface MarkdownFrontmatterOptions<
34
+ Metadata extends MarkdownPluginData,
35
+ > {
36
+ /** Stable plugin and metadata namespace. */
37
+ readonly name: string;
38
+ /** Converts validated key/value fields into typed document metadata. */
39
+ readonly parse: (fields: FrontmatterRecord) => Metadata;
40
+ }
41
+
42
+ export type MarkdownFrontmatterParseResult<
43
+ Metadata extends MarkdownPluginData,
44
+ > =
45
+ | {readonly status: 'none'}
46
+ | {readonly status: 'defer'}
47
+ | {
48
+ readonly status: 'match';
49
+ readonly contentStart: number;
50
+ readonly metadata: Metadata;
51
+ };
52
+
53
+ export interface MarkdownFrontmatter<Metadata extends MarkdownPluginData> {
54
+ readonly plugin: MarkdownPluginEntry<never>;
55
+ parse(
56
+ source: string,
57
+ isFinal?: boolean,
58
+ ): MarkdownFrontmatterParseResult<Metadata>;
59
+ getMetadata(
60
+ document: MarkdownAstRoot<MarkdownExtensionNode>,
61
+ ): Metadata | undefined;
62
+ }
63
+
64
+ type DataRecord = Readonly<Record<string, MarkdownPluginData>>;
65
+
66
+ function asDataRecord(value: MarkdownPluginData | undefined): DataRecord {
67
+ return value != null && typeof value === 'object' && !Array.isArray(value)
68
+ ? (value as DataRecord)
69
+ : {};
70
+ }
71
+
72
+ function parseEnvelope(value: unknown): Envelope | undefined {
73
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
74
+ return;
75
+ }
76
+ const candidate = value as Partial<Envelope>;
77
+ return candidate.brand === ENVELOPE_BRAND &&
78
+ candidate.version === ENVELOPE_VERSION &&
79
+ candidate.values != null &&
80
+ typeof candidate.values === 'object' &&
81
+ !Array.isArray(candidate.values)
82
+ ? (candidate as Envelope)
83
+ : undefined;
84
+ }
85
+
86
+ function readFrontmatter(source: string):
87
+ | {readonly status: 'none'}
88
+ | {readonly status: 'defer'}
89
+ | {
90
+ readonly status: 'match';
91
+ readonly end: number;
92
+ readonly fields: FrontmatterRecord;
93
+ } {
94
+ const openingLength = source.startsWith('---\r\n')
95
+ ? 5
96
+ : source.startsWith('---\n')
97
+ ? 4
98
+ : 0;
99
+ if (openingLength === 0) {
100
+ return {status: 'none'};
101
+ }
102
+ const body = source.slice(openingLength);
103
+ const close = /(?:^|\r?\n)---(?=\r?\n|$)/.exec(body);
104
+ if (close == null) {
105
+ return {status: 'defer'};
106
+ }
107
+ const fields: Record<string, string> = Object.create(null);
108
+ for (const line of body.slice(0, close.index).split(/\r?\n/)) {
109
+ if (line.trim() === '') {
110
+ continue;
111
+ }
112
+ const separator = line.indexOf(':');
113
+ const key = line.slice(0, separator).trim();
114
+ if (separator <= 0 || key === '' || Object.hasOwn(fields, key)) {
115
+ return {status: 'none'};
116
+ }
117
+ fields[key] = line.slice(separator + 1).trim();
118
+ }
119
+ let end = openingLength + close.index + close[0].length;
120
+ if (source.startsWith('\r\n', end)) {
121
+ end += 2;
122
+ } else if (source[end] === '\n') {
123
+ end += 1;
124
+ }
125
+ return {status: 'match', end, fields: Object.freeze(fields)};
126
+ }
127
+
128
+ export function createMarkdownFrontmatter<Metadata extends MarkdownPluginData>(
129
+ options: MarkdownFrontmatterOptions<Metadata>,
130
+ ): MarkdownFrontmatter<Metadata> {
131
+ if (options.name.trim() === '') {
132
+ throw new TypeError('Markdown frontmatter name must be non-empty');
133
+ }
134
+ const name = options.name;
135
+ const parse = (
136
+ source: string,
137
+ isFinal = true,
138
+ ): MarkdownFrontmatterParseResult<Metadata> => {
139
+ const parsed = readFrontmatter(source);
140
+ if (parsed.status === 'defer') {
141
+ return isFinal ? {status: 'none'} : parsed;
142
+ }
143
+ if (parsed.status === 'none') {
144
+ return parsed;
145
+ }
146
+ const metadata = options.parse(parsed.fields);
147
+ if (!isMarkdownPluginData(metadata)) {
148
+ throw new TypeError(
149
+ 'Markdown frontmatter metadata must be finite JSON-like data',
150
+ );
151
+ }
152
+ return {
153
+ status: 'match',
154
+ contentStart: parsed.end,
155
+ metadata: freezeMarkdownPluginData(metadata),
156
+ };
157
+ };
158
+ const transform: MarkdownTransform<never> = (document, context) => {
159
+ const parsed = parse(context.source, context.isFinal);
160
+ if (parsed.status === 'none') {
161
+ return document;
162
+ }
163
+ if (parsed.status === 'defer') {
164
+ return {...document, children: []};
165
+ }
166
+ const documentData = asDataRecord(document.data);
167
+ const prior = parseEnvelope(documentData[ENVELOPE_KEY]);
168
+ const envelope: Envelope = Object.freeze({
169
+ brand: ENVELOPE_BRAND,
170
+ version: ENVELOPE_VERSION,
171
+ values: Object.freeze({
172
+ ...prior?.values,
173
+ [name]: parsed.metadata,
174
+ }),
175
+ });
176
+ return {
177
+ ...document,
178
+ data: Object.freeze({...documentData, [ENVELOPE_KEY]: envelope}),
179
+ children: document.children.filter(
180
+ node => (node.position?.end.offset ?? Infinity) > parsed.contentStart,
181
+ ),
182
+ };
183
+ };
184
+ const plugin = createMarkdownPlugin({
185
+ name,
186
+ apiVersion: 1,
187
+ transform: markMarkdownTransformTrusted(transform),
188
+ });
189
+ return Object.freeze({
190
+ plugin,
191
+ parse,
192
+ getMetadata(document: MarkdownAstRoot<MarkdownExtensionNode>) {
193
+ const data = asDataRecord(document.data);
194
+ return parseEnvelope(data[ENVELOPE_KEY])?.values[name] as
195
+ Metadata | undefined;
196
+ },
197
+ });
198
+ }
@@ -0,0 +1,53 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file index.ts
5
+ * @output Public Markdown plugin protocol, factory, and types
6
+ * @position Subpath entry point: `@astryxdesign/core/Markdown/plugins`
7
+ */
8
+
9
+ export {createMarkdownPlugin, isMarkdownExtensionNode} from './protocol';
10
+ export {createMarkdownTextTransform} from './textTransform';
11
+ export type {
12
+ MarkdownTextTransformContext,
13
+ MarkdownTextTransformOptions,
14
+ } from './textTransform';
15
+ export {createMarkdownFenceTransform} from './semanticFence';
16
+ export type {
17
+ MarkdownFenceContext,
18
+ MarkdownFenceNode,
19
+ MarkdownFenceTransformOptions,
20
+ } from './semanticFence';
21
+ export {
22
+ createMarkdownSourceDecoration,
23
+ getMarkdownSourceDecorations,
24
+ } from './sourceDecoration';
25
+ export type {
26
+ MarkdownSourceDecoration,
27
+ MarkdownSourceDecorationOptions,
28
+ MarkdownSourceDecorationRange,
29
+ } from './sourceDecoration';
30
+ export {createMarkdownFrontmatter} from './frontmatter';
31
+ export type {
32
+ MarkdownFrontmatter,
33
+ MarkdownFrontmatterOptions,
34
+ MarkdownFrontmatterParseResult,
35
+ } from './frontmatter';
36
+ export type {
37
+ MarkdownPluginData,
38
+ MarkdownExtensionNode,
39
+ MarkdownTokenizerInput,
40
+ MarkdownTokenizeResult,
41
+ MarkdownSyntaxContribution,
42
+ MarkdownSyntaxCapability,
43
+ MarkdownTransformContext,
44
+ MarkdownTransform,
45
+ MarkdownExtensionRenderer,
46
+ MarkdownExtensionRenderers,
47
+ MarkdownSyntaxPluginDefinition,
48
+ MarkdownTransformPluginDefinition,
49
+ MarkdownPluginDefinition,
50
+ MarkdownPluginEntry,
51
+ MarkdownNodeOf,
52
+ MarkdownExtensionsOf,
53
+ } from './protocol';