@astryxdesign/core 0.6.2 → 0.6.3-canary.0899f21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/dist/BottomSheet/BottomSheet.d.ts +1 -1
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +3 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts +7 -5
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +56 -19
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +13 -28
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +10 -5
- package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
- package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
- package/dist/Chat/ChatComposerDrawer.js +48 -23
- package/dist/Chat/ChatComposerInput.d.ts +11 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +63 -16
- package/dist/Chat/ChatDictationButton.d.ts +3 -2
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +6 -4
- package/dist/Chat/ChatLayout.d.ts +19 -0
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayout.js +19 -0
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +44 -34
- package/dist/Chat/ChatMessageBubble.d.ts +2 -0
- package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
- package/dist/Chat/ChatMessageBubble.js +5 -3
- package/dist/Chat/ChatMessageList.d.ts +2 -1
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +4 -3
- package/dist/Chat/ChatMessageMetadata.d.ts +5 -4
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatMessageMetadata.js +12 -9
- package/dist/Chat/ChatSendButton.js +1 -1
- package/dist/Chat/index.d.ts +1 -1
- package/dist/Chat/index.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +12 -2
- package/dist/Citation/Citation.d.ts +1 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +3 -2
- package/dist/CodeBlock/highlightRanges.d.ts.map +1 -1
- package/dist/CodeBlock/highlightRanges.js +14 -3
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +6 -1
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +1 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +4 -4
- package/dist/DateInput/NativeDateField.d.ts.map +1 -1
- package/dist/DateInput/NativeDateField.js +16 -16
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +2 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +4 -4
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +8 -8
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
- package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
- package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
- package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
- package/dist/Dialog/DialogHeader.d.ts +8 -1
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +13 -4
- package/dist/Field/InputClearButton.d.ts +4 -2
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +4 -2
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +1 -1
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +7 -7
- package/dist/Icon/Icon.d.ts +6 -31
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +8 -71
- package/dist/Icon/IconDefaultSizeContext.d.ts +4 -0
- package/dist/Icon/IconDefaultSizeContext.d.ts.map +1 -0
- package/dist/Icon/IconDefaultSizeContext.js +18 -0
- package/dist/Icon/IconSize.stylex.d.ts +65 -0
- package/dist/Icon/IconSize.stylex.d.ts.map +1 -0
- package/dist/Icon/IconSize.stylex.js +75 -0
- package/dist/Item/Item.d.ts +1 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +4 -3
- package/dist/Kbd/Kbd.d.ts +5 -3
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +36 -42
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +6 -2
- package/dist/Link/useLinkComponent.d.ts +5 -15
- package/dist/Link/useLinkComponent.d.ts.map +1 -1
- package/dist/Link/useLinkComponent.js +34 -51
- package/dist/List/List.d.ts +19 -1
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +9 -2
- package/dist/List/ListContext.d.ts +1 -0
- package/dist/List/ListContext.d.ts.map +1 -1
- package/dist/List/ListContext.js +2 -1
- package/dist/List/ListItem.d.ts +1 -1
- package/dist/List/ListItem.d.ts.map +1 -1
- package/dist/List/ListItem.js +9 -2
- package/dist/Markdown/Markdown.d.ts +8 -2
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
- package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
- package/dist/Markdown/Markdown.fr23.bench.js +150 -0
- package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
- package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
- package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
- package/dist/Markdown/Markdown.js +262 -109
- package/dist/Markdown/ast.d.ts +162 -0
- package/dist/Markdown/ast.d.ts.map +1 -0
- package/dist/Markdown/ast.js +74 -0
- package/dist/Markdown/index.d.ts +5 -3
- package/dist/Markdown/index.d.ts.map +1 -1
- package/dist/Markdown/index.js +3 -2
- package/dist/Markdown/parser/index.d.ts +10 -0
- package/dist/Markdown/parser/index.d.ts.map +1 -0
- package/dist/Markdown/parser/index.js +10 -0
- package/dist/Markdown/parser.d.ts +126 -58
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +716 -167
- package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
- package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
- package/dist/Markdown/plugins/frontmatter.js +132 -0
- package/dist/Markdown/plugins/index.d.ts +17 -0
- package/dist/Markdown/plugins/index.d.ts.map +1 -0
- package/dist/Markdown/plugins/index.js +14 -0
- package/dist/Markdown/plugins/protocol.d.ts +191 -0
- package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
- package/dist/Markdown/plugins/protocol.js +957 -0
- package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
- package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
- package/dist/Markdown/plugins/semanticFence.js +213 -0
- package/dist/Markdown/plugins/softBreaks.d.ts +3 -0
- package/dist/Markdown/plugins/softBreaks.d.ts.map +1 -0
- package/dist/Markdown/plugins/softBreaks.js +172 -0
- package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
- package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
- package/dist/Markdown/plugins/sourceDecoration.js +393 -0
- package/dist/Markdown/plugins/textTransform.d.ts +30 -0
- package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
- package/dist/Markdown/plugins/textTransform.js +374 -0
- package/dist/Markdown/remark.d.ts +196 -0
- package/dist/Markdown/remark.d.ts.map +1 -0
- package/dist/Markdown/remark.js +1062 -0
- package/dist/Markdown/url.d.ts +6 -0
- package/dist/Markdown/url.d.ts.map +1 -0
- package/dist/Markdown/url.js +31 -0
- package/dist/Markdown/utils.d.ts +4 -2
- package/dist/Markdown/utils.d.ts.map +1 -1
- package/dist/Markdown/utils.js +2 -1
- package/dist/MetadataList/MetadataList.js +3 -3
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +4 -4
- package/dist/Outline/index.d.ts +1 -0
- package/dist/Outline/index.d.ts.map +1 -1
- package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
- package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
- package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
- package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/useOutlineFromMarkdown.js +8 -3
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +5 -0
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +36 -31
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +13 -1
- package/dist/ScrollableArea/ScrollableArea.d.ts +1 -0
- package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -1
- package/dist/ScrollableArea/ScrollableArea.js +1 -0
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
- package/dist/Selector/Selector.d.ts +9 -7
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +16 -7
- package/dist/SideNav/SideNav.d.ts +2 -1
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +9 -3
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +28 -12
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +23 -7
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +11 -0
- package/dist/TabList/Tab.d.ts +1 -1
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +20 -7
- package/dist/Table/Table.d.ts +1 -0
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +29 -13
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +1 -1
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +2 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +4 -4
- package/dist/Timer/Timer.d.ts +49 -0
- package/dist/Timer/Timer.d.ts.map +1 -0
- package/dist/Timer/Timer.js +158 -0
- package/dist/Timer/index.d.ts +9 -0
- package/dist/Timer/index.d.ts.map +1 -0
- package/dist/Timer/index.js +10 -0
- package/dist/ToggleButton/ToggleButton.d.ts +9 -6
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +15 -27
- package/dist/TopNav/TopNavMenu.d.ts +1 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +3 -2
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +3 -0
- package/dist/TreeList/TreeListItem.d.ts +6 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +12 -2
- package/dist/TreeList/TreeListTypes.d.ts +8 -1
- package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +16 -7
- package/dist/astryx.css +27 -11
- package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
- package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
- package/dist/hooks/scrollKeyboardDelegation.js +146 -0
- package/dist/hooks/useClickableContainer.d.ts +10 -1
- package/dist/hooks/useClickableContainer.d.ts.map +1 -1
- package/dist/hooks/useClickableContainer.js +15 -4
- package/dist/hooks/useScrollableArea.d.ts +6 -2
- package/dist/hooks/useScrollableArea.d.ts.map +1 -1
- package/dist/hooks/useScrollableArea.js +17 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/theme/declarationBoundary.d.ts +17 -0
- package/dist/theme/declarationBoundary.d.ts.map +1 -0
- package/dist/theme/declarationBoundary.js +343 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +4 -0
- package/dist/theme/generateThemeRules.d.ts +13 -7
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +140 -34
- package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +9 -0
- package/dist/utils/safeUrl.d.ts +11 -0
- package/dist/utils/safeUrl.d.ts.map +1 -0
- package/dist/utils/safeUrl.js +78 -0
- package/locales/af-ZA.json +530 -1
- package/locales/ar-SA.json +490 -1
- package/locales/ca-ES.json +514 -1
- package/locales/cs-CZ.json +498 -1
- package/locales/da-DK.json +522 -1
- package/locales/de-DE.json +522 -1
- package/locales/el-GR.json +498 -1
- package/locales/es-ES.json +510 -1
- package/locales/fi-FI.json +498 -1
- package/locales/fr-FR.json +534 -1
- package/locales/he-IL.json +498 -1
- package/locales/hu-HU.json +498 -1
- package/locales/it-IT.json +510 -1
- package/locales/ja-JP.json +490 -1
- package/locales/ko-KR.json +498 -1
- package/locales/nl-NL.json +534 -1
- package/locales/no-NO.json +510 -1
- package/locales/pl-PL.json +506 -1
- package/locales/pt-BR.json +506 -1
- package/locales/pt-PT.json +506 -1
- package/locales/ro-RO.json +506 -1
- package/locales/ru-RU.json +498 -1
- package/locales/sr-SP.json +498 -1
- package/locales/sv-SE.json +502 -1
- package/locales/tr-TR.json +498 -1
- package/locales/uk-UA.json +498 -1
- package/locales/vi-VN.json +502 -1
- package/locales/zh-CN.json +490 -1
- package/locales/zh-TW.json +490 -1
- package/package.json +24 -4
- package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
- package/src/BottomSheet/BottomSheet.spec.md +46 -20
- package/src/BottomSheet/BottomSheet.test.tsx +6 -3
- package/src/BottomSheet/BottomSheet.tsx +3 -1
- package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
- package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
- package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
- package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
- package/src/Button/Button.doc.mjs +5 -5
- package/src/Button/Button.tsx +15 -13
- package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
- package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
- package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
- package/src/Button/__tests__/Button.a11y.states.ts +9 -0
- package/src/Chat/ChatComposer.doc.mjs +14 -0
- package/src/Chat/ChatComposer.spec.md +230 -0
- package/src/Chat/ChatComposer.test.tsx +57 -0
- package/src/Chat/ChatComposer.tsx +21 -9
- package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
- package/src/Chat/ChatComposerDrawer.spec.md +267 -0
- package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
- package/src/Chat/ChatComposerDrawer.tsx +38 -9
- package/src/Chat/ChatComposerInput.doc.mjs +114 -26
- package/src/Chat/ChatComposerInput.spec.md +185 -0
- package/src/Chat/ChatComposerInput.test.tsx +113 -3
- package/src/Chat/ChatComposerInput.tsx +78 -13
- package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
- package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
- package/src/Chat/ChatDictationButton.doc.mjs +3 -3
- package/src/Chat/ChatDictationButton.spec.md +160 -0
- package/src/Chat/ChatDictationButton.test.tsx +92 -27
- package/src/Chat/ChatDictationButton.tsx +8 -6
- package/src/Chat/ChatLayout.doc.mjs +12 -4
- package/src/Chat/ChatLayout.spec.md +271 -0
- package/src/Chat/ChatLayout.test.tsx +28 -0
- package/src/Chat/ChatLayout.tsx +19 -0
- package/src/Chat/ChatLayoutScrollButton.spec.md +338 -0
- package/src/Chat/ChatLayoutScrollButton.test.tsx +31 -0
- package/src/Chat/ChatLayoutScrollButton.tsx +31 -14
- package/src/Chat/ChatMessageBubble.doc.mjs +28 -15
- package/src/Chat/ChatMessageBubble.spec.md +263 -0
- package/src/Chat/ChatMessageBubble.test.tsx +93 -0
- package/src/Chat/ChatMessageBubble.tsx +8 -3
- package/src/Chat/ChatMessageList.spec.md +222 -0
- package/src/Chat/ChatMessageList.test.tsx +64 -2
- package/src/Chat/ChatMessageList.tsx +4 -3
- package/src/Chat/ChatMessageMetadata.doc.mjs +57 -3
- package/src/Chat/ChatMessageMetadata.spec.md +220 -0
- package/src/Chat/ChatMessageMetadata.test.tsx +34 -0
- package/src/Chat/ChatMessageMetadata.tsx +13 -13
- package/src/Chat/ChatSendButton.test.tsx +1 -4
- package/src/Chat/ChatSendButton.tsx +1 -1
- package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +514 -0
- package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +740 -0
- package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +457 -0
- package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +91 -0
- package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +91 -0
- package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +456 -0
- package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +413 -0
- package/src/Chat/index.ts +1 -0
- package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
- package/src/CheckboxInput/CheckboxInput.tsx +21 -1
- package/src/Citation/Citation.doc.mjs +2 -2
- package/src/Citation/Citation.test.tsx +17 -0
- package/src/Citation/Citation.tsx +4 -2
- package/src/ClickableCard/ClickableCard.doc.mjs +2 -2
- package/src/ClickableCard/ClickableCard.test.tsx +102 -5
- package/src/CodeBlock/highlightRanges.test.ts +143 -1
- package/src/CodeBlock/highlightRanges.ts +15 -5
- package/src/Collapsible/Collapsible.doc.mjs +11 -0
- package/src/Collapsible/Collapsible.test.tsx +21 -0
- package/src/Collapsible/Collapsible.tsx +5 -0
- package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
- package/src/ComplexSelector/ComplexSelector.tsx +6 -1
- package/src/DateInput/DateInput.tsx +6 -1
- package/src/DateInput/NativeDateField.tsx +24 -8
- package/src/DateInput/TouchDateField.tsx +6 -2
- package/src/DateRangeInput/DateRangeInput.tsx +6 -1
- package/src/DateTimeInput/DateTimeInput.tsx +6 -1
- package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
- package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
- package/src/Dialog/Dialog.doc.mjs +2 -0
- package/src/Dialog/DialogHeader.doc.mjs +22 -0
- package/src/Dialog/DialogHeader.test.tsx +167 -3
- package/src/Dialog/DialogHeader.tsx +37 -6
- package/src/Dialog/modules/DialogHeader.spec.md +101 -42
- package/src/DropdownMenu/DropdownMenu.test.tsx +5 -2
- package/src/Field/InputClearButton.test.tsx +17 -3
- package/src/Field/InputClearButton.tsx +5 -2
- package/src/Field/PanelSearchInput.tsx +5 -2
- package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
- package/src/FileInput/FileInput.tsx +11 -2
- package/src/Icon/Icon.doc.mjs +7 -5
- package/src/Icon/Icon.spec.md +81 -40
- package/src/Icon/Icon.test.tsx +6 -3
- package/src/Icon/Icon.tsx +20 -68
- package/src/Icon/IconDefaultSizeContext.ts +23 -0
- package/src/Icon/IconSize.stylex.ts +70 -0
- package/src/IconButton/IconButton.doc.mjs +3 -3
- package/src/IconButton/IconButton.test.tsx +40 -0
- package/src/Item/Item.doc.mjs +6 -2
- package/src/Item/Item.tsx +9 -3
- package/src/Kbd/Kbd.doc.mjs +3 -3
- package/src/Kbd/Kbd.test.tsx +57 -1
- package/src/Kbd/Kbd.tsx +57 -37
- package/src/Layout/LayoutFooter.doc.mjs +17 -0
- package/src/Link/Link.doc.mjs +17 -4
- package/src/Link/Link.test.tsx +24 -0
- package/src/Link/Link.tsx +5 -0
- package/src/Link/LinkProvider.doc.mjs +2 -2
- package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +261 -0
- package/src/Link/useLinkComponent.test.tsx +333 -0
- package/src/Link/useLinkComponent.ts +29 -57
- package/src/List/List.doc.mjs +6 -0
- package/src/List/List.spec.md +200 -0
- package/src/List/List.test.tsx +117 -1
- package/src/List/List.tsx +22 -2
- package/src/List/ListContext.tsx +3 -1
- package/src/List/ListItem.doc.mjs +2 -2
- package/src/List/ListItem.source-build.test.mjs +41 -0
- package/src/List/ListItem.tsx +26 -2
- package/src/Markdown/Markdown.doc.mjs +300 -2
- package/src/Markdown/Markdown.fr23.bench.ts +174 -0
- package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
- package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
- package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
- package/src/Markdown/Markdown.public.test.ts +130 -3
- package/src/Markdown/Markdown.renderBoundary.test.tsx +72 -0
- package/src/Markdown/Markdown.spec.md +203 -45
- package/src/Markdown/Markdown.test.tsx +63 -0
- package/src/Markdown/Markdown.tsx +414 -135
- package/src/Markdown/ast.test.ts +307 -0
- package/src/Markdown/ast.ts +337 -0
- package/src/Markdown/incremental.test.ts +12 -0
- package/src/Markdown/index.ts +37 -1
- package/src/Markdown/modules/remark.spec.md +269 -0
- package/src/Markdown/modules/softBreaks.spec.md +137 -0
- package/src/Markdown/parser/index.ts +64 -0
- package/src/Markdown/parser.public.test.ts +103 -0
- package/src/Markdown/parser.test.ts +20 -0
- package/src/Markdown/parser.ts +1180 -235
- package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
- package/src/Markdown/plugins/frontmatter.ts +198 -0
- package/src/Markdown/plugins/index.ts +54 -0
- package/src/Markdown/plugins/protocol.test.tsx +1022 -0
- package/src/Markdown/plugins/protocol.ts +1618 -0
- package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
- package/src/Markdown/plugins/semanticFence.ts +424 -0
- package/src/Markdown/plugins/softBreaks.test.tsx +172 -0
- package/src/Markdown/plugins/softBreaks.ts +169 -0
- package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
- package/src/Markdown/plugins/sourceDecoration.ts +539 -0
- package/src/Markdown/plugins/textTransform.test.ts +467 -0
- package/src/Markdown/plugins/textTransform.ts +554 -0
- package/src/Markdown/remark.test.tsx +1877 -0
- package/src/Markdown/remark.ts +1574 -0
- package/src/Markdown/url.ts +36 -0
- package/src/Markdown/utils.ts +13 -1
- package/src/MetadataList/MetadataList.test.tsx +4 -2
- package/src/MetadataList/MetadataList.tsx +3 -3
- package/src/MultiSelector/MultiSelector.test.tsx +1 -35
- package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
- package/src/NumberInput/NumberInput.tsx +6 -1
- package/src/Outline/Outline.doc.mjs +7 -5
- package/src/Outline/Outline.spec.md +1 -1
- package/src/Outline/index.ts +1 -0
- package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
- package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
- package/src/Outline/useOutlineFromMarkdown.ts +15 -3
- package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
- package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +51 -0
- package/src/PowerSearch/PowerSearchValueEditor.tsx +4 -0
- package/src/ProgressBar/ProgressBar.test.tsx +29 -11
- package/src/ProgressBar/ProgressBar.tsx +59 -72
- package/src/RadioList/RadioList.doc.mjs +11 -0
- package/src/RadioList/RadioList.test.tsx +32 -0
- package/src/RadioList/RadioListItem.tsx +25 -1
- package/src/ScrollableArea/ScrollableArea.tsx +1 -0
- package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
- package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
- package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
- package/src/Selector/Selector.doc.mjs +2 -2
- package/src/Selector/Selector.spec.md +51 -49
- package/src/Selector/Selector.test.tsx +61 -55
- package/src/Selector/Selector.tsx +34 -15
- package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
- package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
- package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
- package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
- package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
- package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +45 -0
- package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
- package/src/SideNav/SideNav.doc.mjs +1 -1
- package/src/SideNav/SideNav.test.tsx +10 -0
- package/src/SideNav/SideNav.tsx +14 -2
- package/src/Slider/Slider.doc.mjs +17 -3
- package/src/Slider/Slider.test.tsx +221 -0
- package/src/Slider/Slider.tsx +40 -9
- package/src/Spinner/Spinner.test.tsx +4 -0
- package/src/Spinner/Spinner.tsx +31 -18
- package/src/Switch/Switch.doc.mjs +11 -0
- package/src/Switch/Switch.test.tsx +16 -0
- package/src/Switch/Switch.tsx +28 -0
- package/src/TabList/Tab.tsx +35 -7
- package/src/TabList/TabList.doc.mjs +11 -0
- package/src/TabList/TabList.spec.md +204 -0
- package/src/TabList/TabList.test.tsx +66 -0
- package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
- package/src/Table/Table.doc.mjs +1 -1
- package/src/Table/Table.spec.md +54 -40
- package/src/Table/Table.test.tsx +83 -10
- package/src/Table/Table.tsx +30 -22
- package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
- package/src/TextArea/TextArea.tsx +6 -1
- package/src/TextInput/TextInput.test.tsx +20 -1
- package/src/TextInput/TextInput.tsx +6 -1
- package/src/TimeInput/TimeInput.tsx +6 -1
- package/src/Timer/Timer.doc.mjs +183 -0
- package/src/Timer/Timer.spec.md +215 -0
- package/src/Timer/Timer.test.tsx +302 -0
- package/src/Timer/Timer.tsx +258 -0
- package/src/Timer/index.ts +11 -0
- package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
- package/src/ToggleButton/ToggleButton.test.tsx +299 -4
- package/src/ToggleButton/ToggleButton.tsx +26 -33
- package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
- package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
- package/src/Token/Token.doc.mjs +2 -2
- package/src/Tokenizer/Tokenizer.spec.md +142 -75
- package/src/TopNav/TopNavMenu.doc.mjs +2 -2
- package/src/TopNav/TopNavMenu.test.tsx +30 -0
- package/src/TopNav/TopNavMenu.tsx +7 -2
- package/src/TreeList/TreeList.doc.mjs +3 -3
- package/src/TreeList/TreeList.spec.md +123 -75
- package/src/TreeList/TreeList.test.tsx +29 -0
- package/src/TreeList/TreeList.tsx +3 -0
- package/src/TreeList/TreeListItem.tsx +15 -1
- package/src/TreeList/TreeListTypes.ts +11 -1
- package/src/Typeahead/BaseTypeahead.spec.md +4 -3
- package/src/Typeahead/BaseTypeahead.tsx +21 -7
- package/src/Typeahead/Typeahead.test.tsx +53 -0
- package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
- package/src/__tests__/pressState.ts +93 -0
- package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
- package/src/hooks/scrollKeyboardDelegation.ts +233 -0
- package/src/hooks/useClickableContainer.test.tsx +236 -0
- package/src/hooks/useClickableContainer.ts +20 -4
- package/src/hooks/useScrollableArea.doc.mjs +15 -3
- package/src/hooks/useScrollableArea.test.tsx +59 -1
- package/src/hooks/useScrollableArea.ts +34 -10
- package/src/index.ts +3 -0
- package/src/inputFontFloor.test.ts +106 -0
- package/src/tailwind-theme.css +0 -6
- package/src/theme/Theme.test.tsx +32 -0
- package/src/theme/declarationBoundary.test.ts +126 -0
- package/src/theme/declarationBoundary.ts +347 -0
- package/src/theme/derivedVarRegistry.test.ts +7 -0
- package/src/theme/derivedVarRegistry.ts +1 -0
- package/src/theme/generateThemeRules.test.ts +526 -1
- package/src/theme/generateThemeRules.ts +228 -50
- package/src/theme/tokenValueCompat.test.ts +4 -1
- package/src/utils/interactionOverlay.stylex.ts +20 -0
- package/src/utils/safeUrl.test.ts +88 -0
- package/src/utils/safeUrl.ts +85 -0
|
@@ -0,0 +1,957 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file protocol.ts
|
|
5
|
+
* @input Markdown plugin definitions, syntax tokenizers, immutable transforms, and renderers
|
|
6
|
+
* @output Public plugin protocol plus validated internal preparation and execution
|
|
7
|
+
* @position Shared Markdown extension boundary consumed by the parser and renderer
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { devError, warnOnce } from "../../utils/devWarning.js";
|
|
11
|
+
import { isSafeMarkdownParserUrl } from "../url.js";
|
|
12
|
+
export function isMarkdownExtensionNode(node, plugin, name) {
|
|
13
|
+
return node.type === 'extension' && node.plugin === plugin && node.name === name;
|
|
14
|
+
}
|
|
15
|
+
const markdownTransformPluginName = Symbol('MarkdownTransformPluginName');
|
|
16
|
+
const markdownTransformHasRenderer = Symbol('MarkdownTransformHasRenderer');
|
|
17
|
+
const markdownTransformSourceUnchanged = Symbol('MarkdownTransformSourceUnchanged');
|
|
18
|
+
/** @internal Reads the owning plugin name without expanding the public context. */
|
|
19
|
+
export function getMarkdownTransformPluginName(context) {
|
|
20
|
+
return context[markdownTransformPluginName];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @internal The ownership facts a Core helper needs to validate the nodes a
|
|
25
|
+
* caller's callback handed it, without seeing the rest of the pipeline.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** @internal Reads this transform's ownership, when Core is running it. */
|
|
29
|
+
export function getMarkdownHelperOwnership(context) {
|
|
30
|
+
const internal = context;
|
|
31
|
+
const pluginName = internal[markdownTransformPluginName];
|
|
32
|
+
const hasRenderer = internal[markdownTransformHasRenderer];
|
|
33
|
+
const sourceUnchanged = internal[markdownTransformSourceUnchanged];
|
|
34
|
+
return pluginName === undefined || hasRenderer === undefined || sourceUnchanged === undefined ? undefined : {
|
|
35
|
+
pluginName,
|
|
36
|
+
hasRenderer,
|
|
37
|
+
sourceUnchanged
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const markdownTransformClaim = Symbol('MarkdownTransformClaim');
|
|
41
|
+
const markdownTransformTrusted = Symbol('MarkdownTransformTrusted');
|
|
42
|
+
/**
|
|
43
|
+
* @internal Marks a Core-authored transform whose OUTPUT STRUCTURE is entirely
|
|
44
|
+
* Core-computed — it never inserts a caller-supplied node, never edits an
|
|
45
|
+
* existing node's meaning, provenance, or ownership, and never mutates its
|
|
46
|
+
* input. Core then skips the validation and immutability guards it applies to
|
|
47
|
+
* plugin-authored output, whose purpose is to contain untrusted structure.
|
|
48
|
+
*
|
|
49
|
+
* Only helpers in this package may be marked, and only when every node they
|
|
50
|
+
* emit is built here from validated inputs. A helper that inserts anything the
|
|
51
|
+
* caller supplied — the text helper's replacement nodes, for example — is NOT
|
|
52
|
+
* trusted: Core must still validate the tree it returns.
|
|
53
|
+
*/
|
|
54
|
+
export function markMarkdownTransformTrusted(transform) {
|
|
55
|
+
Object.defineProperty(transform, markdownTransformTrusted, {
|
|
56
|
+
configurable: false,
|
|
57
|
+
enumerable: false,
|
|
58
|
+
value: true,
|
|
59
|
+
writable: false
|
|
60
|
+
});
|
|
61
|
+
return transform;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** @internal Registers a cheap source-level claim check for a helper transform. */
|
|
65
|
+
export function markMarkdownTransformClaim(transform, claims) {
|
|
66
|
+
Object.defineProperty(transform, markdownTransformClaim, {
|
|
67
|
+
configurable: false,
|
|
68
|
+
enumerable: false,
|
|
69
|
+
value: claims,
|
|
70
|
+
writable: false
|
|
71
|
+
});
|
|
72
|
+
return transform;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Opaque, covariant entry safe to store in heterogeneous plugin lists. */
|
|
76
|
+
|
|
77
|
+
const MARKDOWN_PLUGIN_BRAND = '@astryxdesign/core/MarkdownPluginEntry';
|
|
78
|
+
const markdownPluginDefinition = Symbol.for(MARKDOWN_PLUGIN_BRAND);
|
|
79
|
+
function getMarkdownPluginDefinition(publicEntry) {
|
|
80
|
+
if (publicEntry == null || typeof publicEntry !== 'object' || Object.getPrototypeOf(publicEntry) !== Object.prototype) {
|
|
81
|
+
fail('entries must come from a compatible createMarkdownPlugin()');
|
|
82
|
+
}
|
|
83
|
+
const entry = publicEntry;
|
|
84
|
+
const brand = entry[markdownPluginDefinition];
|
|
85
|
+
const definition = brand?.definition;
|
|
86
|
+
if (Reflect.ownKeys(entry).length !== 3 || typeof entry.name !== 'string' || entry.apiVersion !== 1 || !Object.isFrozen(entry) || brand == null || typeof brand !== 'object' || Object.getPrototypeOf(brand) !== Object.prototype || Reflect.ownKeys(brand).length !== 3 || !Object.isFrozen(brand) || brand.kind !== MARKDOWN_PLUGIN_BRAND || brand.apiVersion !== 1 || definition == null || typeof definition !== 'object' || !Object.isFrozen(definition) || definition.name !== entry.name || definition.apiVersion !== entry.apiVersion) {
|
|
87
|
+
fail('entries must come from a compatible createMarkdownPlugin()');
|
|
88
|
+
}
|
|
89
|
+
return definition;
|
|
90
|
+
}
|
|
91
|
+
function deepFreezeConfig(value, seen = new Set()) {
|
|
92
|
+
if (value != null && typeof value === 'object' && !seen.has(value)) {
|
|
93
|
+
seen.add(value);
|
|
94
|
+
for (const nested of Object.values(value)) {
|
|
95
|
+
deepFreezeConfig(nested, seen);
|
|
96
|
+
}
|
|
97
|
+
Object.freeze(value);
|
|
98
|
+
}
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
function fail(message) {
|
|
102
|
+
throw new Error(`Markdown plugin: ${message}`);
|
|
103
|
+
}
|
|
104
|
+
function validateSyntax(pluginName, syntax) {
|
|
105
|
+
const contributions = [...(syntax.inline ?? []), ...(syntax.block ?? [])];
|
|
106
|
+
if (contributions.length === 0) {
|
|
107
|
+
fail(`"${pluginName}" syntax must declare an inline or block contribution`);
|
|
108
|
+
}
|
|
109
|
+
for (const contribution of contributions) {
|
|
110
|
+
if (!Number.isFinite(contribution.maxSpan) || contribution.maxSpan <= 0) {
|
|
111
|
+
fail(`"${pluginName}" syntax maxSpan must be positive and finite`);
|
|
112
|
+
}
|
|
113
|
+
for (const prefix of contribution.startsWith) {
|
|
114
|
+
if (prefix === '' || prefix.length > contribution.maxSpan) {
|
|
115
|
+
fail(`"${pluginName}" syntax prefixes must be non-empty and bounded`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export function createMarkdownPlugin(definition) {
|
|
121
|
+
if (definition.name.trim() === '') {
|
|
122
|
+
fail('name must be a non-empty string');
|
|
123
|
+
}
|
|
124
|
+
if (definition.apiVersion !== 1) {
|
|
125
|
+
fail(`"${definition.name}" uses unsupported apiVersion`);
|
|
126
|
+
}
|
|
127
|
+
if (typeof definition.transform !== 'undefined' && typeof definition.transform !== 'function') {
|
|
128
|
+
fail(`"${definition.name}" transform must be a function`);
|
|
129
|
+
}
|
|
130
|
+
if ('syntax' in definition && definition.syntax != null) {
|
|
131
|
+
if (definition.parseKey.trim() === '') {
|
|
132
|
+
fail(`"${definition.name}" parseKey must be non-empty`);
|
|
133
|
+
}
|
|
134
|
+
validateSyntax(definition.name, definition.syntax);
|
|
135
|
+
if (Object.keys(definition.renderers).length === 0) {
|
|
136
|
+
fail(`"${definition.name}" syntax must provide renderers`);
|
|
137
|
+
}
|
|
138
|
+
} else if (definition.transform == null) {
|
|
139
|
+
fail(`"${definition.name}" must declare syntax or transform`);
|
|
140
|
+
}
|
|
141
|
+
const frozenDefinition = deepFreezeConfig(definition);
|
|
142
|
+
const brand = Object.freeze({
|
|
143
|
+
kind: MARKDOWN_PLUGIN_BRAND,
|
|
144
|
+
apiVersion: 1,
|
|
145
|
+
definition: frozenDefinition
|
|
146
|
+
});
|
|
147
|
+
return Object.freeze({
|
|
148
|
+
name: frozenDefinition.name,
|
|
149
|
+
apiVersion: 1,
|
|
150
|
+
[markdownPluginDefinition]: brand
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
const preparedPluginLists = new WeakMap();
|
|
154
|
+
function appendMapValue(map, key, value) {
|
|
155
|
+
const current = map.get(key);
|
|
156
|
+
if (current == null) {
|
|
157
|
+
map.set(key, [value]);
|
|
158
|
+
} else {
|
|
159
|
+
current.push(value);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function syntaxOnlyEntry(publicEntry) {
|
|
163
|
+
const entry = publicEntry;
|
|
164
|
+
const definition = getMarkdownPluginDefinition(publicEntry);
|
|
165
|
+
if (definition.transform == null) {
|
|
166
|
+
return publicEntry;
|
|
167
|
+
}
|
|
168
|
+
const syntaxDefinition = Object.freeze({
|
|
169
|
+
...definition,
|
|
170
|
+
transform: undefined
|
|
171
|
+
});
|
|
172
|
+
return Object.freeze({
|
|
173
|
+
name: entry.name,
|
|
174
|
+
apiVersion: entry.apiVersion,
|
|
175
|
+
[markdownPluginDefinition]: Object.freeze({
|
|
176
|
+
kind: MARKDOWN_PLUGIN_BRAND,
|
|
177
|
+
apiVersion: 1,
|
|
178
|
+
definition: syntaxDefinition
|
|
179
|
+
})
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
export function prepareMarkdownPlugins(plugins) {
|
|
183
|
+
if (plugins.length === 0) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
const cached = preparedPluginLists.get(plugins);
|
|
187
|
+
if (cached != null) {
|
|
188
|
+
return cached;
|
|
189
|
+
}
|
|
190
|
+
const names = new Set();
|
|
191
|
+
const inlineByFirstCharacter = new Map();
|
|
192
|
+
const blockByFirstCharacter = new Map();
|
|
193
|
+
const transforms = [];
|
|
194
|
+
const renderers = new Map();
|
|
195
|
+
const syntaxIdentity = [];
|
|
196
|
+
const entries = [];
|
|
197
|
+
const syntaxEntries = [];
|
|
198
|
+
for (const publicEntry of plugins) {
|
|
199
|
+
const definition = getMarkdownPluginDefinition(publicEntry);
|
|
200
|
+
const entry = publicEntry;
|
|
201
|
+
if (names.has(definition.name)) {
|
|
202
|
+
fail(`duplicate name "${definition.name}"`);
|
|
203
|
+
}
|
|
204
|
+
names.add(definition.name);
|
|
205
|
+
entries.push(entry);
|
|
206
|
+
if (definition.transform != null) {
|
|
207
|
+
transforms.push({
|
|
208
|
+
pluginName: definition.name,
|
|
209
|
+
transform: definition.transform,
|
|
210
|
+
claims: definition.transform[markdownTransformClaim],
|
|
211
|
+
trusted: definition.transform[markdownTransformTrusted] === true
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
if (definition.renderers != null) {
|
|
215
|
+
for (const [nodeName, renderer] of Object.entries(definition.renderers)) {
|
|
216
|
+
renderers.set(`${definition.name}\0${nodeName}`, {
|
|
217
|
+
pluginName: definition.name,
|
|
218
|
+
renderer: renderer
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if ('syntax' in definition && definition.syntax != null) {
|
|
223
|
+
syntaxEntries.push(syntaxOnlyEntry(publicEntry));
|
|
224
|
+
syntaxIdentity.push(`${definition.name}\0${definition.apiVersion}\0${definition.parseKey}`);
|
|
225
|
+
for (const contribution of definition.syntax.inline ?? []) {
|
|
226
|
+
const prepared = {
|
|
227
|
+
pluginName: definition.name,
|
|
228
|
+
display: 'inline',
|
|
229
|
+
contribution
|
|
230
|
+
};
|
|
231
|
+
for (const firstCharacter of new Set(contribution.startsWith.map(prefix => prefix[0]))) {
|
|
232
|
+
appendMapValue(inlineByFirstCharacter, firstCharacter, prepared);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
for (const contribution of definition.syntax.block ?? []) {
|
|
236
|
+
const prepared = {
|
|
237
|
+
pluginName: definition.name,
|
|
238
|
+
display: 'block',
|
|
239
|
+
contribution
|
|
240
|
+
};
|
|
241
|
+
for (const firstCharacter of new Set(contribution.startsWith.map(prefix => prefix[0]))) {
|
|
242
|
+
appendMapValue(blockByFirstCharacter, firstCharacter, prepared);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const prepared = {
|
|
248
|
+
entries,
|
|
249
|
+
syntaxEntries,
|
|
250
|
+
inlineByFirstCharacter,
|
|
251
|
+
blockByFirstCharacter,
|
|
252
|
+
transforms,
|
|
253
|
+
hasUntrustedTransform: transforms.some(entry => !entry.trusted),
|
|
254
|
+
syntaxIdentity: syntaxIdentity.join('\u0001'),
|
|
255
|
+
renderers
|
|
256
|
+
};
|
|
257
|
+
preparedPluginLists.set(plugins, prepared);
|
|
258
|
+
return prepared;
|
|
259
|
+
}
|
|
260
|
+
export function isMarkdownPluginData(value, seen = new Set()) {
|
|
261
|
+
if (value == null || typeof value === 'boolean' || typeof value === 'string') {
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
if (typeof value === 'number') {
|
|
265
|
+
return Number.isFinite(value);
|
|
266
|
+
}
|
|
267
|
+
if (typeof value !== 'object' || seen.has(value)) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
seen.add(value);
|
|
271
|
+
let valid;
|
|
272
|
+
if (Array.isArray(value)) {
|
|
273
|
+
valid = value.every(item => isMarkdownPluginData(item, seen));
|
|
274
|
+
} else if (Object.getPrototypeOf(value) !== Object.prototype) {
|
|
275
|
+
valid = false;
|
|
276
|
+
} else {
|
|
277
|
+
valid = Object.values(value).every(item => isMarkdownPluginData(item, seen));
|
|
278
|
+
}
|
|
279
|
+
seen.delete(value);
|
|
280
|
+
return valid;
|
|
281
|
+
}
|
|
282
|
+
export function freezeMarkdownPluginData(value) {
|
|
283
|
+
if (value != null && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
284
|
+
for (const nested of Object.values(value)) {
|
|
285
|
+
freezeMarkdownPluginData(nested);
|
|
286
|
+
}
|
|
287
|
+
Object.freeze(value);
|
|
288
|
+
}
|
|
289
|
+
return value;
|
|
290
|
+
}
|
|
291
|
+
function positionKey(position) {
|
|
292
|
+
const start = position?.start.offset;
|
|
293
|
+
const end = position?.end.offset;
|
|
294
|
+
return start == null || end == null ? null : `${start}:${end}`;
|
|
295
|
+
}
|
|
296
|
+
function codeSourceSignature(node) {
|
|
297
|
+
return JSON.stringify([node.lang, node.meta, node.value, node.position ?? null]);
|
|
298
|
+
}
|
|
299
|
+
function collectCodeInternalProperties(root) {
|
|
300
|
+
const properties = new Map();
|
|
301
|
+
const visit = node => {
|
|
302
|
+
if (node.type === 'code') {
|
|
303
|
+
const descriptors = Object.getOwnPropertySymbols(node).map(symbol => [symbol, Object.getOwnPropertyDescriptor(node, symbol)]).filter(entry => entry[1]?.enumerable === true);
|
|
304
|
+
if (descriptors.length > 0) {
|
|
305
|
+
const key = codeSourceSignature(node);
|
|
306
|
+
properties.set(key, [...(properties.get(key) ?? []), {
|
|
307
|
+
descriptors
|
|
308
|
+
}]);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
312
|
+
for (const child of node.children) {
|
|
313
|
+
if (child != null && typeof child === 'object') {
|
|
314
|
+
visit(child);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
visit(root);
|
|
320
|
+
return properties;
|
|
321
|
+
}
|
|
322
|
+
function restoreCodeInternalProperties(previous, next) {
|
|
323
|
+
const properties = collectCodeInternalProperties(previous);
|
|
324
|
+
if (properties.size === 0) {
|
|
325
|
+
return next;
|
|
326
|
+
}
|
|
327
|
+
const visit = node => {
|
|
328
|
+
let current = node;
|
|
329
|
+
if (node.type === 'code') {
|
|
330
|
+
const key = codeSourceSignature(node);
|
|
331
|
+
const queue = properties.get(key);
|
|
332
|
+
const internal = queue?.shift();
|
|
333
|
+
const shouldRestore = internal?.descriptors.some(([symbol, descriptor]) => {
|
|
334
|
+
const current = Object.getOwnPropertyDescriptor(node, symbol);
|
|
335
|
+
return current == null || current.value !== descriptor.value || current.get !== descriptor.get || current.set !== descriptor.set || current.enumerable !== descriptor.enumerable || current.configurable !== descriptor.configurable || current.writable !== descriptor.writable;
|
|
336
|
+
});
|
|
337
|
+
if (internal != null && shouldRestore === true) {
|
|
338
|
+
const clone = {
|
|
339
|
+
...node
|
|
340
|
+
};
|
|
341
|
+
for (const [symbol, descriptor] of internal.descriptors) {
|
|
342
|
+
Object.defineProperty(clone, symbol, descriptor);
|
|
343
|
+
}
|
|
344
|
+
current = clone;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if ('children' in current && Array.isArray(current.children)) {
|
|
348
|
+
const currentChildren = current.children;
|
|
349
|
+
const children = currentChildren.map(child => visit(child));
|
|
350
|
+
if (children.some((child, index) => child !== currentChildren[index])) {
|
|
351
|
+
const parent = {
|
|
352
|
+
...current,
|
|
353
|
+
children
|
|
354
|
+
};
|
|
355
|
+
current = parent;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return current;
|
|
359
|
+
};
|
|
360
|
+
return visit(next);
|
|
361
|
+
}
|
|
362
|
+
function collectSourceInvariants(root) {
|
|
363
|
+
const positions = new Map();
|
|
364
|
+
const visit = node => {
|
|
365
|
+
const key = positionKey(node.position);
|
|
366
|
+
if (key != null) {
|
|
367
|
+
const record = node;
|
|
368
|
+
const invariant = {
|
|
369
|
+
type: node.type,
|
|
370
|
+
depth: record.depth,
|
|
371
|
+
ordered: record.ordered,
|
|
372
|
+
start: record.start,
|
|
373
|
+
delimiter: record.delimiter,
|
|
374
|
+
plugin: record.plugin,
|
|
375
|
+
name: record.name,
|
|
376
|
+
display: record.display
|
|
377
|
+
};
|
|
378
|
+
positions.set(key, [...(positions.get(key) ?? []), invariant]);
|
|
379
|
+
}
|
|
380
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
381
|
+
for (const child of node.children) {
|
|
382
|
+
if (child != null && typeof child === 'object') {
|
|
383
|
+
visit(child);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
visit(root);
|
|
389
|
+
return positions;
|
|
390
|
+
}
|
|
391
|
+
function extensionSignature(node) {
|
|
392
|
+
return JSON.stringify([node.plugin, node.name, node.display, node.data, node.source, positionKey(node.position)]);
|
|
393
|
+
}
|
|
394
|
+
function collectExtensionSignatures(root) {
|
|
395
|
+
const signatures = new Map();
|
|
396
|
+
const visit = node => {
|
|
397
|
+
if (node.type === 'extension') {
|
|
398
|
+
const signature = extensionSignature(node);
|
|
399
|
+
const existing = signatures.get(signature);
|
|
400
|
+
signatures.set(signature, {
|
|
401
|
+
plugin: String(node.plugin),
|
|
402
|
+
count: (existing?.count ?? 0) + 1
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
406
|
+
for (const child of node.children) {
|
|
407
|
+
if (child != null && typeof child === 'object') {
|
|
408
|
+
visit(child);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
visit(root);
|
|
414
|
+
return signatures;
|
|
415
|
+
}
|
|
416
|
+
const sourceHeadingMarker = Symbol('MarkdownSourceHeading');
|
|
417
|
+
function markSourceHeadings(root) {
|
|
418
|
+
const markers = new Set();
|
|
419
|
+
const visit = node => {
|
|
420
|
+
if (node.type === 'heading' && !(sourceHeadingMarker in node)) {
|
|
421
|
+
Object.defineProperty(node, sourceHeadingMarker, {
|
|
422
|
+
configurable: false,
|
|
423
|
+
enumerable: true,
|
|
424
|
+
value: Object.freeze({
|
|
425
|
+
depth: node.depth
|
|
426
|
+
}),
|
|
427
|
+
writable: false
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
if (node.type === 'heading') {
|
|
431
|
+
markers.add(node[sourceHeadingMarker]);
|
|
432
|
+
}
|
|
433
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
434
|
+
for (const child of node.children) {
|
|
435
|
+
if (child != null && typeof child === 'object') {
|
|
436
|
+
visit(child);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
visit(root);
|
|
442
|
+
return markers;
|
|
443
|
+
}
|
|
444
|
+
const PHRASING_TYPES = new Set(['text', 'strong', 'emphasis', 'delete', 'inlineCode', 'inlineMath', 'break', 'link', 'image', 'citation']);
|
|
445
|
+
const BLOCK_TYPES = new Set(['heading', 'paragraph', 'code', 'math', 'blockquote', 'list', 'table', 'thematicBreak', 'image']);
|
|
446
|
+
const PHRASING_PARENTS = new Set(['heading', 'paragraph', 'strong', 'emphasis', 'delete', 'link', 'tableCell']);
|
|
447
|
+
const BLOCK_PARENTS = new Set(['root', 'blockquote', 'listItem']);
|
|
448
|
+
const CHILD_PARENT_TYPES = new Set([...PHRASING_PARENTS, ...BLOCK_PARENTS, 'list', 'table', 'tableRow']);
|
|
449
|
+
function validateAst(root, pluginNames, rendererKeys, sourcePositions, sourceHeadingMarkers, activePluginName, existingExtensions, display) {
|
|
450
|
+
if (root == null || typeof root !== 'object') {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
const seen = new Set();
|
|
454
|
+
const seenHeadingMarkers = new Set();
|
|
455
|
+
let count = 0;
|
|
456
|
+
const visit = (value, parent, insideLink) => {
|
|
457
|
+
if (value == null || typeof value !== 'object' || seen.has(value)) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
if (++count > 100_000) {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
seen.add(value);
|
|
464
|
+
const node = value;
|
|
465
|
+
const type = node.type;
|
|
466
|
+
if (typeof type !== 'string') {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
if (node.data !== undefined && !isMarkdownPluginData(node.data)) {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
if (parent != null) {
|
|
473
|
+
const extensionDisplay = type === 'extension' ? node.display : undefined;
|
|
474
|
+
const phrasing = PHRASING_TYPES.has(type) || extensionDisplay === 'inline';
|
|
475
|
+
const block = BLOCK_TYPES.has(type) || extensionDisplay === 'block';
|
|
476
|
+
if (PHRASING_PARENTS.has(parent) && !phrasing || BLOCK_PARENTS.has(parent) && !block || insideLink && type === 'link' || parent === 'list' && type !== 'listItem' || parent === 'table' && type !== 'tableRow' || parent === 'tableRow' && type !== 'tableCell') {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
const position = node.position;
|
|
481
|
+
const key = positionKey(position);
|
|
482
|
+
if (position != null) {
|
|
483
|
+
const start = position.start?.offset;
|
|
484
|
+
const end = position.end?.offset;
|
|
485
|
+
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (key != null) {
|
|
490
|
+
const invariants = sourcePositions.get(key) ?? [];
|
|
491
|
+
const invariantIndex = invariants.findIndex(invariant => invariant.type === type && invariant.depth === node.depth && invariant.ordered === node.ordered && invariant.start === node.start && invariant.delimiter === node.delimiter && invariant.plugin === node.plugin && invariant.name === node.name && invariant.display === node.display);
|
|
492
|
+
if (invariantIndex < 0) {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
invariants.splice(invariantIndex, 1);
|
|
496
|
+
}
|
|
497
|
+
if (type !== 'extension' && node.source !== undefined) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
switch (type) {
|
|
501
|
+
case 'root':
|
|
502
|
+
if (parent != null) {
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
break;
|
|
506
|
+
case 'heading':
|
|
507
|
+
{
|
|
508
|
+
if (![1, 2, 3, 4, 5, 6].includes(node.depth)) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
const marker = node[sourceHeadingMarker];
|
|
512
|
+
if (marker != null && (!sourceHeadingMarkers.has(marker) || marker.depth !== node.depth || seenHeadingMarkers.has(marker))) {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
if (marker != null) {
|
|
516
|
+
seenHeadingMarkers.add(marker);
|
|
517
|
+
}
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
case 'text':
|
|
521
|
+
case 'inlineCode':
|
|
522
|
+
case 'inlineMath':
|
|
523
|
+
case 'math':
|
|
524
|
+
if (typeof node.value !== 'string') {
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
break;
|
|
528
|
+
case 'code':
|
|
529
|
+
if (typeof node.value !== 'string' || node.lang !== null && typeof node.lang !== 'string' || node.meta !== undefined && typeof node.meta !== 'string') {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
break;
|
|
533
|
+
case 'link':
|
|
534
|
+
if (typeof node.url !== 'string' || !isSafeMarkdownParserUrl(node.url)) {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
break;
|
|
538
|
+
case 'image':
|
|
539
|
+
if (typeof node.url !== 'string' || !isSafeMarkdownParserUrl(node.url) || typeof node.alt !== 'string') {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
break;
|
|
543
|
+
case 'citation':
|
|
544
|
+
if (typeof node.sourceId !== 'string') {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
break;
|
|
548
|
+
case 'list':
|
|
549
|
+
if (typeof node.ordered !== 'boolean' || node.start != null && !Number.isFinite(node.start) || node.delimiter != null && node.delimiter !== '.' && node.delimiter !== ')') {
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
break;
|
|
553
|
+
case 'table':
|
|
554
|
+
if (!Array.isArray(node.align) || !node.align.every(value => value === null || value === 'left' || value === 'center' || value === 'right')) {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
break;
|
|
558
|
+
case 'extension':
|
|
559
|
+
{
|
|
560
|
+
if (typeof node.plugin !== 'string' || !pluginNames.has(node.plugin) || typeof node.name !== 'string' || node.display !== 'inline' && node.display !== 'block' || !isMarkdownPluginData(node.data) || !rendererKeys.has(`${node.plugin}\0${node.name}`)) {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
const signature = extensionSignature(node);
|
|
564
|
+
const existing = existingExtensions.get(signature);
|
|
565
|
+
if (existing != null && existing.count > 0) {
|
|
566
|
+
existing.count--;
|
|
567
|
+
} else if (node.plugin !== activePluginName || node.source !== undefined || node.position !== undefined) {
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
case 'strong':
|
|
573
|
+
case 'emphasis':
|
|
574
|
+
case 'delete':
|
|
575
|
+
case 'break':
|
|
576
|
+
case 'paragraph':
|
|
577
|
+
case 'blockquote':
|
|
578
|
+
case 'listItem':
|
|
579
|
+
case 'tableRow':
|
|
580
|
+
case 'tableCell':
|
|
581
|
+
case 'thematicBreak':
|
|
582
|
+
break;
|
|
583
|
+
default:
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
if (CHILD_PARENT_TYPES.has(type)) {
|
|
587
|
+
if (!Array.isArray(node.children)) {
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
for (const child of node.children) {
|
|
591
|
+
if (!visit(child, type, insideLink || type === 'link')) {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
} else if ('children' in node) {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
return true;
|
|
599
|
+
};
|
|
600
|
+
const candidate = root;
|
|
601
|
+
return visit(candidate, null, false) && candidate.type === 'root' && Array.from(existingExtensions.values()).every(extension => extension.plugin === activePluginName || extension.count === 0) && (display === 'block' || candidate.children.length === 1 && candidate.children[0]?.type === 'paragraph');
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Deeply freezes a Markdown tree.
|
|
606
|
+
*
|
|
607
|
+
* No memo set and no cycle set. Both were measured against this parser's own
|
|
608
|
+
* fixture and together cost four times the freezing they were guarding: a
|
|
609
|
+
* full freeze of a 12k-node tree runs at 19 percent of the parse that built
|
|
610
|
+
* it, a WeakSet memo takes that to 47 percent, and a cycle set to 75. Their
|
|
611
|
+
* absence is safe, not merely cheap: every tree reaching here is acyclic
|
|
612
|
+
* (the parser builds no cycles, and `validateAst` rejects any plugin tree
|
|
613
|
+
* that contains one), and `Object.freeze` on an already-frozen node is a
|
|
614
|
+
* no-op, so re-walking a shared subtree is correct — just not free.
|
|
615
|
+
*
|
|
616
|
+
* Deliberately NOT skipping nodes that look frozen: a node a plugin froze
|
|
617
|
+
* itself may still hold mutable children, and an undecorated sibling a
|
|
618
|
+
* trusted helper carried over by identity must be frozen like any other.
|
|
619
|
+
*/
|
|
620
|
+
function freezeAstNode(value) {
|
|
621
|
+
if (value == null || typeof value !== 'object') {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
if (Array.isArray(value)) {
|
|
625
|
+
for (let index = 0; index < value.length; index++) {
|
|
626
|
+
freezeAstNode(value[index]);
|
|
627
|
+
}
|
|
628
|
+
} else {
|
|
629
|
+
// `for…in` over a plain AST node rather than Object.values: the entries
|
|
630
|
+
// array is the dominant remaining cost of the walk.
|
|
631
|
+
for (const key in value) {
|
|
632
|
+
freezeAstNode(value[key]);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
Object.freeze(value);
|
|
636
|
+
}
|
|
637
|
+
function freezeAst(value) {
|
|
638
|
+
freezeAstNode(value);
|
|
639
|
+
return value;
|
|
640
|
+
}
|
|
641
|
+
const reportedProductionPluginFailures = new Set();
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Freezes only what `next` does not share with `previous`.
|
|
645
|
+
*
|
|
646
|
+
* Valid ONLY when no plugin-authored transform runs in this pipeline. The
|
|
647
|
+
* nodes it skips are the ones a Core helper carried over untouched, and with
|
|
648
|
+
* every transform Core-authored there is no actor that could mutate them:
|
|
649
|
+
* they are left in exactly the state an omitted or empty plugin list leaves
|
|
650
|
+
* them in, which is the state rendering already observes for every
|
|
651
|
+
* zero-plugin document today. The moment one untrusted transform exists,
|
|
652
|
+
* `applyMarkdownTransforms` uses the full `freezeAst` instead, before that
|
|
653
|
+
* transform observes anything — a shared, undecorated sibling included.
|
|
654
|
+
*/
|
|
655
|
+
function freezeBuiltAst(previous, next) {
|
|
656
|
+
if (next == null || typeof next !== 'object' || next === previous) {
|
|
657
|
+
return next;
|
|
658
|
+
}
|
|
659
|
+
const before = previous != null && typeof previous === 'object' ? previous : undefined;
|
|
660
|
+
if (Array.isArray(next)) {
|
|
661
|
+
const beforeArray = Array.isArray(previous) ? previous : undefined;
|
|
662
|
+
for (let index = 0; index < next.length; index++) {
|
|
663
|
+
freezeBuiltAst(beforeArray?.[index], next[index]);
|
|
664
|
+
}
|
|
665
|
+
} else {
|
|
666
|
+
for (const key in next) {
|
|
667
|
+
freezeBuiltAst(before?.[key], next[key]);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
Object.freeze(next);
|
|
671
|
+
return next;
|
|
672
|
+
}
|
|
673
|
+
const HELPER_PHRASING_TYPES = new Set(['text', 'strong', 'emphasis', 'delete', 'inlineCode', 'inlineMath', 'break', 'link', 'image', 'citation', 'extension']);
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* @internal Validates and freezes phrasing nodes a Core helper's caller just
|
|
677
|
+
* authored, so the helper may put them straight into its output tree.
|
|
678
|
+
* applying exactly the rules `validateAst` applies to newly-introduced nodes:
|
|
679
|
+
* representable typed data only, no authored provenance, no foreign extension
|
|
680
|
+
* ownership, no unrenderable extension, revalidated destinations, no nested
|
|
681
|
+
* link, no block content, no cycles, and a bounded node count.
|
|
682
|
+
*
|
|
683
|
+
* A helper that validates and freezes its callback's output this way has
|
|
684
|
+
* nothing left for Core to check, because every other node it returns is one
|
|
685
|
+
* Core already validated. That is what lets such a helper run on the trusted
|
|
686
|
+
* path while keeping FR10 and FR11 intact.
|
|
687
|
+
*
|
|
688
|
+
* Returns the frozen nodes, or throws — the caller reports the failure
|
|
689
|
+
* through the ordinary transform failure path.
|
|
690
|
+
*/
|
|
691
|
+
export function adoptMarkdownHelperNode(node, ownership, insideLink = false) {
|
|
692
|
+
adoptMarkdownHelperNodes(singleNodeScratch(node), ownership, insideLink);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* A one-element view for the single-node case, reused between calls so the
|
|
697
|
+
* common replacement costs no array. Refilled on entry and never held past
|
|
698
|
+
* the synchronous validation below, and a callback that re-enters the
|
|
699
|
+
* helper gets a fresh array rather than disturbing an outer walk.
|
|
700
|
+
*/
|
|
701
|
+
let scratchInUse = false;
|
|
702
|
+
const reusableScratch = [undefined];
|
|
703
|
+
function singleNodeScratch(node) {
|
|
704
|
+
if (scratchInUse) {
|
|
705
|
+
return [node];
|
|
706
|
+
}
|
|
707
|
+
reusableScratch[0] = node;
|
|
708
|
+
return reusableScratch;
|
|
709
|
+
}
|
|
710
|
+
export function adoptMarkdownHelperNodes(nodes, ownership, insideLink = false) {
|
|
711
|
+
// The overwhelmingly common replacement is one flat text node. Checking
|
|
712
|
+
// that shape directly avoids the general walk's setup entirely, and this
|
|
713
|
+
// runs once per match.
|
|
714
|
+
if (nodes.length === 1) {
|
|
715
|
+
const only = nodes[0];
|
|
716
|
+
if (only != null && typeof only === 'object' && only.type === 'text' && typeof only.value === 'string') {
|
|
717
|
+
let extra = false;
|
|
718
|
+
for (const key in only) {
|
|
719
|
+
if (key !== 'type' && key !== 'value') {
|
|
720
|
+
extra = true;
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (!extra) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
// Cycle tracking is allocated only once a node with children appears: the
|
|
730
|
+
// common replacement is a flat node, and this runs once per match.
|
|
731
|
+
let seen;
|
|
732
|
+
let count = 0;
|
|
733
|
+
const visit = (value, withinLink) => {
|
|
734
|
+
if (value == null || typeof value !== 'object' || seen?.has(value)) {
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
if (++count > 10_000) {
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
if (Array.isArray(value.children)) {
|
|
741
|
+
seen ??= new Set();
|
|
742
|
+
seen.add(value);
|
|
743
|
+
}
|
|
744
|
+
const node = value;
|
|
745
|
+
const type = node.type;
|
|
746
|
+
if (typeof type !== 'string' || !HELPER_PHRASING_TYPES.has(type)) {
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
// A helper's callback authors synthetic nodes only: provenance stays
|
|
750
|
+
// Core's to assign, so neither field may be present.
|
|
751
|
+
if (node.position !== undefined) {
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
if (type !== 'extension' && node.source !== undefined) {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
if (node.data !== undefined && !isMarkdownPluginData(node.data)) {
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
760
|
+
switch (type) {
|
|
761
|
+
case 'text':
|
|
762
|
+
case 'inlineCode':
|
|
763
|
+
case 'inlineMath':
|
|
764
|
+
if (typeof node.value !== 'string') {
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
break;
|
|
768
|
+
case 'link':
|
|
769
|
+
if (withinLink || typeof node.url !== 'string' || !isSafeMarkdownParserUrl(node.url)) {
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
break;
|
|
773
|
+
case 'image':
|
|
774
|
+
if (typeof node.url !== 'string' || !isSafeMarkdownParserUrl(node.url) || typeof node.alt !== 'string') {
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
break;
|
|
778
|
+
case 'citation':
|
|
779
|
+
if (typeof node.sourceId !== 'string') {
|
|
780
|
+
return false;
|
|
781
|
+
}
|
|
782
|
+
break;
|
|
783
|
+
case 'extension':
|
|
784
|
+
if (node.plugin !== ownership.pluginName || typeof node.name !== 'string' || !ownership.hasRenderer(node.name) || node.display !== 'inline' || node.source !== undefined || !isMarkdownPluginData(node.data)) {
|
|
785
|
+
return false;
|
|
786
|
+
}
|
|
787
|
+
break;
|
|
788
|
+
case 'strong':
|
|
789
|
+
case 'emphasis':
|
|
790
|
+
case 'delete':
|
|
791
|
+
case 'break':
|
|
792
|
+
break;
|
|
793
|
+
}
|
|
794
|
+
if (type === 'strong' || type === 'emphasis' || type === 'delete') {
|
|
795
|
+
if (!Array.isArray(node.children)) {
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
for (const child of node.children) {
|
|
799
|
+
if (!visit(child, withinLink)) {
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
} else if (type === 'link') {
|
|
804
|
+
if (!Array.isArray(node.children)) {
|
|
805
|
+
return false;
|
|
806
|
+
}
|
|
807
|
+
for (const child of node.children) {
|
|
808
|
+
if (!visit(child, true)) {
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
} else if ('children' in node) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
return true;
|
|
816
|
+
};
|
|
817
|
+
for (const node of nodes) {
|
|
818
|
+
if (!visit(node, insideLink)) {
|
|
819
|
+
throw new TypeError('Markdown helper callback returned an unrepresentable node');
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
// Frozen only once every node passed, so a rejected batch leaves the
|
|
823
|
+
// caller's objects exactly as they were. Freezing is what makes adopting
|
|
824
|
+
// a caller's object safe instead of copying it: the object is now part of
|
|
825
|
+
// an immutable tree, and a later write by whoever still holds a reference
|
|
826
|
+
// cannot reach into the document.
|
|
827
|
+
const reentrant = nodes !== reusableScratch;
|
|
828
|
+
if (!reentrant) {
|
|
829
|
+
scratchInUse = true;
|
|
830
|
+
}
|
|
831
|
+
try {
|
|
832
|
+
for (const node of nodes) {
|
|
833
|
+
freezeAstNode(node);
|
|
834
|
+
}
|
|
835
|
+
} finally {
|
|
836
|
+
if (!reentrant) {
|
|
837
|
+
scratchInUse = false;
|
|
838
|
+
reusableScratch[0] = undefined;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
export function reportMarkdownPluginFailure(pluginName, phase, error) {
|
|
843
|
+
const key = `markdown-plugin:${pluginName}:${phase}`;
|
|
844
|
+
const message = `plugin "${pluginName}" failed in ${phase}; rendered readable fallback.`;
|
|
845
|
+
if (process.env.NODE_ENV === 'production') {
|
|
846
|
+
if (!reportedProductionPluginFailures.has(key)) {
|
|
847
|
+
reportedProductionPluginFailures.add(key);
|
|
848
|
+
devError('Markdown', message);
|
|
849
|
+
}
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
warnOnce(key, 'Markdown', message, error);
|
|
853
|
+
}
|
|
854
|
+
export function applyMarkdownTransforms(root, plugins, source, isFinal, display) {
|
|
855
|
+
if (plugins == null || plugins.transforms.length === 0) {
|
|
856
|
+
return root;
|
|
857
|
+
}
|
|
858
|
+
let pluginNames;
|
|
859
|
+
let rendererKeys;
|
|
860
|
+
let document = root;
|
|
861
|
+
// Heading identity is stamped on the mutable parser tree, before anything
|
|
862
|
+
// freezes it, and only when a plugin-authored transform will actually be
|
|
863
|
+
// validated against it. An all-trusted pipeline never pays for it.
|
|
864
|
+
const sourceHeadingMarkers = plugins.hasUntrustedTransform ? markSourceHeadings(root) : undefined;
|
|
865
|
+
let guarded = false;
|
|
866
|
+
let priorTransformChangedTree = false;
|
|
867
|
+
/** A trusted helper has produced a tree nothing has frozen yet. */
|
|
868
|
+
let deferredFreeze = false;
|
|
869
|
+
for (const prepared of plugins.transforms) {
|
|
870
|
+
if (!priorTransformChangedTree && prepared.claims?.(source) === false) {
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
// A plugin-authored transform observes deeply frozen input and has its
|
|
874
|
+
// output validated. Both guards exist to contain untrusted structure, so
|
|
875
|
+
// a Core-authored trusted helper needs neither — and a pipeline of only
|
|
876
|
+
// trusted helpers never pays for them at all. Every untrusted transform
|
|
877
|
+
// still receives a frozen tree, including one that follows a trusted
|
|
878
|
+
// helper, whose output is not frozen on the way out.
|
|
879
|
+
if (!prepared.trusted) {
|
|
880
|
+
if (!guarded) {
|
|
881
|
+
pluginNames = new Set(plugins.entries.map(entry => entry.name));
|
|
882
|
+
rendererKeys = new Set(plugins.renderers.keys());
|
|
883
|
+
guarded = true;
|
|
884
|
+
}
|
|
885
|
+
// Deeply freezes the whole tree, including anything a trusted helper
|
|
886
|
+
// left shared and mutable, before plugin-authored code observes it.
|
|
887
|
+
// Subtrees already frozen by an earlier pass are skipped.
|
|
888
|
+
document = freezeAst(document);
|
|
889
|
+
deferredFreeze = false;
|
|
890
|
+
}
|
|
891
|
+
try {
|
|
892
|
+
const context = {
|
|
893
|
+
source,
|
|
894
|
+
isFinal,
|
|
895
|
+
display,
|
|
896
|
+
report(message) {
|
|
897
|
+
reportMarkdownPluginFailure(prepared.pluginName, 'transform', message);
|
|
898
|
+
},
|
|
899
|
+
[markdownTransformPluginName]: prepared.pluginName,
|
|
900
|
+
[markdownTransformHasRenderer]: nodeName => plugins.renderers.has(`${prepared.pluginName}\u0000${nodeName}`),
|
|
901
|
+
[markdownTransformSourceUnchanged]: !priorTransformChangedTree
|
|
902
|
+
};
|
|
903
|
+
const next = prepared.transform(document, context);
|
|
904
|
+
if (next != null && typeof next === 'object' && typeof next.then === 'function') {
|
|
905
|
+
void Promise.resolve(next).catch(() => {});
|
|
906
|
+
throw new TypeError('Async Markdown transforms are not supported');
|
|
907
|
+
}
|
|
908
|
+
if (next === document) {
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
if (prepared.trusted) {
|
|
912
|
+
priorTransformChangedTree = true;
|
|
913
|
+
// No freeze here. Nothing untrusted can observe this tree yet —
|
|
914
|
+
// only another Core helper, which never mutates its input — so the
|
|
915
|
+
// pipeline freezes once, below, before anything else sees it. That
|
|
916
|
+
// freeze is a FULL walk, never one that stops at nodes merely
|
|
917
|
+
// shared with an earlier unfrozen tree: an undecorated sibling a
|
|
918
|
+
// helper carried over by identity must be frozen too.
|
|
919
|
+
deferredFreeze = true;
|
|
920
|
+
document = next;
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
const positions = collectSourceInvariants(document);
|
|
924
|
+
const existingExtensions = collectExtensionSignatures(document);
|
|
925
|
+
if (!validateAst(next, pluginNames, rendererKeys, positions, sourceHeadingMarkers, prepared.pluginName, existingExtensions, display)) {
|
|
926
|
+
throw new TypeError('Transform returned an invalid Markdown document');
|
|
927
|
+
}
|
|
928
|
+
priorTransformChangedTree = true;
|
|
929
|
+
document = freezeAst(restoreCodeInternalProperties(document, next));
|
|
930
|
+
} catch (error) {
|
|
931
|
+
reportMarkdownPluginFailure(prepared.pluginName, 'transform', error);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
if (!deferredFreeze) {
|
|
935
|
+
return document;
|
|
936
|
+
}
|
|
937
|
+
// Freeze what the helpers built. With a plugin-authored transform in the
|
|
938
|
+
// list the whole tree is frozen, because that plugin may observe any part
|
|
939
|
+
// of it; with only Core helpers, the untouched remainder is left exactly
|
|
940
|
+
// as a zero-plugin parse leaves it.
|
|
941
|
+
return plugins.hasUntrustedTransform ? freezeAst(document) : freezeBuiltAst(root, document);
|
|
942
|
+
}
|
|
943
|
+
export function getMarkdownExtensionRenderer(plugins, node) {
|
|
944
|
+
return plugins?.renderers.get(`${node.plugin}\0${node.name}`)?.renderer;
|
|
945
|
+
}
|
|
946
|
+
export function markdownExtensionText(plugins, node) {
|
|
947
|
+
const renderer = getMarkdownExtensionRenderer(plugins, node);
|
|
948
|
+
if (renderer == null) {
|
|
949
|
+
return node.source ?? '';
|
|
950
|
+
}
|
|
951
|
+
try {
|
|
952
|
+
return renderer.toText(node);
|
|
953
|
+
} catch (error) {
|
|
954
|
+
reportMarkdownPluginFailure(node.plugin, 'render', error);
|
|
955
|
+
return node.source ?? '';
|
|
956
|
+
}
|
|
957
|
+
}
|