@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
|
@@ -9,11 +9,17 @@
|
|
|
9
9
|
* Used by both the runtime path (Theme injects <style>) and the build
|
|
10
10
|
* path (`astryx theme build` pre-compiles to CSS files).
|
|
11
11
|
*
|
|
12
|
+
* Every consumer-supplied declaration is checked by declarationBoundary.ts
|
|
13
|
+
* before it is emitted, so a value can never end its declaration or rule
|
|
14
|
+
* early. A refused declaration is dropped and reported in the supplied
|
|
15
|
+
* warnings array (build receipts), or through `console.warn` (runtime).
|
|
16
|
+
*
|
|
12
17
|
* Extracted from defineTheme.ts to reduce cyclomatic complexity and provide
|
|
13
18
|
* a clear single-responsibility module for CSS generation.
|
|
14
19
|
*
|
|
15
|
-
* @input DefinedTheme (resolved theme object from defineTheme)
|
|
16
|
-
* @output CSS rule strings, split by layer (component vs prose)
|
|
20
|
+
* @input DefinedTheme (resolved theme object from defineTheme), optional warnings array
|
|
21
|
+
* @output CSS rule strings, split by layer (component vs prose), and warning text
|
|
22
|
+
* for each dropped declaration
|
|
17
23
|
* @position packages/core/src/theme/generateThemeRules.ts
|
|
18
24
|
*/
|
|
19
25
|
|
|
@@ -26,6 +32,10 @@ import {parseStyleKey} from '../utils/parseStyleKey';
|
|
|
26
32
|
import {getDerivedVars} from './derivedVarRegistry';
|
|
27
33
|
import {dataTokenDefaults} from './domainTokens/dataTokens';
|
|
28
34
|
import {cssVar, classPrefix, dataAttrNamespace} from '../naming';
|
|
35
|
+
import {
|
|
36
|
+
checkDeclarationName,
|
|
37
|
+
checkDeclarationValue,
|
|
38
|
+
} from './declarationBoundary';
|
|
29
39
|
|
|
30
40
|
/**
|
|
31
41
|
* Theme @scope selectors.
|
|
@@ -366,6 +376,117 @@ function expandContainerPadding(
|
|
|
366
376
|
return tokens;
|
|
367
377
|
}
|
|
368
378
|
|
|
379
|
+
// =============================================================================
|
|
380
|
+
// Declaration assembly: a declaration always stays one declaration
|
|
381
|
+
// =============================================================================
|
|
382
|
+
|
|
383
|
+
type DiagnosticSink = (message: string) => void;
|
|
384
|
+
|
|
385
|
+
const warnToConsole: DiagnosticSink = message => {
|
|
386
|
+
console.warn(`[astryx theme] ${message}`);
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
/** Build receipts collect warning text; runtime callers warn by default. */
|
|
390
|
+
function diagnosticSink(warnings: string[] | undefined): DiagnosticSink {
|
|
391
|
+
return warnings ? message => warnings.push(message) : warnToConsole;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** Shorten an authored value for a one-line message. */
|
|
395
|
+
function previewValue(value: string): string {
|
|
396
|
+
const oneLine = JSON.stringify(value);
|
|
397
|
+
return oneLine.length > 80 ? `${oneLine.slice(0, 77)}..."` : oneLine;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function reportDrop(
|
|
401
|
+
sink: DiagnosticSink,
|
|
402
|
+
property: string,
|
|
403
|
+
value: string,
|
|
404
|
+
location: string,
|
|
405
|
+
reason: string,
|
|
406
|
+
): void {
|
|
407
|
+
sink(
|
|
408
|
+
`dropped "${property}" in ${location}: ${reason} (value: ${previewValue(value)})`,
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* True when `name: value;` stays exactly one declaration; otherwise reports
|
|
414
|
+
* the drop and returns false. Both checks follow CSS syntax, so valid CSS a
|
|
415
|
+
* browser keeps inside one declaration (a `data:` URI, `Gill\ Sans`, a closed
|
|
416
|
+
* comment, a vendor-prefixed property) is never refused; see
|
|
417
|
+
* declarationBoundary.ts for the rules.
|
|
418
|
+
*/
|
|
419
|
+
function acceptDeclaration(
|
|
420
|
+
property: string,
|
|
421
|
+
value: string,
|
|
422
|
+
location: string,
|
|
423
|
+
sink: DiagnosticSink,
|
|
424
|
+
): boolean {
|
|
425
|
+
const reason = checkDeclarationName(property) ?? checkDeclarationValue(value);
|
|
426
|
+
if (reason === null) {
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
reportDrop(sink, property, value, location, reason);
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Assemble `prop: value;` lines, dropping (and reporting) any entry whose
|
|
435
|
+
* property or value could not stay a single declaration. Theme definitions
|
|
436
|
+
* are code, but apps do assemble them from stored input (brand colors,
|
|
437
|
+
* white-labeling), so the generated stylesheet must never extend beyond the
|
|
438
|
+
* declarations it means to emit.
|
|
439
|
+
*/
|
|
440
|
+
function joinDeclarations(
|
|
441
|
+
entries: [string, string][],
|
|
442
|
+
location: string,
|
|
443
|
+
sink: DiagnosticSink,
|
|
444
|
+
mapProp: (prop: string) => string = p => p,
|
|
445
|
+
): string {
|
|
446
|
+
const lines: string[] = [];
|
|
447
|
+
for (const [rawProp, rawValue] of entries) {
|
|
448
|
+
// Legacy unenrolled tokens retain non-string values. Scan the same text
|
|
449
|
+
// interpolation has always emitted, without changing the normalized theme.
|
|
450
|
+
const value = `${rawValue}`;
|
|
451
|
+
const prop = mapProp(rawProp);
|
|
452
|
+
if (acceptDeclaration(prop, value, location, sink)) {
|
|
453
|
+
lines.push(` ${prop}: ${value};`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return lines.join('\n');
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** The `:scope` token block for portable and theme-local token values. */
|
|
460
|
+
function tokenBlock(
|
|
461
|
+
source: ThemeRuleSource,
|
|
462
|
+
location: string,
|
|
463
|
+
sink: DiagnosticSink,
|
|
464
|
+
): string | null {
|
|
465
|
+
const declarations = [
|
|
466
|
+
joinDeclarations(Object.entries(source.tokens), `${location}tokens`, sink),
|
|
467
|
+
joinDeclarations(
|
|
468
|
+
Object.entries(source.localTokens ?? {}),
|
|
469
|
+
`${location}localTokens`,
|
|
470
|
+
sink,
|
|
471
|
+
),
|
|
472
|
+
]
|
|
473
|
+
.filter(block => block.length > 0)
|
|
474
|
+
.join('\n');
|
|
475
|
+
const hasEntries =
|
|
476
|
+
Object.keys(source.tokens).length > 0 ||
|
|
477
|
+
Object.keys(source.localTokens ?? {}).length > 0;
|
|
478
|
+
return hasEntries ? ` :scope {\n${declarations}\n }` : null;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/** `components.button["variant:secondary"]`-style location for a rule. */
|
|
482
|
+
function componentLocation(
|
|
483
|
+
prefix: string,
|
|
484
|
+
component: string,
|
|
485
|
+
key: string,
|
|
486
|
+
): string {
|
|
487
|
+
return `${prefix}components.${component}[${JSON.stringify(key)}]`;
|
|
488
|
+
}
|
|
489
|
+
|
|
369
490
|
// =============================================================================
|
|
370
491
|
// Public API
|
|
371
492
|
// =============================================================================
|
|
@@ -393,9 +514,12 @@ export interface ThemeRuleSource {
|
|
|
393
514
|
components?: ComponentStyleMap;
|
|
394
515
|
}
|
|
395
516
|
|
|
396
|
-
export function generateThemeRules(
|
|
517
|
+
export function generateThemeRules(
|
|
518
|
+
theme: ThemeRuleSource,
|
|
519
|
+
warnings?: string[],
|
|
520
|
+
): string[] {
|
|
397
521
|
const parts: string[] = [];
|
|
398
|
-
const
|
|
522
|
+
const sink = diagnosticSink(warnings);
|
|
399
523
|
|
|
400
524
|
// Bare prose rules reference semantic variables instead of baking the root
|
|
401
525
|
// value. That lets adaptation token writes take effect through CSS alone
|
|
@@ -403,21 +527,25 @@ export function generateThemeRules(theme: ThemeRuleSource): string[] {
|
|
|
403
527
|
const val = (key: string): string => `var(${key})`;
|
|
404
528
|
|
|
405
529
|
// 1. Token block — CSS custom properties on :scope
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
530
|
+
const tokenRules = tokenBlock(theme, '', sink);
|
|
531
|
+
if (tokenRules !== null) {
|
|
532
|
+
parts.push(tokenRules);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// 1b. Base font — apply the theme's declared body font to the scope root.
|
|
536
|
+
// Components styled with `font-family: inherit` (SideNav items, Buttons)
|
|
537
|
+
// otherwise resolve against the browser default serif, since nothing else
|
|
538
|
+
// sets a page font. `font-family` inherits, so one rule on :scope covers
|
|
539
|
+
// the tree. Only emitted when the theme declares a body font, so a bare
|
|
540
|
+
// theme still contributes no scope rules of its own.
|
|
541
|
+
if (theme.tokens['--font-family-body']) {
|
|
542
|
+
parts.push(` :scope {\n font-family: var(--font-family-body);\n }`);
|
|
415
543
|
}
|
|
416
544
|
|
|
417
545
|
// 2. Component overrides: stable .astryx-* target classes combined with
|
|
418
546
|
// reflected data-* selectors for visual props and runtime states.
|
|
419
547
|
if (theme.components) {
|
|
420
|
-
generateComponentRules(theme.components, parts);
|
|
548
|
+
generateComponentRules(theme.components, parts, {sink});
|
|
421
549
|
}
|
|
422
550
|
|
|
423
551
|
// 3. Prose HTML element rules (h1-h6, p, small, code, hr)
|
|
@@ -513,6 +641,13 @@ function generateHeadingWeightOverrides(
|
|
|
513
641
|
}
|
|
514
642
|
}
|
|
515
643
|
|
|
644
|
+
/**
|
|
645
|
+
* The `fontWeight` a heading rule authors for `styleKey`, or undefined when
|
|
646
|
+
* it has none. A value that cannot stay one declaration is treated as not
|
|
647
|
+
* authored, so the override falls back to the inherited or token weight
|
|
648
|
+
* instead of interpolating it; the drop itself is reported where that same
|
|
649
|
+
* rule's declarations are emitted, so it is not reported twice here.
|
|
650
|
+
*/
|
|
516
651
|
function getAuthoredHeadingWeight(
|
|
517
652
|
headingRuleMap: Record<string, unknown>,
|
|
518
653
|
styleKey: string,
|
|
@@ -522,13 +657,20 @@ function getAuthoredHeadingWeight(
|
|
|
522
657
|
return undefined;
|
|
523
658
|
}
|
|
524
659
|
const fontWeight = (rule as Record<string, unknown>).fontWeight;
|
|
525
|
-
return typeof fontWeight === 'string'
|
|
660
|
+
return typeof fontWeight === 'string' &&
|
|
661
|
+
checkDeclarationValue(fontWeight) === null
|
|
662
|
+
? fontWeight
|
|
663
|
+
: undefined;
|
|
526
664
|
}
|
|
527
665
|
|
|
528
666
|
/** Options shared by root, adaptation, and media-surface component lowering. */
|
|
529
667
|
interface ComponentRuleOptions {
|
|
530
668
|
resetInheritedPaddingSpecificity?: boolean;
|
|
531
669
|
surface?: MediaSurface;
|
|
670
|
+
/** Receives dropped declarations; defaults to `console.warn`. */
|
|
671
|
+
sink?: DiagnosticSink;
|
|
672
|
+
/** Location prefix for diagnostics, e.g. `onDark.` or `adaptations[0].`. */
|
|
673
|
+
location?: string;
|
|
532
674
|
}
|
|
533
675
|
|
|
534
676
|
/**
|
|
@@ -545,6 +687,8 @@ function generateComponentRules(
|
|
|
545
687
|
{
|
|
546
688
|
resetInheritedPaddingSpecificity = false,
|
|
547
689
|
surface,
|
|
690
|
+
sink = warnToConsole,
|
|
691
|
+
location: locationPrefix = '',
|
|
548
692
|
}: ComponentRuleOptions = {},
|
|
549
693
|
): void {
|
|
550
694
|
for (const [component, rules] of Object.entries(components)) {
|
|
@@ -556,6 +700,7 @@ function generateComponentRules(
|
|
|
556
700
|
|
|
557
701
|
const suffix = parseStyleKey(key);
|
|
558
702
|
const baseSelector = componentSelector(component, suffix, surface);
|
|
703
|
+
const location = componentLocation(locationPrefix, component, key);
|
|
559
704
|
|
|
560
705
|
// Separate regular properties from pseudo-class overrides
|
|
561
706
|
const props: [string, string][] = [];
|
|
@@ -564,7 +709,14 @@ function generateComponentRules(
|
|
|
564
709
|
for (const [prop, value] of entries) {
|
|
565
710
|
if (prop.startsWith(':') && typeof value === 'object') {
|
|
566
711
|
pseudos.push([prop, value]);
|
|
567
|
-
} else
|
|
712
|
+
} else if (
|
|
713
|
+
acceptDeclaration(
|
|
714
|
+
toKebabCase(prop),
|
|
715
|
+
`${value as string}`,
|
|
716
|
+
location,
|
|
717
|
+
sink,
|
|
718
|
+
)
|
|
719
|
+
) {
|
|
568
720
|
props.push([prop, value as string]);
|
|
569
721
|
}
|
|
570
722
|
}
|
|
@@ -644,9 +796,12 @@ function generateComponentRules(
|
|
|
644
796
|
|
|
645
797
|
// Emit base rule
|
|
646
798
|
if (finalProps.length > 0) {
|
|
647
|
-
const declarations =
|
|
648
|
-
|
|
649
|
-
|
|
799
|
+
const declarations = joinDeclarations(
|
|
800
|
+
finalProps,
|
|
801
|
+
location,
|
|
802
|
+
sink,
|
|
803
|
+
toKebabCase,
|
|
804
|
+
);
|
|
650
805
|
parts.push(` ${baseSelector} {\n${declarations}\n }`);
|
|
651
806
|
}
|
|
652
807
|
|
|
@@ -654,9 +809,12 @@ function generateComponentRules(
|
|
|
654
809
|
for (const [pseudo, pseudoStyles] of pseudos) {
|
|
655
810
|
const pseudoEntries = Object.entries(pseudoStyles);
|
|
656
811
|
if (pseudoEntries.length > 0) {
|
|
657
|
-
const declarations =
|
|
658
|
-
|
|
659
|
-
.
|
|
812
|
+
const declarations = joinDeclarations(
|
|
813
|
+
pseudoEntries,
|
|
814
|
+
`${location}[${JSON.stringify(pseudo)}]`,
|
|
815
|
+
sink,
|
|
816
|
+
toKebabCase,
|
|
817
|
+
);
|
|
660
818
|
parts.push(
|
|
661
819
|
` ${appendPseudoToSelectorList(baseSelector, pseudo)} {\n${declarations}\n }`,
|
|
662
820
|
);
|
|
@@ -822,8 +980,11 @@ function generateSizeOverrides(
|
|
|
822
980
|
* Component rules (tokens, stable .astryx-* targets plus data-* selectors) are
|
|
823
981
|
* that need to beat StyleX — they stay in astryx-theme (above StyleX layers).
|
|
824
982
|
*/
|
|
825
|
-
export function generateThemeRulesSplit(
|
|
826
|
-
|
|
983
|
+
export function generateThemeRulesSplit(
|
|
984
|
+
theme: DefinedTheme,
|
|
985
|
+
warnings?: string[],
|
|
986
|
+
): ThemeRulesSplit {
|
|
987
|
+
const allRules = generateThemeRules(theme, warnings);
|
|
827
988
|
|
|
828
989
|
const prose: string[] = [];
|
|
829
990
|
const component: string[] = [];
|
|
@@ -847,22 +1008,29 @@ export function generateThemeRulesSplit(theme: DefinedTheme): ThemeRulesSplit {
|
|
|
847
1008
|
* to media contexts — only tokens change. Themes can further customize
|
|
848
1009
|
* via onDark.components / onLight.components.
|
|
849
1010
|
*/
|
|
850
|
-
export function generateOnMediaCSS(
|
|
1011
|
+
export function generateOnMediaCSS(
|
|
1012
|
+
theme: DefinedTheme,
|
|
1013
|
+
warnings?: string[],
|
|
1014
|
+
): string {
|
|
851
1015
|
const parts: string[] = [];
|
|
852
1016
|
const scopeSelector = themeScopeStart(theme.name);
|
|
1017
|
+
const sink = diagnosticSink(warnings);
|
|
853
1018
|
|
|
854
1019
|
for (const surface of ['dark', 'light'] as const) {
|
|
855
1020
|
const onMedia = surface === 'dark' ? theme.__onDark : theme.__onLight;
|
|
856
1021
|
if (!onMedia) {
|
|
857
1022
|
continue;
|
|
858
1023
|
}
|
|
1024
|
+
const location = surface === 'dark' ? 'onDark.' : 'onLight.';
|
|
859
1025
|
|
|
860
1026
|
// Token overrides
|
|
861
1027
|
const tokenEntries = Object.entries(onMedia.tokens);
|
|
862
1028
|
if (tokenEntries.length > 0) {
|
|
863
|
-
const declarations =
|
|
864
|
-
|
|
865
|
-
|
|
1029
|
+
const declarations = joinDeclarations(
|
|
1030
|
+
tokenEntries,
|
|
1031
|
+
`${location}tokens`,
|
|
1032
|
+
sink,
|
|
1033
|
+
);
|
|
866
1034
|
parts.push(` ${mediaSelector(surface)} {\n${declarations}\n }`);
|
|
867
1035
|
}
|
|
868
1036
|
|
|
@@ -872,6 +1040,8 @@ export function generateOnMediaCSS(theme: DefinedTheme): string {
|
|
|
872
1040
|
generateComponentRules(onMedia.components, parts, {
|
|
873
1041
|
resetInheritedPaddingSpecificity: true,
|
|
874
1042
|
surface,
|
|
1043
|
+
sink,
|
|
1044
|
+
location,
|
|
875
1045
|
});
|
|
876
1046
|
generateColorOverrides(onMedia.components, parts, surface);
|
|
877
1047
|
generateSizeOverrides(onMedia.components, parts, surface);
|
|
@@ -895,22 +1065,22 @@ export function generateOnMediaCSS(theme: DefinedTheme): string {
|
|
|
895
1065
|
}
|
|
896
1066
|
|
|
897
1067
|
/** Generate only the declarations one adaptation rule writes. */
|
|
898
|
-
function generateAdaptationRuleRules(
|
|
1068
|
+
function generateAdaptationRuleRules(
|
|
1069
|
+
rule: ThemeRuleSource,
|
|
1070
|
+
location: string,
|
|
1071
|
+
sink: DiagnosticSink,
|
|
1072
|
+
): string[] {
|
|
899
1073
|
const parts: string[] = [];
|
|
900
|
-
const
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
];
|
|
904
|
-
if (tokenEntries.length > 0) {
|
|
905
|
-
const declarations = tokenEntries
|
|
906
|
-
.map(([prop, value]) => ` ${prop}: ${value};`)
|
|
907
|
-
.join('\n');
|
|
908
|
-
parts.push(` :scope {\n${declarations}\n }`);
|
|
1074
|
+
const tokens = tokenBlock(rule, location, sink);
|
|
1075
|
+
if (tokens !== null) {
|
|
1076
|
+
parts.push(tokens);
|
|
909
1077
|
}
|
|
910
1078
|
|
|
911
1079
|
if (rule.components) {
|
|
912
1080
|
generateComponentRules(rule.components, parts, {
|
|
913
1081
|
resetInheritedPaddingSpecificity: true,
|
|
1082
|
+
sink,
|
|
1083
|
+
location,
|
|
914
1084
|
});
|
|
915
1085
|
generateColorOverrides(rule.components, parts);
|
|
916
1086
|
generateSizeOverrides(rule.components, parts);
|
|
@@ -948,7 +1118,11 @@ function generateAdaptationMediaBlock(
|
|
|
948
1118
|
* rule. Bare prose follows semantic variables, so token writes need no duplicate
|
|
949
1119
|
* prose selectors here.
|
|
950
1120
|
*/
|
|
951
|
-
export function generateAdaptationCSS(
|
|
1121
|
+
export function generateAdaptationCSS(
|
|
1122
|
+
theme: DefinedTheme,
|
|
1123
|
+
warnings?: string[],
|
|
1124
|
+
): ThemeCSSOutput {
|
|
1125
|
+
const sink = diagnosticSink(warnings);
|
|
952
1126
|
// Built themes can reach this compiler without passing through defineTheme.
|
|
953
1127
|
// Validate retained metadata even when it has no rules (and therefore emits
|
|
954
1128
|
// no CSS): width points remain observable through AppShell and inheritance.
|
|
@@ -982,8 +1156,12 @@ export function generateAdaptationCSS(theme: DefinedTheme): ThemeCSSOutput {
|
|
|
982
1156
|
// active whenever the later condition matches, even if the earlier condition
|
|
983
1157
|
// does not. Pairwise merging cannot repair that: multiple mutually exclusive
|
|
984
1158
|
// earlier rules can each overlap one broad later rule.
|
|
985
|
-
for (const rule of rules) {
|
|
986
|
-
const parts = generateAdaptationRuleRules(
|
|
1159
|
+
for (const [index, rule] of rules.entries()) {
|
|
1160
|
+
const parts = generateAdaptationRuleRules(
|
|
1161
|
+
rule,
|
|
1162
|
+
`adaptations[${index}].`,
|
|
1163
|
+
sink,
|
|
1164
|
+
);
|
|
987
1165
|
if (parts.length === 0) {
|
|
988
1166
|
continue;
|
|
989
1167
|
}
|
|
@@ -1029,12 +1207,9 @@ export function generateAdaptationCSS(theme: DefinedTheme): ThemeCSSOutput {
|
|
|
1029
1207
|
return {prose: '', component: blocks.join('\n\n')};
|
|
1030
1208
|
}
|
|
1031
1209
|
|
|
1032
|
-
/** Indent
|
|
1210
|
+
/** Indent the rule start without rewriting newlines inside authored values. */
|
|
1033
1211
|
function indentRule(rule: string): string {
|
|
1034
|
-
return rule
|
|
1035
|
-
.split('\n')
|
|
1036
|
-
.map(line => (line.length > 0 ? ` ${line}` : line))
|
|
1037
|
-
.join('\n');
|
|
1212
|
+
return ` ${rule}`;
|
|
1038
1213
|
}
|
|
1039
1214
|
|
|
1040
1215
|
/**
|
|
@@ -1075,8 +1250,11 @@ export function generateDataTokenDefaultsCSS(): string {
|
|
|
1075
1250
|
* The theme-independent `--color-data-*` defaults are not part of this output:
|
|
1076
1251
|
* see `generateDataTokenDefaultsCSS`.
|
|
1077
1252
|
*/
|
|
1078
|
-
export function generateThemeCSS(
|
|
1079
|
-
|
|
1253
|
+
export function generateThemeCSS(
|
|
1254
|
+
theme: DefinedTheme,
|
|
1255
|
+
warnings?: string[],
|
|
1256
|
+
): ThemeCSSOutput {
|
|
1257
|
+
const {component, prose} = generateThemeRulesSplit(theme, warnings);
|
|
1080
1258
|
const scopeSelector = themeScopeStart(theme.name);
|
|
1081
1259
|
const scopeTo = THEME_SCOPE_TO;
|
|
1082
1260
|
|
|
@@ -1096,14 +1274,14 @@ export function generateThemeCSS(theme: DefinedTheme): ThemeCSSOutput {
|
|
|
1096
1274
|
|
|
1097
1275
|
// Adaptations follow the root theme in authored order. Media-surface rules
|
|
1098
1276
|
// follow adaptations so onDark/onLight keep their specified precedence.
|
|
1099
|
-
const adaptationCss = generateAdaptationCSS(theme);
|
|
1277
|
+
const adaptationCss = generateAdaptationCSS(theme, warnings);
|
|
1100
1278
|
if (adaptationCss.component) {
|
|
1101
1279
|
componentCss = componentCss
|
|
1102
1280
|
? `${componentCss}\n\n${adaptationCss.component}`
|
|
1103
1281
|
: adaptationCss.component;
|
|
1104
1282
|
}
|
|
1105
1283
|
|
|
1106
|
-
const onMediaCss = generateOnMediaCSS(theme);
|
|
1284
|
+
const onMediaCss = generateOnMediaCSS(theme, warnings);
|
|
1107
1285
|
if (onMediaCss) {
|
|
1108
1286
|
componentCss = componentCss
|
|
1109
1287
|
? `${componentCss}\n\n${onMediaCss}`
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import {describe, expect, it} from 'vitest';
|
|
25
|
-
import {defineTheme} from './defineTheme';
|
|
25
|
+
import {defineTheme, generateThemeCSS} from './defineTheme';
|
|
26
26
|
import {resolveTokenValue} from './resolveThemeValues';
|
|
27
27
|
|
|
28
28
|
/** Exactly what the pre-extraction implementation did with each shape. */
|
|
@@ -43,6 +43,9 @@ describe('root token values keep their historical acceptance', () => {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
expect(theme.tokens['--color-background-body']).toBe(expected);
|
|
46
|
+
expect(generateThemeCSS(theme).component).toContain(
|
|
47
|
+
`--color-background-body: ${String(expected)};`,
|
|
48
|
+
);
|
|
46
49
|
});
|
|
47
50
|
|
|
48
51
|
it('still resolves the documented shapes', () => {
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
* @output Exports reusable background-color and background-image state styles
|
|
11
11
|
* @position Internal styling utility for interactive core surfaces
|
|
12
12
|
*
|
|
13
|
+
* Every surface that paints a press composes one of these (or carries its own
|
|
14
|
+
* `:active` rule), so a change to how the system answers a press is made here
|
|
15
|
+
* once. `pressedBackgroundColor` is the press without the hover, for controls
|
|
16
|
+
* whose hover is a colour or nothing.
|
|
17
|
+
*
|
|
13
18
|
* Keep the enabled guard outside the individual states. StyleX assigns an
|
|
14
19
|
* extra priority bucket (and generated selector specificity) to media-nested
|
|
15
20
|
* rules. Repeating `:active` inside the hover-capable branch gives hover and
|
|
@@ -71,4 +76,19 @@ export const interactionOverlayStyles = stylex.create({
|
|
|
71
76
|
},
|
|
72
77
|
},
|
|
73
78
|
},
|
|
79
|
+
/**
|
|
80
|
+
* The pressed arm alone, for a control whose hover answer is its own — a
|
|
81
|
+
* text link changes colour, a disclosure row has none — so a press paints
|
|
82
|
+
* the system's pressed overlay without adding a hover surface the control
|
|
83
|
+
* never had. Same enabled guard as the paired states above.
|
|
84
|
+
*/
|
|
85
|
+
pressedBackgroundColor: {
|
|
86
|
+
backgroundColor: {
|
|
87
|
+
default: null,
|
|
88
|
+
[ENABLED]: {
|
|
89
|
+
default: null,
|
|
90
|
+
':active': colorVars['--color-overlay-pressed'],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
74
94
|
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, expect, it} from 'vitest';
|
|
4
|
+
import {isSafeDestination, isSafeUrl, sanitizeUrl} from './safeUrl';
|
|
5
|
+
|
|
6
|
+
// Expectations come from the navigation contract, not another policy helper.
|
|
7
|
+
const accepted = [
|
|
8
|
+
'https://example.com/a?b=c#d',
|
|
9
|
+
'http://example.com',
|
|
10
|
+
'//example.com/protocol-relative',
|
|
11
|
+
'/relative/path',
|
|
12
|
+
'relative/path',
|
|
13
|
+
'#fragment',
|
|
14
|
+
'mailto:a@example.com',
|
|
15
|
+
'tel:+1234567890',
|
|
16
|
+
'custom:open/document',
|
|
17
|
+
'data:image/png;base64,iVBORw0KGgo=',
|
|
18
|
+
'data:text/plain,download',
|
|
19
|
+
];
|
|
20
|
+
const rejected = [
|
|
21
|
+
'javascript:alert(1)',
|
|
22
|
+
'JaVaScRiPt:alert(1)',
|
|
23
|
+
' javascript:alert(1)',
|
|
24
|
+
'vbscript:MsgBox(1)',
|
|
25
|
+
'data:text/html,<script>alert(1)</script>',
|
|
26
|
+
'DATA:TEXT/HTML;base64,PHNjcmlwdD4=',
|
|
27
|
+
'java\tscript:alert(1)',
|
|
28
|
+
'java\nscript:alert(1)',
|
|
29
|
+
'\x00javascript:alert(1)',
|
|
30
|
+
'java\x7fscript:alert(1)',
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
describe('navigation strings', () => {
|
|
34
|
+
it.each(accepted)('accepts %s', url => {
|
|
35
|
+
expect(isSafeUrl(url)).toBe(true);
|
|
36
|
+
expect(isSafeDestination(url)).toBe(true);
|
|
37
|
+
expect(sanitizeUrl(url)).toBe(url);
|
|
38
|
+
});
|
|
39
|
+
it.each(rejected)('rejects %s', url => {
|
|
40
|
+
expect(isSafeUrl(url)).toBe(false);
|
|
41
|
+
expect(isSafeDestination(url)).toBe(false);
|
|
42
|
+
expect(sanitizeUrl(url)).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
it('normalizes accepted strings only when requested', () => {
|
|
45
|
+
expect(sanitizeUrl(' /docs\n')).toBe('/docs');
|
|
46
|
+
expect(sanitizeUrl('/do\x00cs')).toBe('/docs');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('structured destinations', () => {
|
|
51
|
+
it.each([
|
|
52
|
+
{pathname: '/docs', search: '?q=1', hash: '#a'},
|
|
53
|
+
{href: 'https://example.com'},
|
|
54
|
+
{protocol: 'https:', hostname: 'example.com', pathname: '/docs'},
|
|
55
|
+
{protocol: 'custom', pathname: 'document'},
|
|
56
|
+
{protocol: 'mailto:', pathname: 'a@example.com'},
|
|
57
|
+
{protocol: 'data:', pathname: 'image/png;base64,aA=='},
|
|
58
|
+
{search: '?page=2'},
|
|
59
|
+
{hash: '#section'},
|
|
60
|
+
{},
|
|
61
|
+
new URL('https://example.com/docs'),
|
|
62
|
+
undefined,
|
|
63
|
+
null,
|
|
64
|
+
])('accepts supported destination %j', destination => {
|
|
65
|
+
expect(isSafeDestination(destination)).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it.each([
|
|
68
|
+
{pathname: 'javascript:alert(1)'},
|
|
69
|
+
{pathname: 'java\x00script:alert(1)'},
|
|
70
|
+
{href: 'vbscript:MsgBox(1)'},
|
|
71
|
+
{pathname: '/ok', href: 'javascript:1'},
|
|
72
|
+
{protocol: 'javascript:', pathname: 'alert(1)'},
|
|
73
|
+
{protocol: 'javascript', pathname: 'alert(1)'},
|
|
74
|
+
{protocol: ' JaVa\tsCrIpT ', pathname: 'alert(1)'},
|
|
75
|
+
{protocol: 'vbscript', pathname: 'MsgBox(1)'},
|
|
76
|
+
{protocol: 'data:', pathname: 'text/html,<b>x</b>'},
|
|
77
|
+
{protocol: 'data', pathname: 'TEXT/HTML;base64,PGI+eDwvYj4='},
|
|
78
|
+
{protocol: 'data:', host: 'text/', pathname: 'html,<b>x</b>'},
|
|
79
|
+
{protocol: 'data:', hostname: 'text/html,<b>x</b>'},
|
|
80
|
+
{href: '/safe', protocol: 'javascript:'},
|
|
81
|
+
new URL('javascript:alert(1)'),
|
|
82
|
+
{pathname: 42},
|
|
83
|
+
{protocol: {toString: (): string => 'javascript:'}},
|
|
84
|
+
3,
|
|
85
|
+
])('rejects blocked or unsupported destination %j', destination => {
|
|
86
|
+
expect(isSafeDestination(destination)).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file safeUrl.ts
|
|
5
|
+
* @input Navigation strings or supported structured router destinations
|
|
6
|
+
* @output The shared blocked-scheme decision, without rewriting accepted inputs
|
|
7
|
+
* @position Core navigation policy; embedded-resource policy is separate
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
function normalizeUrl(url: string): string {
|
|
11
|
+
// eslint-disable-next-line no-control-regex -- inspect schemes without browser-ignored controls
|
|
12
|
+
return url.replace(/[\x00-\x1f\x7f]/g, '').trim();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Return a normalized navigation URL, or null for a blocked scheme. */
|
|
16
|
+
export function sanitizeUrl(url: string): string | null {
|
|
17
|
+
const normalized = normalizeUrl(url);
|
|
18
|
+
const lower = normalized.toLowerCase();
|
|
19
|
+
if (
|
|
20
|
+
lower.startsWith('javascript:') ||
|
|
21
|
+
lower.startsWith('vbscript:') ||
|
|
22
|
+
lower.startsWith('data:text/html')
|
|
23
|
+
) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return normalized;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Inspect a navigation string without changing the value used by its sink. */
|
|
30
|
+
export function isSafeUrl(url: string): boolean {
|
|
31
|
+
return sanitizeUrl(url) !== null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Inspect React Router paths, Next.js URL objects, and URL instances without
|
|
36
|
+
* serializing or replacing the object handed to the router. Search/query/hash
|
|
37
|
+
* are route-relative data, not schemes. Absent destinations remain absent.
|
|
38
|
+
*/
|
|
39
|
+
export function isSafeDestination(destination: unknown): boolean {
|
|
40
|
+
if (destination == null) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (typeof destination === 'string') {
|
|
44
|
+
return isSafeUrl(destination);
|
|
45
|
+
}
|
|
46
|
+
if (typeof destination !== 'object') {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const {pathname, href, protocol, host, hostname} = destination as {
|
|
51
|
+
pathname?: unknown;
|
|
52
|
+
href?: unknown;
|
|
53
|
+
protocol?: unknown;
|
|
54
|
+
host?: unknown;
|
|
55
|
+
hostname?: unknown;
|
|
56
|
+
};
|
|
57
|
+
for (const field of [pathname, href, protocol, host, hostname]) {
|
|
58
|
+
if (field != null && typeof field !== 'string') {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (typeof pathname === 'string' && !isSafeUrl(pathname)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (typeof href === 'string' && !isSafeUrl(href)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (typeof protocol === 'string' && protocol !== '') {
|
|
69
|
+
// URL-object formatters accept a protocol with or without its colon and
|
|
70
|
+
// can assemble a data media type from the host and pathname fields.
|
|
71
|
+
const normalizedProtocol = normalizeUrl(protocol);
|
|
72
|
+
const prefix = normalizedProtocol.endsWith(':')
|
|
73
|
+
? normalizedProtocol
|
|
74
|
+
: `${normalizedProtocol}:`;
|
|
75
|
+
if (!isSafeUrl(prefix)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const body = (host || hostname || '') as string;
|
|
79
|
+
const path = typeof pathname === 'string' ? pathname : '';
|
|
80
|
+
if (!isSafeUrl(`${prefix}${body}${path}`)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|