tempest-react-sdk 0.39.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/_rolldown/runtime.cjs +1 -1
- package/dist/access/access-control-context.cjs.map +1 -1
- package/dist/access/use-can.cjs.map +1 -1
- package/dist/app/AppProviders.cjs.map +1 -1
- package/dist/app/AppProviders.js.map +1 -1
- package/dist/audio/use-audio-recorder.cjs.map +1 -1
- package/dist/audio/use-audio.cjs.map +1 -1
- package/dist/audio/use-microphone.cjs.map +1 -1
- package/dist/auth/create-auth-store.cjs.map +1 -1
- package/dist/auth/lazy-with-retry.cjs.map +1 -1
- package/dist/auth/passkey.cjs.map +1 -1
- package/dist/auth/passkey.js.map +1 -1
- package/dist/auth/use-passkey.cjs.map +1 -1
- package/dist/br/BrazilMap.cjs +1 -1
- package/dist/br/BrazilMap.cjs.map +1 -1
- package/dist/br/BrazilMap.js +2 -2
- package/dist/br/BrazilMap.js.map +1 -1
- package/dist/br/BrazilStateCitySelect.cjs.map +1 -1
- package/dist/br/BrazilStateCitySelect.js.map +1 -1
- package/dist/br/BrazilStateMap.cjs +1 -1
- package/dist/br/BrazilStateMap.cjs.map +1 -1
- package/dist/br/BrazilStateMap.js +2 -2
- package/dist/br/BrazilStateMap.js.map +1 -1
- package/dist/br/MapLegend.cjs.map +1 -1
- package/dist/br/MapLegend.js.map +1 -1
- package/dist/br/MapMarkers.cjs.map +1 -1
- package/dist/br/MapMarkers.js.map +1 -1
- package/dist/br/MunicipalitySearch.cjs.map +1 -1
- package/dist/br/MunicipalitySearch.js.map +1 -1
- package/dist/br/PixQRCode.cjs.map +1 -1
- package/dist/br/PixQRCode.js.map +1 -1
- package/dist/br/use-map-hover.cjs.map +1 -1
- package/dist/br/use-map-zoom.cjs.map +1 -1
- package/dist/capture/media-recorder.cjs +1 -1
- package/dist/capture/media-recorder.js +2 -2
- package/dist/capture/use-barcode-scanner.cjs +1 -1
- package/dist/capture/use-barcode-scanner.cjs.map +1 -1
- package/dist/capture/use-barcode-scanner.js +2 -2
- package/dist/capture/use-barcode-scanner.js.map +1 -1
- package/dist/capture/use-screen-capture.cjs.map +1 -1
- package/dist/capture/use-speech-recognition.cjs.map +1 -1
- package/dist/capture/use-torch.cjs.map +1 -1
- package/dist/capture/use-video-recorder.cjs.map +1 -1
- package/dist/charts/AreaChart.cjs.map +1 -1
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.cjs.map +1 -1
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/LineChart.cjs.map +1 -1
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/PieChart.cjs.map +1 -1
- package/dist/charts/PieChart.js.map +1 -1
- package/dist/charts/RadarChart.cjs.map +1 -1
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/use-chart-colors.cjs.map +1 -1
- package/dist/components/AIChat/AIChat.cjs.map +1 -1
- package/dist/components/AIChat/AIChat.js.map +1 -1
- package/dist/components/AIChat/AIChatComposer.cjs.map +1 -1
- package/dist/components/AIChat/AIChatComposer.js.map +1 -1
- package/dist/components/AIChat/AIChatTurn.cjs.map +1 -1
- package/dist/components/AIChat/AIChatTurn.js.map +1 -1
- package/dist/components/Accordion/Accordion.cjs.map +1 -1
- package/dist/components/Accordion/Accordion.js.map +1 -1
- package/dist/components/Alert/Alert.cjs.map +1 -1
- package/dist/components/Alert/Alert.js.map +1 -1
- package/dist/components/AppBar/AppBar.cjs.map +1 -1
- package/dist/components/AppBar/AppBar.js.map +1 -1
- package/dist/components/AppShell/AppShell.cjs.map +1 -1
- package/dist/components/AppShell/AppShell.js.map +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.cjs.map +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.js.map +1 -1
- package/dist/components/AudioRecorder/AudioRecorder.cjs.map +1 -1
- package/dist/components/AudioRecorder/AudioRecorder.js.map +1 -1
- package/dist/components/Avatar/Avatar.cjs.map +1 -1
- package/dist/components/Avatar/Avatar.js.map +1 -1
- package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -1
- package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -1
- package/dist/components/Badge/Badge.cjs.map +1 -1
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/Banner/Banner.cjs.map +1 -1
- package/dist/components/Banner/Banner.js.map +1 -1
- package/dist/components/BarcodeScanner/BarcodeScanner.cjs.map +1 -1
- package/dist/components/BarcodeScanner/BarcodeScanner.js.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigation.cjs.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigation.js.map +1 -1
- package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
- package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/components/Button/Button.cjs.map +1 -1
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Calendar/Calendar.cjs +1 -1
- package/dist/components/Calendar/Calendar.cjs.map +1 -1
- package/dist/components/Calendar/Calendar.js +38 -41
- package/dist/components/Calendar/Calendar.js.map +1 -1
- package/dist/components/Card/Card.cjs.map +1 -1
- package/dist/components/Card/Card.js.map +1 -1
- package/dist/components/Carousel/Carousel.cjs.map +1 -1
- package/dist/components/Carousel/Carousel.js.map +1 -1
- package/dist/components/Chat/Chat.cjs.map +1 -1
- package/dist/components/Chat/Chat.js.map +1 -1
- package/dist/components/Chat/ChatComposer.cjs.map +1 -1
- package/dist/components/Chat/ChatComposer.js.map +1 -1
- package/dist/components/Chat/chat-groups.cjs +1 -1
- package/dist/components/Chat/chat-groups.cjs.map +1 -1
- package/dist/components/Chat/chat-groups.js +2 -2
- package/dist/components/Chat/chat-groups.js.map +1 -1
- package/dist/components/Checkbox/Checkbox.cjs.map +1 -1
- package/dist/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/ChipInput/ChipInput.cjs.map +1 -1
- package/dist/components/ChipInput/ChipInput.js.map +1 -1
- package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.js.map +1 -1
- package/dist/components/Collapsible/Collapsible.cjs.map +1 -1
- package/dist/components/Collapsible/Collapsible.js.map +1 -1
- package/dist/components/Combobox/Combobox.cjs.map +1 -1
- package/dist/components/Combobox/Combobox.js.map +1 -1
- package/dist/components/Command/Command.cjs.map +1 -1
- package/dist/components/Command/Command.js.map +1 -1
- package/dist/components/ConfirmDialog/ConfirmDialog.cjs.map +1 -1
- package/dist/components/ConfirmDialog/ConfirmDialog.js.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.cjs.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/components/CopyButton/CopyButton.cjs.map +1 -1
- package/dist/components/CopyButton/CopyButton.js.map +1 -1
- package/dist/components/DataList/DataList.cjs.map +1 -1
- package/dist/components/DataList/DataList.js.map +1 -1
- package/dist/components/DataTable/DataTable.cjs +1 -1
- package/dist/components/DataTable/DataTable.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.js +2 -2
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/DataTable/EditableCell.cjs.map +1 -1
- package/dist/components/DataTable/EditableCell.js.map +1 -1
- package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.cjs.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
- package/dist/components/DescriptionList/DescriptionList.cjs.map +1 -1
- package/dist/components/DescriptionList/DescriptionList.js.map +1 -1
- package/dist/components/Divider/Divider.cjs.map +1 -1
- package/dist/components/Divider/Divider.js.map +1 -1
- package/dist/components/Drawer/Drawer.cjs.map +1 -1
- package/dist/components/Drawer/Drawer.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/Dropzone/Dropzone.cjs.map +1 -1
- package/dist/components/Dropzone/Dropzone.js.map +1 -1
- package/dist/components/EmptyState/EmptyState.cjs.map +1 -1
- package/dist/components/EmptyState/EmptyState.js.map +1 -1
- package/dist/components/ErrorState/ErrorState.cjs.map +1 -1
- package/dist/components/ErrorState/ErrorState.js.map +1 -1
- package/dist/components/FileUpload/FileUpload.cjs +1 -1
- package/dist/components/FileUpload/FileUpload.cjs.map +1 -1
- package/dist/components/FileUpload/FileUpload.js +1 -1
- package/dist/components/FileUpload/FileUpload.js.map +1 -1
- package/dist/components/FilterBar/FilterBar.cjs.map +1 -1
- package/dist/components/FilterBar/FilterBar.js.map +1 -1
- package/dist/components/FloatingActionButton/FloatingActionButton.cjs.map +1 -1
- package/dist/components/FloatingActionButton/FloatingActionButton.js.map +1 -1
- package/dist/components/Form/Form.cjs.map +1 -1
- package/dist/components/Form/Form.js.map +1 -1
- package/dist/components/HoverCard/HoverCard.cjs.map +1 -1
- package/dist/components/HoverCard/HoverCard.js.map +1 -1
- package/dist/components/Image/Image.cjs.map +1 -1
- package/dist/components/ImageCropper/ImageCropper.cjs +1 -1
- package/dist/components/ImageCropper/ImageCropper.cjs.map +1 -1
- package/dist/components/ImageCropper/ImageCropper.js +1 -1
- package/dist/components/ImageCropper/ImageCropper.js.map +1 -1
- package/dist/components/Input/Input.cjs.map +1 -1
- package/dist/components/Input/Input.js.map +1 -1
- package/dist/components/InstallBanner/InstallBanner.cjs.map +1 -1
- package/dist/components/InstallBanner/InstallBanner.js.map +1 -1
- package/dist/components/InstallButton/InstallButton.cjs.map +1 -1
- package/dist/components/InstallButton/InstallButton.js.map +1 -1
- package/dist/components/Kanban/Kanban.cjs.map +1 -1
- package/dist/components/Kanban/Kanban.js.map +1 -1
- package/dist/components/Label/Label.cjs.map +1 -1
- package/dist/components/Label/Label.js.map +1 -1
- package/dist/components/Lightbox/Lightbox.cjs.map +1 -1
- package/dist/components/Lightbox/Lightbox.js.map +1 -1
- package/dist/components/ListTile/ListTile.cjs.map +1 -1
- package/dist/components/ListTile/ListTile.js.map +1 -1
- package/dist/components/Markdown/Markdown.cjs +1 -1
- package/dist/components/Markdown/Markdown.cjs.map +1 -1
- package/dist/components/Markdown/Markdown.js +2 -2
- package/dist/components/Markdown/Markdown.js.map +1 -1
- package/dist/components/Masonry/Masonry.cjs.map +1 -1
- package/dist/components/Masonry/Masonry.js.map +1 -1
- package/dist/components/Menubar/Menubar.cjs.map +1 -1
- package/dist/components/Menubar/Menubar.js.map +1 -1
- package/dist/components/Modal/Modal.cjs.map +1 -1
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/ModalsManager/ModalsManager.cjs.map +1 -1
- package/dist/components/ModalsManager/ModalsManager.js.map +1 -1
- package/dist/components/Money/Money.cjs.map +1 -1
- package/dist/components/Money/Money.js.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/components/NProgress/NProgress.cjs.map +1 -1
- package/dist/components/Navbar/Navbar.cjs.map +1 -1
- package/dist/components/Navbar/Navbar.js.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.cjs.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.js.map +1 -1
- package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -1
- package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -1
- package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -1
- package/dist/components/OfflineIndicator/OfflineIndicator.cjs.map +1 -1
- package/dist/components/OfflineIndicator/OfflineIndicator.js.map +1 -1
- package/dist/components/Page/Page.cjs.map +1 -1
- package/dist/components/Page/Page.js.map +1 -1
- package/dist/components/Pagination/Pagination.cjs.map +1 -1
- package/dist/components/Pagination/Pagination.js.map +1 -1
- package/dist/components/PasswordInput/PasswordInput.cjs.map +1 -1
- package/dist/components/PasswordInput/PasswordInput.js.map +1 -1
- package/dist/components/PinInput/PinInput.cjs.map +1 -1
- package/dist/components/PinInput/PinInput.js.map +1 -1
- package/dist/components/Popover/Popover.cjs.map +1 -1
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Portal/Portal.cjs.map +1 -1
- package/dist/components/Progress/Progress.cjs.map +1 -1
- package/dist/components/Progress/Progress.js.map +1 -1
- package/dist/components/QRCode/QRCode.cjs.map +1 -1
- package/dist/components/QRCode/QRCode.js.map +1 -1
- package/dist/components/Radio/Radio.cjs.map +1 -1
- package/dist/components/Radio/Radio.js.map +1 -1
- package/dist/components/RangeSlider/RangeSlider.cjs.map +1 -1
- package/dist/components/RangeSlider/RangeSlider.js.map +1 -1
- package/dist/components/RatingStars/RatingStars.cjs.map +1 -1
- package/dist/components/RatingStars/RatingStars.js.map +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
- package/dist/components/RelativeTime/RelativeTime.cjs.map +1 -1
- package/dist/components/RelativeTime/RelativeTime.js.map +1 -1
- package/dist/components/Resizable/Resizable.cjs.map +1 -1
- package/dist/components/Resizable/Resizable.js.map +1 -1
- package/dist/components/Scheduler/Scheduler.cjs.map +1 -1
- package/dist/components/Scheduler/Scheduler.js.map +1 -1
- package/dist/components/ScrollArea/ScrollArea.cjs.map +1 -1
- package/dist/components/SearchBar/SearchBar.cjs.map +1 -1
- package/dist/components/SearchBar/SearchBar.js.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.cjs.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
- package/dist/components/Select/Select.cjs.map +1 -1
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
- package/dist/components/Sidebar/Sidebar.js.map +1 -1
- package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -1
- package/dist/components/SignaturePad/SignaturePad.js.map +1 -1
- package/dist/components/Slider/Slider.cjs.map +1 -1
- package/dist/components/Slider/Slider.js.map +1 -1
- package/dist/components/Sparkline/Sparkline.cjs.map +1 -1
- package/dist/components/Sparkline/Sparkline.js.map +1 -1
- package/dist/components/Spinner/Spinner.cjs.map +1 -1
- package/dist/components/Spinner/Spinner.js.map +1 -1
- package/dist/components/Stat/Stat.cjs.map +1 -1
- package/dist/components/Stat/Stat.js.map +1 -1
- package/dist/components/Stepper/Stepper.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.js.map +1 -1
- package/dist/components/StepperInput/StepperInput.cjs.map +1 -1
- package/dist/components/StepperInput/StepperInput.js.map +1 -1
- package/dist/components/Switch/Switch.cjs.map +1 -1
- package/dist/components/Switch/Switch.js.map +1 -1
- package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs.map +1 -1
- package/dist/components/SyncStatusBadge/SyncStatusBadge.js.map +1 -1
- package/dist/components/Table/Table.cjs.map +1 -1
- package/dist/components/Table/Table.js.map +1 -1
- package/dist/components/Tabs/Tabs.cjs.map +1 -1
- package/dist/components/Tabs/Tabs.js.map +1 -1
- package/dist/components/Tag/Tag.cjs.map +1 -1
- package/dist/components/Tag/Tag.js.map +1 -1
- package/dist/components/Textarea/Textarea.cjs.map +1 -1
- package/dist/components/Textarea/Textarea.js.map +1 -1
- package/dist/components/TimePicker/TimePicker.cjs.map +1 -1
- package/dist/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/components/Timeline/Timeline.cjs.map +1 -1
- package/dist/components/Timeline/Timeline.js.map +1 -1
- package/dist/components/Toast/ToastProvider.cjs.map +1 -1
- package/dist/components/Toast/ToastProvider.js.map +1 -1
- package/dist/components/Toggle/Toggle.cjs.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroup.cjs.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/Tour/Tour.cjs.map +1 -1
- package/dist/components/Tour/Tour.js.map +1 -1
- package/dist/components/Transfer/Transfer.cjs.map +1 -1
- package/dist/components/Transfer/Transfer.js.map +1 -1
- package/dist/components/TreeView/TreeView.cjs +1 -1
- package/dist/components/TreeView/TreeView.cjs.map +1 -1
- package/dist/components/TreeView/TreeView.js +1 -4
- package/dist/components/TreeView/TreeView.js.map +1 -1
- package/dist/components/UpdatePrompt/UpdatePrompt.cjs.map +1 -1
- package/dist/components/UpdatePrompt/UpdatePrompt.js.map +1 -1
- package/dist/components/VirtualList/VirtualList.cjs.map +1 -1
- package/dist/components/VirtualList/VirtualList.js.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.js.map +1 -1
- package/dist/components/VisuallyHidden/VisuallyHidden.cjs.map +1 -1
- package/dist/components/Wizard/Wizard.cjs.map +1 -1
- package/dist/components/Wizard/Wizard.js.map +1 -1
- package/dist/data/data-provider-context.cjs.map +1 -1
- package/dist/data/use-resource.cjs.map +1 -1
- package/dist/editor/RichTextEditor.cjs.map +1 -1
- package/dist/editor/RichTextEditor.js.map +1 -1
- package/dist/error-boundary/use-error-handler.cjs.map +1 -1
- package/dist/feature-flags/FeatureFlagsProvider.cjs.map +1 -1
- package/dist/forms/FormField.cjs.map +1 -1
- package/dist/forms/masked-inputs.cjs +1 -1
- package/dist/forms/masked-inputs.cjs.map +1 -1
- package/dist/forms/masked-inputs.js +1 -1
- package/dist/forms/masked-inputs.js.map +1 -1
- package/dist/forms/use-viacep.cjs.map +1 -1
- package/dist/forms/use-zod-form.cjs.map +1 -1
- package/dist/forms/zod-resolver.cjs.map +1 -1
- package/dist/forms/zod-resolver.js.map +1 -1
- package/dist/geo/TrajectoryMap.cjs.map +1 -1
- package/dist/geo/TrajectoryMap.js.map +1 -1
- package/dist/geo/leaflet-map.cjs.map +1 -1
- package/dist/geo/leaflet-map.js.map +1 -1
- package/dist/geo/use-position-tracker.cjs.map +1 -1
- package/dist/hooks/use-announce.cjs.map +1 -1
- package/dist/hooks/use-async.cjs.map +1 -1
- package/dist/hooks/use-before-install-prompt.cjs.map +1 -1
- package/dist/hooks/use-breakpoint.cjs.map +1 -1
- package/dist/hooks/use-click-outside.cjs.map +1 -1
- package/dist/hooks/use-client-filter.cjs.map +1 -1
- package/dist/hooks/use-clipboard.cjs.map +1 -1
- package/dist/hooks/use-counter.cjs.map +1 -1
- package/dist/hooks/use-debounce.cjs.map +1 -1
- package/dist/hooks/use-deep-memo.cjs.map +1 -1
- package/dist/hooks/use-disclosure.cjs.map +1 -1
- package/dist/hooks/use-document-visibility.cjs.map +1 -1
- package/dist/hooks/use-event-listener.cjs.map +1 -1
- package/dist/hooks/use-geolocation.cjs.map +1 -1
- package/dist/hooks/use-hover.cjs.map +1 -1
- package/dist/hooks/use-idle.cjs.map +1 -1
- package/dist/hooks/use-install-prompt.cjs +1 -1
- package/dist/hooks/use-install-prompt.cjs.map +1 -1
- package/dist/hooks/use-install-prompt.js +1 -1
- package/dist/hooks/use-install-prompt.js.map +1 -1
- package/dist/hooks/use-intersection-observer.cjs.map +1 -1
- package/dist/hooks/use-interval.cjs.map +1 -1
- package/dist/hooks/use-is-first-render.cjs.map +1 -1
- package/dist/hooks/use-keyboard-shortcut.cjs +1 -1
- package/dist/hooks/use-keyboard-shortcut.cjs.map +1 -1
- package/dist/hooks/use-keyboard-shortcut.js +1 -1
- package/dist/hooks/use-keyboard-shortcut.js.map +1 -1
- package/dist/hooks/use-list-state.cjs.map +1 -1
- package/dist/hooks/use-local-storage.cjs.map +1 -1
- package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
- package/dist/hooks/use-long-press.cjs.map +1 -1
- package/dist/hooks/use-map.cjs.map +1 -1
- package/dist/hooks/use-media-devices.cjs.map +1 -1
- package/dist/hooks/use-media-permission.cjs.map +1 -1
- package/dist/hooks/use-media-query.cjs.map +1 -1
- package/dist/hooks/use-object-url.cjs.map +1 -1
- package/dist/hooks/use-online.cjs.map +1 -1
- package/dist/hooks/use-pagination.cjs.map +1 -1
- package/dist/hooks/use-previous.cjs.map +1 -1
- package/dist/hooks/use-queue.cjs.map +1 -1
- package/dist/hooks/use-resize-observer.cjs.map +1 -1
- package/dist/hooks/use-scroll-overflow.cjs.map +1 -1
- package/dist/hooks/use-service-worker-update.cjs.map +1 -1
- package/dist/hooks/use-set.cjs.map +1 -1
- package/dist/hooks/use-sortable.cjs +1 -1
- package/dist/hooks/use-sortable.cjs.map +1 -1
- package/dist/hooks/use-sortable.js +0 -2
- package/dist/hooks/use-sortable.js.map +1 -1
- package/dist/hooks/use-stable-callback.cjs.map +1 -1
- package/dist/hooks/use-storage-estimate.cjs.map +1 -1
- package/dist/hooks/use-throttle.cjs.map +1 -1
- package/dist/hooks/use-timeout.cjs.map +1 -1
- package/dist/hooks/use-toggle.cjs.map +1 -1
- package/dist/hooks/use-window-size.cjs.map +1 -1
- package/dist/http/api-client.cjs +1 -1
- package/dist/http/api-client.cjs.map +1 -1
- package/dist/http/api-client.js +7 -5
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/resumable-upload.cjs +1 -1
- package/dist/http/resumable-upload.cjs.map +1 -1
- package/dist/http/resumable-upload.js +1 -1
- package/dist/http/resumable-upload.js.map +1 -1
- package/dist/http/use-poll.cjs.map +1 -1
- package/dist/i18n/I18nProvider.cjs.map +1 -1
- package/dist/icons/Icon.cjs.map +1 -1
- package/dist/icons/IconProvider.cjs.map +1 -1
- package/dist/icons/icon-context.cjs.map +1 -1
- package/dist/icons/use-icon.cjs.map +1 -1
- package/dist/imaging/use-image-processing.cjs.map +1 -1
- package/dist/oauth/GoogleSignIn.cjs.map +1 -1
- package/dist/oauth/GoogleSignIn.js.map +1 -1
- package/dist/oauth/use-oauth-callback.cjs.map +1 -1
- package/dist/offline/use-offline-sync.cjs.map +1 -1
- package/dist/push/use-push-subscription.cjs.map +1 -1
- package/dist/query/QueryProvider.cjs.map +1 -1
- package/dist/query/constants.cjs +1 -1
- package/dist/query/constants.cjs.map +1 -1
- package/dist/query/constants.js +10 -10
- package/dist/query/constants.js.map +1 -1
- package/dist/query/create-query-keys.cjs +1 -1
- package/dist/query/create-query-keys.cjs.map +1 -1
- package/dist/query/create-query-keys.js +1 -1
- package/dist/query/create-query-keys.js.map +1 -1
- package/dist/query/offline-persistence.cjs.map +1 -1
- package/dist/query/use-cursor-query.cjs.map +1 -1
- package/dist/query/use-offline-mutation.cjs.map +1 -1
- package/dist/query/use-paginated-query.cjs.map +1 -1
- package/dist/router/AppRouter.cjs +1 -1
- package/dist/router/AppRouter.cjs.map +1 -1
- package/dist/router/AppRouter.js +7 -2
- package/dist/router/AppRouter.js.map +1 -1
- package/dist/sse/use-event-stream.cjs.map +1 -1
- package/dist/store/create-store.cjs.map +1 -1
- package/dist/tabular/use-tabular-predictor.cjs.map +1 -1
- package/dist/telemetry/TelemetryProvider.cjs.map +1 -1
- package/dist/tempest-react-sdk.d.ts +25 -26
- package/dist/theme/ThemeProvider.cjs.map +1 -1
- package/dist/theme/color.cjs +1 -1
- package/dist/theme/color.cjs.map +1 -1
- package/dist/theme/color.js +1 -1
- package/dist/theme/color.js.map +1 -1
- package/dist/utils/cn.cjs +1 -1
- package/dist/utils/cn.cjs.map +1 -1
- package/dist/utils/cn.js +1 -1
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/objects.cjs +1 -1
- package/dist/utils/objects.cjs.map +1 -1
- package/dist/utils/objects.js +1 -1
- package/dist/utils/objects.js.map +1 -1
- package/dist/utils/xlsx.cjs +1 -1
- package/dist/utils/xlsx.cjs.map +1 -1
- package/dist/utils/xlsx.js +3 -3
- package/dist/utils/xlsx.js.map +1 -1
- package/dist/vision/core/exceptions.cjs.map +1 -1
- package/dist/vision/core/exceptions.js.map +1 -1
- package/dist/vision/core/metadata.cjs +1 -1
- package/dist/vision/core/metadata.cjs.map +1 -1
- package/dist/vision/core/metadata.js +1 -1
- package/dist/vision/core/metadata.js.map +1 -1
- package/dist/vision/io/image.cjs +1 -1
- package/dist/vision/io/image.cjs.map +1 -1
- package/dist/vision/io/image.js +4 -2
- package/dist/vision/io/image.js.map +1 -1
- package/dist/vision/preprocess/image.cjs +1 -1
- package/dist/vision/preprocess/image.cjs.map +1 -1
- package/dist/vision/preprocess/image.js +4 -2
- package/dist/vision/preprocess/image.js.map +1 -1
- package/dist/vision/tasks/base.cjs +1 -1
- package/dist/vision/tasks/base.cjs.map +1 -1
- package/dist/vision/tasks/base.js +3 -1
- package/dist/vision/tasks/base.js.map +1 -1
- package/dist/vision/tasks/classifier.cjs +1 -1
- package/dist/vision/tasks/classifier.cjs.map +1 -1
- package/dist/vision/tasks/classifier.js +2 -2
- package/dist/vision/tasks/classifier.js.map +1 -1
- package/dist/vision/tasks/detectClassify.cjs +1 -1
- package/dist/vision/tasks/detectClassify.cjs.map +1 -1
- package/dist/vision/tasks/detectClassify.js +16 -15
- package/dist/vision/tasks/detectClassify.js.map +1 -1
- package/dist/vision/use-camera-stream.cjs.map +1 -1
- package/dist/vision/use-live-luminance.cjs.map +1 -1
- package/dist/vite/create-vite-config.cjs +1 -1
- package/dist/vite/create-vite-config.cjs.map +1 -1
- package/dist/vite/create-vite-config.js +3 -3
- package/dist/vite/create-vite-config.js.map +1 -1
- package/dist/vite/tempest-icons.cjs.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
- package/dist/vite/tempest-pwa-icons.cjs.map +1 -1
- package/dist/ws/use-web-socket.cjs +1 -1
- package/dist/ws/use-web-socket.cjs.map +1 -1
- package/dist/ws/use-web-socket.js +5 -3
- package/dist/ws/use-web-socket.js.map +1 -1
- package/package.json +2 -2
- package/template/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"offline-persistence.cjs","names":[],"sources":["../../src/query/offline-persistence.ts"],"sourcesContent":["import { dehydrate, hydrate, type QueryClient } from \"@tanstack/react-query\";\nimport { createOfflineStore } from \"../offline/create-offline-store\";\n\n/** Options for {@link persistQueryClientOffline}. */\nexport interface OfflineQueryPersistenceOptions {\n /** The `QueryClient` to snapshot and restore. */\n queryClient: QueryClient;\n /** IndexedDB database name for the snapshot. Default `\"TempestQueryCache\"`. */\n databaseName?: string;\n /** Row key under which the snapshot is stored. Default `\"tanstack-query\"`. */\n key?: string;\n /**\n * Minimum gap between snapshot writes, in ms — cache churn is coalesced into\n * one trailing write per window. Default `1000`.\n */\n throttleMs?: number;\n}\n\n/** Handle returned by {@link persistQueryClientOffline}. */\nexport interface OfflineQueryPersistence {\n /**\n * Load the persisted snapshot into the `QueryClient`. Call once on boot,\n * before rendering queries, so cached data is available offline.\n */\n restore: () => Promise<void>;\n /** Write the current cache snapshot immediately (bypassing the throttle). */\n flush: () => Promise<void>;\n /** Stop persisting and drop the pending write. */\n unsubscribe: () => void;\n /** Delete the persisted snapshot (e.g. on logout). */\n clear: () => Promise<void>;\n}\n\ninterface Snapshot {\n id: string;\n state: ReturnType<typeof dehydrate>;\n}\n\n/**\n * Persist a TanStack `QueryClient` cache to IndexedDB (via\n * {@link createOfflineStore}) and restore it on boot — so a reload or a cold\n * offline start shows the last-known data instead of empty screens.\n *\n * Self-contained: uses `dehydrate`/`hydrate` from `@tanstack/react-query`\n * directly, so no `@tanstack/react-query-persist-client` dependency is needed.\n * Writes are throttled and subscribe to the query cache; call `unsubscribe` on\n * teardown. Dexie is a peer dependency of the offline store — install it.\n *\n * @param options - The client plus storage/throttle configuration.\n * @returns A handle with `restore`, `flush`, `clear` and `unsubscribe`.\n *\n * @example\n * const persistence = persistQueryClientOffline({ queryClient });\n * await persistence.restore(); // before first render\n * // ... later, on logout:\n * await persistence.clear();\n */\nexport function persistQueryClientOffline(\n options: OfflineQueryPersistenceOptions,\n): OfflineQueryPersistence {\n const { queryClient, throttleMs = 1000 } = options;\n const key = options.key ?? \"tanstack-query\";\n const store = createOfflineStore<Snapshot, string>({\n databaseName: options.databaseName ?? \"TempestQueryCache\",\n version: 1,\n tableName: \"cache\",\n indexes: \"&id\",\n keyPath: \"id\",\n });\n\n async function save(): Promise<void> {\n await store.put({ id: key, state: dehydrate(queryClient) });\n }\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n function schedule(): void {\n if (timer) return;\n timer = setTimeout(() => {\n timer = null;\n void save();\n }, throttleMs);\n }\n\n const unsubscribeCache = queryClient.getQueryCache().subscribe(schedule);\n\n return {\n async restore() {\n const snapshot = await store.get(key);\n if (snapshot) hydrate(queryClient, snapshot.state);\n },\n async flush() {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n await save();\n },\n unsubscribe() {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n unsubscribeCache();\n },\n async clear() {\n await store.delete(key);\n },\n };\n}\n"],"mappings":"8FAyDA,SAAgB,EACZ,EACuB,CACvB,GAAM,CAAE,cAAa,aAAa,KAAS,EACrC,EAAM,EAAQ,KAAO,iBACrB,EAAQ,EAAA,mBAAqC,CAC/C,aAAc,EAAQ,cAAgB,oBACtC,QAAS,EACT,UAAW,QACX,QAAS,MACT,QAAS,IACb,CAAC,EAED,eAAe,GAAsB,CACjC,MAAM,EAAM,IAAI,CAAE,GAAI,EAAK,OAAA,
|
|
1
|
+
{"version":3,"file":"offline-persistence.cjs","names":[],"sources":["../../src/query/offline-persistence.ts"],"sourcesContent":["import { dehydrate, hydrate, type QueryClient } from \"@tanstack/react-query\";\nimport { createOfflineStore } from \"../offline/create-offline-store\";\n\n/** Options for {@link persistQueryClientOffline}. */\nexport interface OfflineQueryPersistenceOptions {\n /** The `QueryClient` to snapshot and restore. */\n queryClient: QueryClient;\n /** IndexedDB database name for the snapshot. Default `\"TempestQueryCache\"`. */\n databaseName?: string;\n /** Row key under which the snapshot is stored. Default `\"tanstack-query\"`. */\n key?: string;\n /**\n * Minimum gap between snapshot writes, in ms — cache churn is coalesced into\n * one trailing write per window. Default `1000`.\n */\n throttleMs?: number;\n}\n\n/** Handle returned by {@link persistQueryClientOffline}. */\nexport interface OfflineQueryPersistence {\n /**\n * Load the persisted snapshot into the `QueryClient`. Call once on boot,\n * before rendering queries, so cached data is available offline.\n */\n restore: () => Promise<void>;\n /** Write the current cache snapshot immediately (bypassing the throttle). */\n flush: () => Promise<void>;\n /** Stop persisting and drop the pending write. */\n unsubscribe: () => void;\n /** Delete the persisted snapshot (e.g. on logout). */\n clear: () => Promise<void>;\n}\n\ninterface Snapshot {\n id: string;\n state: ReturnType<typeof dehydrate>;\n}\n\n/**\n * Persist a TanStack `QueryClient` cache to IndexedDB (via\n * {@link createOfflineStore}) and restore it on boot — so a reload or a cold\n * offline start shows the last-known data instead of empty screens.\n *\n * Self-contained: uses `dehydrate`/`hydrate` from `@tanstack/react-query`\n * directly, so no `@tanstack/react-query-persist-client` dependency is needed.\n * Writes are throttled and subscribe to the query cache; call `unsubscribe` on\n * teardown. Dexie is a peer dependency of the offline store — install it.\n *\n * @param options - The client plus storage/throttle configuration.\n * @returns A handle with `restore`, `flush`, `clear` and `unsubscribe`.\n *\n * @example\n * const persistence = persistQueryClientOffline({ queryClient });\n * await persistence.restore(); // before first render\n * // ... later, on logout:\n * await persistence.clear();\n */\nexport function persistQueryClientOffline(\n options: OfflineQueryPersistenceOptions,\n): OfflineQueryPersistence {\n const { queryClient, throttleMs = 1000 } = options;\n const key = options.key ?? \"tanstack-query\";\n const store = createOfflineStore<Snapshot, string>({\n databaseName: options.databaseName ?? \"TempestQueryCache\",\n version: 1,\n tableName: \"cache\",\n indexes: \"&id\",\n keyPath: \"id\",\n });\n\n async function save(): Promise<void> {\n await store.put({ id: key, state: dehydrate(queryClient) });\n }\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n function schedule(): void {\n if (timer) return;\n timer = setTimeout(() => {\n timer = null;\n void save();\n }, throttleMs);\n }\n\n const unsubscribeCache = queryClient.getQueryCache().subscribe(schedule);\n\n return {\n async restore() {\n const snapshot = await store.get(key);\n if (snapshot) hydrate(queryClient, snapshot.state);\n },\n async flush() {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n await save();\n },\n unsubscribe() {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n unsubscribeCache();\n },\n async clear() {\n await store.delete(key);\n },\n };\n}\n"],"mappings":"8FAyDA,SAAgB,EACZ,EACuB,CACvB,GAAM,CAAE,cAAa,aAAa,KAAS,EACrC,EAAM,EAAQ,KAAO,iBACrB,EAAQ,EAAA,mBAAqC,CAC/C,aAAc,EAAQ,cAAgB,oBACtC,QAAS,EACT,UAAW,QACX,QAAS,MACT,QAAS,IACb,CAAC,EAED,eAAe,GAAsB,CACjC,MAAM,EAAM,IAAI,CAAE,GAAI,EAAK,OAAA,EAAO,EAAA,UAAA,CAAU,CAAW,CAAE,CAAC,CAC9D,CAEA,IAAI,EAA8C,KAClD,SAAS,GAAiB,CAClB,AACJ,IAAQ,eAAiB,CACrB,EAAQ,KACR,EAAU,CACd,EAAG,CAAU,CACjB,CAEA,IAAM,EAAmB,EAAY,cAAc,CAAC,CAAC,UAAU,CAAQ,EAEvE,MAAO,CACH,MAAM,SAAU,CACZ,IAAM,EAAW,MAAM,EAAM,IAAI,CAAG,EAChC,IAAU,EAAA,EAAA,QAAA,CAAQ,EAAa,EAAS,KAAK,CACrD,EACA,MAAM,OAAQ,CACV,AAEI,KADA,aAAa,CAAK,EACV,MAEZ,MAAM,EAAK,CACf,EACA,aAAc,CACV,AAEI,KADA,aAAa,CAAK,EACV,MAEZ,EAAiB,CACrB,EACA,MAAM,OAAQ,CACV,MAAM,EAAM,OAAO,CAAG,CAC1B,CACJ,CACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-cursor-query.cjs","names":[],"sources":["../../src/query/use-cursor-query.ts"],"sourcesContent":["import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport type { InfiniteData, QueryKey, UseInfiniteQueryOptions } from \"@tanstack/react-query\";\n\nimport type { CursorPage, CursorParams } from \"./pagination\";\n\nexport interface UseCursorQueryOptions<T> extends Omit<\n UseInfiniteQueryOptions<\n CursorPage<T>,\n Error,\n InfiniteData<CursorPage<T>, string | null>,\n QueryKey,\n string | null\n >,\n \"queryKey\" | \"queryFn\" | \"initialPageParam\" | \"getNextPageParam\"\n> {\n /** Base query key — `limit`/sort is appended for cache isolation. */\n queryKey: QueryKey;\n /** Fetcher receiving the cursor params; return the backend envelope. */\n queryFn: (params: CursorParams) => Promise<CursorPage<T>> | CursorPage<T>;\n /** Batch size sent as `limit`. Default: 20. */\n limit?: number;\n /** Initial `order_by`. */\n orderBy?: string;\n /** Initial `ascending`. Default: false. */\n ascending?: boolean;\n}\n\nexport interface UseCursorQueryResult<T> {\n /** All rows fetched so far, flattened across batches. */\n items: T[];\n /** The raw batches, in fetch order. */\n pages: CursorPage<T>[];\n hasNextPage: boolean;\n isLoading: boolean;\n isFetchingNextPage: boolean;\n error: Error | null;\n /** Fetch the next batch (no-op when exhausted). */\n fetchNextPage: () => void;\n /** Refetch from the first batch. */\n refetch: () => void;\n}\n\n/**\n * Cursor-pagination hook over TanStack Query's `useInfiniteQuery` for the\n * Tempest `CursorPaginationSchema` envelope\n * (`{ items, next_cursor, has_more, limit }`).\n *\n * The opaque `next_cursor` is fed straight back as the next page param; the\n * loop stops when `has_more` is false (or `next_cursor` is null).\n *\n * @example\n * const feed = useCursorQuery<Post>({\n * queryKey: [\"feed\"],\n * limit: 30,\n * queryFn: (params) => postsService.listPosts(params),\n * });\n * // feed.items, feed.fetchNextPage(), feed.hasNextPage\n */\nexport function useCursorQuery<T>(options: UseCursorQueryOptions<T>): UseCursorQueryResult<T> {\n const { queryKey, queryFn, limit = 20, orderBy, ascending = false, ...queryOptions } = options;\n\n const query = useInfiniteQuery<\n CursorPage<T>,\n Error,\n InfiniteData<CursorPage<T>, string | null>,\n QueryKey,\n string | null\n >({\n ...queryOptions,\n queryKey: [...queryKey, { limit, orderBy, ascending }],\n initialPageParam: null,\n queryFn: ({ pageParam }) =>\n queryFn({ cursor: pageParam, limit, order_by: orderBy, ascending }),\n getNextPageParam: (last) => (last.has_more ? last.next_cursor : undefined),\n });\n\n const pages = query.data?.pages ?? [];\n\n return {\n items: pages.flatMap((p) => p.items),\n pages,\n hasNextPage: query.hasNextPage,\n isLoading: query.isLoading,\n isFetchingNextPage: query.isFetchingNextPage,\n error: query.error,\n fetchNextPage: () => void query.fetchNextPage(),\n refetch: () => void query.refetch(),\n };\n}\n"],"mappings":"uCA0DA,SAAgB,EAAkB,EAA4D,CAC1F,GAAM,CAAE,WAAU,UAAS,QAAQ,GAAI,UAAS,YAAY,GAAO,GAAG,GAAiB,EAEjF,GAAA,
|
|
1
|
+
{"version":3,"file":"use-cursor-query.cjs","names":[],"sources":["../../src/query/use-cursor-query.ts"],"sourcesContent":["import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport type { InfiniteData, QueryKey, UseInfiniteQueryOptions } from \"@tanstack/react-query\";\n\nimport type { CursorPage, CursorParams } from \"./pagination\";\n\nexport interface UseCursorQueryOptions<T> extends Omit<\n UseInfiniteQueryOptions<\n CursorPage<T>,\n Error,\n InfiniteData<CursorPage<T>, string | null>,\n QueryKey,\n string | null\n >,\n \"queryKey\" | \"queryFn\" | \"initialPageParam\" | \"getNextPageParam\"\n> {\n /** Base query key — `limit`/sort is appended for cache isolation. */\n queryKey: QueryKey;\n /** Fetcher receiving the cursor params; return the backend envelope. */\n queryFn: (params: CursorParams) => Promise<CursorPage<T>> | CursorPage<T>;\n /** Batch size sent as `limit`. Default: 20. */\n limit?: number;\n /** Initial `order_by`. */\n orderBy?: string;\n /** Initial `ascending`. Default: false. */\n ascending?: boolean;\n}\n\nexport interface UseCursorQueryResult<T> {\n /** All rows fetched so far, flattened across batches. */\n items: T[];\n /** The raw batches, in fetch order. */\n pages: CursorPage<T>[];\n hasNextPage: boolean;\n isLoading: boolean;\n isFetchingNextPage: boolean;\n error: Error | null;\n /** Fetch the next batch (no-op when exhausted). */\n fetchNextPage: () => void;\n /** Refetch from the first batch. */\n refetch: () => void;\n}\n\n/**\n * Cursor-pagination hook over TanStack Query's `useInfiniteQuery` for the\n * Tempest `CursorPaginationSchema` envelope\n * (`{ items, next_cursor, has_more, limit }`).\n *\n * The opaque `next_cursor` is fed straight back as the next page param; the\n * loop stops when `has_more` is false (or `next_cursor` is null).\n *\n * @example\n * const feed = useCursorQuery<Post>({\n * queryKey: [\"feed\"],\n * limit: 30,\n * queryFn: (params) => postsService.listPosts(params),\n * });\n * // feed.items, feed.fetchNextPage(), feed.hasNextPage\n */\nexport function useCursorQuery<T>(options: UseCursorQueryOptions<T>): UseCursorQueryResult<T> {\n const { queryKey, queryFn, limit = 20, orderBy, ascending = false, ...queryOptions } = options;\n\n const query = useInfiniteQuery<\n CursorPage<T>,\n Error,\n InfiniteData<CursorPage<T>, string | null>,\n QueryKey,\n string | null\n >({\n ...queryOptions,\n queryKey: [...queryKey, { limit, orderBy, ascending }],\n initialPageParam: null,\n queryFn: ({ pageParam }) =>\n queryFn({ cursor: pageParam, limit, order_by: orderBy, ascending }),\n getNextPageParam: (last) => (last.has_more ? last.next_cursor : undefined),\n });\n\n const pages = query.data?.pages ?? [];\n\n return {\n items: pages.flatMap((p) => p.items),\n pages,\n hasNextPage: query.hasNextPage,\n isLoading: query.isLoading,\n isFetchingNextPage: query.isFetchingNextPage,\n error: query.error,\n fetchNextPage: () => void query.fetchNextPage(),\n refetch: () => void query.refetch(),\n };\n}\n"],"mappings":"uCA0DA,SAAgB,EAAkB,EAA4D,CAC1F,GAAM,CAAE,WAAU,UAAS,QAAQ,GAAI,UAAS,YAAY,GAAO,GAAG,GAAiB,EAEjF,GAAA,EAAQ,EAAA,iBAAA,CAMZ,CACE,GAAG,EACH,SAAU,CAAC,GAAG,EAAU,CAAE,QAAO,UAAS,WAAU,CAAC,EACrD,iBAAkB,KAClB,SAAU,CAAE,eACR,EAAQ,CAAE,OAAQ,EAAW,QAAO,SAAU,EAAS,WAAU,CAAC,EACtE,iBAAmB,GAAU,EAAK,SAAW,EAAK,YAAc,IAAA,EACpE,CAAC,EAEK,EAAQ,EAAM,MAAM,OAAS,CAAC,EAEpC,MAAO,CACH,MAAO,EAAM,QAAS,GAAM,EAAE,KAAK,EACnC,QACA,YAAa,EAAM,YACnB,UAAW,EAAM,UACjB,mBAAoB,EAAM,mBAC1B,MAAO,EAAM,MACb,kBAAqB,KAAK,EAAM,cAAc,EAC9C,YAAe,KAAK,EAAM,QAAQ,CACtC,CACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-offline-mutation.cjs","names":[],"sources":["../../src/query/use-offline-mutation.ts"],"sourcesContent":["import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport type { QueryKey, UseMutationResult } from \"@tanstack/react-query\";\nimport type { OfflineSync, OutboxOp, SyncTrigger } from \"../offline\";\n\n/** The outbox entry a mutation's variables map to. */\nexport interface OutboxDraft<TPayload> {\n /** The mutation kind. */\n op: OutboxOp;\n /** Primary key of the affected record. */\n recordId: string;\n /** Record snapshot for `create`/`update` (omit for `delete`). */\n payload?: TPayload;\n}\n\n/**\n * Configuration for {@link useOfflineMutation}.\n *\n * @typeParam TVariables - The `mutate(variables)` input shape.\n * @typeParam TData - The cached query data optimistically patched.\n * @typeParam TPayload - Record snapshot carried by outbox entries.\n */\nexport interface UseOfflineMutationOptions<TVariables, TData, TPayload> {\n /** The engine the mutation enqueues into. */\n sync: OfflineSync<TPayload>;\n /** Map `mutate` variables into an outbox entry. */\n toEntry: (variables: TVariables) => OutboxDraft<TPayload>;\n /**\n * Query key to optimistically patch and (optionally) invalidate. Omit to\n * only enqueue without touching the React Query cache.\n */\n queryKey?: QueryKey;\n /**\n * Produce the next cached value from the current one and the variables.\n * Runs in `onMutate`; the previous value is restored automatically if the\n * enqueue throws. Requires `queryKey`.\n */\n applyOptimistic?: (current: TData | undefined, variables: TVariables) => TData;\n /**\n * Trigger a flush after enqueueing. `true` (default) uses\n * `\"after-mutation\"`; pass a custom {@link SyncTrigger} string, or `false`\n * to leave flushing to the caller / auto-flush hooks.\n */\n flush?: boolean | SyncTrigger;\n /** Invalidate `queryKey` in `onSettled` so a later read refetches. Default `false`. */\n invalidate?: boolean;\n}\n\ninterface MutationContext<TData> {\n previous: TData | undefined;\n}\n\n/**\n * Optimistic mutation that writes to an {@link OfflineSync} outbox instead of\n * hitting the network directly, bridging the sync engine and TanStack Query.\n *\n * On `mutate` it enqueues the entry, optimistically patches the given query\n * cache key and kicks a flush; if the enqueue itself throws, the cache is\n * rolled back to its previous value. Server delivery happens later inside the\n * engine's flush loop, so the UI updates instantly and survives reloads and\n * offline periods.\n *\n * @typeParam TVariables - The `mutate(variables)` input shape.\n * @typeParam TData - The cached query data optimistically patched.\n * @typeParam TPayload - Record snapshot carried by outbox entries.\n * @param options - Engine, variables→entry mapping and cache wiring.\n * @returns The TanStack {@link UseMutationResult}; the mutation resolves to the\n * generated outbox entry id.\n *\n * @example\n * const addNote = useOfflineMutation({\n * sync: notesSync,\n * queryKey: [\"notes\"],\n * toEntry: (note: Note) => ({ op: \"create\", recordId: note.id, payload: note }),\n * applyOptimistic: (current: Note[] = [], note) => [...current, note],\n * });\n * addNote.mutate(newNote);\n */\nexport function useOfflineMutation<TVariables, TData = unknown, TPayload = unknown>(\n options: UseOfflineMutationOptions<TVariables, TData, TPayload>,\n): UseMutationResult<string, Error, TVariables, MutationContext<TData>> {\n const { sync, toEntry, queryKey, applyOptimistic, flush = true, invalidate = false } = options;\n const client = useQueryClient();\n\n return useMutation<string, Error, TVariables, MutationContext<TData>>({\n mutationFn: async (variables) => {\n const draft = toEntry(variables);\n const id = await sync.enqueue(draft.op, draft.recordId, draft.payload);\n if (flush !== false) {\n const trigger: SyncTrigger = flush === true ? \"after-mutation\" : flush;\n void sync.flush(trigger);\n }\n return id;\n },\n onMutate: async (variables) => {\n if (!queryKey || !applyOptimistic) return { previous: undefined };\n await client.cancelQueries({ queryKey });\n const previous = client.getQueryData<TData>(queryKey);\n client.setQueryData<TData>(queryKey, (current) => applyOptimistic(current, variables));\n return { previous };\n },\n onError: (_error, _variables, context) => {\n if (queryKey && context) client.setQueryData(queryKey, context.previous);\n },\n onSettled: () => {\n if (invalidate && queryKey) void client.invalidateQueries({ queryKey });\n },\n });\n}\n"],"mappings":"uCA6EA,SAAgB,EACZ,EACoE,CACpE,GAAM,CAAE,OAAM,UAAS,WAAU,kBAAiB,QAAQ,GAAM,aAAa,IAAU,EACjF,GAAA,
|
|
1
|
+
{"version":3,"file":"use-offline-mutation.cjs","names":[],"sources":["../../src/query/use-offline-mutation.ts"],"sourcesContent":["import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport type { QueryKey, UseMutationResult } from \"@tanstack/react-query\";\nimport type { OfflineSync, OutboxOp, SyncTrigger } from \"../offline\";\n\n/** The outbox entry a mutation's variables map to. */\nexport interface OutboxDraft<TPayload> {\n /** The mutation kind. */\n op: OutboxOp;\n /** Primary key of the affected record. */\n recordId: string;\n /** Record snapshot for `create`/`update` (omit for `delete`). */\n payload?: TPayload;\n}\n\n/**\n * Configuration for {@link useOfflineMutation}.\n *\n * @typeParam TVariables - The `mutate(variables)` input shape.\n * @typeParam TData - The cached query data optimistically patched.\n * @typeParam TPayload - Record snapshot carried by outbox entries.\n */\nexport interface UseOfflineMutationOptions<TVariables, TData, TPayload> {\n /** The engine the mutation enqueues into. */\n sync: OfflineSync<TPayload>;\n /** Map `mutate` variables into an outbox entry. */\n toEntry: (variables: TVariables) => OutboxDraft<TPayload>;\n /**\n * Query key to optimistically patch and (optionally) invalidate. Omit to\n * only enqueue without touching the React Query cache.\n */\n queryKey?: QueryKey;\n /**\n * Produce the next cached value from the current one and the variables.\n * Runs in `onMutate`; the previous value is restored automatically if the\n * enqueue throws. Requires `queryKey`.\n */\n applyOptimistic?: (current: TData | undefined, variables: TVariables) => TData;\n /**\n * Trigger a flush after enqueueing. `true` (default) uses\n * `\"after-mutation\"`; pass a custom {@link SyncTrigger} string, or `false`\n * to leave flushing to the caller / auto-flush hooks.\n */\n flush?: boolean | SyncTrigger;\n /** Invalidate `queryKey` in `onSettled` so a later read refetches. Default `false`. */\n invalidate?: boolean;\n}\n\ninterface MutationContext<TData> {\n previous: TData | undefined;\n}\n\n/**\n * Optimistic mutation that writes to an {@link OfflineSync} outbox instead of\n * hitting the network directly, bridging the sync engine and TanStack Query.\n *\n * On `mutate` it enqueues the entry, optimistically patches the given query\n * cache key and kicks a flush; if the enqueue itself throws, the cache is\n * rolled back to its previous value. Server delivery happens later inside the\n * engine's flush loop, so the UI updates instantly and survives reloads and\n * offline periods.\n *\n * @typeParam TVariables - The `mutate(variables)` input shape.\n * @typeParam TData - The cached query data optimistically patched.\n * @typeParam TPayload - Record snapshot carried by outbox entries.\n * @param options - Engine, variables→entry mapping and cache wiring.\n * @returns The TanStack {@link UseMutationResult}; the mutation resolves to the\n * generated outbox entry id.\n *\n * @example\n * const addNote = useOfflineMutation({\n * sync: notesSync,\n * queryKey: [\"notes\"],\n * toEntry: (note: Note) => ({ op: \"create\", recordId: note.id, payload: note }),\n * applyOptimistic: (current: Note[] = [], note) => [...current, note],\n * });\n * addNote.mutate(newNote);\n */\nexport function useOfflineMutation<TVariables, TData = unknown, TPayload = unknown>(\n options: UseOfflineMutationOptions<TVariables, TData, TPayload>,\n): UseMutationResult<string, Error, TVariables, MutationContext<TData>> {\n const { sync, toEntry, queryKey, applyOptimistic, flush = true, invalidate = false } = options;\n const client = useQueryClient();\n\n return useMutation<string, Error, TVariables, MutationContext<TData>>({\n mutationFn: async (variables) => {\n const draft = toEntry(variables);\n const id = await sync.enqueue(draft.op, draft.recordId, draft.payload);\n if (flush !== false) {\n const trigger: SyncTrigger = flush === true ? \"after-mutation\" : flush;\n void sync.flush(trigger);\n }\n return id;\n },\n onMutate: async (variables) => {\n if (!queryKey || !applyOptimistic) return { previous: undefined };\n await client.cancelQueries({ queryKey });\n const previous = client.getQueryData<TData>(queryKey);\n client.setQueryData<TData>(queryKey, (current) => applyOptimistic(current, variables));\n return { previous };\n },\n onError: (_error, _variables, context) => {\n if (queryKey && context) client.setQueryData(queryKey, context.previous);\n },\n onSettled: () => {\n if (invalidate && queryKey) void client.invalidateQueries({ queryKey });\n },\n });\n}\n"],"mappings":"uCA6EA,SAAgB,EACZ,EACoE,CACpE,GAAM,CAAE,OAAM,UAAS,WAAU,kBAAiB,QAAQ,GAAM,aAAa,IAAU,EACjF,GAAA,EAAS,EAAA,eAAA,CAAe,EAE9B,OAAA,EAAO,EAAA,YAAA,CAA+D,CAClE,WAAY,KAAO,IAAc,CAC7B,IAAM,EAAQ,EAAQ,CAAS,EACzB,EAAK,MAAM,EAAK,QAAQ,EAAM,GAAI,EAAM,SAAU,EAAM,OAAO,EACrE,GAAI,IAAU,GAAO,CACjB,IAAM,EAAuB,IAAU,GAAO,iBAAmB,EACjE,EAAU,MAAM,CAAO,CAC3B,CACA,OAAO,CACX,EACA,SAAU,KAAO,IAAc,CAC3B,GAAI,CAAC,GAAY,CAAC,EAAiB,MAAO,CAAE,SAAU,IAAA,EAAU,EAChE,MAAM,EAAO,cAAc,CAAE,UAAS,CAAC,EACvC,IAAM,EAAW,EAAO,aAAoB,CAAQ,EAEpD,OADA,EAAO,aAAoB,EAAW,GAAY,EAAgB,EAAS,CAAS,CAAC,EAC9E,CAAE,UAAS,CACtB,EACA,SAAU,EAAQ,EAAY,IAAY,CAClC,GAAY,GAAS,EAAO,aAAa,EAAU,EAAQ,QAAQ,CAC3E,EACA,cAAiB,CACT,GAAc,GAAU,EAAY,kBAAkB,CAAE,UAAS,CAAC,CAC1E,CACJ,CAAC,CACL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-paginated-query.cjs","names":[],"sources":["../../src/query/use-paginated-query.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport type { QueryKey, UseQueryOptions } from \"@tanstack/react-query\";\n\nimport type { OffsetPage, OffsetParams } from \"./pagination\";\n\nexport interface UsePaginatedQueryOptions<T> extends Omit<\n UseQueryOptions<OffsetPage<T>, Error, OffsetPage<T>, QueryKey>,\n \"queryKey\" | \"queryFn\"\n> {\n /** Base query key — the current page/sort is appended for cache isolation. */\n queryKey: QueryKey;\n /** Fetcher receiving the offset params; return the backend envelope. */\n queryFn: (params: OffsetParams) => Promise<OffsetPage<T>> | OffsetPage<T>;\n /** Initial 1-based page. Default: 1. */\n initialPage?: number;\n /** Page size. Default: 20. */\n pageSize?: number;\n /**\n * Query-param name for the page size. fastapi-pagination uses `\"size\"`\n * (default); SDK `BasePaginationFilterSchema` configs may use `\"page_size\"`.\n */\n sizeParam?: \"size\" | \"page_size\";\n /** Initial `order_by` (only sent when set). */\n orderBy?: string;\n /** Initial `ascending` (only sent when `orderBy` is set). Default: false. */\n ascending?: boolean;\n}\n\nexport interface UsePaginatedQueryResult<T> {\n /** The current page envelope, or undefined while the first load is pending. */\n page: OffsetPage<T> | undefined;\n /** The rows of the current page (empty array while pending). */\n items: T[];\n /** Current 1-based page number (controlled by the hook). */\n pageNumber: number;\n /** Total page count from the last successful response. */\n pageCount: number;\n /** Total row count from the last successful response. */\n total: number;\n hasNext: boolean;\n hasPrev: boolean;\n isLoading: boolean;\n isFetching: boolean;\n error: Error | null;\n /** Jump to a specific 1-based page (clamped to >= 1). */\n setPage: (page: number) => void;\n /** Go to the next page when available. */\n next: () => void;\n /** Go to the previous page when available. */\n prev: () => void;\n /** Refetch the current page. */\n refetch: () => void;\n}\n\n/**\n * Offset-pagination hook over TanStack Query for the fastapi-pagination /\n * Tempest envelope (`{ items, total, page, size, pages }`).\n *\n * It owns the page state, sends `page` + the size param (`size` by default,\n * or `page_size` via `sizeParam`) plus optional `order_by`/`ascending` to your\n * fetcher, keeps the previous page visible while the next loads, and derives\n * `hasNext`/`hasPrev`/`pageCount`.\n *\n * @example\n * const users = usePaginatedQuery<User>({\n * queryKey: [\"users\"],\n * pageSize: 25,\n * queryFn: (params) => usersService.listUsers(params),\n * });\n * // users.items, users.next(), users.hasNext, users.pageCount\n */\nexport function usePaginatedQuery<T>(\n options: UsePaginatedQueryOptions<T>,\n): UsePaginatedQueryResult<T> {\n const {\n queryKey,\n queryFn,\n initialPage = 1,\n pageSize = 20,\n sizeParam = \"size\",\n orderBy,\n ascending = false,\n ...queryOptions\n } = options;\n\n const [pageNumber, setPageNumber] = useState(initialPage);\n\n const query = useQuery<OffsetPage<T>, Error>({\n ...queryOptions,\n queryKey: [...queryKey, { page: pageNumber, pageSize, sizeParam, orderBy, ascending }],\n queryFn: () => {\n const params: OffsetParams = { page: pageNumber, [sizeParam]: pageSize };\n if (orderBy) {\n params.order_by = orderBy;\n params.ascending = ascending;\n }\n return queryFn(params);\n },\n placeholderData: keepPreviousData,\n });\n\n const page = query.data;\n const pageCount = page?.pages ?? 0;\n const hasNext = pageNumber < pageCount;\n const hasPrev = pageNumber > 1;\n\n const setPage = useCallback((next: number) => setPageNumber(Math.max(1, next)), []);\n const next = useCallback(() => setPageNumber((p) => (p < pageCount ? p + 1 : p)), [pageCount]);\n const prev = useCallback(() => setPageNumber((p) => (p > 1 ? p - 1 : p)), []);\n\n return {\n page,\n items: page?.items ?? [],\n pageNumber,\n pageCount,\n total: page?.total ?? 0,\n hasNext,\n hasPrev,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n error: query.error,\n setPage,\n next,\n prev,\n refetch: () => void query.refetch(),\n };\n}\n"],"mappings":"0DAwEA,SAAgB,EACZ,EAC0B,CAC1B,GAAM,CACF,WACA,UACA,cAAc,EACd,WAAW,GACX,YAAY,OACZ,UACA,YAAY,GACZ,GAAG,GACH,EAEE,CAAC,EAAY,IAAA,
|
|
1
|
+
{"version":3,"file":"use-paginated-query.cjs","names":[],"sources":["../../src/query/use-paginated-query.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport type { QueryKey, UseQueryOptions } from \"@tanstack/react-query\";\n\nimport type { OffsetPage, OffsetParams } from \"./pagination\";\n\nexport interface UsePaginatedQueryOptions<T> extends Omit<\n UseQueryOptions<OffsetPage<T>, Error, OffsetPage<T>, QueryKey>,\n \"queryKey\" | \"queryFn\"\n> {\n /** Base query key — the current page/sort is appended for cache isolation. */\n queryKey: QueryKey;\n /** Fetcher receiving the offset params; return the backend envelope. */\n queryFn: (params: OffsetParams) => Promise<OffsetPage<T>> | OffsetPage<T>;\n /** Initial 1-based page. Default: 1. */\n initialPage?: number;\n /** Page size. Default: 20. */\n pageSize?: number;\n /**\n * Query-param name for the page size. fastapi-pagination uses `\"size\"`\n * (default); SDK `BasePaginationFilterSchema` configs may use `\"page_size\"`.\n */\n sizeParam?: \"size\" | \"page_size\";\n /** Initial `order_by` (only sent when set). */\n orderBy?: string;\n /** Initial `ascending` (only sent when `orderBy` is set). Default: false. */\n ascending?: boolean;\n}\n\nexport interface UsePaginatedQueryResult<T> {\n /** The current page envelope, or undefined while the first load is pending. */\n page: OffsetPage<T> | undefined;\n /** The rows of the current page (empty array while pending). */\n items: T[];\n /** Current 1-based page number (controlled by the hook). */\n pageNumber: number;\n /** Total page count from the last successful response. */\n pageCount: number;\n /** Total row count from the last successful response. */\n total: number;\n hasNext: boolean;\n hasPrev: boolean;\n isLoading: boolean;\n isFetching: boolean;\n error: Error | null;\n /** Jump to a specific 1-based page (clamped to >= 1). */\n setPage: (page: number) => void;\n /** Go to the next page when available. */\n next: () => void;\n /** Go to the previous page when available. */\n prev: () => void;\n /** Refetch the current page. */\n refetch: () => void;\n}\n\n/**\n * Offset-pagination hook over TanStack Query for the fastapi-pagination /\n * Tempest envelope (`{ items, total, page, size, pages }`).\n *\n * It owns the page state, sends `page` + the size param (`size` by default,\n * or `page_size` via `sizeParam`) plus optional `order_by`/`ascending` to your\n * fetcher, keeps the previous page visible while the next loads, and derives\n * `hasNext`/`hasPrev`/`pageCount`.\n *\n * @example\n * const users = usePaginatedQuery<User>({\n * queryKey: [\"users\"],\n * pageSize: 25,\n * queryFn: (params) => usersService.listUsers(params),\n * });\n * // users.items, users.next(), users.hasNext, users.pageCount\n */\nexport function usePaginatedQuery<T>(\n options: UsePaginatedQueryOptions<T>,\n): UsePaginatedQueryResult<T> {\n const {\n queryKey,\n queryFn,\n initialPage = 1,\n pageSize = 20,\n sizeParam = \"size\",\n orderBy,\n ascending = false,\n ...queryOptions\n } = options;\n\n const [pageNumber, setPageNumber] = useState(initialPage);\n\n const query = useQuery<OffsetPage<T>, Error>({\n ...queryOptions,\n queryKey: [...queryKey, { page: pageNumber, pageSize, sizeParam, orderBy, ascending }],\n queryFn: () => {\n const params: OffsetParams = { page: pageNumber, [sizeParam]: pageSize };\n if (orderBy) {\n params.order_by = orderBy;\n params.ascending = ascending;\n }\n return queryFn(params);\n },\n placeholderData: keepPreviousData,\n });\n\n const page = query.data;\n const pageCount = page?.pages ?? 0;\n const hasNext = pageNumber < pageCount;\n const hasPrev = pageNumber > 1;\n\n const setPage = useCallback((next: number) => setPageNumber(Math.max(1, next)), []);\n const next = useCallback(() => setPageNumber((p) => (p < pageCount ? p + 1 : p)), [pageCount]);\n const prev = useCallback(() => setPageNumber((p) => (p > 1 ? p - 1 : p)), []);\n\n return {\n page,\n items: page?.items ?? [],\n pageNumber,\n pageCount,\n total: page?.total ?? 0,\n hasNext,\n hasPrev,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n error: query.error,\n setPage,\n next,\n prev,\n refetch: () => void query.refetch(),\n };\n}\n"],"mappings":"0DAwEA,SAAgB,EACZ,EAC0B,CAC1B,GAAM,CACF,WACA,UACA,cAAc,EACd,WAAW,GACX,YAAY,OACZ,UACA,YAAY,GACZ,GAAG,GACH,EAEE,CAAC,EAAY,IAAA,EAAiB,EAAA,SAAA,CAAS,CAAW,EAElD,GAAA,EAAQ,EAAA,SAAA,CAA+B,CACzC,GAAG,EACH,SAAU,CAAC,GAAG,EAAU,CAAE,KAAM,EAAY,WAAU,YAAW,UAAS,WAAU,CAAC,EACrF,YAAe,CACX,IAAM,EAAuB,CAAE,KAAM,GAAa,GAAY,CAAS,EAKvE,OAJI,IACA,EAAO,SAAW,EAClB,EAAO,UAAY,GAEhB,EAAQ,CAAM,CACzB,EACA,gBAAiB,EAAA,gBACrB,CAAC,EAEK,EAAO,EAAM,KACb,EAAY,GAAM,OAAS,EAC3B,EAAU,EAAa,EACvB,EAAU,EAAa,EAEvB,GAAA,EAAU,EAAA,YAAA,CAAa,GAAiB,EAAc,KAAK,IAAI,EAAG,CAAI,CAAC,EAAG,CAAC,CAAC,EAC5E,GAAA,EAAO,EAAA,YAAA,KAAkB,EAAe,GAAO,EAAI,EAAY,EAAI,EAAI,CAAE,EAAG,CAAC,CAAS,CAAC,EACvF,GAAA,EAAO,EAAA,YAAA,KAAkB,EAAe,GAAO,EAAI,EAAI,EAAI,EAAI,CAAE,EAAG,CAAC,CAAC,EAE5E,MAAO,CACH,OACA,MAAO,GAAM,OAAS,CAAC,EACvB,aACA,YACA,MAAO,GAAM,OAAS,EACtB,UACA,UACA,UAAW,EAAM,UACjB,WAAY,EAAM,WAClB,MAAO,EAAM,MACb,UACA,OACA,OACA,YAAe,KAAK,EAAM,QAAQ,CACtC,CACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../auth/lazy-with-retry.cjs"),t=require("./RouteGuard.cjs");let n=require("react"),r=require("react/jsx-runtime"),i=require("react-router");function a(e){return e}var o=new WeakMap;function s(t){let n=o.get(t);return n||(n=e.lazyWithRetry(t.lazy),o.set(t,n)),n}function c({guard:e,redirectTo:n,children:i}){return(0,r.jsx)(t.RouteGuard,{when:
|
|
1
|
+
const e=require("../auth/lazy-with-retry.cjs"),t=require("./RouteGuard.cjs");let n=require("react"),r=require("react/jsx-runtime"),i=require("react-router");function a(e){return e}var o=new WeakMap;function s(t){let n=o.get(t);return n||(n=e.lazyWithRetry(t.lazy),o.set(t,n)),n}function c({guard:e,redirectTo:n,children:i}){let a=typeof e==`function`?e():e;return(0,r.jsx)(t.RouteGuard,{when:a,redirectTo:n,children:i})}function l(e){return e.map((e,t)=>{let n=e.element;if(e.lazy){let t=s(e);n=(0,r.jsx)(t,{})}return e.guard!==void 0&&(n=(0,r.jsx)(c,{guard:e.guard,redirectTo:e.redirectTo,children:n})),e.index?(0,r.jsx)(i.Route,{index:!0,element:n},`index-${t}`):(0,r.jsx)(i.Route,{path:e.path,element:n,caseSensitive:e.caseSensitive,children:e.children?l(e.children):null},e.path??`route-${t}`)})}var u={browser:i.BrowserRouter,hash:i.HashRouter,memory:i.MemoryRouter};function d({routes:e,router:t=`browser`,basename:a,initialEntries:o,fallback:s=null}){let c=(0,n.useMemo)(()=>l(e),[e]),d=u[t];return(0,r.jsx)(d,{...t===`memory`?{basename:a,initialEntries:o}:{basename:a},children:(0,r.jsx)(n.Suspense,{fallback:s,children:(0,r.jsx)(i.Routes,{children:c})})})}exports.AppRouter=d,exports.defineRoutes=a;
|
|
2
2
|
//# sourceMappingURL=AppRouter.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppRouter.cjs","names":[],"sources":["../../src/router/AppRouter.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { BrowserRouter, HashRouter, MemoryRouter, Routes, Route } from \"react-router\";\nimport { lazyWithRetry } from \"../auth/lazy-with-retry\";\nimport { RouteGuard } from \"./RouteGuard\";\nimport type { RouterKind, TempestRouteObject } from \"./types\";\n\n/**\n * Identity helper that types a declarative route tree. Use it so editors give\n * autocomplete and type-checking on every node; the array is returned as-is.\n *\n * @example\n * export const routes = defineRoutes([\n * { path: \"/\", element: <Layout />, children: [\n * { index: true, lazy: () => import(\"./pages/Home\") },\n * { path: \"settings\", lazy: () => import(\"./pages/Settings\"),\n * guard: () => useAuth.getState().isAuthenticated, redirectTo: \"/login\" },\n * ] },\n * ]);\n */\nexport function defineRoutes(routes: TempestRouteObject[]): TempestRouteObject[] {\n return routes;\n}\n\nconst lazyCache = new WeakMap<TempestRouteObject, ComponentType<unknown>>();\n\nfunction resolveLazy(route: TempestRouteObject): ComponentType<unknown> {\n let comp = lazyCache.get(route);\n if (!comp) {\n comp = lazyWithRetry(route.lazy!);\n lazyCache.set(route, comp);\n }\n return comp;\n}\n\nfunction GuardedElement({\n guard,\n redirectTo,\n children,\n}: {\n guard: boolean | (() => boolean);\n redirectTo?: string;\n children: ReactNode;\n}) {\n const allowed = typeof guard === \"function\" ? guard() : guard;\n return (\n <RouteGuard when={allowed} redirectTo={redirectTo}>\n {children}\n </RouteGuard>\n );\n}\n\nfunction toRouteElements(routes: TempestRouteObject[]): ReactNode {\n return routes.map((route, i) => {\n let element: ReactNode = route.element;\n if (route.lazy) {\n const LazyComponent = resolveLazy(route);\n element = <LazyComponent />;\n }\n if (route.guard !== undefined) {\n element = (\n <GuardedElement guard={route.guard} redirectTo={route.redirectTo}>\n {element}\n </GuardedElement>\n );\n }\n\n if (route.index) {\n return <Route key={`index-${i}`} index element={element} />;\n }\n\n return (\n <Route\n key={route.path ?? `route-${i}`}\n path={route.path}\n element={element}\n caseSensitive={route.caseSensitive}\n >\n {route.children ? toRouteElements(route.children) : null}\n </Route>\n );\n });\n}\n\nconst ROUTERS: Record<RouterKind, typeof BrowserRouter> = {\n browser: BrowserRouter,\n hash: HashRouter,\n memory: MemoryRouter,\n};\n\nexport interface AppRouterProps {\n /** Declarative route tree, ideally built with {@link defineRoutes}. */\n routes: TempestRouteObject[];\n /** History strategy (default: `\"browser\"`). */\n router?: RouterKind;\n /** App-wide base path forwarded to the underlying router. */\n basename?: string;\n /** Initial URLs for the `\"memory\"` router (tests / non-DOM hosts). */\n initialEntries?: string[];\n /** Suspense fallback shown while a `lazy` route chunk loads. */\n fallback?: ReactNode;\n}\n\n/**\n * Render a full React Router (v7, declarative mode) from a {@link defineRoutes}\n * tree: picks the history strategy, wraps everything in a `<Suspense>` boundary\n * for `lazy` routes, and applies per-route `guard` redirects. This is the\n * single entry point apps mount at their root.\n *\n * @example\n * <AppRouter routes={routes} fallback={<Spinner />} />\n */\nexport function AppRouter({\n routes,\n router = \"browser\",\n basename,\n initialEntries,\n fallback = null,\n}: AppRouterProps) {\n const tree = useMemo(() => toRouteElements(routes), [routes]);\n const Router = ROUTERS[router];\n const routerProps = router === \"memory\" ? { basename, initialEntries } : { basename };\n\n return (\n <Router {...routerProps}>\n <Suspense fallback={fallback}>\n <Routes>{tree}</Routes>\n </Suspense>\n </Router>\n );\n}\n"],"mappings":"6JAoBA,SAAgB,EAAa,EAAoD,CAC7E,OAAO,CACX,CAEA,IAAM,EAAY,IAAI,QAEtB,SAAS,EAAY,EAAmD,CACpE,IAAI,EAAO,EAAU,IAAI,CAAK,EAK9B,OAJK,IACD,EAAO,EAAA,cAAc,EAAM,IAAK,EAChC,EAAU,IAAI,EAAO,CAAI,GAEtB,CACX,CAEA,SAAS,EAAe,CACpB,QACA,aACA,YAKD,
|
|
1
|
+
{"version":3,"file":"AppRouter.cjs","names":[],"sources":["../../src/router/AppRouter.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { BrowserRouter, HashRouter, MemoryRouter, Routes, Route } from \"react-router\";\nimport { lazyWithRetry } from \"../auth/lazy-with-retry\";\nimport { RouteGuard } from \"./RouteGuard\";\nimport type { RouterKind, TempestRouteObject } from \"./types\";\n\n/**\n * Identity helper that types a declarative route tree. Use it so editors give\n * autocomplete and type-checking on every node; the array is returned as-is.\n *\n * @example\n * export const routes = defineRoutes([\n * { path: \"/\", element: <Layout />, children: [\n * { index: true, lazy: () => import(\"./pages/Home\") },\n * { path: \"settings\", lazy: () => import(\"./pages/Settings\"),\n * guard: () => useAuth.getState().isAuthenticated, redirectTo: \"/login\" },\n * ] },\n * ]);\n */\nexport function defineRoutes(routes: TempestRouteObject[]): TempestRouteObject[] {\n return routes;\n}\n\nconst lazyCache = new WeakMap<TempestRouteObject, ComponentType<unknown>>();\n\nfunction resolveLazy(route: TempestRouteObject): ComponentType<unknown> {\n let comp = lazyCache.get(route);\n if (!comp) {\n comp = lazyWithRetry(route.lazy!);\n lazyCache.set(route, comp);\n }\n return comp;\n}\n\nfunction GuardedElement({\n guard,\n redirectTo,\n children,\n}: {\n guard: boolean | (() => boolean);\n redirectTo?: string;\n children: ReactNode;\n}) {\n const allowed = typeof guard === \"function\" ? guard() : guard;\n return (\n <RouteGuard when={allowed} redirectTo={redirectTo}>\n {children}\n </RouteGuard>\n );\n}\n\nfunction toRouteElements(routes: TempestRouteObject[]): ReactNode {\n return routes.map((route, i) => {\n let element: ReactNode = route.element;\n if (route.lazy) {\n const LazyComponent = resolveLazy(route);\n element = <LazyComponent />;\n }\n if (route.guard !== undefined) {\n element = (\n <GuardedElement guard={route.guard} redirectTo={route.redirectTo}>\n {element}\n </GuardedElement>\n );\n }\n\n if (route.index) {\n return <Route key={`index-${i}`} index element={element} />;\n }\n\n return (\n <Route\n key={route.path ?? `route-${i}`}\n path={route.path}\n element={element}\n caseSensitive={route.caseSensitive}\n >\n {route.children ? toRouteElements(route.children) : null}\n </Route>\n );\n });\n}\n\nconst ROUTERS: Record<RouterKind, typeof BrowserRouter> = {\n browser: BrowserRouter,\n hash: HashRouter,\n memory: MemoryRouter,\n};\n\nexport interface AppRouterProps {\n /** Declarative route tree, ideally built with {@link defineRoutes}. */\n routes: TempestRouteObject[];\n /** History strategy (default: `\"browser\"`). */\n router?: RouterKind;\n /** App-wide base path forwarded to the underlying router. */\n basename?: string;\n /** Initial URLs for the `\"memory\"` router (tests / non-DOM hosts). */\n initialEntries?: string[];\n /** Suspense fallback shown while a `lazy` route chunk loads. */\n fallback?: ReactNode;\n}\n\n/**\n * Render a full React Router (v7, declarative mode) from a {@link defineRoutes}\n * tree: picks the history strategy, wraps everything in a `<Suspense>` boundary\n * for `lazy` routes, and applies per-route `guard` redirects. This is the\n * single entry point apps mount at their root.\n *\n * @example\n * <AppRouter routes={routes} fallback={<Spinner />} />\n */\nexport function AppRouter({\n routes,\n router = \"browser\",\n basename,\n initialEntries,\n fallback = null,\n}: AppRouterProps) {\n const tree = useMemo(() => toRouteElements(routes), [routes]);\n const Router = ROUTERS[router];\n const routerProps = router === \"memory\" ? { basename, initialEntries } : { basename };\n\n return (\n <Router {...routerProps}>\n <Suspense fallback={fallback}>\n <Routes>{tree}</Routes>\n </Suspense>\n </Router>\n );\n}\n"],"mappings":"6JAoBA,SAAgB,EAAa,EAAoD,CAC7E,OAAO,CACX,CAEA,IAAM,EAAY,IAAI,QAEtB,SAAS,EAAY,EAAmD,CACpE,IAAI,EAAO,EAAU,IAAI,CAAK,EAK9B,OAJK,IACD,EAAO,EAAA,cAAc,EAAM,IAAK,EAChC,EAAU,IAAI,EAAO,CAAI,GAEtB,CACX,CAEA,SAAS,EAAe,CACpB,QACA,aACA,YAKD,CACC,IAAM,EAAU,OAAO,GAAU,WAAa,EAAM,EAAI,EACxD,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,KAAM,EAAqB,aAClC,UACO,CAAA,CAEpB,CAEA,SAAS,EAAgB,EAAyC,CAC9D,OAAO,EAAO,KAAK,EAAO,IAAM,CAC5B,IAAI,EAAqB,EAAM,QAC/B,GAAI,EAAM,KAAM,CACZ,IAAM,EAAgB,EAAY,CAAK,EACvC,GAAU,EAAA,EAAA,IAAA,CAAC,EAAD,CAAgB,CAAA,CAC9B,CAaA,OAZI,EAAM,QAAU,IAAA,KAChB,GACI,EAAA,EAAA,IAAA,CAAC,EAAD,CAAgB,MAAO,EAAM,MAAO,WAAY,EAAM,WACjD,SAAA,CACW,CAAA,GAIpB,EAAM,OACC,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CAA0B,MAAA,GAAe,SAAU,EAAvC,SAAS,GAA8B,GAI1D,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CAEI,KAAM,EAAM,KACH,UACT,cAAe,EAAM,cAEpB,SAAA,EAAM,SAAW,EAAgB,EAAM,QAAQ,EAAI,IACjD,EANE,EAAM,MAAQ,SAAS,GAMzB,CAEf,CAAC,CACL,CAEA,IAAM,EAAoD,CACtD,QAAS,EAAA,cACT,KAAM,EAAA,WACN,OAAQ,EAAA,YACZ,EAwBA,SAAgB,EAAU,CACtB,SACA,SAAS,UACT,WACA,iBACA,WAAW,MACI,CACf,IAAM,GAAA,EAAO,EAAA,QAAA,KAAc,EAAgB,CAAM,EAAG,CAAC,CAAM,CAAC,EACtD,EAAS,EAAQ,GAGvB,OACI,EAAA,EAAA,IAAA,CAAC,EAAD,CAAQ,GAHQ,IAAW,SAAW,CAAE,WAAU,gBAAe,EAAI,CAAE,UAAS,EAI5E,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,SAAD,CAAoB,WAChB,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAA,SAAS,CAAa,CAAA,CAChB,CAAA,CACN,CAAA,CAEhB"}
|
package/dist/router/AppRouter.js
CHANGED
|
@@ -13,8 +13,9 @@ function f(t) {
|
|
|
13
13
|
return n || (n = e(t.lazy), d.set(t, n)), n;
|
|
14
14
|
}
|
|
15
15
|
function p({ guard: e, redirectTo: n, children: r }) {
|
|
16
|
+
let a = typeof e == "function" ? e() : e;
|
|
16
17
|
return /* @__PURE__ */ i(t, {
|
|
17
|
-
when:
|
|
18
|
+
when: a,
|
|
18
19
|
redirectTo: n,
|
|
19
20
|
children: r
|
|
20
21
|
});
|
|
@@ -22,7 +23,11 @@ function p({ guard: e, redirectTo: n, children: r }) {
|
|
|
22
23
|
function m(e) {
|
|
23
24
|
return e.map((e, t) => {
|
|
24
25
|
let n = e.element;
|
|
25
|
-
|
|
26
|
+
if (e.lazy) {
|
|
27
|
+
let t = f(e);
|
|
28
|
+
n = /* @__PURE__ */ i(t, {});
|
|
29
|
+
}
|
|
30
|
+
return e.guard !== void 0 && (n = /* @__PURE__ */ i(p, {
|
|
26
31
|
guard: e.guard,
|
|
27
32
|
redirectTo: e.redirectTo,
|
|
28
33
|
children: n
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppRouter.js","names":[],"sources":["../../src/router/AppRouter.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { BrowserRouter, HashRouter, MemoryRouter, Routes, Route } from \"react-router\";\nimport { lazyWithRetry } from \"../auth/lazy-with-retry\";\nimport { RouteGuard } from \"./RouteGuard\";\nimport type { RouterKind, TempestRouteObject } from \"./types\";\n\n/**\n * Identity helper that types a declarative route tree. Use it so editors give\n * autocomplete and type-checking on every node; the array is returned as-is.\n *\n * @example\n * export const routes = defineRoutes([\n * { path: \"/\", element: <Layout />, children: [\n * { index: true, lazy: () => import(\"./pages/Home\") },\n * { path: \"settings\", lazy: () => import(\"./pages/Settings\"),\n * guard: () => useAuth.getState().isAuthenticated, redirectTo: \"/login\" },\n * ] },\n * ]);\n */\nexport function defineRoutes(routes: TempestRouteObject[]): TempestRouteObject[] {\n return routes;\n}\n\nconst lazyCache = new WeakMap<TempestRouteObject, ComponentType<unknown>>();\n\nfunction resolveLazy(route: TempestRouteObject): ComponentType<unknown> {\n let comp = lazyCache.get(route);\n if (!comp) {\n comp = lazyWithRetry(route.lazy!);\n lazyCache.set(route, comp);\n }\n return comp;\n}\n\nfunction GuardedElement({\n guard,\n redirectTo,\n children,\n}: {\n guard: boolean | (() => boolean);\n redirectTo?: string;\n children: ReactNode;\n}) {\n const allowed = typeof guard === \"function\" ? guard() : guard;\n return (\n <RouteGuard when={allowed} redirectTo={redirectTo}>\n {children}\n </RouteGuard>\n );\n}\n\nfunction toRouteElements(routes: TempestRouteObject[]): ReactNode {\n return routes.map((route, i) => {\n let element: ReactNode = route.element;\n if (route.lazy) {\n const LazyComponent = resolveLazy(route);\n element = <LazyComponent />;\n }\n if (route.guard !== undefined) {\n element = (\n <GuardedElement guard={route.guard} redirectTo={route.redirectTo}>\n {element}\n </GuardedElement>\n );\n }\n\n if (route.index) {\n return <Route key={`index-${i}`} index element={element} />;\n }\n\n return (\n <Route\n key={route.path ?? `route-${i}`}\n path={route.path}\n element={element}\n caseSensitive={route.caseSensitive}\n >\n {route.children ? toRouteElements(route.children) : null}\n </Route>\n );\n });\n}\n\nconst ROUTERS: Record<RouterKind, typeof BrowserRouter> = {\n browser: BrowserRouter,\n hash: HashRouter,\n memory: MemoryRouter,\n};\n\nexport interface AppRouterProps {\n /** Declarative route tree, ideally built with {@link defineRoutes}. */\n routes: TempestRouteObject[];\n /** History strategy (default: `\"browser\"`). */\n router?: RouterKind;\n /** App-wide base path forwarded to the underlying router. */\n basename?: string;\n /** Initial URLs for the `\"memory\"` router (tests / non-DOM hosts). */\n initialEntries?: string[];\n /** Suspense fallback shown while a `lazy` route chunk loads. */\n fallback?: ReactNode;\n}\n\n/**\n * Render a full React Router (v7, declarative mode) from a {@link defineRoutes}\n * tree: picks the history strategy, wraps everything in a `<Suspense>` boundary\n * for `lazy` routes, and applies per-route `guard` redirects. This is the\n * single entry point apps mount at their root.\n *\n * @example\n * <AppRouter routes={routes} fallback={<Spinner />} />\n */\nexport function AppRouter({\n routes,\n router = \"browser\",\n basename,\n initialEntries,\n fallback = null,\n}: AppRouterProps) {\n const tree = useMemo(() => toRouteElements(routes), [routes]);\n const Router = ROUTERS[router];\n const routerProps = router === \"memory\" ? { basename, initialEntries } : { basename };\n\n return (\n <Router {...routerProps}>\n <Suspense fallback={fallback}>\n <Routes>{tree}</Routes>\n </Suspense>\n </Router>\n );\n}\n"],"mappings":";;;;;;AAoBA,SAAgB,EAAa,GAAoD;CAC7E,OAAO;AACX;AAEA,IAAM,oBAAY,IAAI,QAAoD;AAE1E,SAAS,EAAY,GAAmD;CACpE,IAAI,IAAO,EAAU,IAAI,CAAK;CAK9B,OAJK,MACD,IAAO,EAAc,EAAM,IAAK,GAChC,EAAU,IAAI,GAAO,CAAI,IAEtB;AACX;AAEA,SAAS,EAAe,EACpB,UACA,eACA,eAKD;
|
|
1
|
+
{"version":3,"file":"AppRouter.js","names":[],"sources":["../../src/router/AppRouter.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { BrowserRouter, HashRouter, MemoryRouter, Routes, Route } from \"react-router\";\nimport { lazyWithRetry } from \"../auth/lazy-with-retry\";\nimport { RouteGuard } from \"./RouteGuard\";\nimport type { RouterKind, TempestRouteObject } from \"./types\";\n\n/**\n * Identity helper that types a declarative route tree. Use it so editors give\n * autocomplete and type-checking on every node; the array is returned as-is.\n *\n * @example\n * export const routes = defineRoutes([\n * { path: \"/\", element: <Layout />, children: [\n * { index: true, lazy: () => import(\"./pages/Home\") },\n * { path: \"settings\", lazy: () => import(\"./pages/Settings\"),\n * guard: () => useAuth.getState().isAuthenticated, redirectTo: \"/login\" },\n * ] },\n * ]);\n */\nexport function defineRoutes(routes: TempestRouteObject[]): TempestRouteObject[] {\n return routes;\n}\n\nconst lazyCache = new WeakMap<TempestRouteObject, ComponentType<unknown>>();\n\nfunction resolveLazy(route: TempestRouteObject): ComponentType<unknown> {\n let comp = lazyCache.get(route);\n if (!comp) {\n comp = lazyWithRetry(route.lazy!);\n lazyCache.set(route, comp);\n }\n return comp;\n}\n\nfunction GuardedElement({\n guard,\n redirectTo,\n children,\n}: {\n guard: boolean | (() => boolean);\n redirectTo?: string;\n children: ReactNode;\n}) {\n const allowed = typeof guard === \"function\" ? guard() : guard;\n return (\n <RouteGuard when={allowed} redirectTo={redirectTo}>\n {children}\n </RouteGuard>\n );\n}\n\nfunction toRouteElements(routes: TempestRouteObject[]): ReactNode {\n return routes.map((route, i) => {\n let element: ReactNode = route.element;\n if (route.lazy) {\n const LazyComponent = resolveLazy(route);\n element = <LazyComponent />;\n }\n if (route.guard !== undefined) {\n element = (\n <GuardedElement guard={route.guard} redirectTo={route.redirectTo}>\n {element}\n </GuardedElement>\n );\n }\n\n if (route.index) {\n return <Route key={`index-${i}`} index element={element} />;\n }\n\n return (\n <Route\n key={route.path ?? `route-${i}`}\n path={route.path}\n element={element}\n caseSensitive={route.caseSensitive}\n >\n {route.children ? toRouteElements(route.children) : null}\n </Route>\n );\n });\n}\n\nconst ROUTERS: Record<RouterKind, typeof BrowserRouter> = {\n browser: BrowserRouter,\n hash: HashRouter,\n memory: MemoryRouter,\n};\n\nexport interface AppRouterProps {\n /** Declarative route tree, ideally built with {@link defineRoutes}. */\n routes: TempestRouteObject[];\n /** History strategy (default: `\"browser\"`). */\n router?: RouterKind;\n /** App-wide base path forwarded to the underlying router. */\n basename?: string;\n /** Initial URLs for the `\"memory\"` router (tests / non-DOM hosts). */\n initialEntries?: string[];\n /** Suspense fallback shown while a `lazy` route chunk loads. */\n fallback?: ReactNode;\n}\n\n/**\n * Render a full React Router (v7, declarative mode) from a {@link defineRoutes}\n * tree: picks the history strategy, wraps everything in a `<Suspense>` boundary\n * for `lazy` routes, and applies per-route `guard` redirects. This is the\n * single entry point apps mount at their root.\n *\n * @example\n * <AppRouter routes={routes} fallback={<Spinner />} />\n */\nexport function AppRouter({\n routes,\n router = \"browser\",\n basename,\n initialEntries,\n fallback = null,\n}: AppRouterProps) {\n const tree = useMemo(() => toRouteElements(routes), [routes]);\n const Router = ROUTERS[router];\n const routerProps = router === \"memory\" ? { basename, initialEntries } : { basename };\n\n return (\n <Router {...routerProps}>\n <Suspense fallback={fallback}>\n <Routes>{tree}</Routes>\n </Suspense>\n </Router>\n );\n}\n"],"mappings":";;;;;;AAoBA,SAAgB,EAAa,GAAoD;CAC7E,OAAO;AACX;AAEA,IAAM,oBAAY,IAAI,QAAoD;AAE1E,SAAS,EAAY,GAAmD;CACpE,IAAI,IAAO,EAAU,IAAI,CAAK;CAK9B,OAJK,MACD,IAAO,EAAc,EAAM,IAAK,GAChC,EAAU,IAAI,GAAO,CAAI,IAEtB;AACX;AAEA,SAAS,EAAe,EACpB,UACA,eACA,eAKD;CACC,IAAM,IAAU,OAAO,KAAU,aAAa,EAAM,IAAI;CACxD,OACI,kBAAC,GAAD;EAAY,MAAM;EAAqB;EAClC;CACO,CAAA;AAEpB;AAEA,SAAS,EAAgB,GAAyC;CAC9D,OAAO,EAAO,KAAK,GAAO,MAAM;EAC5B,IAAI,IAAqB,EAAM;EAC/B,IAAI,EAAM,MAAM;GACZ,IAAM,IAAgB,EAAY,CAAK;GACvC,IAAU,kBAAC,GAAD,CAAgB,CAAA;EAC9B;EAaA,OAZI,EAAM,UAAU,KAAA,MAChB,IACI,kBAAC,GAAD;GAAgB,OAAO,EAAM;GAAO,YAAY,EAAM;GACjD,UAAA;EACW,CAAA,IAIpB,EAAM,QACC,kBAAC,GAAD;GAA0B,OAAA;GAAe;EAAU,GAAvC,SAAS,GAA8B,IAI1D,kBAAC,GAAD;GAEI,MAAM,EAAM;GACH;GACT,eAAe,EAAM;GAEpB,UAAA,EAAM,WAAW,EAAgB,EAAM,QAAQ,IAAI;EACjD,GANE,EAAM,QAAQ,SAAS,GAMzB;CAEf,CAAC;AACL;AAEA,IAAM,IAAoD;CACtD,SAAS;CACT,MAAM;CACN,QAAQ;AACZ;AAwBA,SAAgB,EAAU,EACtB,WACA,YAAS,WACT,aACA,mBACA,cAAW,QACI;CACf,IAAM,IAAO,QAAc,EAAgB,CAAM,GAAG,CAAC,CAAM,CAAC,GACtD,IAAS,EAAQ;CAGvB,OACI,kBAAC,GAAD;EAAQ,GAHQ,MAAW,WAAW;GAAE;GAAU;EAAe,IAAI,EAAE,YAAS;EAI5E,UAAA,kBAAC,GAAD;GAAoB;GAChB,UAAA,kBAAC,GAAD,EAAA,UAAS,EAAa,CAAA;EAChB,CAAA;CACN,CAAA;AAEhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-event-stream.cjs","names":[],"sources":["../../src/sse/use-event-stream.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport {\n createEventStream,\n type CreateEventStreamOptions,\n type EventStreamMessage,\n type EventStreamStatus,\n} from \"./create-event-stream\";\n\nexport interface UseEventStreamOptions<T> extends Omit<\n CreateEventStreamOptions<T>,\n \"onStatusChange\"\n> {\n /** When false, the stream is not opened. Useful for \"wait for auth\". Default: true. */\n enabled?: boolean;\n}\n\nexport interface UseEventStreamResult<T> {\n status: EventStreamStatus;\n /** Last message received (excluding heartbeats). */\n lastMessage: EventStreamMessage<T> | null;\n /** Force a reconnect. */\n reconnect: () => void;\n}\n\n/**\n * React hook wrapper around {@link createEventStream}. Connection lifecycle is\n * tied to the component (and the `url`/`enabled` dependencies); the stream\n * closes on unmount.\n *\n * @example\n * useEventStream<Notification>(`${API}/notifications/stream`, {\n * enabled: !!user,\n * withCredentials: true,\n * onMessage: ({ data }) => addNotification(data),\n * });\n */\nexport function useEventStream<T = unknown>(\n url: string,\n options: UseEventStreamOptions<T> = {},\n): UseEventStreamResult<T> {\n const { enabled = true, onMessage, ...rest } = options;\n const [status, setStatus] = useState<EventStreamStatus>(\"idle\");\n const [lastMessage, setLastMessage] = useState<EventStreamMessage<T> | null>(null);\n const reconnectRef = useRef<(() => void) | null>(null);\n\n const onMessageRef = useRef(onMessage);\n useEffect(() => {\n onMessageRef.current = onMessage;\n }, [onMessage]);\n\n useEffect(() => {\n if (!enabled || !url) {\n setStatus(\"idle\");\n return;\n }\n\n const controller = createEventStream<T>(url, {\n ...rest,\n onStatusChange: setStatus,\n onMessage: (message) => {\n setLastMessage(message);\n onMessageRef.current?.(message);\n },\n });\n reconnectRef.current = controller.reconnect;\n\n return () => {\n controller.close();\n reconnectRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, enabled]);\n\n return {\n status,\n lastMessage,\n reconnect: () => reconnectRef.current?.(),\n };\n}\n"],"mappings":"oEAoCA,SAAgB,EACZ,EACA,EAAoC,CAAC,EACd,CACvB,GAAM,CAAE,UAAU,GAAM,YAAW,GAAG,GAAS,EACzC,CAAC,EAAQ,IAAA,
|
|
1
|
+
{"version":3,"file":"use-event-stream.cjs","names":[],"sources":["../../src/sse/use-event-stream.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport {\n createEventStream,\n type CreateEventStreamOptions,\n type EventStreamMessage,\n type EventStreamStatus,\n} from \"./create-event-stream\";\n\nexport interface UseEventStreamOptions<T> extends Omit<\n CreateEventStreamOptions<T>,\n \"onStatusChange\"\n> {\n /** When false, the stream is not opened. Useful for \"wait for auth\". Default: true. */\n enabled?: boolean;\n}\n\nexport interface UseEventStreamResult<T> {\n status: EventStreamStatus;\n /** Last message received (excluding heartbeats). */\n lastMessage: EventStreamMessage<T> | null;\n /** Force a reconnect. */\n reconnect: () => void;\n}\n\n/**\n * React hook wrapper around {@link createEventStream}. Connection lifecycle is\n * tied to the component (and the `url`/`enabled` dependencies); the stream\n * closes on unmount.\n *\n * @example\n * useEventStream<Notification>(`${API}/notifications/stream`, {\n * enabled: !!user,\n * withCredentials: true,\n * onMessage: ({ data }) => addNotification(data),\n * });\n */\nexport function useEventStream<T = unknown>(\n url: string,\n options: UseEventStreamOptions<T> = {},\n): UseEventStreamResult<T> {\n const { enabled = true, onMessage, ...rest } = options;\n const [status, setStatus] = useState<EventStreamStatus>(\"idle\");\n const [lastMessage, setLastMessage] = useState<EventStreamMessage<T> | null>(null);\n const reconnectRef = useRef<(() => void) | null>(null);\n\n const onMessageRef = useRef(onMessage);\n useEffect(() => {\n onMessageRef.current = onMessage;\n }, [onMessage]);\n\n useEffect(() => {\n if (!enabled || !url) {\n setStatus(\"idle\");\n return;\n }\n\n const controller = createEventStream<T>(url, {\n ...rest,\n onStatusChange: setStatus,\n onMessage: (message) => {\n setLastMessage(message);\n onMessageRef.current?.(message);\n },\n });\n reconnectRef.current = controller.reconnect;\n\n return () => {\n controller.close();\n reconnectRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, enabled]);\n\n return {\n status,\n lastMessage,\n reconnect: () => reconnectRef.current?.(),\n };\n}\n"],"mappings":"oEAoCA,SAAgB,EACZ,EACA,EAAoC,CAAC,EACd,CACvB,GAAM,CAAE,UAAU,GAAM,YAAW,GAAG,GAAS,EACzC,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAA4B,MAAM,EACxD,CAAC,EAAa,IAAA,EAAkB,EAAA,SAAA,CAAuC,IAAI,EAC3E,GAAA,EAAe,EAAA,OAAA,CAA4B,IAAI,EAE/C,GAAA,EAAe,EAAA,OAAA,CAAO,CAAS,EA4BrC,OA3BA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAa,QAAU,CAC3B,EAAG,CAAC,CAAS,CAAC,GAEd,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,GAAW,CAAC,EAAK,CAClB,EAAU,MAAM,EAChB,MACJ,CAEA,IAAM,EAAa,EAAA,kBAAqB,EAAK,CACzC,GAAG,EACH,eAAgB,EAChB,UAAY,GAAY,CACpB,EAAe,CAAO,EACtB,EAAa,UAAU,CAAO,CAClC,CACJ,CAAC,EAGD,MAFA,GAAa,QAAU,EAAW,cAErB,CACT,EAAW,MAAM,EACjB,EAAa,QAAU,IAC3B,CAEJ,EAAG,CAAC,EAAK,CAAO,CAAC,EAEV,CACH,SACA,cACA,cAAiB,EAAa,UAAU,CAC5C,CACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-store.cjs","names":[],"sources":["../../src/store/create-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport type { StateCreator, StoreApi, UseBoundStore } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\nimport type { PersistOptions } from \"zustand/middleware\";\n\n/**\n * Persistence options for {@link createStore}. Mirrors the subset of Zustand's\n * `persist` middleware that apps usually need, with sensible Tempest defaults.\n */\nexport interface CreateStorePersistOptions<T> {\n /** Storage key (required to enable persistence). */\n name: string;\n /** Which Web Storage to use (default: `\"local\"`). */\n storage?: \"local\" | \"session\";\n /**\n * Pick the slice of state to persist. Defaults to persisting the whole\n * state. Use this to avoid writing transient/derived fields.\n */\n partialize?: PersistOptions<T, Partial<T>>[\"partialize\"];\n /** Persisted schema version, forwarded to Zustand for migrations. */\n version?: number;\n /** Migration function forwarded to Zustand's `persist` middleware. */\n migrate?: PersistOptions<T, Partial<T>>[\"migrate\"];\n}\n\n/**\n * Options accepted by {@link createStore}.\n */\nexport interface CreateStoreOptions<T> {\n /** Enable `localStorage`/`sessionStorage` persistence for the store. */\n persist?: CreateStorePersistOptions<T>;\n}\n\n/**\n * Create a typed Zustand store with optional `persist` middleware, wired with\n * Tempest defaults. This is the generic counterpart to `createAuthStore`: use\n * it for any domain slice (cart, preferences, wizard state, …) without\n * re-writing the persistence boilerplate in every app.\n *\n * @typeParam T - The store state shape (state + actions).\n * @param initializer - Standard Zustand `StateCreator` (`(set, get) => state`).\n * @param options - Optional persistence configuration.\n * @returns A bound Zustand hook (`useStore`) with the usual `getState`/`setState`/`subscribe` API.\n *\n * @example\n * interface CartState {\n * items: string[];\n * add: (id: string) => void;\n * clear: () => void;\n * }\n *\n * const useCart = createStore<CartState>(\n * (set) => ({\n * items: [],\n * add: (id) => set((s) => ({ items: [...s.items, id] })),\n * clear: () => set({ items: [] }),\n * }),\n * { persist: { name: \"cart\", partialize: (s) => ({ items: s.items }) } },\n * );\n */\nexport function createStore<T>(\n initializer: StateCreator<T, [], []>,\n options: CreateStoreOptions<T> = {},\n): UseBoundStore<StoreApi<T>> {\n if (!options.persist) {\n return create<T>()(initializer);\n }\n\n const { name, storage = \"local\", partialize, version, migrate } = options.persist;\n const storageImpl = storage === \"session\" ? () => sessionStorage : () => localStorage;\n\n return create<T>()(\n persist(initializer, {\n name,\n storage: createJSONStorage(storageImpl),\n ...(partialize ? { partialize } : {}),\n ...(version !== undefined ? { version } : {}),\n ...(migrate ? { migrate } : {}),\n }),\n );\n}\n"],"mappings":"yDA4DA,SAAgB,EACZ,EACA,EAAiC,CAAC,EACR,CAC1B,GAAI,CAAC,EAAQ,QACT,OAAA,
|
|
1
|
+
{"version":3,"file":"create-store.cjs","names":[],"sources":["../../src/store/create-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport type { StateCreator, StoreApi, UseBoundStore } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\nimport type { PersistOptions } from \"zustand/middleware\";\n\n/**\n * Persistence options for {@link createStore}. Mirrors the subset of Zustand's\n * `persist` middleware that apps usually need, with sensible Tempest defaults.\n */\nexport interface CreateStorePersistOptions<T> {\n /** Storage key (required to enable persistence). */\n name: string;\n /** Which Web Storage to use (default: `\"local\"`). */\n storage?: \"local\" | \"session\";\n /**\n * Pick the slice of state to persist. Defaults to persisting the whole\n * state. Use this to avoid writing transient/derived fields.\n */\n partialize?: PersistOptions<T, Partial<T>>[\"partialize\"];\n /** Persisted schema version, forwarded to Zustand for migrations. */\n version?: number;\n /** Migration function forwarded to Zustand's `persist` middleware. */\n migrate?: PersistOptions<T, Partial<T>>[\"migrate\"];\n}\n\n/**\n * Options accepted by {@link createStore}.\n */\nexport interface CreateStoreOptions<T> {\n /** Enable `localStorage`/`sessionStorage` persistence for the store. */\n persist?: CreateStorePersistOptions<T>;\n}\n\n/**\n * Create a typed Zustand store with optional `persist` middleware, wired with\n * Tempest defaults. This is the generic counterpart to `createAuthStore`: use\n * it for any domain slice (cart, preferences, wizard state, …) without\n * re-writing the persistence boilerplate in every app.\n *\n * @typeParam T - The store state shape (state + actions).\n * @param initializer - Standard Zustand `StateCreator` (`(set, get) => state`).\n * @param options - Optional persistence configuration.\n * @returns A bound Zustand hook (`useStore`) with the usual `getState`/`setState`/`subscribe` API.\n *\n * @example\n * interface CartState {\n * items: string[];\n * add: (id: string) => void;\n * clear: () => void;\n * }\n *\n * const useCart = createStore<CartState>(\n * (set) => ({\n * items: [],\n * add: (id) => set((s) => ({ items: [...s.items, id] })),\n * clear: () => set({ items: [] }),\n * }),\n * { persist: { name: \"cart\", partialize: (s) => ({ items: s.items }) } },\n * );\n */\nexport function createStore<T>(\n initializer: StateCreator<T, [], []>,\n options: CreateStoreOptions<T> = {},\n): UseBoundStore<StoreApi<T>> {\n if (!options.persist) {\n return create<T>()(initializer);\n }\n\n const { name, storage = \"local\", partialize, version, migrate } = options.persist;\n const storageImpl = storage === \"session\" ? () => sessionStorage : () => localStorage;\n\n return create<T>()(\n persist(initializer, {\n name,\n storage: createJSONStorage(storageImpl),\n ...(partialize ? { partialize } : {}),\n ...(version !== undefined ? { version } : {}),\n ...(migrate ? { migrate } : {}),\n }),\n );\n}\n"],"mappings":"yDA4DA,SAAgB,EACZ,EACA,EAAiC,CAAC,EACR,CAC1B,GAAI,CAAC,EAAQ,QACT,OAAA,EAAO,EAAA,OAAA,CAAU,CAAC,CAAC,CAAW,EAGlC,GAAM,CAAE,OAAM,UAAU,QAAS,aAAY,UAAS,WAAY,EAAQ,QACpE,EAAc,IAAY,cAAkB,mBAAuB,aAEzE,OAAA,EAAO,EAAA,OAAA,CAAU,CAAC,EAAA,EACd,EAAA,QAAA,CAAQ,EAAa,CACjB,OACA,SAAA,EAAS,EAAA,kBAAA,CAAkB,CAAW,EACtC,GAAI,EAAa,CAAE,YAAW,EAAI,CAAC,EACnC,GAAI,IAAY,IAAA,GAA0B,CAAC,EAAf,CAAE,SAAQ,EACtC,GAAI,EAAU,CAAE,SAAQ,EAAI,CAAC,CACjC,CAAC,CACL,CACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tabular-predictor.cjs","names":[],"sources":["../../src/tabular/use-tabular-predictor.ts"],"sourcesContent":["/**\n * React binding for {@link TabularPredictor}.\n *\n * Loading a model is async, cancellable, and has to be undone on unmount —\n * three things every component that touches inference gets wrong the same\n * way. The hook owns that lifecycle so a component only deals with\n * `status` and `predict`.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { fetchModelBytes, type ModelCacheOptions } from \"./cache\";\nimport { TabularPredictor } from \"./predictor\";\nimport type {\n FeatureRow,\n TabularModelSource,\n TabularPrediction,\n TabularPredictorOptions,\n} from \"./types\";\n\n/** Lifecycle of the model behind the hook. */\nexport type TabularPredictorStatus = \"idle\" | \"loading\" | \"ready\" | \"error\";\n\n/** Options for {@link useTabularPredictor}. */\nexport interface UseTabularPredictorOptions extends TabularPredictorOptions {\n /**\n * Cache the model bytes on the device, so later loads work offline.\n *\n * On by default when the source is a URL: an app that runs inference in\n * the browser almost always wants it to keep working without a network,\n * and the failure mode of not caching only shows up in a tunnel.\n */\n readonly cache?: boolean | ModelCacheOptions;\n}\n\n/** What {@link useTabularPredictor} returns. */\nexport interface UseTabularPredictorResult {\n /** The loaded predictor, or `null` while loading or on error. */\n readonly predictor: TabularPredictor | null;\n /** Where the load is. */\n readonly status: TabularPredictorStatus;\n /** Why the load failed. */\n readonly error: Error | null;\n /** Whether the model is loaded and can answer. */\n readonly isReady: boolean;\n /**\n * Predict for a batch of rows.\n *\n * @throws When called before the model is ready — awaiting `isReady`\n * is the caller's job, and a silent empty result would hide the bug.\n */\n readonly predict: (rows: readonly FeatureRow[]) => Promise<TabularPrediction>;\n /** Load the model again, e.g. after a failure or a new version. */\n readonly reload: () => void;\n}\n\n/**\n * Load a tabular model and keep it for the component's lifetime.\n *\n * @example\n * ```tsx\n * function RiskWidget() {\n * const { predict, isReady } = useTabularPredictor(\"/models/risk-v3.onnx\");\n * const [score, setScore] = useState<number | null>(null);\n *\n * async function onSubmit(features: number[]) {\n * const { probabilities } = await predict([features]);\n * setScore(probabilities[0]?.[1] ?? null);\n * }\n *\n * return <button disabled={!isReady} onClick={() => onSubmit([1, 2, 3, 4])}>Score</button>;\n * }\n * ```\n *\n * @param source Model URL, or the bytes when the app already has them.\n * Pass `null` to hold off loading (a gate, a lazy tab).\n * @param options Predictor options plus caching.\n * @returns The predictor, its status, and a `predict` bound to it.\n */\nexport function useTabularPredictor(\n source: TabularModelSource | null,\n options: UseTabularPredictorOptions = {},\n): UseTabularPredictorResult {\n const [predictor, setPredictor] = useState<TabularPredictor | null>(null);\n const [status, setStatus] = useState<TabularPredictorStatus>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const [attempt, setAttempt] = useState(0);\n\n const optionsRef = useRef(options);\n\n useEffect(() => {\n optionsRef.current = options;\n });\n\n useEffect(() => {\n if (source === null) {\n setStatus(\"idle\");\n return;\n }\n\n let cancelled = false;\n let loaded: TabularPredictor | null = null;\n\n setStatus(\"loading\");\n setError(null);\n\n void (async () => {\n try {\n const current = optionsRef.current;\n const cache = current.cache ?? true;\n const bytes =\n typeof source === \"string\" && cache !== false\n ? await fetchModelBytes(source, typeof cache === \"object\" ? cache : {})\n : source;\n const created = await TabularPredictor.create(bytes, {\n providers: current.providers,\n warmup: current.warmup,\n sessionOptions: current.sessionOptions,\n });\n loaded = created;\n if (cancelled) {\n await created.dispose();\n return;\n }\n setPredictor(created);\n setStatus(\"ready\");\n } catch (caught) {\n if (cancelled) return;\n setPredictor(null);\n setError(caught instanceof Error ? caught : new Error(String(caught)));\n setStatus(\"error\");\n }\n })();\n\n return () => {\n cancelled = true;\n setPredictor(null);\n void loaded?.dispose();\n };\n }, [source, attempt]);\n\n const predict = useCallback(\n async (rows: readonly FeatureRow[]): Promise<TabularPrediction> => {\n if (predictor === null) {\n throw new Error(\n \"predict() was called before the model finished loading. \" +\n \"Gate on `isReady`.\",\n );\n }\n return await predictor.predict(rows);\n },\n [predictor],\n );\n\n const reload = useCallback(() => setAttempt((value) => value + 1), []);\n\n return {\n predictor,\n status,\n error,\n isReady: status === \"ready\" && predictor !== null,\n predict,\n reload,\n };\n}\n"],"mappings":"mFA+EA,SAAgB,EACZ,EACA,EAAsC,CAAC,EACd,CACzB,GAAM,CAAC,EAAW,IAAA,
|
|
1
|
+
{"version":3,"file":"use-tabular-predictor.cjs","names":[],"sources":["../../src/tabular/use-tabular-predictor.ts"],"sourcesContent":["/**\n * React binding for {@link TabularPredictor}.\n *\n * Loading a model is async, cancellable, and has to be undone on unmount —\n * three things every component that touches inference gets wrong the same\n * way. The hook owns that lifecycle so a component only deals with\n * `status` and `predict`.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { fetchModelBytes, type ModelCacheOptions } from \"./cache\";\nimport { TabularPredictor } from \"./predictor\";\nimport type {\n FeatureRow,\n TabularModelSource,\n TabularPrediction,\n TabularPredictorOptions,\n} from \"./types\";\n\n/** Lifecycle of the model behind the hook. */\nexport type TabularPredictorStatus = \"idle\" | \"loading\" | \"ready\" | \"error\";\n\n/** Options for {@link useTabularPredictor}. */\nexport interface UseTabularPredictorOptions extends TabularPredictorOptions {\n /**\n * Cache the model bytes on the device, so later loads work offline.\n *\n * On by default when the source is a URL: an app that runs inference in\n * the browser almost always wants it to keep working without a network,\n * and the failure mode of not caching only shows up in a tunnel.\n */\n readonly cache?: boolean | ModelCacheOptions;\n}\n\n/** What {@link useTabularPredictor} returns. */\nexport interface UseTabularPredictorResult {\n /** The loaded predictor, or `null` while loading or on error. */\n readonly predictor: TabularPredictor | null;\n /** Where the load is. */\n readonly status: TabularPredictorStatus;\n /** Why the load failed. */\n readonly error: Error | null;\n /** Whether the model is loaded and can answer. */\n readonly isReady: boolean;\n /**\n * Predict for a batch of rows.\n *\n * @throws When called before the model is ready — awaiting `isReady`\n * is the caller's job, and a silent empty result would hide the bug.\n */\n readonly predict: (rows: readonly FeatureRow[]) => Promise<TabularPrediction>;\n /** Load the model again, e.g. after a failure or a new version. */\n readonly reload: () => void;\n}\n\n/**\n * Load a tabular model and keep it for the component's lifetime.\n *\n * @example\n * ```tsx\n * function RiskWidget() {\n * const { predict, isReady } = useTabularPredictor(\"/models/risk-v3.onnx\");\n * const [score, setScore] = useState<number | null>(null);\n *\n * async function onSubmit(features: number[]) {\n * const { probabilities } = await predict([features]);\n * setScore(probabilities[0]?.[1] ?? null);\n * }\n *\n * return <button disabled={!isReady} onClick={() => onSubmit([1, 2, 3, 4])}>Score</button>;\n * }\n * ```\n *\n * @param source Model URL, or the bytes when the app already has them.\n * Pass `null` to hold off loading (a gate, a lazy tab).\n * @param options Predictor options plus caching.\n * @returns The predictor, its status, and a `predict` bound to it.\n */\nexport function useTabularPredictor(\n source: TabularModelSource | null,\n options: UseTabularPredictorOptions = {},\n): UseTabularPredictorResult {\n const [predictor, setPredictor] = useState<TabularPredictor | null>(null);\n const [status, setStatus] = useState<TabularPredictorStatus>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const [attempt, setAttempt] = useState(0);\n\n const optionsRef = useRef(options);\n\n useEffect(() => {\n optionsRef.current = options;\n });\n\n useEffect(() => {\n if (source === null) {\n setStatus(\"idle\");\n return;\n }\n\n let cancelled = false;\n let loaded: TabularPredictor | null = null;\n\n setStatus(\"loading\");\n setError(null);\n\n void (async () => {\n try {\n const current = optionsRef.current;\n const cache = current.cache ?? true;\n const bytes =\n typeof source === \"string\" && cache !== false\n ? await fetchModelBytes(source, typeof cache === \"object\" ? cache : {})\n : source;\n const created = await TabularPredictor.create(bytes, {\n providers: current.providers,\n warmup: current.warmup,\n sessionOptions: current.sessionOptions,\n });\n loaded = created;\n if (cancelled) {\n await created.dispose();\n return;\n }\n setPredictor(created);\n setStatus(\"ready\");\n } catch (caught) {\n if (cancelled) return;\n setPredictor(null);\n setError(caught instanceof Error ? caught : new Error(String(caught)));\n setStatus(\"error\");\n }\n })();\n\n return () => {\n cancelled = true;\n setPredictor(null);\n void loaded?.dispose();\n };\n }, [source, attempt]);\n\n const predict = useCallback(\n async (rows: readonly FeatureRow[]): Promise<TabularPrediction> => {\n if (predictor === null) {\n throw new Error(\n \"predict() was called before the model finished loading. \" +\n \"Gate on `isReady`.\",\n );\n }\n return await predictor.predict(rows);\n },\n [predictor],\n );\n\n const reload = useCallback(() => setAttempt((value) => value + 1), []);\n\n return {\n predictor,\n status,\n error,\n isReady: status === \"ready\" && predictor !== null,\n predict,\n reload,\n };\n}\n"],"mappings":"mFA+EA,SAAgB,EACZ,EACA,EAAsC,CAAC,EACd,CACzB,GAAM,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAkC,IAAI,EAClE,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAAiC,MAAM,EAC7D,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAuB,IAAI,EAC/C,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAS,CAAC,EAElC,GAAA,EAAa,EAAA,OAAA,CAAO,CAAO,GAEjC,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAW,QAAU,CACzB,CAAC,GAED,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,IAAW,KAAM,CACjB,EAAU,MAAM,EAChB,MACJ,CAEA,IAAI,EAAY,GACZ,EAAkC,KAiCtC,OA/BA,EAAU,SAAS,EACnB,EAAS,IAAI,GAEP,SAAY,CACd,GAAI,CACA,IAAM,EAAU,EAAW,QACrB,EAAQ,EAAQ,OAAS,GACzB,EACF,OAAO,GAAW,UAAY,IAAU,GAClC,MAAM,EAAA,gBAAgB,EAAQ,OAAO,GAAU,SAAW,EAAQ,CAAC,CAAC,EACpE,EACJ,EAAU,MAAM,EAAA,iBAAiB,OAAO,EAAO,CACjD,UAAW,EAAQ,UACnB,OAAQ,EAAQ,OAChB,eAAgB,EAAQ,cAC5B,CAAC,EAED,GADA,EAAS,EACL,EAAW,CACX,MAAM,EAAQ,QAAQ,EACtB,MACJ,CACA,EAAa,CAAO,EACpB,EAAU,OAAO,CACrB,OAAS,EAAQ,CACb,GAAI,EAAW,OACf,EAAa,IAAI,EACjB,EAAS,aAAkB,MAAQ,EAAa,MAAM,OAAO,CAAM,CAAC,CAAC,EACrE,EAAU,OAAO,CACrB,CACJ,EAAA,CAAG,MAEU,CACT,EAAY,GACZ,EAAa,IAAI,EACjB,GAAa,QAAQ,CACzB,CACJ,EAAG,CAAC,EAAQ,CAAO,CAAC,EAEpB,IAAM,GAAA,EAAU,EAAA,YAAA,CACZ,KAAO,IAA4D,CAC/D,GAAI,IAAc,KACd,MAAU,MACN,4EAEJ,EAEJ,OAAO,MAAM,EAAU,QAAQ,CAAI,CACvC,EACA,CAAC,CAAS,CACd,EAEM,GAAA,EAAS,EAAA,YAAA,KAAkB,EAAY,GAAU,EAAQ,CAAC,EAAG,CAAC,CAAC,EAErE,MAAO,CACH,YACA,SACA,QACA,QAAS,IAAW,SAAW,IAAc,KAC7C,UACA,QACJ,CACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TelemetryProvider.cjs","names":[],"sources":["../../src/telemetry/TelemetryProvider.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { TelemetryAdapter } from \"./types\";\n\nconst TelemetryContext = createContext<TelemetryAdapter | null>(null);\n\nexport interface TelemetryProviderProps {\n adapter: TelemetryAdapter;\n children: ReactNode;\n}\n\n/**\n * Inject a telemetry adapter into the tree. Apps can swap implementations\n * (Sentry, Datadog, PostHog, console) without touching call sites.\n */\nexport function TelemetryProvider({ adapter, children }: TelemetryProviderProps) {\n useEffect(() => {\n void adapter.init?.();\n return () => {\n void adapter.flush?.();\n };\n }, [adapter]);\n const value = useMemo(() => adapter, [adapter]);\n return <TelemetryContext.Provider value={value}>{children}</TelemetryContext.Provider>;\n}\n\n/**\n * Access the active telemetry adapter. Returns `null` when no provider is\n * mounted (calls are silently dropped — useful for unit tests).\n */\nexport function useTelemetry(): TelemetryAdapter | null {\n return useContext(TelemetryContext);\n}\n"],"mappings":"sDAIA,IAAM,GAAA,
|
|
1
|
+
{"version":3,"file":"TelemetryProvider.cjs","names":[],"sources":["../../src/telemetry/TelemetryProvider.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { TelemetryAdapter } from \"./types\";\n\nconst TelemetryContext = createContext<TelemetryAdapter | null>(null);\n\nexport interface TelemetryProviderProps {\n adapter: TelemetryAdapter;\n children: ReactNode;\n}\n\n/**\n * Inject a telemetry adapter into the tree. Apps can swap implementations\n * (Sentry, Datadog, PostHog, console) without touching call sites.\n */\nexport function TelemetryProvider({ adapter, children }: TelemetryProviderProps) {\n useEffect(() => {\n void adapter.init?.();\n return () => {\n void adapter.flush?.();\n };\n }, [adapter]);\n const value = useMemo(() => adapter, [adapter]);\n return <TelemetryContext.Provider value={value}>{children}</TelemetryContext.Provider>;\n}\n\n/**\n * Access the active telemetry adapter. Returns `null` when no provider is\n * mounted (calls are silently dropped — useful for unit tests).\n */\nexport function useTelemetry(): TelemetryAdapter | null {\n return useContext(TelemetryContext);\n}\n"],"mappings":"sDAIA,IAAM,GAAA,EAAmB,EAAA,cAAA,CAAuC,IAAI,EAWpE,SAAgB,EAAkB,CAAE,UAAS,YAAoC,EAC7E,EAAA,EAAA,UAAA,MACI,EAAa,OAAO,MACP,CACT,EAAa,QAAQ,CACzB,GACD,CAAC,CAAO,CAAC,EACZ,IAAM,GAAA,EAAQ,EAAA,QAAA,KAAc,EAAS,CAAC,CAAO,CAAC,EAC9C,OAAO,EAAA,EAAA,IAAA,CAAC,EAAiB,SAAlB,CAAkC,QAAQ,UAAoC,CAAA,CACzF,CAMA,SAAgB,GAAwC,CACpD,OAAA,EAAO,EAAA,WAAA,CAAW,CAAgB,CACtC"}
|
|
@@ -59,6 +59,7 @@ import { redirect } from 'react-router';
|
|
|
59
59
|
import { Ref } from 'react';
|
|
60
60
|
import { RefAttributes } from 'react';
|
|
61
61
|
import { RefObject } from 'react';
|
|
62
|
+
import { Resolver } from 'react-hook-form';
|
|
62
63
|
import { Route } from 'react-router';
|
|
63
64
|
import { Routes } from 'react-router';
|
|
64
65
|
import { SelectHTMLAttributes } from 'react';
|
|
@@ -1366,7 +1367,7 @@ export declare interface BarcodeScanResult {
|
|
|
1366
1367
|
* @param value - Base64url text, with or without `=` padding.
|
|
1367
1368
|
* @returns The decoded bytes.
|
|
1368
1369
|
*/
|
|
1369
|
-
export declare function base64UrlToBytes(value: string): Uint8Array
|
|
1370
|
+
export declare function base64UrlToBytes(value: string): Uint8Array<ArrayBuffer>;
|
|
1370
1371
|
|
|
1371
1372
|
/**
|
|
1372
1373
|
* Initial bearing (forward azimuth) from `origin` to `destination`, in degrees
|
|
@@ -1862,8 +1863,8 @@ export declare interface CenterProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
1862
1863
|
}
|
|
1863
1864
|
|
|
1864
1865
|
export declare const CEPInput: ForwardRefExoticComponent<Omit<InputProps, "value" | "onChange"> & {
|
|
1865
|
-
value
|
|
1866
|
-
onChange
|
|
1866
|
+
value?: string;
|
|
1867
|
+
onChange?: (value: string) => void;
|
|
1867
1868
|
} & RefAttributes<HTMLInputElement>>;
|
|
1868
1869
|
|
|
1869
1870
|
/** A `var(--tempest-chart-…)` reference, so the value follows the active theme. */
|
|
@@ -2201,8 +2202,8 @@ export declare interface ClickOutsideProps extends HTMLAttributes<HTMLDivElement
|
|
|
2201
2202
|
export declare function cn(...values: ClassValue[]): string;
|
|
2202
2203
|
|
|
2203
2204
|
export declare const CNPJInput: ForwardRefExoticComponent<Omit<InputProps, "value" | "onChange"> & {
|
|
2204
|
-
value
|
|
2205
|
-
onChange
|
|
2205
|
+
value?: string;
|
|
2206
|
+
onChange?: (value: string) => void;
|
|
2206
2207
|
} & RefAttributes<HTMLInputElement>>;
|
|
2207
2208
|
|
|
2208
2209
|
/**
|
|
@@ -2554,8 +2555,8 @@ export declare interface CounterHandlers {
|
|
|
2554
2555
|
}
|
|
2555
2556
|
|
|
2556
2557
|
export declare const CPFInput: ForwardRefExoticComponent<Omit<InputProps, "value" | "onChange"> & {
|
|
2557
|
-
value
|
|
2558
|
-
onChange
|
|
2558
|
+
value?: string;
|
|
2559
|
+
onChange?: (value: string) => void;
|
|
2559
2560
|
} & RefAttributes<HTMLInputElement>>;
|
|
2560
2561
|
|
|
2561
2562
|
/**
|
|
@@ -8140,8 +8141,8 @@ export declare interface PermissionsFromTokenOptions {
|
|
|
8140
8141
|
export declare function persistQueryClientOffline(options: OfflineQueryPersistenceOptions): OfflineQueryPersistence;
|
|
8141
8142
|
|
|
8142
8143
|
export declare const PhoneInput: ForwardRefExoticComponent<Omit<InputProps, "value" | "onChange"> & {
|
|
8143
|
-
value
|
|
8144
|
-
onChange
|
|
8144
|
+
value?: string;
|
|
8145
|
+
onChange?: (value: string) => void;
|
|
8145
8146
|
} & RefAttributes<HTMLInputElement>>;
|
|
8146
8147
|
|
|
8147
8148
|
/**
|
|
@@ -8973,20 +8974,6 @@ export declare type ResolvedTheme = "light" | "dark";
|
|
|
8973
8974
|
*/
|
|
8974
8975
|
export declare function resolveLanguage(language: string | undefined): CodeLanguage;
|
|
8975
8976
|
|
|
8976
|
-
declare type Resolver<T> = (values: unknown, context: unknown, options: {
|
|
8977
|
-
criteriaMode?: "firstError" | "all";
|
|
8978
|
-
}) => Promise<ResolverOutput<T>>;
|
|
8979
|
-
|
|
8980
|
-
declare interface ResolverError {
|
|
8981
|
-
type: string;
|
|
8982
|
-
message: string;
|
|
8983
|
-
}
|
|
8984
|
-
|
|
8985
|
-
declare interface ResolverOutput<T> {
|
|
8986
|
-
values: T | object;
|
|
8987
|
-
errors: Record<string, ResolverError | object>;
|
|
8988
|
-
}
|
|
8989
|
-
|
|
8990
8977
|
/**
|
|
8991
8978
|
* Responsive value — either a single value applied at all breakpoints, or
|
|
8992
8979
|
* an object with `mobile` / `tablet` / `desktop` overrides. Apps can mix
|
|
@@ -14749,7 +14736,10 @@ export declare interface WizardStep {
|
|
|
14749
14736
|
*
|
|
14750
14737
|
* @param headers - Column headers written as the first row.
|
|
14751
14738
|
* @param rows - Data rows; each value is a string, a number, or `null` (empty).
|
|
14752
|
-
* @returns The `.xlsx` file contents
|
|
14739
|
+
* @returns The `.xlsx` file contents, backed by a plain `ArrayBuffer` so the
|
|
14740
|
+
* bytes go straight into `new Blob([...])` — the default `Uint8Array` is
|
|
14741
|
+
* `Uint8Array<ArrayBufferLike>`, which `BlobPart` rejects because it also
|
|
14742
|
+
* admits `SharedArrayBuffer`.
|
|
14753
14743
|
*
|
|
14754
14744
|
* @example
|
|
14755
14745
|
* const bytes = writeXlsx(
|
|
@@ -14760,16 +14750,25 @@ export declare interface WizardStep {
|
|
|
14760
14750
|
* type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
14761
14751
|
* });
|
|
14762
14752
|
*/
|
|
14763
|
-
export declare function writeXlsx(headers: readonly string[], rows: readonly (readonly (string | number | null)[])[]): Uint8Array
|
|
14753
|
+
export declare function writeXlsx(headers: readonly string[], rows: readonly (readonly (string | number | null)[])[]): Uint8Array<ArrayBuffer>;
|
|
14764
14754
|
|
|
14765
14755
|
/**
|
|
14766
14756
|
* Minimal `react-hook-form` resolver built on top of zod. Mirrors the shape
|
|
14767
14757
|
* produced by `@hookform/resolvers/zod` so it can be passed straight to
|
|
14768
14758
|
* `useForm({ resolver })`.
|
|
14769
14759
|
*
|
|
14760
|
+
* Typed with react-hook-form's own `Resolver`, not a local look-alike: a
|
|
14761
|
+
* structurally similar type of our own compiled fine here and was then rejected
|
|
14762
|
+
* at the `useForm({ resolver })` call site — the only place a resolver is ever
|
|
14763
|
+
* used — which is why `useZodForm` had to cast its way past it.
|
|
14764
|
+
*
|
|
14765
|
+
* Of the resolver options only `criteriaMode` is read; the rest of
|
|
14766
|
+
* `ResolverOptions` is accepted and ignored, because react-hook-form always
|
|
14767
|
+
* passes the whole object.
|
|
14768
|
+
*
|
|
14770
14769
|
* @example
|
|
14771
14770
|
* const form = useForm<LoginForm>({ resolver: zodResolver(loginSchema) });
|
|
14772
14771
|
*/
|
|
14773
|
-
export declare function zodResolver<TSchema extends z.ZodTypeAny>(schema: TSchema): Resolver<z.infer<TSchema
|
|
14772
|
+
export declare function zodResolver<TSchema extends z.ZodTypeAny>(schema: TSchema): Resolver<z.infer<TSchema> & FieldValues>;
|
|
14774
14773
|
|
|
14775
14774
|
export { }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.cjs","names":[],"sources":["../../src/theme/ThemeProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { ResolvedTheme, ThemeMode } from \"./types\";\n\nexport interface ThemeContextValue {\n /** Raw user preference (light / dark / system). */\n theme: ThemeMode;\n /** Effective theme actually applied to the DOM (light or dark). */\n resolvedTheme: ResolvedTheme;\n /** Update the preference. Persisted to localStorage when `storageKey` is set. */\n setTheme: (next: ThemeMode) => void;\n /** Convenience: flip light ↔ dark. When in `system` mode, switches to the opposite of the current resolved theme. */\n toggle: () => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nexport interface ThemeProviderProps {\n children: ReactNode;\n /** Initial preference when nothing is stored. Default: `\"system\"`. */\n defaultTheme?: ThemeMode;\n /** localStorage key used to persist the preference. Pass `null` to disable persistence. Default: `\"tempest-theme\"`. */\n storageKey?: string | null;\n /**\n * Element that receives the theme attribute(s). Defaults to\n * `document.documentElement`. Override when scoping the theme to a subtree.\n */\n target?: () => HTMLElement | null;\n /**\n * Attribute name(s) written on the target with the resolved theme\n * (`\"light\"` / `\"dark\"`). Default: `\"data-tempest-theme\"`.\n *\n * Pass an array to mirror the theme onto more than one attribute — handy\n * when the SDK components read `data-tempest-theme` but the host app's own\n * CSS keys off a different attribute (e.g. `[\"data-tempest-theme\",\n * \"data-theme\"]`). Avoids a separate sync effect in the consumer.\n */\n attribute?: string | string[];\n /**\n * When set, keeps `<meta name=\"theme-color\">` in sync with the resolved\n * theme — `content` becomes `themeColor.dark` in dark mode and\n * `themeColor.light` in light mode. The meta tag must already exist in the\n * document `<head>`. No-op when omitted.\n */\n themeColor?: { light: string; dark: string };\n}\n\nfunction resolve(mode: ThemeMode): ResolvedTheme {\n if (mode === \"dark\" || mode === \"light\") return mode;\n if (typeof window === \"undefined\") return \"light\";\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\n/**\n * Write the resolved theme onto every configured attribute and, when a\n * `themeColor` map is provided, sync the `<meta name=\"theme-color\">` tag.\n */\nfunction applyResolved(\n element: HTMLElement,\n resolved: ResolvedTheme,\n attribute: string | string[],\n themeColor?: { light: string; dark: string },\n): void {\n const attrs = Array.isArray(attribute) ? attribute : [attribute];\n for (const attr of attrs) element.setAttribute(attr, resolved);\n if (themeColor && typeof document !== \"undefined\") {\n const meta = document.querySelector<HTMLMetaElement>('meta[name=\"theme-color\"]');\n if (meta) meta.content = themeColor[resolved];\n }\n}\n\nfunction readStored(storageKey: string | null): ThemeMode | null {\n if (!storageKey || typeof window === \"undefined\") return null;\n try {\n const value = window.localStorage.getItem(storageKey);\n if (value === \"light\" || value === \"dark\" || value === \"system\") return value;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Wire dark/light theming. Writes a data attribute on a target element (the\n * `<html>` element by default) and exposes the current preference via\n * {@link useTheme}.\n *\n * Pair with `themeInitScript()` in the HTML head to prevent the flash of\n * incorrect theme on first paint.\n *\n * @tempest-limits empty-catch — the theme is applied to the DOM before it is\n * persisted, so a `localStorage` write refused by quota or private mode leaves the\n * user with the theme they just picked and only forfeits it on the next load.\n * Throwing from the setter would break the switch that already worked.\n */\nexport function ThemeProvider({\n children,\n defaultTheme = \"system\",\n storageKey = \"tempest-theme\",\n target,\n attribute = \"data-tempest-theme\",\n themeColor,\n}: ThemeProviderProps) {\n const [theme, setThemeState] = useState<ThemeMode>(\n () => readStored(storageKey) ?? defaultTheme,\n );\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => resolve(theme));\n\n const targetRef = useRef<typeof target>(target);\n useEffect(() => {\n targetRef.current = target;\n }, [target]);\n\n const attributeKey = Array.isArray(attribute) ? attribute.join(\",\") : attribute;\n const themeColorRef = useRef(themeColor);\n useEffect(() => {\n themeColorRef.current = themeColor;\n }, [themeColor]);\n\n useEffect(() => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next = resolve(theme);\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n // attributeKey is the stable string form of `attribute`.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n useEffect(() => {\n if (theme !== \"system\" || typeof window === \"undefined\") return;\n const list = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const handler = (): void => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next: ResolvedTheme = list.matches ? \"dark\" : \"light\";\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n };\n list.addEventListener(\"change\", handler);\n return () => list.removeEventListener(\"change\", handler);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n const setTheme = useCallback(\n (next: ThemeMode) => {\n setThemeState(next);\n if (storageKey && typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(storageKey, next);\n } catch {\n /* empty */\n }\n }\n },\n [storageKey],\n );\n\n const toggle = useCallback(() => {\n setTheme(resolvedTheme === \"dark\" ? \"light\" : \"dark\");\n }, [resolvedTheme, setTheme]);\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, resolvedTheme, setTheme, toggle }),\n [theme, resolvedTheme, setTheme, toggle],\n );\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\n/**\n * Read and mutate the current theme. Must be used inside a {@link ThemeProvider}.\n */\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) throw new Error(\"useTheme must be used inside a <ThemeProvider>\");\n return ctx;\n}\n"],"mappings":"sDAuBA,IAAM,GAAA,
|
|
1
|
+
{"version":3,"file":"ThemeProvider.cjs","names":[],"sources":["../../src/theme/ThemeProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { ResolvedTheme, ThemeMode } from \"./types\";\n\nexport interface ThemeContextValue {\n /** Raw user preference (light / dark / system). */\n theme: ThemeMode;\n /** Effective theme actually applied to the DOM (light or dark). */\n resolvedTheme: ResolvedTheme;\n /** Update the preference. Persisted to localStorage when `storageKey` is set. */\n setTheme: (next: ThemeMode) => void;\n /** Convenience: flip light ↔ dark. When in `system` mode, switches to the opposite of the current resolved theme. */\n toggle: () => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nexport interface ThemeProviderProps {\n children: ReactNode;\n /** Initial preference when nothing is stored. Default: `\"system\"`. */\n defaultTheme?: ThemeMode;\n /** localStorage key used to persist the preference. Pass `null` to disable persistence. Default: `\"tempest-theme\"`. */\n storageKey?: string | null;\n /**\n * Element that receives the theme attribute(s). Defaults to\n * `document.documentElement`. Override when scoping the theme to a subtree.\n */\n target?: () => HTMLElement | null;\n /**\n * Attribute name(s) written on the target with the resolved theme\n * (`\"light\"` / `\"dark\"`). Default: `\"data-tempest-theme\"`.\n *\n * Pass an array to mirror the theme onto more than one attribute — handy\n * when the SDK components read `data-tempest-theme` but the host app's own\n * CSS keys off a different attribute (e.g. `[\"data-tempest-theme\",\n * \"data-theme\"]`). Avoids a separate sync effect in the consumer.\n */\n attribute?: string | string[];\n /**\n * When set, keeps `<meta name=\"theme-color\">` in sync with the resolved\n * theme — `content` becomes `themeColor.dark` in dark mode and\n * `themeColor.light` in light mode. The meta tag must already exist in the\n * document `<head>`. No-op when omitted.\n */\n themeColor?: { light: string; dark: string };\n}\n\nfunction resolve(mode: ThemeMode): ResolvedTheme {\n if (mode === \"dark\" || mode === \"light\") return mode;\n if (typeof window === \"undefined\") return \"light\";\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\n/**\n * Write the resolved theme onto every configured attribute and, when a\n * `themeColor` map is provided, sync the `<meta name=\"theme-color\">` tag.\n */\nfunction applyResolved(\n element: HTMLElement,\n resolved: ResolvedTheme,\n attribute: string | string[],\n themeColor?: { light: string; dark: string },\n): void {\n const attrs = Array.isArray(attribute) ? attribute : [attribute];\n for (const attr of attrs) element.setAttribute(attr, resolved);\n if (themeColor && typeof document !== \"undefined\") {\n const meta = document.querySelector<HTMLMetaElement>('meta[name=\"theme-color\"]');\n if (meta) meta.content = themeColor[resolved];\n }\n}\n\nfunction readStored(storageKey: string | null): ThemeMode | null {\n if (!storageKey || typeof window === \"undefined\") return null;\n try {\n const value = window.localStorage.getItem(storageKey);\n if (value === \"light\" || value === \"dark\" || value === \"system\") return value;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Wire dark/light theming. Writes a data attribute on a target element (the\n * `<html>` element by default) and exposes the current preference via\n * {@link useTheme}.\n *\n * Pair with `themeInitScript()` in the HTML head to prevent the flash of\n * incorrect theme on first paint.\n *\n * @tempest-limits empty-catch — the theme is applied to the DOM before it is\n * persisted, so a `localStorage` write refused by quota or private mode leaves the\n * user with the theme they just picked and only forfeits it on the next load.\n * Throwing from the setter would break the switch that already worked.\n */\nexport function ThemeProvider({\n children,\n defaultTheme = \"system\",\n storageKey = \"tempest-theme\",\n target,\n attribute = \"data-tempest-theme\",\n themeColor,\n}: ThemeProviderProps) {\n const [theme, setThemeState] = useState<ThemeMode>(\n () => readStored(storageKey) ?? defaultTheme,\n );\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => resolve(theme));\n\n const targetRef = useRef<typeof target>(target);\n useEffect(() => {\n targetRef.current = target;\n }, [target]);\n\n const attributeKey = Array.isArray(attribute) ? attribute.join(\",\") : attribute;\n const themeColorRef = useRef(themeColor);\n useEffect(() => {\n themeColorRef.current = themeColor;\n }, [themeColor]);\n\n useEffect(() => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next = resolve(theme);\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n // attributeKey is the stable string form of `attribute`.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n useEffect(() => {\n if (theme !== \"system\" || typeof window === \"undefined\") return;\n const list = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const handler = (): void => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next: ResolvedTheme = list.matches ? \"dark\" : \"light\";\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n };\n list.addEventListener(\"change\", handler);\n return () => list.removeEventListener(\"change\", handler);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n const setTheme = useCallback(\n (next: ThemeMode) => {\n setThemeState(next);\n if (storageKey && typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(storageKey, next);\n } catch {\n /* empty */\n }\n }\n },\n [storageKey],\n );\n\n const toggle = useCallback(() => {\n setTheme(resolvedTheme === \"dark\" ? \"light\" : \"dark\");\n }, [resolvedTheme, setTheme]);\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, resolvedTheme, setTheme, toggle }),\n [theme, resolvedTheme, setTheme, toggle],\n );\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\n/**\n * Read and mutate the current theme. Must be used inside a {@link ThemeProvider}.\n */\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) throw new Error(\"useTheme must be used inside a <ThemeProvider>\");\n return ctx;\n}\n"],"mappings":"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"}
|
package/dist/theme/color.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=[50,100,200,300,400,500,600,700,800,900],t={50:.966,100:.923,200:.844,300:.743,400:.642,500:.563,600:.48,700:.391,800:.325,900:.251},n={50:.982,100:.963,200:.927,300:.872,400:.71,500:.544,600:.442,700:.369,800:.278,900:.21},r={50:.159,100:.205,200:.254,300:.314,400:.381,500:.5,600:.571,700:.762,800:.86,900:.964},i={50:.238,100:.288,200:.362,300:.452,400:.544,500:.632,600:.712,700:.788,800:.862,900:.928},a={50:.18,100:.34,200:.6,300:.82,400:.95,500:1,600:.98,700:.88,800:.74,900:.58};function o(e,t,n){return Math.min(n,Math.max(t,e))}function s(e){let t=e.trim().replace(/^#/,``),n=t.length===3||t.length===4?t.slice(0,3).split(``).map(e=>e+e).join(``):t.slice(0,6);if(!/^[0-9a-fA-F]{6}$/.test(n))throw Error(`Invalid hex color: "${e}"`);return`#${n.toLowerCase()}`}function c(e){let t=s(e).slice(1);return{r:parseInt(t.slice(0,2),16)/255,g:parseInt(t.slice(2,4),16)/255,b:parseInt(t.slice(4,6),16)/255}}function l(e,t,n){let r=e=>Math.round(o(e,0,1)*255).toString(16).padStart(2,`0`);return`#${r(e)}${r(t)}${r(n)}`}function u(e){return e<=.04045?e/12.92:((e+.055)/1.055)**2.4}function d(e){return e<=.0031308?e*12.92:1.055*e**(1/2.4)-.055}function f(e){let{r:t,g:n,b:r}=c(e),i=u(t),a=u(n),o=u(r),s=Math.cbrt(.4122214708*i+.5363325363*a+.0514459929*o),l=Math.cbrt(.2119034982*i+.6806995451*a+.1073969566*o),d=Math.cbrt(.0883024619*i+.2817188376*a+.6299787005*o),f=.2104542553*s+.793617785*l-.0040720468*d,p=1.9779984951*s-2.428592205*l+.4505937099*d,m=.0259040371*s+.7827717662*l-.808675766*d,h=Math.sqrt(p*p+m*m),g=h<1e-6?0:Math.atan2(m,p)*180/Math.PI;return{l:f,c:h,h:g<0?g+360:g}}function p({l:e,c:t,h:n}){let r=n*Math.PI/180,i=Math.cos(r)*t,a=Math.sin(r)*t,o=(e+.3963377774*i+.2158037573*a)**3,s=(e-.1055613458*i-.0638541728*a)**3,c=(e-.0894841775*i-1.291485548*a)**3;return{r:4.0767416621*o-3.3077115913*s+.2309699292*c,g:-1.2684380046*o+2.6097574011*s-.3413193965*c,b:-.0041960863*o-.7034186147*s+1.707614701*c}}function m({r:e,g:t,b:n}){return e>=-1e-4&&e<=1.0001&&t>=-1e-4&&t<=1.0001&&n>=-1e-4&&n<=1.0001}function h(e){let t=Math.max(0,e.c),n=p({...e,c:t});for(let r=0;r<24&&!m(n);r+=1)t*=.9,n=p({...e,c:t});return l(d(n.r),d(n.g),d(n.b))}function g(o,s=`light`,c={}){let l=f(o),u=c.neutral?s===`dark`?r:n:s===`dark`?i:t,d=c.anchor===!1?u:y(u,l.l,s),p={};for(let t of e)p[t]=h({l:d[t],c:l.c*a[t],h:l.h});return p}var _=.985,v=.12;function y(t,n,r){let i={},a=t[500],o=r===`dark`?t[900]:t[50],s=r===`dark`?t[50]:t[900],c=Math.min(_,Math.max(o,n+.05)),l=Math.max(v,Math.min(s,n-.05));for(let r of e){let e=t[r];
|
|
1
|
+
var e=[50,100,200,300,400,500,600,700,800,900],t={50:.966,100:.923,200:.844,300:.743,400:.642,500:.563,600:.48,700:.391,800:.325,900:.251},n={50:.982,100:.963,200:.927,300:.872,400:.71,500:.544,600:.442,700:.369,800:.278,900:.21},r={50:.159,100:.205,200:.254,300:.314,400:.381,500:.5,600:.571,700:.762,800:.86,900:.964},i={50:.238,100:.288,200:.362,300:.452,400:.544,500:.632,600:.712,700:.788,800:.862,900:.928},a={50:.18,100:.34,200:.6,300:.82,400:.95,500:1,600:.98,700:.88,800:.74,900:.58};function o(e,t,n){return Math.min(n,Math.max(t,e))}function s(e){let t=e.trim().replace(/^#/,``),n=t.length===3||t.length===4?t.slice(0,3).split(``).map(e=>e+e).join(``):t.slice(0,6);if(!/^[0-9a-fA-F]{6}$/.test(n))throw Error(`Invalid hex color: "${e}"`);return`#${n.toLowerCase()}`}function c(e){let t=s(e).slice(1);return{r:parseInt(t.slice(0,2),16)/255,g:parseInt(t.slice(2,4),16)/255,b:parseInt(t.slice(4,6),16)/255}}function l(e,t,n){let r=e=>Math.round(o(e,0,1)*255).toString(16).padStart(2,`0`);return`#${r(e)}${r(t)}${r(n)}`}function u(e){return e<=.04045?e/12.92:((e+.055)/1.055)**2.4}function d(e){return e<=.0031308?e*12.92:1.055*e**(1/2.4)-.055}function f(e){let{r:t,g:n,b:r}=c(e),i=u(t),a=u(n),o=u(r),s=Math.cbrt(.4122214708*i+.5363325363*a+.0514459929*o),l=Math.cbrt(.2119034982*i+.6806995451*a+.1073969566*o),d=Math.cbrt(.0883024619*i+.2817188376*a+.6299787005*o),f=.2104542553*s+.793617785*l-.0040720468*d,p=1.9779984951*s-2.428592205*l+.4505937099*d,m=.0259040371*s+.7827717662*l-.808675766*d,h=Math.sqrt(p*p+m*m),g=h<1e-6?0:Math.atan2(m,p)*180/Math.PI;return{l:f,c:h,h:g<0?g+360:g}}function p({l:e,c:t,h:n}){let r=n*Math.PI/180,i=Math.cos(r)*t,a=Math.sin(r)*t,o=(e+.3963377774*i+.2158037573*a)**3,s=(e-.1055613458*i-.0638541728*a)**3,c=(e-.0894841775*i-1.291485548*a)**3;return{r:4.0767416621*o-3.3077115913*s+.2309699292*c,g:-1.2684380046*o+2.6097574011*s-.3413193965*c,b:-.0041960863*o-.7034186147*s+1.707614701*c}}function m({r:e,g:t,b:n}){return e>=-1e-4&&e<=1.0001&&t>=-1e-4&&t<=1.0001&&n>=-1e-4&&n<=1.0001}function h(e){let t=Math.max(0,e.c),n=p({...e,c:t});for(let r=0;r<24&&!m(n);r+=1)t*=.9,n=p({...e,c:t});return l(d(n.r),d(n.g),d(n.b))}function g(o,s=`light`,c={}){let l=f(o),u=c.neutral?s===`dark`?r:n:s===`dark`?i:t,d=c.anchor===!1?u:y(u,l.l,s),p={};for(let t of e)p[t]=h({l:d[t],c:l.c*a[t],h:l.h});return p}var _=.985,v=.12;function y(t,n,r){let i={},a=t[500],o=r===`dark`?t[900]:t[50],s=r===`dark`?t[50]:t[900],c=Math.min(_,Math.max(o,n+.05)),l=Math.max(v,Math.min(s,n-.05));for(let r of e){let e=t[r];i[r]=e===a?n:e>a?n+(e-a)/(o-a)*(c-n):n-(a-e)/(a-s)*(n-l)}return i}function b(e){let{r:t,g:n,b:r}=c(e);return .2126*u(t)+.7152*u(n)+.0722*u(r)}function x(e,t){let n=b(e),r=b(t),i=Math.max(n,r),a=Math.min(n,r);return(i+.05)/(a+.05)}function S(e,t=`#ffffff`,n=`#101828`){return x(e,t)>=x(e,n)?t:n}function C(e,t){let{r:n,g:r,b:i}=c(e),a=e=>Math.round(o(e,0,1)*255);return`rgb(${a(n)} ${a(r)} ${a(i)} / ${o(t,0,1)})`}exports.contrastRatio=x,exports.createColorScale=g,exports.hexToOklch=f,exports.hexToRgb=c,exports.hexToRgbaString=C,exports.oklchToHex=h,exports.readableForeground=S,exports.relativeLuminance=b,exports.rgbToHex=l;
|
|
2
2
|
//# sourceMappingURL=color.cjs.map
|
package/dist/theme/color.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color.cjs","names":[],"sources":["../../src/theme/color.ts"],"sourcesContent":["/**\n * @tempest-limits file-lines — OKLCH ↔ sRGB with the gamut mapping in between: the\n * transfer function, the LMS matrices, the chroma search that finds the nearest in-\n * gamut colour and the contrast ratio used to check it. Matrices split across files\n * are matrices that get edited one half at a time.\n */\n/**\n * Color math behind {@link createTheme} — OKLab/OKLCH conversions, tint scale\n * generation and WCAG contrast picking.\n *\n * Everything here is pure and dependency-free: the SDK generates brand ramps at\n * runtime in the browser, so a color library would be a disproportionate cost\n * for ~150 lines of well-specified math. OKLCH is used instead of HSL because\n * HSL lightness is not perceptual — an HSL ramp of a yellow and of a blue at the\n * same `L` read as wildly different brightness, which is exactly what breaks a\n * generated palette.\n */\n\n/** A color in the OKLCH space: perceptual lightness, chroma and hue. */\nexport interface Oklch {\n /** Perceptual lightness, `0` (black) to `1` (white). */\n l: number;\n /** Chroma (colorfulness). `0` is gray; sRGB rarely exceeds `~0.37`. */\n c: number;\n /** Hue angle in degrees, `0`–`360`. */\n h: number;\n}\n\n/** The ten steps of a Tempest tint scale, lightest to darkest. */\nexport type ScaleStep = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;\n\n/** A generated tint scale, keyed by step. */\nexport type ColorScale = Record<ScaleStep, string>;\n\nconst SCALE_STEPS: ScaleStep[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];\n\n/**\n * Target lightness per step for a **brand** ramp in light mode.\n *\n * These are the measured OKLCH lightnesses of the hand-written `--tempest-primary-*`\n * scale in `colors.css`, so a generated brand ramp lands in the same visual range\n * as the built-in one instead of merely near it.\n */\nconst LIGHT_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.966,\n 100: 0.923,\n 200: 0.844,\n 300: 0.743,\n 400: 0.642,\n 500: 0.563,\n 600: 0.48,\n 700: 0.391,\n 800: 0.325,\n 900: 0.251,\n};\n\n/**\n * Target lightness per step for a **neutral** ramp in light mode — measured from\n * the hand-written `--tempest-gray-*` scale.\n *\n * Deliberately **wider** than the brand curve at both ends (`0.982` → `0.210` vs\n * `0.966` → `0.251`): a neutral carries surfaces that must read as near-white and\n * text that must read as near-black, and the pair `text-muted` on `surface-3` has\n * to clear AA. Reusing the brand curve for neutrals is what dropped that pair to\n * 3.5:1 and made the browser axe sweep fail on every generated theme.\n */\nconst NEUTRAL_LIGHT_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.982,\n 100: 0.963,\n 200: 0.927,\n 300: 0.872,\n 400: 0.71,\n 500: 0.544,\n 600: 0.442,\n 700: 0.369,\n 800: 0.278,\n 900: 0.21,\n};\n\n/**\n * Neutral ramp for dark mode, measured from the `[data-tempest-theme=\"dark\"]`\n * surface/text tokens: `50` is the page background and `900` is text-grade.\n */\nconst NEUTRAL_DARK_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.159,\n 100: 0.205,\n 200: 0.254,\n 300: 0.314,\n 400: 0.381,\n 500: 0.5,\n 600: 0.571,\n 700: 0.762,\n 800: 0.86,\n 900: 0.964,\n};\n\n/**\n * Target lightness for a dark-theme ramp — the ramp is **inverted**: `50` is the\n * darkest tint (a surface) and `900` the lightest (text-grade), matching the\n * `[data-tempest-theme=\"dark\"]` block in `colors.css`.\n */\nconst DARK_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.238,\n 100: 0.288,\n 200: 0.362,\n 300: 0.452,\n 400: 0.544,\n 500: 0.632,\n 600: 0.712,\n 700: 0.788,\n 800: 0.862,\n 900: 0.928,\n};\n\n/**\n * Chroma multiplier per step, relative to the input color's chroma.\n *\n * Peaks around `500`–`600` and falls off at both ends: near-white and near-black\n * tints hold very little chroma before they look muddy or leave the sRGB gamut.\n */\nconst CHROMA_CURVE: Record<ScaleStep, number> = {\n 50: 0.18,\n 100: 0.34,\n 200: 0.6,\n 300: 0.82,\n 400: 0.95,\n 500: 1,\n 600: 0.98,\n 700: 0.88,\n 800: 0.74,\n 900: 0.58,\n};\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\n/** Expand `#abc` to `#aabbcc` and normalize to a lowercase 6-digit hex. */\nfunction normalizeHex(hex: string): string {\n const raw = hex.trim().replace(/^#/, \"\");\n const expanded =\n raw.length === 3 || raw.length === 4\n ? raw\n .slice(0, 3)\n .split(\"\")\n .map((char) => char + char)\n .join(\"\")\n : raw.slice(0, 6);\n if (!/^[0-9a-fA-F]{6}$/.test(expanded)) {\n throw new Error(`Invalid hex color: \"${hex}\"`);\n }\n return `#${expanded.toLowerCase()}`;\n}\n\n/** Parse a hex color into sRGB channels in the `0`–`1` range. */\nexport function hexToRgb(hex: string): { r: number; g: number; b: number } {\n const normalized = normalizeHex(hex).slice(1);\n return {\n r: parseInt(normalized.slice(0, 2), 16) / 255,\n g: parseInt(normalized.slice(2, 4), 16) / 255,\n b: parseInt(normalized.slice(4, 6), 16) / 255,\n };\n}\n\n/** Serialize sRGB channels (`0`–`1`, clamped) back to a `#rrggbb` string. */\nexport function rgbToHex(r: number, g: number, b: number): string {\n const channel = (value: number): string =>\n Math.round(clamp(value, 0, 1) * 255)\n .toString(16)\n .padStart(2, \"0\");\n return `#${channel(r)}${channel(g)}${channel(b)}`;\n}\n\nfunction srgbToLinear(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\nfunction linearToSrgb(channel: number): number {\n return channel <= 0.0031308 ? channel * 12.92 : 1.055 * Math.pow(channel, 1 / 2.4) - 0.055;\n}\n\n/** Convert a hex color to OKLCH. */\nexport function hexToOklch(hex: string): Oklch {\n const { r, g, b } = hexToRgb(hex);\n const lr = srgbToLinear(r);\n const lg = srgbToLinear(g);\n const lb = srgbToLinear(b);\n\n const long = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);\n const medium = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);\n const short = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);\n\n const l = 0.2104542553 * long + 0.793617785 * medium - 0.0040720468 * short;\n const a = 1.9779984951 * long - 2.428592205 * medium + 0.4505937099 * short;\n const bAxis = 0.0259040371 * long + 0.7827717662 * medium - 0.808675766 * short;\n\n const c = Math.sqrt(a * a + bAxis * bAxis);\n const hue = c < 1e-6 ? 0 : (Math.atan2(bAxis, a) * 180) / Math.PI;\n\n return { l, c, h: hue < 0 ? hue + 360 : hue };\n}\n\n/** Convert OKLCH to linear-light sRGB, without gamut clamping. */\nfunction oklchToLinearRgb({ l, c, h }: Oklch): { r: number; g: number; b: number } {\n const hRad = (h * Math.PI) / 180;\n const a = Math.cos(hRad) * c;\n const bAxis = Math.sin(hRad) * c;\n\n const long = (l + 0.3963377774 * a + 0.2158037573 * bAxis) ** 3;\n const medium = (l - 0.1055613458 * a - 0.0638541728 * bAxis) ** 3;\n const short = (l - 0.0894841775 * a - 1.291485548 * bAxis) ** 3;\n\n return {\n r: 4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short,\n g: -1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short,\n b: -0.0041960863 * long - 0.7034186147 * medium + 1.707614701 * short,\n };\n}\n\nfunction inGamut({ r, g, b }: { r: number; g: number; b: number }): boolean {\n const epsilon = 1e-4;\n return (\n r >= -epsilon &&\n r <= 1 + epsilon &&\n g >= -epsilon &&\n g <= 1 + epsilon &&\n b >= -epsilon &&\n b <= 1 + epsilon\n );\n}\n\n/**\n * Convert OKLCH to a hex color, reducing chroma until the result fits sRGB.\n *\n * Lightness and hue are preserved: desaturating is far less noticeable than\n * shifting either of them, and a naive channel clamp would do both.\n */\nexport function oklchToHex(color: Oklch): string {\n let chroma = Math.max(0, color.c);\n let rgb = oklchToLinearRgb({ ...color, c: chroma });\n\n for (let i = 0; i < 24 && !inGamut(rgb); i += 1) {\n chroma *= 0.9;\n rgb = oklchToLinearRgb({ ...color, c: chroma });\n }\n\n return rgbToHex(linearToSrgb(rgb.r), linearToSrgb(rgb.g), linearToSrgb(rgb.b));\n}\n\n/** Options for {@link createColorScale}. */\nexport interface ColorScaleOptions {\n /**\n * Pin step `500` to the input color's exact lightness. Default `true`.\n *\n * Right for a **brand** color: the hex a designer hands over is the one the\n * buttons must be. Wrong for a **neutral**: nobody checks that `gray-500` is\n * exactly the input, while everybody notices that a surface stopped being\n * near-white — anchoring rescales both halves around the input and compresses\n * exactly the range a neutral needs wide. With `false`, the ramp keeps the\n * tuned lightness curve and the input only contributes hue and chroma (a warm\n * or cool neutral, as asked).\n */\n anchor?: boolean;\n /**\n * Use the neutral lightness curve instead of the brand one. Default `false`.\n *\n * The neutral curve is wider at both ends, which is what keeps\n * `text-muted`-on-`surface-3` above AA. Implies the ramp is meant for\n * surfaces, borders and text rather than for an action color.\n */\n neutral?: boolean;\n}\n\n/**\n * Build a ten-step tint scale from a single color.\n *\n * The input color's hue is kept throughout and its chroma sets the intensity of\n * the whole ramp, so a muted brand color yields a muted scale instead of being\n * \"corrected\" into something the brand never approved.\n *\n * @param hex - Any hex color (`#abc` or `#aabbcc`), the intended `500` step.\n * @param mode - `\"light\"` for a light→dark ramp, `\"dark\"` for the inverted ramp\n * used under `[data-tempest-theme=\"dark\"]`.\n * @param options - See {@link ColorScaleOptions}.\n */\nexport function createColorScale(\n hex: string,\n mode: \"light\" | \"dark\" = \"light\",\n options: ColorScaleOptions = {},\n): ColorScale {\n const base = hexToOklch(hex);\n const targets = options.neutral\n ? mode === \"dark\"\n ? NEUTRAL_DARK_LIGHTNESS\n : NEUTRAL_LIGHT_LIGHTNESS\n : mode === \"dark\"\n ? DARK_LIGHTNESS\n : LIGHT_LIGHTNESS;\n const lightness =\n options.anchor === false ? targets : anchorLightnessAt500(targets, base.l, mode);\n const scale = {} as ColorScale;\n\n for (const step of SCALE_STEPS) {\n scale[step] = oklchToHex({\n l: lightness[step],\n c: base.c * CHROMA_CURVE[step],\n h: base.h,\n });\n }\n\n return scale;\n}\n\n/** Widest lightness band a generated ramp may span, so both ends stay usable. */\nconst LIGHTNESS_CEILING = 0.985;\nconst LIGHTNESS_FLOOR = 0.12;\n\n/**\n * Re-anchor a lightness ramp so step `500` lands on the brand color exactly.\n *\n * Without this, `500` is forced onto the ramp's own target lightness: a brand\n * `#7c3aed` came back as `#9161fe` — same hue, same chroma, *re-lightened*. It\n * looks fine in isolation and is wrong anyway, because the one color a designer\n * hands over is the one the buttons must actually be.\n *\n * Each half of the ramp is scaled independently around the anchor, so the shape\n * of the original curve survives and the ramp stays monotonic even for a very\n * light brand (yellow) or a very dark one (navy) — those simply get a shorter\n * run on the crowded side.\n *\n * @param targets - The default per-step lightness for this scheme.\n * @param anchor - Lightness of the brand color, used verbatim at step `500`.\n * @param mode - `\"dark\"` inverts which side of the ramp is the light one.\n */\nfunction anchorLightnessAt500(\n targets: Record<ScaleStep, number>,\n anchor: number,\n mode: \"light\" | \"dark\",\n): Record<ScaleStep, number> {\n const anchored = {} as Record<ScaleStep, number>;\n const base = targets[500];\n const lightEnd = mode === \"dark\" ? targets[900] : targets[50];\n const darkEnd = mode === \"dark\" ? targets[50] : targets[900];\n\n const top = Math.min(LIGHTNESS_CEILING, Math.max(lightEnd, anchor + 0.05));\n const bottom = Math.max(LIGHTNESS_FLOOR, Math.min(darkEnd, anchor - 0.05));\n\n for (const step of SCALE_STEPS) {\n const target = targets[step];\n if (target === base) {\n anchored[step] = anchor;\n } else if (target > base) {\n const ratio = (target - base) / (lightEnd - base);\n anchored[step] = anchor + ratio * (top - anchor);\n } else {\n const ratio = (base - target) / (base - darkEnd);\n anchored[step] = anchor - ratio * (anchor - bottom);\n }\n }\n\n return anchored;\n}\n\n/** WCAG 2.x relative luminance of a hex color. */\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex);\n return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);\n}\n\n/** WCAG 2.x contrast ratio between two hex colors, from `1` to `21`. */\nexport function contrastRatio(a: string, b: string): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Pick the readable foreground for a background, by contrast ratio.\n *\n * Used for `--tempest-primary-foreground`: a generated brand color can land\n * anywhere on the lightness axis, and hardcoding white would silently produce\n * unreadable buttons for light brands (yellow, lime, cyan).\n */\nexport function readableForeground(\n background: string,\n light = \"#ffffff\",\n dark = \"#101828\",\n): string {\n return contrastRatio(background, light) >= contrastRatio(background, dark) ? light : dark;\n}\n\n/** Format a hex color as `rgb(r g b / alpha)`, for focus rings and overlays. */\nexport function hexToRgbaString(hex: string, alpha: number): string {\n const { r, g, b } = hexToRgb(hex);\n const channel = (value: number): number => Math.round(clamp(value, 0, 1) * 255);\n return `rgb(${channel(r)} ${channel(g)} ${channel(b)} / ${clamp(alpha, 0, 1)})`;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"color.cjs","names":[],"sources":["../../src/theme/color.ts"],"sourcesContent":["/**\n * @tempest-limits file-lines — OKLCH ↔ sRGB with the gamut mapping in between: the\n * transfer function, the LMS matrices, the chroma search that finds the nearest in-\n * gamut colour and the contrast ratio used to check it. Matrices split across files\n * are matrices that get edited one half at a time.\n */\n/**\n * Color math behind {@link createTheme} — OKLab/OKLCH conversions, tint scale\n * generation and WCAG contrast picking.\n *\n * Everything here is pure and dependency-free: the SDK generates brand ramps at\n * runtime in the browser, so a color library would be a disproportionate cost\n * for ~150 lines of well-specified math. OKLCH is used instead of HSL because\n * HSL lightness is not perceptual — an HSL ramp of a yellow and of a blue at the\n * same `L` read as wildly different brightness, which is exactly what breaks a\n * generated palette.\n */\n\n/** A color in the OKLCH space: perceptual lightness, chroma and hue. */\nexport interface Oklch {\n /** Perceptual lightness, `0` (black) to `1` (white). */\n l: number;\n /** Chroma (colorfulness). `0` is gray; sRGB rarely exceeds `~0.37`. */\n c: number;\n /** Hue angle in degrees, `0`–`360`. */\n h: number;\n}\n\n/** The ten steps of a Tempest tint scale, lightest to darkest. */\nexport type ScaleStep = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;\n\n/** A generated tint scale, keyed by step. */\nexport type ColorScale = Record<ScaleStep, string>;\n\nconst SCALE_STEPS: ScaleStep[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];\n\n/**\n * Target lightness per step for a **brand** ramp in light mode.\n *\n * These are the measured OKLCH lightnesses of the hand-written `--tempest-primary-*`\n * scale in `colors.css`, so a generated brand ramp lands in the same visual range\n * as the built-in one instead of merely near it.\n */\nconst LIGHT_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.966,\n 100: 0.923,\n 200: 0.844,\n 300: 0.743,\n 400: 0.642,\n 500: 0.563,\n 600: 0.48,\n 700: 0.391,\n 800: 0.325,\n 900: 0.251,\n};\n\n/**\n * Target lightness per step for a **neutral** ramp in light mode — measured from\n * the hand-written `--tempest-gray-*` scale.\n *\n * Deliberately **wider** than the brand curve at both ends (`0.982` → `0.210` vs\n * `0.966` → `0.251`): a neutral carries surfaces that must read as near-white and\n * text that must read as near-black, and the pair `text-muted` on `surface-3` has\n * to clear AA. Reusing the brand curve for neutrals is what dropped that pair to\n * 3.5:1 and made the browser axe sweep fail on every generated theme.\n */\nconst NEUTRAL_LIGHT_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.982,\n 100: 0.963,\n 200: 0.927,\n 300: 0.872,\n 400: 0.71,\n 500: 0.544,\n 600: 0.442,\n 700: 0.369,\n 800: 0.278,\n 900: 0.21,\n};\n\n/**\n * Neutral ramp for dark mode, measured from the `[data-tempest-theme=\"dark\"]`\n * surface/text tokens: `50` is the page background and `900` is text-grade.\n */\nconst NEUTRAL_DARK_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.159,\n 100: 0.205,\n 200: 0.254,\n 300: 0.314,\n 400: 0.381,\n 500: 0.5,\n 600: 0.571,\n 700: 0.762,\n 800: 0.86,\n 900: 0.964,\n};\n\n/**\n * Target lightness for a dark-theme ramp — the ramp is **inverted**: `50` is the\n * darkest tint (a surface) and `900` the lightest (text-grade), matching the\n * `[data-tempest-theme=\"dark\"]` block in `colors.css`.\n */\nconst DARK_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.238,\n 100: 0.288,\n 200: 0.362,\n 300: 0.452,\n 400: 0.544,\n 500: 0.632,\n 600: 0.712,\n 700: 0.788,\n 800: 0.862,\n 900: 0.928,\n};\n\n/**\n * Chroma multiplier per step, relative to the input color's chroma.\n *\n * Peaks around `500`–`600` and falls off at both ends: near-white and near-black\n * tints hold very little chroma before they look muddy or leave the sRGB gamut.\n */\nconst CHROMA_CURVE: Record<ScaleStep, number> = {\n 50: 0.18,\n 100: 0.34,\n 200: 0.6,\n 300: 0.82,\n 400: 0.95,\n 500: 1,\n 600: 0.98,\n 700: 0.88,\n 800: 0.74,\n 900: 0.58,\n};\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\n/** Expand `#abc` to `#aabbcc` and normalize to a lowercase 6-digit hex. */\nfunction normalizeHex(hex: string): string {\n const raw = hex.trim().replace(/^#/, \"\");\n const expanded =\n raw.length === 3 || raw.length === 4\n ? raw\n .slice(0, 3)\n .split(\"\")\n .map((char) => char + char)\n .join(\"\")\n : raw.slice(0, 6);\n if (!/^[0-9a-fA-F]{6}$/.test(expanded)) {\n throw new Error(`Invalid hex color: \"${hex}\"`);\n }\n return `#${expanded.toLowerCase()}`;\n}\n\n/** Parse a hex color into sRGB channels in the `0`–`1` range. */\nexport function hexToRgb(hex: string): { r: number; g: number; b: number } {\n const normalized = normalizeHex(hex).slice(1);\n return {\n r: parseInt(normalized.slice(0, 2), 16) / 255,\n g: parseInt(normalized.slice(2, 4), 16) / 255,\n b: parseInt(normalized.slice(4, 6), 16) / 255,\n };\n}\n\n/** Serialize sRGB channels (`0`–`1`, clamped) back to a `#rrggbb` string. */\nexport function rgbToHex(r: number, g: number, b: number): string {\n const channel = (value: number): string =>\n Math.round(clamp(value, 0, 1) * 255)\n .toString(16)\n .padStart(2, \"0\");\n return `#${channel(r)}${channel(g)}${channel(b)}`;\n}\n\nfunction srgbToLinear(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\nfunction linearToSrgb(channel: number): number {\n return channel <= 0.0031308 ? channel * 12.92 : 1.055 * Math.pow(channel, 1 / 2.4) - 0.055;\n}\n\n/** Convert a hex color to OKLCH. */\nexport function hexToOklch(hex: string): Oklch {\n const { r, g, b } = hexToRgb(hex);\n const lr = srgbToLinear(r);\n const lg = srgbToLinear(g);\n const lb = srgbToLinear(b);\n\n const long = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);\n const medium = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);\n const short = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);\n\n const l = 0.2104542553 * long + 0.793617785 * medium - 0.0040720468 * short;\n const a = 1.9779984951 * long - 2.428592205 * medium + 0.4505937099 * short;\n const bAxis = 0.0259040371 * long + 0.7827717662 * medium - 0.808675766 * short;\n\n const c = Math.sqrt(a * a + bAxis * bAxis);\n const hue = c < 1e-6 ? 0 : (Math.atan2(bAxis, a) * 180) / Math.PI;\n\n return { l, c, h: hue < 0 ? hue + 360 : hue };\n}\n\n/** Convert OKLCH to linear-light sRGB, without gamut clamping. */\nfunction oklchToLinearRgb({ l, c, h }: Oklch): { r: number; g: number; b: number } {\n const hRad = (h * Math.PI) / 180;\n const a = Math.cos(hRad) * c;\n const bAxis = Math.sin(hRad) * c;\n\n const long = (l + 0.3963377774 * a + 0.2158037573 * bAxis) ** 3;\n const medium = (l - 0.1055613458 * a - 0.0638541728 * bAxis) ** 3;\n const short = (l - 0.0894841775 * a - 1.291485548 * bAxis) ** 3;\n\n return {\n r: 4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short,\n g: -1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short,\n b: -0.0041960863 * long - 0.7034186147 * medium + 1.707614701 * short,\n };\n}\n\nfunction inGamut({ r, g, b }: { r: number; g: number; b: number }): boolean {\n const epsilon = 1e-4;\n return (\n r >= -epsilon &&\n r <= 1 + epsilon &&\n g >= -epsilon &&\n g <= 1 + epsilon &&\n b >= -epsilon &&\n b <= 1 + epsilon\n );\n}\n\n/**\n * Convert OKLCH to a hex color, reducing chroma until the result fits sRGB.\n *\n * Lightness and hue are preserved: desaturating is far less noticeable than\n * shifting either of them, and a naive channel clamp would do both.\n */\nexport function oklchToHex(color: Oklch): string {\n let chroma = Math.max(0, color.c);\n let rgb = oklchToLinearRgb({ ...color, c: chroma });\n\n for (let i = 0; i < 24 && !inGamut(rgb); i += 1) {\n chroma *= 0.9;\n rgb = oklchToLinearRgb({ ...color, c: chroma });\n }\n\n return rgbToHex(linearToSrgb(rgb.r), linearToSrgb(rgb.g), linearToSrgb(rgb.b));\n}\n\n/** Options for {@link createColorScale}. */\nexport interface ColorScaleOptions {\n /**\n * Pin step `500` to the input color's exact lightness. Default `true`.\n *\n * Right for a **brand** color: the hex a designer hands over is the one the\n * buttons must be. Wrong for a **neutral**: nobody checks that `gray-500` is\n * exactly the input, while everybody notices that a surface stopped being\n * near-white — anchoring rescales both halves around the input and compresses\n * exactly the range a neutral needs wide. With `false`, the ramp keeps the\n * tuned lightness curve and the input only contributes hue and chroma (a warm\n * or cool neutral, as asked).\n */\n anchor?: boolean;\n /**\n * Use the neutral lightness curve instead of the brand one. Default `false`.\n *\n * The neutral curve is wider at both ends, which is what keeps\n * `text-muted`-on-`surface-3` above AA. Implies the ramp is meant for\n * surfaces, borders and text rather than for an action color.\n */\n neutral?: boolean;\n}\n\n/**\n * Build a ten-step tint scale from a single color.\n *\n * The input color's hue is kept throughout and its chroma sets the intensity of\n * the whole ramp, so a muted brand color yields a muted scale instead of being\n * \"corrected\" into something the brand never approved.\n *\n * @param hex - Any hex color (`#abc` or `#aabbcc`), the intended `500` step.\n * @param mode - `\"light\"` for a light→dark ramp, `\"dark\"` for the inverted ramp\n * used under `[data-tempest-theme=\"dark\"]`.\n * @param options - See {@link ColorScaleOptions}.\n */\nexport function createColorScale(\n hex: string,\n mode: \"light\" | \"dark\" = \"light\",\n options: ColorScaleOptions = {},\n): ColorScale {\n const base = hexToOklch(hex);\n const targets = options.neutral\n ? mode === \"dark\"\n ? NEUTRAL_DARK_LIGHTNESS\n : NEUTRAL_LIGHT_LIGHTNESS\n : mode === \"dark\"\n ? DARK_LIGHTNESS\n : LIGHT_LIGHTNESS;\n const lightness =\n options.anchor === false ? targets : anchorLightnessAt500(targets, base.l, mode);\n const scale = {} as ColorScale;\n\n for (const step of SCALE_STEPS) {\n scale[step] = oklchToHex({\n l: lightness[step],\n c: base.c * CHROMA_CURVE[step],\n h: base.h,\n });\n }\n\n return scale;\n}\n\n/** Widest lightness band a generated ramp may span, so both ends stay usable. */\nconst LIGHTNESS_CEILING = 0.985;\nconst LIGHTNESS_FLOOR = 0.12;\n\n/**\n * Re-anchor a lightness ramp so step `500` lands on the brand color exactly.\n *\n * Without this, `500` is forced onto the ramp's own target lightness: a brand\n * `#7c3aed` came back as `#9161fe` — same hue, same chroma, *re-lightened*. It\n * looks fine in isolation and is wrong anyway, because the one color a designer\n * hands over is the one the buttons must actually be.\n *\n * Each half of the ramp is scaled independently around the anchor, so the shape\n * of the original curve survives and the ramp stays monotonic even for a very\n * light brand (yellow) or a very dark one (navy) — those simply get a shorter\n * run on the crowded side.\n *\n * @param targets - The default per-step lightness for this scheme.\n * @param anchor - Lightness of the brand color, used verbatim at step `500`.\n * @param mode - `\"dark\"` inverts which side of the ramp is the light one.\n */\nfunction anchorLightnessAt500(\n targets: Record<ScaleStep, number>,\n anchor: number,\n mode: \"light\" | \"dark\",\n): Record<ScaleStep, number> {\n const anchored = {} as Record<ScaleStep, number>;\n const base = targets[500];\n const lightEnd = mode === \"dark\" ? targets[900] : targets[50];\n const darkEnd = mode === \"dark\" ? targets[50] : targets[900];\n\n const top = Math.min(LIGHTNESS_CEILING, Math.max(lightEnd, anchor + 0.05));\n const bottom = Math.max(LIGHTNESS_FLOOR, Math.min(darkEnd, anchor - 0.05));\n\n for (const step of SCALE_STEPS) {\n const target = targets[step];\n if (target === base) {\n anchored[step] = anchor;\n } else if (target > base) {\n const ratio = (target - base) / (lightEnd - base);\n anchored[step] = anchor + ratio * (top - anchor);\n } else {\n const ratio = (base - target) / (base - darkEnd);\n anchored[step] = anchor - ratio * (anchor - bottom);\n }\n }\n\n return anchored;\n}\n\n/** WCAG 2.x relative luminance of a hex color. */\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex);\n return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);\n}\n\n/** WCAG 2.x contrast ratio between two hex colors, from `1` to `21`. */\nexport function contrastRatio(a: string, b: string): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Pick the readable foreground for a background, by contrast ratio.\n *\n * Used for `--tempest-primary-foreground`: a generated brand color can land\n * anywhere on the lightness axis, and hardcoding white would silently produce\n * unreadable buttons for light brands (yellow, lime, cyan).\n */\nexport function readableForeground(\n background: string,\n light = \"#ffffff\",\n dark = \"#101828\",\n): string {\n return contrastRatio(background, light) >= contrastRatio(background, dark) ? light : dark;\n}\n\n/** Format a hex color as `rgb(r g b / alpha)`, for focus rings and overlays. */\nexport function hexToRgbaString(hex: string, alpha: number): string {\n const { r, g, b } = hexToRgb(hex);\n const channel = (value: number): number => Math.round(clamp(value, 0, 1) * 255);\n return `rgb(${channel(r)} ${channel(g)} ${channel(b)} / ${clamp(alpha, 0, 1)})`;\n}\n"],"mappings":"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"}
|