tempest-react-sdk 0.78.0 → 0.79.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/README.md +6 -6
- package/bin/lib/openapi/generate.mjs +168 -64
- package/bin/lib/openapi/generate.pagination.test.mjs +3 -2
- package/bin/lib/openapi/generate.test.mjs +263 -1
- package/dist/access/Can.cjs.map +1 -1
- package/dist/access/Can.js.map +1 -1
- package/dist/access/access-control-context.cjs.map +1 -1
- package/dist/access/access-control-context.js.map +1 -1
- package/dist/access/create-role-access-control.cjs.map +1 -1
- package/dist/access/create-role-access-control.js.map +1 -1
- package/dist/access/permissions-from-token.cjs.map +1 -1
- package/dist/access/permissions-from-token.js.map +1 -1
- package/dist/access/use-can.cjs.map +1 -1
- package/dist/access/use-can.js.map +1 -1
- package/dist/app/AppProviders.cjs.map +1 -1
- package/dist/app/AppProviders.js.map +1 -1
- package/dist/audio/audio-bus.cjs.map +1 -1
- package/dist/audio/audio-bus.js.map +1 -1
- package/dist/audio/audio-output.cjs.map +1 -1
- package/dist/audio/audio-output.js.map +1 -1
- package/dist/audio/audio-player.cjs.map +1 -1
- package/dist/audio/audio-player.js.map +1 -1
- package/dist/audio/audio-recorder.cjs.map +1 -1
- package/dist/audio/audio-recorder.js.map +1 -1
- package/dist/audio/duration.cjs.map +1 -1
- package/dist/audio/duration.js.map +1 -1
- package/dist/audio/level-meter.cjs.map +1 -1
- package/dist/audio/level-meter.js.map +1 -1
- package/dist/audio/media-access.cjs.map +1 -1
- package/dist/audio/media-access.js.map +1 -1
- package/dist/audio/noise-floor.cjs.map +1 -1
- package/dist/audio/noise-floor.js.map +1 -1
- package/dist/audio/sfx-pool.cjs.map +1 -1
- package/dist/audio/sfx-pool.js.map +1 -1
- package/dist/audio/use-audio-bus.cjs.map +1 -1
- package/dist/audio/use-audio-bus.js.map +1 -1
- package/dist/audio/use-audio-recorder.cjs.map +1 -1
- package/dist/audio/use-audio-recorder.js.map +1 -1
- package/dist/audio/use-audio.cjs.map +1 -1
- package/dist/audio/use-audio.js.map +1 -1
- package/dist/audio/use-microphone.cjs +1 -1
- package/dist/audio/use-microphone.cjs.map +1 -1
- package/dist/audio/use-microphone.js +1 -5
- package/dist/audio/use-microphone.js.map +1 -1
- package/dist/audio/use-sfx-pool.cjs.map +1 -1
- package/dist/audio/use-sfx-pool.js.map +1 -1
- package/dist/audio/voice-activity.cjs.map +1 -1
- package/dist/audio/voice-activity.js.map +1 -1
- package/dist/audio/voice-chain-stages.cjs.map +1 -1
- package/dist/audio/voice-chain-stages.js.map +1 -1
- package/dist/audio/voice-chain.cjs.map +1 -1
- package/dist/audio/voice-chain.js.map +1 -1
- package/dist/audio/wav.cjs.map +1 -1
- package/dist/audio/wav.js.map +1 -1
- package/dist/auth/AuthGuard.cjs.map +1 -1
- package/dist/auth/AuthGuard.js.map +1 -1
- package/dist/auth/create-auth-store.cjs.map +1 -1
- package/dist/auth/create-auth-store.js.map +1 -1
- package/dist/auth/create-tempest-auth.cjs +1 -1
- package/dist/auth/create-tempest-auth.cjs.map +1 -1
- package/dist/auth/create-tempest-auth.js +1 -3
- package/dist/auth/create-tempest-auth.js.map +1 -1
- package/dist/auth/jwt.cjs.map +1 -1
- package/dist/auth/jwt.js.map +1 -1
- package/dist/auth/lazy-with-retry.cjs.map +1 -1
- package/dist/auth/lazy-with-retry.js.map +1 -1
- package/dist/auth/passkey.cjs.map +1 -1
- package/dist/auth/passkey.js.map +1 -1
- package/dist/auth/refresh-queue.cjs.map +1 -1
- package/dist/auth/refresh-queue.js.map +1 -1
- package/dist/auth/use-passkey.cjs +1 -1
- package/dist/auth/use-passkey.cjs.map +1 -1
- package/dist/auth/use-passkey.js +1 -1
- package/dist/auth/use-passkey.js.map +1 -1
- package/dist/br/BrazilMap.cjs.map +1 -1
- package/dist/br/BrazilMap.js.map +1 -1
- package/dist/br/BrazilMap.module.cjs.map +1 -1
- package/dist/br/BrazilMap.module.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.map +1 -1
- 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/MapLegend.module.cjs.map +1 -1
- package/dist/br/MapLegend.module.js.map +1 -1
- package/dist/br/MapMarkers.cjs.map +1 -1
- package/dist/br/MapMarkers.js.map +1 -1
- package/dist/br/MapTooltip.cjs.map +1 -1
- package/dist/br/MapTooltip.js.map +1 -1
- package/dist/br/MunicipalitySearch.cjs +1 -1
- package/dist/br/MunicipalitySearch.cjs.map +1 -1
- package/dist/br/MunicipalitySearch.js +2 -5
- package/dist/br/MunicipalitySearch.js.map +1 -1
- package/dist/br/MunicipalitySearch.module.cjs.map +1 -1
- package/dist/br/MunicipalitySearch.module.js.map +1 -1
- package/dist/br/PixQRCode.cjs.map +1 -1
- package/dist/br/PixQRCode.js.map +1 -1
- package/dist/br/PixQRCode.module.cjs.map +1 -1
- package/dist/br/PixQRCode.module.js.map +1 -1
- package/dist/br/boleto.cjs.map +1 -1
- package/dist/br/boleto.js.map +1 -1
- package/dist/br/br-geo.cjs.map +1 -1
- package/dist/br/br-geo.js.map +1 -1
- package/dist/br/data/br-centroids.cjs.map +1 -1
- package/dist/br/data/br-centroids.js.map +1 -1
- package/dist/br/data/br-locations.cjs.map +1 -1
- package/dist/br/data/br-locations.js.map +1 -1
- package/dist/br/data/br-uf-geo.cjs.map +1 -1
- package/dist/br/data/br-uf-geo.js.map +1 -1
- package/dist/br/data/mun/AC.cjs.map +1 -1
- package/dist/br/data/mun/AC.js.map +1 -1
- package/dist/br/data/mun/AL.cjs.map +1 -1
- package/dist/br/data/mun/AL.js.map +1 -1
- package/dist/br/data/mun/AM.cjs.map +1 -1
- package/dist/br/data/mun/AM.js.map +1 -1
- package/dist/br/data/mun/AP.cjs.map +1 -1
- package/dist/br/data/mun/AP.js.map +1 -1
- package/dist/br/data/mun/BA.cjs.map +1 -1
- package/dist/br/data/mun/BA.js.map +1 -1
- package/dist/br/data/mun/CE.cjs.map +1 -1
- package/dist/br/data/mun/CE.js.map +1 -1
- package/dist/br/data/mun/DF.cjs.map +1 -1
- package/dist/br/data/mun/DF.js.map +1 -1
- package/dist/br/data/mun/ES.cjs.map +1 -1
- package/dist/br/data/mun/ES.js.map +1 -1
- package/dist/br/data/mun/GO.cjs.map +1 -1
- package/dist/br/data/mun/GO.js.map +1 -1
- package/dist/br/data/mun/MA.cjs.map +1 -1
- package/dist/br/data/mun/MA.js.map +1 -1
- package/dist/br/data/mun/MG.cjs.map +1 -1
- package/dist/br/data/mun/MG.js.map +1 -1
- package/dist/br/data/mun/MS.cjs.map +1 -1
- package/dist/br/data/mun/MS.js.map +1 -1
- package/dist/br/data/mun/MT.cjs.map +1 -1
- package/dist/br/data/mun/MT.js.map +1 -1
- package/dist/br/data/mun/PA.cjs.map +1 -1
- package/dist/br/data/mun/PA.js.map +1 -1
- package/dist/br/data/mun/PB.cjs.map +1 -1
- package/dist/br/data/mun/PB.js.map +1 -1
- package/dist/br/data/mun/PE.cjs.map +1 -1
- package/dist/br/data/mun/PE.js.map +1 -1
- package/dist/br/data/mun/PI.cjs.map +1 -1
- package/dist/br/data/mun/PI.js.map +1 -1
- package/dist/br/data/mun/PR.cjs.map +1 -1
- package/dist/br/data/mun/PR.js.map +1 -1
- package/dist/br/data/mun/RJ.cjs.map +1 -1
- package/dist/br/data/mun/RJ.js.map +1 -1
- package/dist/br/data/mun/RN.cjs.map +1 -1
- package/dist/br/data/mun/RN.js.map +1 -1
- package/dist/br/data/mun/RO.cjs.map +1 -1
- package/dist/br/data/mun/RO.js.map +1 -1
- package/dist/br/data/mun/RR.cjs.map +1 -1
- package/dist/br/data/mun/RR.js.map +1 -1
- package/dist/br/data/mun/RS.cjs.map +1 -1
- package/dist/br/data/mun/RS.js.map +1 -1
- package/dist/br/data/mun/SC.cjs.map +1 -1
- package/dist/br/data/mun/SC.js.map +1 -1
- package/dist/br/data/mun/SE.cjs.map +1 -1
- package/dist/br/data/mun/SE.js.map +1 -1
- package/dist/br/data/mun/SP.cjs.map +1 -1
- package/dist/br/data/mun/SP.js.map +1 -1
- package/dist/br/data/mun/TO.cjs.map +1 -1
- package/dist/br/data/mun/TO.js.map +1 -1
- package/dist/br/geocode.cjs.map +1 -1
- package/dist/br/geocode.js.map +1 -1
- package/dist/br/holidays.cjs.map +1 -1
- package/dist/br/holidays.js.map +1 -1
- package/dist/br/locations.cjs.map +1 -1
- package/dist/br/locations.js.map +1 -1
- package/dist/br/nfe.cjs.map +1 -1
- package/dist/br/nfe.js.map +1 -1
- package/dist/br/pix.cjs +1 -1
- package/dist/br/pix.cjs.map +1 -1
- package/dist/br/pix.js +108 -74
- package/dist/br/pix.js.map +1 -1
- package/dist/br/regions.cjs.map +1 -1
- package/dist/br/regions.js.map +1 -1
- package/dist/br/scales.cjs.map +1 -1
- package/dist/br/scales.js.map +1 -1
- package/dist/br/state-geo.cjs.map +1 -1
- package/dist/br/state-geo.js.map +1 -1
- package/dist/br/svg-utils.cjs.map +1 -1
- package/dist/br/svg-utils.js.map +1 -1
- package/dist/br/use-map-hover.cjs.map +1 -1
- package/dist/br/use-map-hover.js.map +1 -1
- package/dist/br/use-map-zoom.cjs.map +1 -1
- package/dist/br/use-map-zoom.js.map +1 -1
- package/dist/br/whatsapp.cjs.map +1 -1
- package/dist/br/whatsapp.js.map +1 -1
- package/dist/br.cjs.map +1 -1
- package/dist/br.d.ts +6 -2
- package/dist/br.js.map +1 -1
- package/dist/capture/barcode.cjs.map +1 -1
- package/dist/capture/barcode.js.map +1 -1
- package/dist/capture/media-recorder.cjs.map +1 -1
- package/dist/capture/media-recorder.js.map +1 -1
- package/dist/capture/use-barcode-scanner.cjs.map +1 -1
- package/dist/capture/use-barcode-scanner.js.map +1 -1
- package/dist/capture/use-screen-capture.cjs +1 -1
- package/dist/capture/use-screen-capture.cjs.map +1 -1
- package/dist/capture/use-screen-capture.js +1 -7
- package/dist/capture/use-screen-capture.js.map +1 -1
- package/dist/capture/use-speech-recognition.cjs +1 -1
- package/dist/capture/use-speech-recognition.cjs.map +1 -1
- package/dist/capture/use-speech-recognition.js +35 -36
- package/dist/capture/use-speech-recognition.js.map +1 -1
- package/dist/capture/use-torch.cjs.map +1 -1
- package/dist/capture/use-torch.js.map +1 -1
- package/dist/capture/use-video-recorder.cjs.map +1 -1
- package/dist/capture/use-video-recorder.js.map +1 -1
- package/dist/capture/video-recorder.cjs.map +1 -1
- package/dist/capture/video-recorder.js.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/palette.cjs.map +1 -1
- package/dist/charts/palette.js.map +1 -1
- package/dist/charts/scales.cjs.map +1 -1
- package/dist/charts/scales.js.map +1 -1
- package/dist/charts/types.cjs.map +1 -1
- package/dist/charts/types.js.map +1 -1
- package/dist/charts/use-chart-colors.cjs.map +1 -1
- package/dist/charts/use-chart-colors.js.map +1 -1
- package/dist/components/AIChat/AIChat.cjs +1 -1
- package/dist/components/AIChat/AIChat.cjs.map +1 -1
- package/dist/components/AIChat/AIChat.js +1 -5
- package/dist/components/AIChat/AIChat.js.map +1 -1
- package/dist/components/AIChat/AIChat.module.cjs.map +1 -1
- package/dist/components/AIChat/AIChat.module.js.map +1 -1
- package/dist/components/AIChat/AIChatComposer.cjs +1 -1
- package/dist/components/AIChat/AIChatComposer.cjs.map +1 -1
- package/dist/components/AIChat/AIChatComposer.js +1 -7
- 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/AIChat/ai-chat-turns.cjs.map +1 -1
- package/dist/components/AIChat/ai-chat-turns.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/Accordion/Accordion.module.cjs.map +1 -1
- package/dist/components/Accordion/Accordion.module.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/Alert/Alert.module.cjs.map +1 -1
- package/dist/components/Alert/Alert.module.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/AppBar/AppBar.module.cjs.map +1 -1
- package/dist/components/AppBar/AppBar.module.js.map +1 -1
- package/dist/components/AppBar/use-sticky-body-warning.cjs +1 -1
- package/dist/components/AppBar/use-sticky-body-warning.cjs.map +1 -1
- package/dist/components/AppBar/use-sticky-body-warning.js +1 -1
- package/dist/components/AppBar/use-sticky-body-warning.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/AppShell/AppShell.module.cjs.map +1 -1
- package/dist/components/AppShell/AppShell.module.js.map +1 -1
- package/dist/components/AspectRatio/AspectRatio.cjs.map +1 -1
- package/dist/components/AspectRatio/AspectRatio.js.map +1 -1
- package/dist/components/AspectRatio/AspectRatio.module.cjs.map +1 -1
- package/dist/components/AspectRatio/AspectRatio.module.js.map +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.cjs +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.cjs.map +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.js +1 -7
- package/dist/components/AudioPlayer/AudioPlayer.js.map +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.module.cjs.map +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.module.js.map +1 -1
- package/dist/components/AudioRecorder/AudioRecorder.cjs +1 -1
- package/dist/components/AudioRecorder/AudioRecorder.cjs.map +1 -1
- package/dist/components/AudioRecorder/AudioRecorder.js +16 -21
- package/dist/components/AudioRecorder/AudioRecorder.js.map +1 -1
- package/dist/components/AudioRecorder/AudioRecorder.module.cjs.map +1 -1
- package/dist/components/AudioRecorder/AudioRecorder.module.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/Avatar/Avatar.module.cjs.map +1 -1
- package/dist/components/Avatar/Avatar.module.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/AvatarGroup/AvatarGroup.module.cjs.map +1 -1
- package/dist/components/AvatarGroup/AvatarGroup.module.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/Badge/Badge.module.cjs.map +1 -1
- package/dist/components/Badge/Badge.module.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/Banner/Banner.module.cjs.map +1 -1
- package/dist/components/Banner/Banner.module.js.map +1 -1
- package/dist/components/BarList/BarList.cjs.map +1 -1
- package/dist/components/BarList/BarList.js.map +1 -1
- package/dist/components/BarList/BarList.module.cjs.map +1 -1
- package/dist/components/BarList/BarList.module.js.map +1 -1
- package/dist/components/BarList/bar-list-model.cjs.map +1 -1
- package/dist/components/BarList/bar-list-model.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/BarcodeScanner/BarcodeScanner.module.cjs.map +1 -1
- package/dist/components/BarcodeScanner/BarcodeScanner.module.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/BottomNavigation/BottomNavigation.module.cjs.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigation.module.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/BottomSheet/BottomSheet.module.cjs.map +1 -1
- package/dist/components/BottomSheet/BottomSheet.module.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/Breadcrumbs/Breadcrumbs.module.cjs.map +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.module.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/Button/Button.module.cjs.map +1 -1
- package/dist/components/Button/Button.module.js.map +1 -1
- package/dist/components/Button/ButtonContent.cjs.map +1 -1
- package/dist/components/Button/ButtonContent.js.map +1 -1
- package/dist/components/Button/ButtonSlot.cjs.map +1 -1
- package/dist/components/Button/ButtonSlot.js.map +1 -1
- package/dist/components/Button/button-class.cjs.map +1 -1
- package/dist/components/Button/button-class.js.map +1 -1
- package/dist/components/Button/link-rel.cjs.map +1 -1
- package/dist/components/Button/link-rel.js.map +1 -1
- package/dist/components/Button/slot.cjs.map +1 -1
- package/dist/components/Button/slot.js.map +1 -1
- package/dist/components/Calendar/Calendar.cjs.map +1 -1
- package/dist/components/Calendar/Calendar.js.map +1 -1
- package/dist/components/Calendar/Calendar.module.cjs.map +1 -1
- package/dist/components/Calendar/Calendar.module.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/Card/Card.module.cjs.map +1 -1
- package/dist/components/Card/Card.module.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/Carousel/Carousel.module.cjs.map +1 -1
- package/dist/components/Carousel/Carousel.module.js.map +1 -1
- package/dist/components/Center/Center.cjs.map +1 -1
- package/dist/components/Center/Center.js.map +1 -1
- package/dist/components/Center/Center.module.cjs.map +1 -1
- package/dist/components/Center/Center.module.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/Chat.module.cjs.map +1 -1
- package/dist/components/Chat/Chat.module.js.map +1 -1
- package/dist/components/Chat/ChatActionsMenu.cjs.map +1 -1
- package/dist/components/Chat/ChatActionsMenu.js.map +1 -1
- package/dist/components/Chat/ChatAttachments.cjs.map +1 -1
- package/dist/components/Chat/ChatAttachments.js.map +1 -1
- package/dist/components/Chat/ChatBubble.cjs.map +1 -1
- package/dist/components/Chat/ChatBubble.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 -6
- package/dist/components/Chat/chat-groups.js.map +1 -1
- package/dist/components/Chat/chat-receipt.cjs.map +1 -1
- package/dist/components/Chat/chat-receipt.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/Checkbox/Checkbox.module.cjs.map +1 -1
- package/dist/components/Checkbox/Checkbox.module.js.map +1 -1
- package/dist/components/ChipInput/ChipInput.cjs +1 -1
- package/dist/components/ChipInput/ChipInput.cjs.map +1 -1
- package/dist/components/ChipInput/ChipInput.js +2 -12
- package/dist/components/ChipInput/ChipInput.js.map +1 -1
- package/dist/components/ChipInput/ChipInput.module.cjs.map +1 -1
- package/dist/components/ChipInput/ChipInput.module.js.map +1 -1
- package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
- package/dist/components/ClickOutside/ClickOutside.js.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.js.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.module.cjs.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.module.js.map +1 -1
- package/dist/components/CodeBlock/tokenize.cjs.map +1 -1
- package/dist/components/CodeBlock/tokenize.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/Collapsible/Collapsible.module.cjs.map +1 -1
- package/dist/components/Collapsible/Collapsible.module.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/Combobox/Combobox.module.cjs.map +1 -1
- package/dist/components/Combobox/Combobox.module.js.map +1 -1
- package/dist/components/Combobox/use-listbox-dismiss.cjs.map +1 -1
- package/dist/components/Combobox/use-listbox-dismiss.js.map +1 -1
- package/dist/components/Command/Command.cjs +1 -1
- package/dist/components/Command/Command.cjs.map +1 -1
- package/dist/components/Command/Command.js +4 -11
- package/dist/components/Command/Command.js.map +1 -1
- package/dist/components/Command/Command.module.cjs.map +1 -1
- package/dist/components/Command/Command.module.js.map +1 -1
- package/dist/components/ConditionalWrapper/ConditionalWrapper.cjs.map +1 -1
- package/dist/components/ConditionalWrapper/ConditionalWrapper.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 +1 -1
- package/dist/components/ContextMenu/ContextMenu.cjs.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.js +1 -1
- package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.module.cjs.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.module.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/CopyButton/CopyButton.module.cjs.map +1 -1
- package/dist/components/CopyButton/CopyButton.module.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/DataList/DataList.module.cjs.map +1 -1
- package/dist/components/DataList/DataList.module.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 +6 -14
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/DataTable/DataTable.module.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.module.js.map +1 -1
- package/dist/components/DataTable/EditableCell.cjs +1 -1
- package/dist/components/DataTable/EditableCell.cjs.map +1 -1
- package/dist/components/DataTable/EditableCell.js +1 -9
- package/dist/components/DataTable/EditableCell.js.map +1 -1
- package/dist/components/DataTable/LoadingRows.cjs.map +1 -1
- package/dist/components/DataTable/LoadingRows.js.map +1 -1
- package/dist/components/DataTable/edit-labels.cjs.map +1 -1
- package/dist/components/DataTable/edit-labels.js.map +1 -1
- package/dist/components/DataTable/use-dev-warnings.cjs.map +1 -1
- package/dist/components/DataTable/use-dev-warnings.js.map +1 -1
- package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
- package/dist/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.cjs.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.module.cjs.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.module.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/DescriptionList/DescriptionList.module.cjs.map +1 -1
- package/dist/components/DescriptionList/DescriptionList.module.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/Divider/Divider.module.cjs.map +1 -1
- package/dist/components/Divider/Divider.module.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/Drawer/Drawer.module.cjs.map +1 -1
- package/dist/components/Drawer/Drawer.module.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.cjs +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.js +2 -14
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.module.cjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.module.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/Dropzone/Dropzone.module.cjs.map +1 -1
- package/dist/components/Dropzone/Dropzone.module.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/EmptyState/EmptyState.module.cjs.map +1 -1
- package/dist/components/EmptyState/EmptyState.module.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/ErrorState/ErrorState.module.cjs.map +1 -1
- package/dist/components/ErrorState/ErrorState.module.js.map +1 -1
- package/dist/components/ErrorText/ErrorText.cjs.map +1 -1
- package/dist/components/ErrorText/ErrorText.js.map +1 -1
- package/dist/components/ErrorText/ErrorText.module.cjs.map +1 -1
- package/dist/components/ErrorText/ErrorText.module.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 +7 -17
- package/dist/components/FileUpload/FileUpload.js.map +1 -1
- package/dist/components/FileUpload/FileUpload.module.cjs.map +1 -1
- package/dist/components/FileUpload/FileUpload.module.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/FilterBar/FilterBar.module.cjs.map +1 -1
- package/dist/components/FilterBar/FilterBar.module.js.map +1 -1
- package/dist/components/FilterBar/filter-apply.cjs.map +1 -1
- package/dist/components/FilterBar/filter-apply.js.map +1 -1
- package/dist/components/FilterBar/filter-model.cjs.map +1 -1
- package/dist/components/FilterBar/filter-model.js.map +1 -1
- package/dist/components/FilterBar/filter-query.cjs +1 -1
- package/dist/components/FilterBar/filter-query.cjs.map +1 -1
- package/dist/components/FilterBar/filter-query.js +2 -5
- package/dist/components/FilterBar/filter-query.js.map +1 -1
- package/dist/components/FilterPanel/FilterPanel.cjs.map +1 -1
- package/dist/components/FilterPanel/FilterPanel.js.map +1 -1
- package/dist/components/FilterPanel/FilterPanel.module.cjs.map +1 -1
- package/dist/components/FilterPanel/FilterPanel.module.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/FloatingActionButton/FloatingActionButton.module.cjs.map +1 -1
- package/dist/components/FloatingActionButton/FloatingActionButton.module.js.map +1 -1
- package/dist/components/For/For.cjs.map +1 -1
- package/dist/components/For/For.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/Form/Form.module.cjs.map +1 -1
- package/dist/components/Form/Form.module.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/HoverCard/HoverCard.module.cjs.map +1 -1
- package/dist/components/HoverCard/HoverCard.module.js.map +1 -1
- package/dist/components/Image/Image.cjs.map +1 -1
- package/dist/components/Image/Image.js.map +1 -1
- package/dist/components/Image/Image.module.cjs.map +1 -1
- package/dist/components/Image/Image.module.js.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 +4 -8
- package/dist/components/ImageCropper/ImageCropper.js.map +1 -1
- package/dist/components/ImageCropper/ImageCropper.module.cjs.map +1 -1
- package/dist/components/ImageCropper/ImageCropper.module.js.map +1 -1
- package/dist/components/ImageCropper/crop-geometry.cjs.map +1 -1
- package/dist/components/ImageCropper/crop-geometry.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/Input/Input.module.cjs.map +1 -1
- package/dist/components/Input/Input.module.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/InstallBanner/InstallBanner.module.cjs.map +1 -1
- package/dist/components/InstallBanner/InstallBanner.module.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/InstallButton/InstallButton.module.cjs.map +1 -1
- package/dist/components/InstallButton/InstallButton.module.js.map +1 -1
- package/dist/components/InstallButton/install-hints.cjs.map +1 -1
- package/dist/components/InstallButton/install-hints.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/Kanban/Kanban.module.cjs.map +1 -1
- package/dist/components/Kanban/Kanban.module.js.map +1 -1
- package/dist/components/Kbd/Kbd.cjs.map +1 -1
- package/dist/components/Kbd/Kbd.js.map +1 -1
- package/dist/components/Kbd/Kbd.module.cjs.map +1 -1
- package/dist/components/Kbd/Kbd.module.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/Label/Label.module.cjs.map +1 -1
- package/dist/components/Label/Label.module.js.map +1 -1
- package/dist/components/Layout/Layout.cjs.map +1 -1
- package/dist/components/Layout/Layout.js.map +1 -1
- package/dist/components/Layout/Layout.module.cjs.map +1 -1
- package/dist/components/Layout/Layout.module.js.map +1 -1
- package/dist/components/Lightbox/Lightbox.cjs +1 -1
- package/dist/components/Lightbox/Lightbox.cjs.map +1 -1
- package/dist/components/Lightbox/Lightbox.js +1 -1
- package/dist/components/Lightbox/Lightbox.js.map +1 -1
- package/dist/components/Lightbox/Lightbox.module.cjs.map +1 -1
- package/dist/components/Lightbox/Lightbox.module.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/ListTile/ListTile.module.cjs.map +1 -1
- package/dist/components/ListTile/ListTile.module.js.map +1 -1
- package/dist/components/Markdown/Markdown.cjs.map +1 -1
- package/dist/components/Markdown/Markdown.js.map +1 -1
- package/dist/components/Markdown/Markdown.module.cjs.map +1 -1
- package/dist/components/Markdown/Markdown.module.js.map +1 -1
- package/dist/components/Markdown/markdown-parse.cjs +2 -2
- package/dist/components/Markdown/markdown-parse.cjs.map +1 -1
- package/dist/components/Markdown/markdown-parse.js +8 -27
- package/dist/components/Markdown/markdown-parse.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/Masonry/Masonry.module.cjs.map +1 -1
- package/dist/components/Masonry/Masonry.module.js.map +1 -1
- package/dist/components/Masonry/masonry-layout.cjs.map +1 -1
- package/dist/components/Masonry/masonry-layout.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/Menubar/Menubar.module.cjs.map +1 -1
- package/dist/components/Menubar/Menubar.module.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/Modal/Modal.module.cjs.map +1 -1
- package/dist/components/Modal/Modal.module.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/ModalsManager/ModalsManager.module.cjs.map +1 -1
- package/dist/components/ModalsManager/ModalsManager.module.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/MultiSelect/MultiSelect.module.cjs.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.module.js.map +1 -1
- package/dist/components/NProgress/NProgress.cjs.map +1 -1
- package/dist/components/NProgress/NProgress.js.map +1 -1
- package/dist/components/NProgress/NProgress.module.cjs.map +1 -1
- package/dist/components/NProgress/NProgress.module.js.map +1 -1
- package/dist/components/Navbar/Navbar.cjs.map +1 -1
- package/dist/components/Navbar/Navbar.js.map +1 -1
- package/dist/components/Navbar/Navbar.module.cjs.map +1 -1
- package/dist/components/Navbar/Navbar.module.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/NavigationMenu/NavigationMenu.module.cjs.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.module.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/NavigationRail/NavigationRail.module.cjs.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.module.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/NotificationCenter.module.cjs.map +1 -1
- package/dist/components/NotificationCenter/NotificationCenter.module.js.map +1 -1
- package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -1
- package/dist/components/NotificationCenter/use-notification-inbox.js.map +1 -1
- package/dist/components/OfflineIndicator/OfflineIndicator.cjs.map +1 -1
- package/dist/components/OfflineIndicator/OfflineIndicator.js.map +1 -1
- package/dist/components/OfflineIndicator/OfflineIndicator.module.cjs.map +1 -1
- package/dist/components/OfflineIndicator/OfflineIndicator.module.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/Page/Page.module.cjs.map +1 -1
- package/dist/components/Page/Page.module.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/Pagination/Pagination.module.cjs.map +1 -1
- package/dist/components/Pagination/Pagination.module.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/PasswordInput/PasswordInput.module.cjs.map +1 -1
- package/dist/components/PasswordInput/PasswordInput.module.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/PinInput/PinInput.module.cjs.map +1 -1
- package/dist/components/PinInput/PinInput.module.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/Popover/Popover.module.cjs.map +1 -1
- package/dist/components/Popover/Popover.module.js.map +1 -1
- package/dist/components/Portal/Portal.cjs.map +1 -1
- package/dist/components/Portal/Portal.js.map +1 -1
- package/dist/components/Portal/anchor-position.cjs.map +1 -1
- package/dist/components/Portal/anchor-position.js.map +1 -1
- package/dist/components/Portal/escape-layer.cjs.map +1 -1
- package/dist/components/Portal/escape-layer.js.map +1 -1
- package/dist/components/Portal/portal-host.cjs.map +1 -1
- package/dist/components/Portal/portal-host.js.map +1 -1
- package/dist/components/Portal/tab-bridge.cjs +1 -1
- package/dist/components/Portal/tab-bridge.cjs.map +1 -1
- package/dist/components/Portal/tab-bridge.js +1 -5
- package/dist/components/Portal/tab-bridge.js.map +1 -1
- package/dist/components/Progress/Progress.cjs.map +1 -1
- package/dist/components/Progress/Progress.js.map +1 -1
- package/dist/components/Progress/Progress.module.cjs.map +1 -1
- package/dist/components/Progress/Progress.module.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/QRCode/QRCode.module.cjs.map +1 -1
- package/dist/components/QRCode/QRCode.module.js.map +1 -1
- package/dist/components/QRCode/qr-encode.cjs +1 -1
- package/dist/components/QRCode/qr-encode.cjs.map +1 -1
- package/dist/components/QRCode/qr-encode.js +7 -13
- package/dist/components/QRCode/qr-encode.js.map +1 -1
- package/dist/components/QRCode/qr-tables.cjs.map +1 -1
- package/dist/components/QRCode/qr-tables.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/Radio/Radio.module.cjs.map +1 -1
- package/dist/components/Radio/Radio.module.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/RangeSlider/RangeSlider.module.cjs.map +1 -1
- package/dist/components/RangeSlider/RangeSlider.module.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/RatingStars/RatingStars.module.cjs.map +1 -1
- package/dist/components/RatingStars/RatingStars.module.js.map +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.cjs +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.js +10 -18
- package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.module.cjs.map +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.module.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 +1 -1
- package/dist/components/Resizable/Resizable.cjs.map +1 -1
- package/dist/components/Resizable/Resizable.js +1 -9
- package/dist/components/Resizable/Resizable.js.map +1 -1
- package/dist/components/Resizable/Resizable.module.cjs.map +1 -1
- package/dist/components/Resizable/Resizable.module.js.map +1 -1
- package/dist/components/Responsive/Responsive.cjs.map +1 -1
- package/dist/components/Responsive/Responsive.js.map +1 -1
- package/dist/components/Reveal/Reveal.cjs.map +1 -1
- package/dist/components/Reveal/Reveal.js.map +1 -1
- package/dist/components/Reveal/Reveal.module.cjs.map +1 -1
- package/dist/components/Reveal/Reveal.module.js.map +1 -1
- package/dist/components/Reveal/reveal-threshold.cjs.map +1 -1
- package/dist/components/Reveal/reveal-threshold.js.map +1 -1
- package/dist/components/SafeArea/SafeArea.cjs.map +1 -1
- package/dist/components/SafeArea/SafeArea.js.map +1 -1
- package/dist/components/SafeArea/SafeArea.module.cjs.map +1 -1
- package/dist/components/SafeArea/SafeArea.module.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/Scheduler/Scheduler.module.cjs.map +1 -1
- package/dist/components/Scheduler/Scheduler.module.js.map +1 -1
- package/dist/components/Scheduler/scheduler-layout.cjs.map +1 -1
- package/dist/components/Scheduler/scheduler-layout.js.map +1 -1
- package/dist/components/ScrollArea/ScrollArea.cjs.map +1 -1
- package/dist/components/ScrollArea/ScrollArea.js.map +1 -1
- package/dist/components/ScrollArea/ScrollArea.module.cjs.map +1 -1
- package/dist/components/ScrollArea/ScrollArea.module.js.map +1 -1
- package/dist/components/SearchBar/SearchBar.cjs.map +1 -1
- package/dist/components/SearchBar/SearchBar.js.map +1 -1
- package/dist/components/SearchBar/SearchBar.module.cjs.map +1 -1
- package/dist/components/SearchBar/SearchBar.module.js.map +1 -1
- package/dist/components/SectionHeader/SectionHeader.cjs.map +1 -1
- package/dist/components/SectionHeader/SectionHeader.js.map +1 -1
- package/dist/components/SectionHeader/SectionHeader.module.cjs.map +1 -1
- package/dist/components/SectionHeader/SectionHeader.module.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/SegmentedControl/SegmentedControl.module.cjs.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.module.js.map +1 -1
- package/dist/components/Select/CaretIcon.cjs.map +1 -1
- package/dist/components/Select/CaretIcon.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/Select/Select.module.cjs.map +1 -1
- package/dist/components/Select/Select.module.js.map +1 -1
- package/dist/components/Select/options.cjs.map +1 -1
- package/dist/components/Select/options.js.map +1 -1
- package/dist/components/Sidebar/Sidebar.cjs +1 -1
- package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
- package/dist/components/Sidebar/Sidebar.js +13 -23
- package/dist/components/Sidebar/Sidebar.js.map +1 -1
- package/dist/components/Sidebar/Sidebar.module.cjs.map +1 -1
- package/dist/components/Sidebar/Sidebar.module.js.map +1 -1
- package/dist/components/Sidebar/SidebarEntry.cjs.map +1 -1
- package/dist/components/Sidebar/SidebarEntry.js.map +1 -1
- package/dist/components/Sidebar/active-nav-key.cjs.map +1 -1
- package/dist/components/Sidebar/active-nav-key.js.map +1 -1
- package/dist/components/SignaturePad/SignaturePad.cjs +1 -1
- package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -1
- package/dist/components/SignaturePad/SignaturePad.js +1 -5
- package/dist/components/SignaturePad/SignaturePad.js.map +1 -1
- package/dist/components/SignaturePad/SignaturePad.module.cjs.map +1 -1
- package/dist/components/SignaturePad/SignaturePad.module.js.map +1 -1
- package/dist/components/Skeleton/Skeleton.cjs.map +1 -1
- package/dist/components/Skeleton/Skeleton.js.map +1 -1
- package/dist/components/Skeleton/Skeleton.module.cjs.map +1 -1
- package/dist/components/Skeleton/Skeleton.module.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/Slider/Slider.module.cjs.map +1 -1
- package/dist/components/Slider/Slider.module.js.map +1 -1
- package/dist/components/Spacer/Spacer.cjs.map +1 -1
- package/dist/components/Spacer/Spacer.js.map +1 -1
- package/dist/components/Spacer/Spacer.module.cjs.map +1 -1
- package/dist/components/Spacer/Spacer.module.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/Sparkline/Sparkline.module.cjs.map +1 -1
- package/dist/components/Sparkline/Sparkline.module.js.map +1 -1
- package/dist/components/Sparkline/sparkline-geometry.cjs.map +1 -1
- package/dist/components/Sparkline/sparkline-geometry.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/Spinner/Spinner.module.cjs.map +1 -1
- package/dist/components/Spinner/Spinner.module.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/Stat/Stat.module.cjs.map +1 -1
- package/dist/components/Stat/Stat.module.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/Stepper/Stepper.module.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.module.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/StepperInput/StepperInput.module.cjs.map +1 -1
- package/dist/components/StepperInput/StepperInput.module.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/Switch/Switch.module.cjs.map +1 -1
- package/dist/components/Switch/Switch.module.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/SyncStatusBadge/SyncStatusBadge.module.cjs.map +1 -1
- package/dist/components/SyncStatusBadge/SyncStatusBadge.module.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/Table/Table.module.cjs.map +1 -1
- package/dist/components/Table/Table.module.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/Tabs/Tabs.module.cjs.map +1 -1
- package/dist/components/Tabs/Tabs.module.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/Tag/Tag.module.cjs.map +1 -1
- package/dist/components/Tag/Tag.module.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/Textarea/Textarea.module.cjs.map +1 -1
- package/dist/components/Textarea/Textarea.module.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/TimePicker/TimePicker.module.cjs.map +1 -1
- package/dist/components/TimePicker/TimePicker.module.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/Timeline/Timeline.module.cjs.map +1 -1
- package/dist/components/Timeline/Timeline.module.js.map +1 -1
- package/dist/components/Toast/Toast.module.cjs.map +1 -1
- package/dist/components/Toast/Toast.module.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/Toggle/Toggle.js.map +1 -1
- package/dist/components/Toggle/Toggle.module.cjs.map +1 -1
- package/dist/components/Toggle/Toggle.module.js.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroup.cjs.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroup.module.cjs.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroup.module.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/Tooltip/Tooltip.module.cjs.map +1 -1
- package/dist/components/Tooltip/Tooltip.module.js.map +1 -1
- package/dist/components/Tour/Tour.cjs +1 -1
- package/dist/components/Tour/Tour.cjs.map +1 -1
- package/dist/components/Tour/Tour.js +1 -5
- package/dist/components/Tour/Tour.js.map +1 -1
- package/dist/components/Tour/Tour.module.cjs.map +1 -1
- package/dist/components/Tour/Tour.module.js.map +1 -1
- package/dist/components/Tour/tour-position.cjs.map +1 -1
- package/dist/components/Tour/tour-position.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/Transfer/Transfer.module.cjs.map +1 -1
- package/dist/components/Transfer/Transfer.module.js.map +1 -1
- package/dist/components/Transfer/transfer-state.cjs.map +1 -1
- package/dist/components/Transfer/transfer-state.js.map +1 -1
- package/dist/components/TreeView/TreeView.cjs.map +1 -1
- package/dist/components/TreeView/TreeView.js.map +1 -1
- package/dist/components/TreeView/TreeView.module.cjs.map +1 -1
- package/dist/components/TreeView/TreeView.module.js.map +1 -1
- package/dist/components/TruncateText/TruncateText.cjs.map +1 -1
- package/dist/components/TruncateText/TruncateText.js.map +1 -1
- package/dist/components/TruncateText/TruncateText.module.cjs.map +1 -1
- package/dist/components/TruncateText/TruncateText.module.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/UpdatePrompt/UpdatePrompt.module.cjs.map +1 -1
- package/dist/components/UpdatePrompt/UpdatePrompt.module.js.map +1 -1
- package/dist/components/VideoPlayer/VideoPlayer.cjs +1 -1
- package/dist/components/VideoPlayer/VideoPlayer.cjs.map +1 -1
- package/dist/components/VideoPlayer/VideoPlayer.js +1 -7
- package/dist/components/VideoPlayer/VideoPlayer.js.map +1 -1
- package/dist/components/VideoPlayer/VideoPlayer.module.cjs.map +1 -1
- package/dist/components/VideoPlayer/VideoPlayer.module.js.map +1 -1
- package/dist/components/VideoPlayer/playback-rates.cjs.map +1 -1
- package/dist/components/VideoPlayer/playback-rates.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/VirtualList/VirtualList.module.cjs.map +1 -1
- package/dist/components/VirtualList/VirtualList.module.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/VirtualTable/VirtualTable.module.cjs.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.module.js.map +1 -1
- package/dist/components/VisuallyHidden/VisuallyHidden.cjs.map +1 -1
- package/dist/components/VisuallyHidden/VisuallyHidden.js.map +1 -1
- package/dist/components/VisuallyHidden/VisuallyHidden.module.cjs.map +1 -1
- package/dist/components/VisuallyHidden/VisuallyHidden.module.js.map +1 -1
- package/dist/components/Wizard/Wizard.cjs +1 -1
- package/dist/components/Wizard/Wizard.cjs.map +1 -1
- package/dist/components/Wizard/Wizard.js +4 -11
- package/dist/components/Wizard/Wizard.js.map +1 -1
- package/dist/components/Wizard/Wizard.module.cjs.map +1 -1
- package/dist/components/Wizard/Wizard.module.js.map +1 -1
- package/dist/data/create-data-provider.cjs.map +1 -1
- package/dist/data/create-data-provider.js.map +1 -1
- package/dist/data/data-provider-context.cjs.map +1 -1
- package/dist/data/data-provider-context.js.map +1 -1
- package/dist/data/use-resource.cjs.map +1 -1
- package/dist/data/use-resource.js.map +1 -1
- package/dist/editor/RichTextEditor.cjs.map +1 -1
- package/dist/editor/RichTextEditor.js.map +1 -1
- package/dist/editor/RichTextEditor.module.cjs.map +1 -1
- package/dist/editor/RichTextEditor.module.js.map +1 -1
- package/dist/editor.js +1 -2
- package/dist/error-boundary/ErrorBoundary.cjs.map +1 -1
- package/dist/error-boundary/ErrorBoundary.js.map +1 -1
- package/dist/error-boundary/use-error-handler.cjs.map +1 -1
- package/dist/error-boundary/use-error-handler.js.map +1 -1
- package/dist/feature-flags/FeatureFlagsProvider.cjs.map +1 -1
- package/dist/feature-flags/FeatureFlagsProvider.js.map +1 -1
- package/dist/feature-flags/growthbook-adapter.cjs.map +1 -1
- package/dist/feature-flags/growthbook-adapter.js.map +1 -1
- package/dist/feature-flags/in-memory-adapter.cjs.map +1 -1
- package/dist/feature-flags/in-memory-adapter.js.map +1 -1
- package/dist/feature-flags/launchdarkly-adapter.cjs.map +1 -1
- package/dist/feature-flags/launchdarkly-adapter.js.map +1 -1
- package/dist/forms/FormField.cjs.map +1 -1
- package/dist/forms/FormField.js.map +1 -1
- package/dist/forms/br-validators.cjs.map +1 -1
- package/dist/forms/br-validators.js.map +1 -1
- package/dist/forms/masked-inputs.cjs.map +1 -1
- package/dist/forms/masked-inputs.js.map +1 -1
- package/dist/forms/use-viacep.cjs.map +1 -1
- package/dist/forms/use-viacep.js.map +1 -1
- package/dist/forms/use-zod-form.cjs.map +1 -1
- package/dist/forms/use-zod-form.js.map +1 -1
- package/dist/forms/validate-form.cjs.map +1 -1
- package/dist/forms/validate-form.js.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/TrajectoryMap.module.cjs.map +1 -1
- package/dist/geo/TrajectoryMap.module.js.map +1 -1
- package/dist/geo/bounds.cjs.map +1 -1
- package/dist/geo/bounds.js.map +1 -1
- package/dist/geo/create-position-tracker.cjs +1 -1
- package/dist/geo/create-position-tracker.cjs.map +1 -1
- package/dist/geo/create-position-tracker.js +1 -7
- package/dist/geo/create-position-tracker.js.map +1 -1
- package/dist/geo/distance.cjs.map +1 -1
- package/dist/geo/distance.js.map +1 -1
- package/dist/geo/estimate.cjs.map +1 -1
- package/dist/geo/estimate.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/projection.cjs.map +1 -1
- package/dist/geo/projection.js.map +1 -1
- package/dist/geo/routing.cjs.map +1 -1
- package/dist/geo/routing.js.map +1 -1
- package/dist/geo/types.cjs.map +1 -1
- package/dist/geo/types.js.map +1 -1
- package/dist/geo/use-position-tracker.cjs.map +1 -1
- package/dist/geo/use-position-tracker.js.map +1 -1
- package/dist/hooks/pwa-env.cjs.map +1 -1
- package/dist/hooks/pwa-env.js.map +1 -1
- package/dist/hooks/tab-sequence.cjs.map +1 -1
- package/dist/hooks/tab-sequence.js.map +1 -1
- package/dist/hooks/use-announce.cjs +1 -1
- package/dist/hooks/use-announce.cjs.map +1 -1
- package/dist/hooks/use-announce.js +2 -2
- package/dist/hooks/use-announce.js.map +1 -1
- package/dist/hooks/use-async.cjs.map +1 -1
- package/dist/hooks/use-async.js.map +1 -1
- package/dist/hooks/use-before-install-prompt.cjs.map +1 -1
- package/dist/hooks/use-before-install-prompt.js.map +1 -1
- package/dist/hooks/use-breakpoint.cjs.map +1 -1
- package/dist/hooks/use-breakpoint.js.map +1 -1
- package/dist/hooks/use-click-outside.cjs.map +1 -1
- package/dist/hooks/use-click-outside.js.map +1 -1
- package/dist/hooks/use-client-filter.cjs.map +1 -1
- package/dist/hooks/use-client-filter.js.map +1 -1
- package/dist/hooks/use-clipboard.cjs.map +1 -1
- package/dist/hooks/use-clipboard.js.map +1 -1
- package/dist/hooks/use-countdown.cjs.map +1 -1
- package/dist/hooks/use-countdown.js.map +1 -1
- package/dist/hooks/use-counter.cjs.map +1 -1
- package/dist/hooks/use-counter.js.map +1 -1
- package/dist/hooks/use-debounce.cjs.map +1 -1
- package/dist/hooks/use-debounce.js.map +1 -1
- package/dist/hooks/use-deep-memo.cjs.map +1 -1
- package/dist/hooks/use-deep-memo.js.map +1 -1
- package/dist/hooks/use-disclosure.cjs.map +1 -1
- package/dist/hooks/use-disclosure.js.map +1 -1
- package/dist/hooks/use-document-title.cjs.map +1 -1
- package/dist/hooks/use-document-title.js.map +1 -1
- package/dist/hooks/use-document-visibility.cjs.map +1 -1
- package/dist/hooks/use-document-visibility.js.map +1 -1
- package/dist/hooks/use-draft-filters.cjs.map +1 -1
- package/dist/hooks/use-draft-filters.js.map +1 -1
- package/dist/hooks/use-event-listener.cjs.map +1 -1
- package/dist/hooks/use-event-listener.js.map +1 -1
- package/dist/hooks/use-favicon.cjs.map +1 -1
- package/dist/hooks/use-favicon.js.map +1 -1
- package/dist/hooks/use-focus-trap.cjs.map +1 -1
- package/dist/hooks/use-focus-trap.js.map +1 -1
- package/dist/hooks/use-fullscreen-element.cjs.map +1 -1
- package/dist/hooks/use-fullscreen-element.js.map +1 -1
- package/dist/hooks/use-fullscreen.cjs +1 -1
- package/dist/hooks/use-fullscreen.cjs.map +1 -1
- package/dist/hooks/use-fullscreen.js +7 -23
- package/dist/hooks/use-fullscreen.js.map +1 -1
- package/dist/hooks/use-geolocation.cjs.map +1 -1
- package/dist/hooks/use-geolocation.js.map +1 -1
- package/dist/hooks/use-hover.cjs.map +1 -1
- package/dist/hooks/use-hover.js.map +1 -1
- package/dist/hooks/use-idle.cjs.map +1 -1
- package/dist/hooks/use-idle.js.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-intersection-observer.js.map +1 -1
- package/dist/hooks/use-interval.cjs.map +1 -1
- package/dist/hooks/use-interval.js.map +1 -1
- package/dist/hooks/use-is-first-render.cjs.map +1 -1
- package/dist/hooks/use-is-first-render.js.map +1 -1
- package/dist/hooks/use-keyboard-shortcut.cjs.map +1 -1
- package/dist/hooks/use-keyboard-shortcut.js.map +1 -1
- package/dist/hooks/use-latest-ref.cjs.map +1 -1
- package/dist/hooks/use-latest-ref.js.map +1 -1
- package/dist/hooks/use-list-state.cjs.map +1 -1
- package/dist/hooks/use-list-state.js.map +1 -1
- package/dist/hooks/use-local-storage.cjs +1 -1
- package/dist/hooks/use-local-storage.cjs.map +1 -1
- package/dist/hooks/use-local-storage.js +2 -5
- package/dist/hooks/use-local-storage.js.map +1 -1
- package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
- package/dist/hooks/use-long-press-handlers.js.map +1 -1
- package/dist/hooks/use-long-press.cjs.map +1 -1
- package/dist/hooks/use-long-press.js.map +1 -1
- package/dist/hooks/use-map.cjs.map +1 -1
- package/dist/hooks/use-map.js.map +1 -1
- package/dist/hooks/use-media-devices.cjs.map +1 -1
- package/dist/hooks/use-media-devices.js.map +1 -1
- package/dist/hooks/use-media-permission.cjs +1 -1
- package/dist/hooks/use-media-permission.cjs.map +1 -1
- package/dist/hooks/use-media-permission.js +2 -5
- package/dist/hooks/use-media-permission.js.map +1 -1
- package/dist/hooks/use-media-query.cjs.map +1 -1
- package/dist/hooks/use-media-query.js.map +1 -1
- package/dist/hooks/use-object-url.cjs.map +1 -1
- package/dist/hooks/use-object-url.js.map +1 -1
- package/dist/hooks/use-online.cjs.map +1 -1
- package/dist/hooks/use-online.js.map +1 -1
- package/dist/hooks/use-pagination.cjs.map +1 -1
- package/dist/hooks/use-pagination.js.map +1 -1
- package/dist/hooks/use-previous.cjs.map +1 -1
- package/dist/hooks/use-previous.js.map +1 -1
- package/dist/hooks/use-push-to-talk.cjs.map +1 -1
- package/dist/hooks/use-push-to-talk.js.map +1 -1
- package/dist/hooks/use-queue.cjs.map +1 -1
- package/dist/hooks/use-queue.js.map +1 -1
- package/dist/hooks/use-resize-observer.cjs.map +1 -1
- package/dist/hooks/use-resize-observer.js.map +1 -1
- package/dist/hooks/use-scroll-lock.cjs.map +1 -1
- package/dist/hooks/use-scroll-lock.js.map +1 -1
- package/dist/hooks/use-scroll-overflow.cjs.map +1 -1
- package/dist/hooks/use-scroll-overflow.js.map +1 -1
- package/dist/hooks/use-service-worker-update.cjs.map +1 -1
- package/dist/hooks/use-service-worker-update.js.map +1 -1
- package/dist/hooks/use-set.cjs.map +1 -1
- package/dist/hooks/use-set.js.map +1 -1
- package/dist/hooks/use-sortable.cjs.map +1 -1
- package/dist/hooks/use-sortable.js.map +1 -1
- package/dist/hooks/use-stable-callback.cjs.map +1 -1
- package/dist/hooks/use-stable-callback.js.map +1 -1
- package/dist/hooks/use-storage-estimate.cjs.map +1 -1
- package/dist/hooks/use-storage-estimate.js.map +1 -1
- package/dist/hooks/use-throttle.cjs.map +1 -1
- package/dist/hooks/use-throttle.js.map +1 -1
- package/dist/hooks/use-timeout.cjs.map +1 -1
- package/dist/hooks/use-timeout.js.map +1 -1
- package/dist/hooks/use-toggle.cjs.map +1 -1
- package/dist/hooks/use-toggle.js.map +1 -1
- package/dist/hooks/use-typewriter.cjs.map +1 -1
- package/dist/hooks/use-typewriter.js.map +1 -1
- package/dist/hooks/use-window-size.cjs.map +1 -1
- package/dist/hooks/use-window-size.js.map +1 -1
- package/dist/http/api-client.cjs.map +1 -1
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/build-url.cjs.map +1 -1
- package/dist/http/build-url.js.map +1 -1
- package/dist/http/credential-scope.cjs.map +1 -1
- package/dist/http/credential-scope.js.map +1 -1
- package/dist/http/csrf.cjs.map +1 -1
- package/dist/http/csrf.js.map +1 -1
- package/dist/http/decode-response.cjs.map +1 -1
- package/dist/http/decode-response.js.map +1 -1
- package/dist/http/describe-api-error.cjs.map +1 -1
- package/dist/http/describe-api-error.js.map +1 -1
- package/dist/http/errors.cjs.map +1 -1
- package/dist/http/errors.js.map +1 -1
- package/dist/http/idempotency.cjs.map +1 -1
- package/dist/http/idempotency.js.map +1 -1
- package/dist/http/parse-response.cjs.map +1 -1
- package/dist/http/parse-response.js.map +1 -1
- package/dist/http/resumable-upload.cjs.map +1 -1
- package/dist/http/resumable-upload.js.map +1 -1
- package/dist/http/retry.cjs.map +1 -1
- package/dist/http/retry.js.map +1 -1
- package/dist/http/timeout.cjs.map +1 -1
- package/dist/http/timeout.js.map +1 -1
- package/dist/http/upload-with-progress.cjs +1 -1
- package/dist/http/upload-with-progress.cjs.map +1 -1
- package/dist/http/upload-with-progress.js +2 -7
- package/dist/http/upload-with-progress.js.map +1 -1
- package/dist/http/use-describe-api-error.cjs.map +1 -1
- package/dist/http/use-describe-api-error.js.map +1 -1
- package/dist/http/use-poll.cjs +1 -1
- package/dist/http/use-poll.cjs.map +1 -1
- package/dist/http/use-poll.js +2 -5
- package/dist/http/use-poll.js.map +1 -1
- package/dist/i18n/I18nProvider.cjs.map +1 -1
- package/dist/i18n/I18nProvider.js.map +1 -1
- package/dist/i18n/create-i18n.cjs.map +1 -1
- package/dist/i18n/create-i18n.js.map +1 -1
- package/dist/icons/Icon.cjs.map +1 -1
- package/dist/icons/Icon.js.map +1 -1
- package/dist/icons/IconPicker.cjs.map +1 -1
- package/dist/icons/IconPicker.js.map +1 -1
- package/dist/icons/IconPicker.module.cjs.map +1 -1
- package/dist/icons/IconPicker.module.js.map +1 -1
- package/dist/icons/IconProvider.cjs.map +1 -1
- package/dist/icons/IconProvider.js.map +1 -1
- package/dist/icons/alias.cjs.map +1 -1
- package/dist/icons/alias.js.map +1 -1
- package/dist/icons/generated/aliases.cjs.map +1 -1
- package/dist/icons/generated/aliases.js.map +1 -1
- package/dist/icons/generated/icon-names.cjs.map +1 -1
- package/dist/icons/generated/icon-names.js.map +1 -1
- package/dist/icons/generated/loaders.cjs.map +1 -1
- package/dist/icons/generated/loaders.js.map +1 -1
- package/dist/icons/generated/shard-00.cjs.map +1 -1
- package/dist/icons/generated/shard-00.js.map +1 -1
- package/dist/icons/generated/shard-01.cjs.map +1 -1
- package/dist/icons/generated/shard-01.js.map +1 -1
- package/dist/icons/generated/shard-02.cjs.map +1 -1
- package/dist/icons/generated/shard-02.js.map +1 -1
- package/dist/icons/generated/shard-03.cjs.map +1 -1
- package/dist/icons/generated/shard-03.js.map +1 -1
- package/dist/icons/generated/shard-04.cjs.map +1 -1
- package/dist/icons/generated/shard-04.js.map +1 -1
- package/dist/icons/generated/shard-05.cjs.map +1 -1
- package/dist/icons/generated/shard-05.js.map +1 -1
- package/dist/icons/generated/shard-06.cjs.map +1 -1
- package/dist/icons/generated/shard-06.js.map +1 -1
- package/dist/icons/generated/shard-07.cjs.map +1 -1
- package/dist/icons/generated/shard-07.js.map +1 -1
- package/dist/icons/generated/shard-08.cjs.map +1 -1
- package/dist/icons/generated/shard-08.js.map +1 -1
- package/dist/icons/generated/shard-09.cjs.map +1 -1
- package/dist/icons/generated/shard-09.js.map +1 -1
- package/dist/icons/generated/shard-10.cjs.map +1 -1
- package/dist/icons/generated/shard-10.js.map +1 -1
- package/dist/icons/generated/shard-11.cjs.map +1 -1
- package/dist/icons/generated/shard-11.js.map +1 -1
- package/dist/icons/generated/shard-12.cjs.map +1 -1
- package/dist/icons/generated/shard-12.js.map +1 -1
- package/dist/icons/generated/shard-13.cjs.map +1 -1
- package/dist/icons/generated/shard-13.js.map +1 -1
- package/dist/icons/generated/shard-14.cjs.map +1 -1
- package/dist/icons/generated/shard-14.js.map +1 -1
- package/dist/icons/generated/shard-15.cjs.map +1 -1
- package/dist/icons/generated/shard-15.js.map +1 -1
- package/dist/icons/generated/shard-16.cjs.map +1 -1
- package/dist/icons/generated/shard-16.js.map +1 -1
- package/dist/icons/generated/shard-17.cjs.map +1 -1
- package/dist/icons/generated/shard-17.js.map +1 -1
- package/dist/icons/generated/shard-18.cjs.map +1 -1
- package/dist/icons/generated/shard-18.js.map +1 -1
- package/dist/icons/generated/shard-19.cjs.map +1 -1
- package/dist/icons/generated/shard-19.js.map +1 -1
- package/dist/icons/generated/shard-20.cjs.map +1 -1
- package/dist/icons/generated/shard-20.js.map +1 -1
- package/dist/icons/generated/shard-21.cjs.map +1 -1
- package/dist/icons/generated/shard-21.js.map +1 -1
- package/dist/icons/generated/shard-22.cjs.map +1 -1
- package/dist/icons/generated/shard-22.js.map +1 -1
- package/dist/icons/generated/shard-23.cjs.map +1 -1
- package/dist/icons/generated/shard-23.js.map +1 -1
- package/dist/icons/generated/shard-24.cjs.map +1 -1
- package/dist/icons/generated/shard-24.js.map +1 -1
- package/dist/icons/generated/shard-25.cjs.map +1 -1
- package/dist/icons/generated/shard-25.js.map +1 -1
- package/dist/icons/generated/shard-26.cjs.map +1 -1
- package/dist/icons/generated/shard-26.js.map +1 -1
- package/dist/icons/generated/shard-27.cjs.map +1 -1
- package/dist/icons/generated/shard-27.js.map +1 -1
- package/dist/icons/generated/shard-28.cjs.map +1 -1
- package/dist/icons/generated/shard-28.js.map +1 -1
- package/dist/icons/generated/shard-29.cjs.map +1 -1
- package/dist/icons/generated/shard-29.js.map +1 -1
- package/dist/icons/generated/shard-30.cjs.map +1 -1
- package/dist/icons/generated/shard-30.js.map +1 -1
- package/dist/icons/generated/shard-31.cjs.map +1 -1
- package/dist/icons/generated/shard-31.js.map +1 -1
- package/dist/icons/generated/shard-32.cjs.map +1 -1
- package/dist/icons/generated/shard-32.js.map +1 -1
- package/dist/icons/generated/shard-33.cjs.map +1 -1
- package/dist/icons/generated/shard-33.js.map +1 -1
- package/dist/icons/generated/shard-34.cjs.map +1 -1
- package/dist/icons/generated/shard-34.js.map +1 -1
- package/dist/icons/generated/shard-35.cjs.map +1 -1
- package/dist/icons/generated/shard-35.js.map +1 -1
- package/dist/icons/generated/shard-36.cjs.map +1 -1
- package/dist/icons/generated/shard-36.js.map +1 -1
- package/dist/icons/generated/shard-37.cjs.map +1 -1
- package/dist/icons/generated/shard-37.js.map +1 -1
- package/dist/icons/generated/shard-38.cjs.map +1 -1
- package/dist/icons/generated/shard-38.js.map +1 -1
- package/dist/icons/generated/shard-39.cjs.map +1 -1
- package/dist/icons/generated/shard-39.js.map +1 -1
- package/dist/icons/generated/shard-40.cjs.map +1 -1
- package/dist/icons/generated/shard-40.js.map +1 -1
- package/dist/icons/generated/shard-41.cjs.map +1 -1
- package/dist/icons/generated/shard-41.js.map +1 -1
- package/dist/icons/generated/shard-42.cjs.map +1 -1
- package/dist/icons/generated/shard-42.js.map +1 -1
- package/dist/icons/generated/shard-43.cjs.map +1 -1
- package/dist/icons/generated/shard-43.js.map +1 -1
- package/dist/icons/generated/shard-44.cjs.map +1 -1
- package/dist/icons/generated/shard-44.js.map +1 -1
- package/dist/icons/generated/shard-45.cjs.map +1 -1
- package/dist/icons/generated/shard-45.js.map +1 -1
- package/dist/icons/icon-context.cjs.map +1 -1
- package/dist/icons/icon-context.js.map +1 -1
- package/dist/icons/is-icon-name.cjs.map +1 -1
- package/dist/icons/is-icon-name.js.map +1 -1
- package/dist/icons/material-symbols.cjs.map +1 -1
- package/dist/icons/material-symbols.js.map +1 -1
- package/dist/icons/normalize-icon-name.cjs.map +1 -1
- package/dist/icons/normalize-icon-name.js.map +1 -1
- package/dist/icons/shard-cache.cjs +1 -1
- package/dist/icons/shard-cache.cjs.map +1 -1
- package/dist/icons/shard-cache.js +2 -5
- package/dist/icons/shard-cache.js.map +1 -1
- package/dist/icons/use-icon.cjs.map +1 -1
- package/dist/icons/use-icon.js.map +1 -1
- package/dist/icons/validate-icon-name.cjs.map +1 -1
- package/dist/icons/validate-icon-name.js.map +1 -1
- package/dist/icons-virtual.cjs.map +1 -1
- package/dist/icons-virtual.js.map +1 -1
- package/dist/imaging/canvas.cjs.map +1 -1
- package/dist/imaging/canvas.js.map +1 -1
- package/dist/imaging/compress.cjs.map +1 -1
- package/dist/imaging/compress.js.map +1 -1
- package/dist/imaging/decode.cjs.map +1 -1
- package/dist/imaging/decode.js.map +1 -1
- package/dist/imaging/encode.cjs.map +1 -1
- package/dist/imaging/encode.js.map +1 -1
- package/dist/imaging/exceptions.cjs.map +1 -1
- package/dist/imaging/exceptions.js.map +1 -1
- package/dist/imaging/frame.cjs.map +1 -1
- package/dist/imaging/frame.js.map +1 -1
- package/dist/imaging/thumbnails.cjs.map +1 -1
- package/dist/imaging/thumbnails.js.map +1 -1
- package/dist/imaging/transform.cjs.map +1 -1
- package/dist/imaging/transform.js.map +1 -1
- package/dist/imaging/use-image-processing.cjs.map +1 -1
- package/dist/imaging/use-image-processing.js.map +1 -1
- package/dist/logger/logger.cjs.map +1 -1
- package/dist/logger/logger.js.map +1 -1
- package/dist/neural.cjs.map +1 -1
- package/dist/neural.js.map +1 -1
- package/dist/oauth/GoogleSignIn.cjs +1 -1
- package/dist/oauth/GoogleSignIn.cjs.map +1 -1
- package/dist/oauth/GoogleSignIn.js +6 -9
- package/dist/oauth/GoogleSignIn.js.map +1 -1
- package/dist/oauth/callback.cjs.map +1 -1
- package/dist/oauth/callback.js.map +1 -1
- package/dist/oauth/use-oauth-callback.cjs.map +1 -1
- package/dist/oauth/use-oauth-callback.js.map +1 -1
- package/dist/offline/conflict.cjs.map +1 -1
- package/dist/offline/conflict.js.map +1 -1
- package/dist/offline/create-offline-database.cjs.map +1 -1
- package/dist/offline/create-offline-database.js.map +1 -1
- package/dist/offline/create-offline-store.cjs +1 -1
- package/dist/offline/create-offline-store.cjs.map +1 -1
- package/dist/offline/create-offline-store.js +1 -5
- package/dist/offline/create-offline-store.js.map +1 -1
- package/dist/offline/create-offline-sync.cjs +1 -1
- package/dist/offline/create-offline-sync.cjs.map +1 -1
- package/dist/offline/create-offline-sync.js +9 -14
- package/dist/offline/create-offline-sync.js.map +1 -1
- package/dist/offline/use-offline-sync.cjs.map +1 -1
- package/dist/offline/use-offline-sync.js.map +1 -1
- package/dist/perf/cache-size.cjs.map +1 -1
- package/dist/perf/cache-size.js.map +1 -1
- package/dist/perf/device.cjs.map +1 -1
- package/dist/perf/device.js.map +1 -1
- package/dist/perf/format.cjs.map +1 -1
- package/dist/perf/format.js.map +1 -1
- package/dist/perf/profiler.cjs.map +1 -1
- package/dist/perf/profiler.js.map +1 -1
- package/dist/push/use-push-subscription.cjs.map +1 -1
- package/dist/push/use-push-subscription.js.map +1 -1
- package/dist/push/utils.cjs.map +1 -1
- package/dist/push/utils.js.map +1 -1
- package/dist/push/web-push-client.cjs.map +1 -1
- package/dist/push/web-push-client.js.map +1 -1
- package/dist/query/QueryProvider.cjs.map +1 -1
- package/dist/query/QueryProvider.js.map +1 -1
- package/dist/query/constants.cjs.map +1 -1
- package/dist/query/constants.js.map +1 -1
- package/dist/query/create-query-keys.cjs.map +1 -1
- package/dist/query/create-query-keys.js.map +1 -1
- package/dist/query/foreign-client-warning.cjs.map +1 -1
- package/dist/query/foreign-client-warning.js.map +1 -1
- package/dist/query/offline-persistence.cjs +1 -1
- package/dist/query/offline-persistence.cjs.map +1 -1
- package/dist/query/offline-persistence.js +2 -10
- package/dist/query/offline-persistence.js.map +1 -1
- package/dist/query/optimistic.cjs.map +1 -1
- package/dist/query/optimistic.js.map +1 -1
- package/dist/query/pagination.cjs.map +1 -1
- package/dist/query/pagination.js.map +1 -1
- package/dist/query/retry-policy.cjs.map +1 -1
- package/dist/query/retry-policy.js.map +1 -1
- package/dist/query/use-cursor-query.cjs.map +1 -1
- package/dist/query/use-cursor-query.js.map +1 -1
- package/dist/query/use-offline-mutation.cjs.map +1 -1
- package/dist/query/use-offline-mutation.js.map +1 -1
- package/dist/query/use-paginated-query.cjs.map +1 -1
- package/dist/query/use-paginated-query.js.map +1 -1
- package/dist/router/AppRouter.cjs.map +1 -1
- package/dist/router/AppRouter.js.map +1 -1
- package/dist/router/RouteGuard.cjs.map +1 -1
- package/dist/router/RouteGuard.js.map +1 -1
- package/dist/router/index.js +2 -2
- package/dist/share/share-or-download.cjs.map +1 -1
- package/dist/share/share-or-download.js.map +1 -1
- package/dist/share/share.cjs.map +1 -1
- package/dist/share/share.js.map +1 -1
- package/dist/sse/create-event-stream.cjs.map +1 -1
- package/dist/sse/create-event-stream.js.map +1 -1
- package/dist/sse/use-event-stream.cjs.map +1 -1
- package/dist/sse/use-event-stream.js.map +1 -1
- package/dist/store/create-selectors.cjs.map +1 -1
- package/dist/store/create-selectors.js.map +1 -1
- package/dist/store/create-store.cjs.map +1 -1
- package/dist/store/create-store.js.map +1 -1
- package/dist/sw/background-sync.cjs.map +1 -1
- package/dist/sw/background-sync.js.map +1 -1
- package/dist/sw/cache-inspect.cjs.map +1 -1
- package/dist/sw/cache-inspect.js.map +1 -1
- package/dist/sw/cache.cjs +1 -1
- package/dist/sw/cache.cjs.map +1 -1
- package/dist/sw/cache.js +2 -6
- package/dist/sw/cache.js.map +1 -1
- package/dist/sw/create-push-handler.cjs.map +1 -1
- package/dist/sw/create-push-handler.js.map +1 -1
- package/dist/sw/credential-headers.cjs.map +1 -1
- package/dist/sw/credential-headers.js.map +1 -1
- package/dist/sw/periodic-sync.cjs.map +1 -1
- package/dist/sw/periodic-sync.js.map +1 -1
- package/dist/sw/register-service-worker.cjs.map +1 -1
- package/dist/sw/register-service-worker.js.map +1 -1
- package/dist/sw/session-data.cjs.map +1 -1
- package/dist/sw/session-data.js.map +1 -1
- package/dist/tabular/assets.cjs.map +1 -1
- package/dist/tabular/assets.js.map +1 -1
- package/dist/tabular/cache.cjs.map +1 -1
- package/dist/tabular/cache.js.map +1 -1
- package/dist/tabular/compact.cjs.map +1 -1
- package/dist/tabular/compact.js.map +1 -1
- package/dist/tabular/exceptions.cjs.map +1 -1
- package/dist/tabular/exceptions.js.map +1 -1
- package/dist/tabular/manifest.cjs.map +1 -1
- package/dist/tabular/manifest.js.map +1 -1
- package/dist/tabular/predictor.cjs.map +1 -1
- package/dist/tabular/predictor.js.map +1 -1
- package/dist/tabular/use-tabular-predictor.cjs.map +1 -1
- package/dist/tabular/use-tabular-predictor.js.map +1 -1
- package/dist/telemetry/TelemetryProvider.cjs.map +1 -1
- package/dist/telemetry/TelemetryProvider.js.map +1 -1
- package/dist/telemetry/app-error-adapter.cjs.map +1 -1
- package/dist/telemetry/app-error-adapter.js.map +1 -1
- package/dist/telemetry/app-error-queue.cjs.map +1 -1
- package/dist/telemetry/app-error-queue.js.map +1 -1
- package/dist/telemetry/app-error-reporter.cjs.map +1 -1
- package/dist/telemetry/app-error-reporter.js.map +1 -1
- package/dist/telemetry/app-error-schema.cjs.map +1 -1
- package/dist/telemetry/app-error-schema.js.map +1 -1
- package/dist/telemetry/app-error-transport.cjs.map +1 -1
- package/dist/telemetry/app-error-transport.js.map +1 -1
- package/dist/telemetry/console-adapter.cjs.map +1 -1
- package/dist/telemetry/console-adapter.js.map +1 -1
- package/dist/telemetry/describe-app-error.cjs.map +1 -1
- package/dist/telemetry/describe-app-error.js.map +1 -1
- package/dist/telemetry/posthog-adapter.cjs +1 -1
- package/dist/telemetry/posthog-adapter.cjs.map +1 -1
- package/dist/telemetry/posthog-adapter.js +1 -5
- package/dist/telemetry/posthog-adapter.js.map +1 -1
- package/dist/telemetry/sentry-adapter.cjs.map +1 -1
- package/dist/telemetry/sentry-adapter.js.map +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +234 -3
- package/dist/tempest-react-sdk.js +85 -85
- package/dist/testing/create-mock-handlers.cjs.map +1 -1
- package/dist/testing/create-mock-handlers.js.map +1 -1
- package/dist/testing.js +1 -2
- package/dist/theme/ThemeProvider.cjs.map +1 -1
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/theme/apply-theme.cjs.map +1 -1
- package/dist/theme/apply-theme.js.map +1 -1
- package/dist/theme/color.cjs.map +1 -1
- package/dist/theme/color.js.map +1 -1
- package/dist/theme/create-theme.cjs.map +1 -1
- package/dist/theme/create-theme.js.map +1 -1
- package/dist/theme/data-viz-ramps.cjs.map +1 -1
- package/dist/theme/data-viz-ramps.js.map +1 -1
- package/dist/theme/initial-theme.cjs.map +1 -1
- package/dist/theme/initial-theme.js.map +1 -1
- package/dist/theme/inverse-render.cjs.map +1 -1
- package/dist/theme/inverse-render.js.map +1 -1
- package/dist/theme/inverse-restore.cjs.map +1 -1
- package/dist/theme/inverse-restore.js.map +1 -1
- package/dist/theme/inverse-surface.cjs.map +1 -1
- package/dist/theme/inverse-surface.js.map +1 -1
- package/dist/theme/status-tokens.cjs.map +1 -1
- package/dist/theme/status-tokens.js.map +1 -1
- package/dist/theme/theme-presets.cjs.map +1 -1
- package/dist/theme/theme-presets.js.map +1 -1
- package/dist/utils/absent.cjs.map +1 -1
- package/dist/utils/absent.js.map +1 -1
- package/dist/utils/arrays.cjs.map +1 -1
- package/dist/utils/arrays.js.map +1 -1
- package/dist/utils/base64.cjs.map +1 -1
- package/dist/utils/base64.js.map +1 -1
- package/dist/utils/cn.cjs.map +1 -1
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/compare-values.cjs.map +1 -1
- package/dist/utils/compare-values.js.map +1 -1
- package/dist/utils/compressed-storage.cjs.map +1 -1
- package/dist/utils/compressed-storage.js.map +1 -1
- package/dist/utils/csv.cjs.map +1 -1
- package/dist/utils/csv.js.map +1 -1
- package/dist/utils/dev-mode.cjs.map +1 -1
- package/dist/utils/dev-mode.js.map +1 -1
- package/dist/utils/duplicate-instance.cjs.map +1 -1
- package/dist/utils/duplicate-instance.js.map +1 -1
- package/dist/utils/format.cjs.map +1 -1
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/functions.cjs +1 -1
- package/dist/utils/functions.cjs.map +1 -1
- package/dist/utils/functions.js +1 -1
- package/dist/utils/functions.js.map +1 -1
- package/dist/utils/guards.cjs.map +1 -1
- package/dist/utils/guards.js.map +1 -1
- package/dist/utils/ids.cjs.map +1 -1
- package/dist/utils/ids.js.map +1 -1
- package/dist/utils/intl-cache.cjs.map +1 -1
- package/dist/utils/intl-cache.js.map +1 -1
- package/dist/utils/json-frame.cjs.map +1 -1
- package/dist/utils/json-frame.js.map +1 -1
- package/dist/utils/numbers.cjs.map +1 -1
- package/dist/utils/numbers.js.map +1 -1
- package/dist/utils/objects.cjs.map +1 -1
- package/dist/utils/objects.js.map +1 -1
- package/dist/utils/owner-fingerprint.cjs.map +1 -1
- package/dist/utils/owner-fingerprint.js.map +1 -1
- package/dist/utils/promises.cjs.map +1 -1
- package/dist/utils/promises.js.map +1 -1
- package/dist/utils/relative-time.cjs.map +1 -1
- package/dist/utils/relative-time.js.map +1 -1
- package/dist/utils/safe-url.cjs.map +1 -1
- package/dist/utils/safe-url.js.map +1 -1
- package/dist/utils/schema-like.cjs.map +1 -1
- package/dist/utils/schema-like.js.map +1 -1
- package/dist/utils/storage.cjs +1 -1
- package/dist/utils/storage.cjs.map +1 -1
- package/dist/utils/storage.js +2 -2
- package/dist/utils/storage.js.map +1 -1
- package/dist/utils/strings.cjs.map +1 -1
- package/dist/utils/strings.js.map +1 -1
- package/dist/utils/xlsx.cjs.map +1 -1
- package/dist/utils/xlsx.js.map +1 -1
- package/dist/vision/core/canvas.cjs.map +1 -1
- package/dist/vision/core/canvas.js.map +1 -1
- package/dist/vision/core/dtypes.cjs.map +1 -1
- package/dist/vision/core/dtypes.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/graph.cjs.map +1 -1
- package/dist/vision/core/graph.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 +9 -26
- package/dist/vision/core/metadata.js.map +1 -1
- package/dist/vision/core/providers.cjs.map +1 -1
- package/dist/vision/core/providers.js.map +1 -1
- package/dist/vision/core/session.cjs.map +1 -1
- package/dist/vision/core/session.js.map +1 -1
- package/dist/vision/core/timing.cjs.map +1 -1
- package/dist/vision/core/timing.js.map +1 -1
- package/dist/vision/fusion.cjs.map +1 -1
- package/dist/vision/fusion.js.map +1 -1
- package/dist/vision/index.cjs.map +1 -1
- package/dist/vision/index.js.map +1 -1
- package/dist/vision/io/image.cjs.map +1 -1
- package/dist/vision/io/image.js.map +1 -1
- package/dist/vision/labels.cjs.map +1 -1
- package/dist/vision/labels.js.map +1 -1
- package/dist/vision/luminance.cjs.map +1 -1
- package/dist/vision/luminance.js.map +1 -1
- package/dist/vision/normalization.cjs.map +1 -1
- package/dist/vision/normalization.js.map +1 -1
- package/dist/vision/postprocess/classification.cjs.map +1 -1
- package/dist/vision/postprocess/classification.js.map +1 -1
- package/dist/vision/postprocess/detection.cjs.map +1 -1
- package/dist/vision/postprocess/detection.js.map +1 -1
- package/dist/vision/postprocess/segmentation.cjs.map +1 -1
- package/dist/vision/postprocess/segmentation.js.map +1 -1
- package/dist/vision/preprocess/image.cjs.map +1 -1
- package/dist/vision/preprocess/image.js.map +1 -1
- package/dist/vision/preprocess/pipeline.cjs.map +1 -1
- package/dist/vision/preprocess/pipeline.js.map +1 -1
- package/dist/vision/results.cjs.map +1 -1
- package/dist/vision/results.js.map +1 -1
- package/dist/vision/tasks/base.cjs.map +1 -1
- package/dist/vision/tasks/base.js.map +1 -1
- package/dist/vision/tasks/classifier.cjs.map +1 -1
- package/dist/vision/tasks/classifier.js.map +1 -1
- package/dist/vision/tasks/detectClassify.cjs.map +1 -1
- package/dist/vision/tasks/detectClassify.js.map +1 -1
- package/dist/vision/tasks/detector.cjs.map +1 -1
- package/dist/vision/tasks/detector.js.map +1 -1
- package/dist/vision/tasks/segmenter.cjs.map +1 -1
- package/dist/vision/tasks/segmenter.js.map +1 -1
- package/dist/vision/types.cjs.map +1 -1
- package/dist/vision/types.js.map +1 -1
- package/dist/vision/use-camera-stream.cjs +1 -1
- package/dist/vision/use-camera-stream.cjs.map +1 -1
- package/dist/vision/use-camera-stream.js +3 -9
- package/dist/vision/use-camera-stream.js.map +1 -1
- package/dist/vision/use-live-luminance.cjs +1 -1
- package/dist/vision/use-live-luminance.cjs.map +1 -1
- package/dist/vision/use-live-luminance.js +1 -5
- package/dist/vision/use-live-luminance.js.map +1 -1
- package/dist/vite/base-url.cjs.map +1 -1
- package/dist/vite/base-url.js.map +1 -1
- package/dist/vite/create-vite-config.cjs.map +1 -1
- package/dist/vite/create-vite-config.js.map +1 -1
- package/dist/vite/narrow-tokens.cjs.map +1 -1
- package/dist/vite/narrow-tokens.js.map +1 -1
- package/dist/vite/tempest-csp.cjs +1 -1
- package/dist/vite/tempest-csp.cjs.map +1 -1
- package/dist/vite/tempest-csp.js +2 -10
- package/dist/vite/tempest-csp.js.map +1 -1
- package/dist/vite/tempest-icons.cjs.map +1 -1
- package/dist/vite/tempest-icons.js.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.js +9 -16
- package/dist/vite/tempest-pwa-dev-sw.js.map +1 -1
- package/dist/vite/tempest-pwa-icons.cjs.map +1 -1
- package/dist/vite/tempest-pwa-icons.js.map +1 -1
- package/dist/vite/tempest-pwa-manifest.cjs.map +1 -1
- package/dist/vite/tempest-pwa-manifest.js.map +1 -1
- package/dist/vite/tempest-styles.cjs.map +1 -1
- package/dist/vite/tempest-styles.js.map +1 -1
- package/dist/vite/tempest-vitest.cjs.map +1 -1
- package/dist/vite/tempest-vitest.js.map +1 -1
- package/dist/webrtc/link-stats.cjs.map +1 -1
- package/dist/webrtc/link-stats.js.map +1 -1
- package/dist/webrtc/mesh-quality.cjs.map +1 -1
- package/dist/webrtc/mesh-quality.js.map +1 -1
- package/dist/webrtc/opus-sdp.cjs.map +1 -1
- package/dist/webrtc/opus-sdp.js.map +1 -1
- package/dist/webrtc/peer-link.cjs +1 -1
- package/dist/webrtc/peer-link.cjs.map +1 -1
- package/dist/webrtc/peer-link.js +2 -5
- package/dist/webrtc/peer-link.js.map +1 -1
- package/dist/webrtc/peer-mesh.cjs +1 -1
- package/dist/webrtc/peer-mesh.cjs.map +1 -1
- package/dist/webrtc/peer-mesh.js +8 -24
- package/dist/webrtc/peer-mesh.js.map +1 -1
- package/dist/webrtc/sender-bitrate.cjs.map +1 -1
- package/dist/webrtc/sender-bitrate.js.map +1 -1
- package/dist/webrtc/use-link-stats.cjs.map +1 -1
- package/dist/webrtc/use-link-stats.js.map +1 -1
- package/dist/ws/create-web-socket.cjs +1 -1
- package/dist/ws/create-web-socket.cjs.map +1 -1
- package/dist/ws/create-web-socket.js +4 -22
- package/dist/ws/create-web-socket.js.map +1 -1
- package/dist/ws/resilience.cjs.map +1 -1
- package/dist/ws/resilience.js.map +1 -1
- package/dist/ws/use-web-socket.cjs.map +1 -1
- package/dist/ws/use-web-socket.js.map +1 -1
- package/package.json +9 -6
- package/template/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-chain.cjs","names":[],"sources":["../../src/audio/voice-chain.ts"],"sourcesContent":["import {\n compressorStage,\n deEsserStage,\n gateStage,\n highPassStage,\n hissCutStage,\n limiterStage,\n presenceStage,\n} from \"./voice-chain-stages\";\n\n/** Which stages a voice chain builds. */\nexport interface VoiceChainSettings {\n /** Cut everything below speech: fans, traffic rumble, desk knocks, plosives. */\n highPass: boolean;\n /** Silence the line when nobody is talking. */\n gate: boolean;\n /** RMS below which the gate closes, 0..1. See {@link suggestGateThreshold}. */\n gateThreshold: number;\n /** Even out the distance between a whisper and a shout. */\n compressor: boolean;\n /** Lift the consonant band so speech reads without getting louder. */\n presence: boolean;\n /** Roll off hiss above what speech uses. */\n hissCut: boolean;\n /** Ride down harsh S and CH sounds. */\n deEsser: boolean;\n /**\n * Hard ceiling before the signal leaves.\n *\n * On by default and worth leaving on: the leveller is 3:1, which controls the\n * average and lets a peak through, and `gain` can reach well above 1.\n */\n limiter: boolean;\n}\n\n/**\n * What a call wants before anybody touches a slider.\n *\n * High-pass, leveller and limiter only. Gate, presence, hiss cut and de-esser are\n * off because each has an audible cost when it is not needed — a gate set for the\n * wrong room clips words, presence on a bright microphone is harsh, and a de-esser\n * on a voice that does not hiss just dulls it.\n */\nexport const DEFAULT_VOICE_CHAIN: VoiceChainSettings = {\n highPass: true,\n gate: false,\n gateThreshold: 0.02,\n compressor: true,\n presence: false,\n hissCut: false,\n deEsser: false,\n limiter: true,\n};\n\n/** Options for {@link createVoiceChain}. */\nexport interface VoiceChainOptions {\n /** Output multiplier, applied after every stage and before the limiter. Default `1`. */\n gain?: number;\n /**\n * Reuse an existing `AudioContext` instead of creating one.\n *\n * Browsers cap live contexts (Chrome allows around six), so a page that already\n * has one — a level meter, an audio bus — should hand it over.\n */\n context?: AudioContext;\n /**\n * Whether `release()` also stops `source`. Default `true`.\n *\n * Pass `false` when the track belongs to somebody else: a settings dialog\n * building a second chain over its live preview would otherwise kill the meter\n * the person is watching while they listen.\n */\n ownsSource?: boolean;\n}\n\n/** A running voice chain. */\nexport interface VoiceChain {\n /** The processed track to publish. */\n track: MediaStreamTrack;\n /** Stop the detectors, disconnect the graph, and release the track. */\n release: () => void;\n /**\n * Whether a graph was actually built.\n *\n * `false` when this engine has no Web Audio, and also when the settings asked\n * for nothing — in both cases `track` is `source`, untouched.\n */\n readonly supported: boolean;\n}\n\n/** The `AudioContext` constructor this engine exposes, prefixed or not. */\nfunction audioContextConstructor(): typeof AudioContext | undefined {\n if (typeof AudioContext !== \"undefined\") return AudioContext;\n return (globalThis as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n}\n\n/**\n * Whether any of this would change the signal.\n *\n * A chain that only passes audio through still costs a re-encode and a handful of\n * nodes, so a caller that rebuilds on every settings change should ask first —\n * and {@link createVoiceChain} asks too, handing back `source` rather than\n * building a graph that does nothing.\n *\n * @param settings - The stages under consideration.\n * @param gain - The output multiplier that would be applied. Default `1`.\n * @returns `true` when the chain would be audibly transparent.\n */\nexport function isVoiceChainIdle(settings: VoiceChainSettings, gain = 1): boolean {\n return (\n !settings.highPass &&\n !settings.gate &&\n !settings.compressor &&\n !settings.presence &&\n !settings.hissCut &&\n !settings.deEsser &&\n !settings.limiter &&\n gain === 1\n );\n}\n\n/** A chain that owns nothing, for an engine or a settings object with no work to do. */\nfunction passThrough(source: MediaStreamTrack, ownsSource: boolean): VoiceChain {\n return {\n track: source,\n release: () => {\n if (ownsSource) source.stop();\n },\n supported: false,\n };\n}\n\n/**\n * Build the processing a microphone is published through.\n *\n * Ordered the way a console is: cut what is not speech, gate what is left, level\n * it, shape it, set how loud it goes out, then hold a ceiling. **The order is not\n * cosmetic.** Gating before the high-pass lets a rumble hold the gate open;\n * compressing before the gate lifts the noise floor up to meet the threshold; and\n * a limiter placed before the output gain is a ceiling the gain then walks\n * straight through.\n *\n * This sits *after* the browser's own echo cancellation and noise suppression,\n * which stay on: the two solve different problems. The browser's is trained on\n * stationary noise inside the capture pipeline; this cuts what it leaves behind\n * and decides when the line should be silent at all.\n *\n * @param source - The captured microphone track.\n * @param settings - Which stages to build. Default {@link DEFAULT_VOICE_CHAIN}.\n * @param options - See {@link VoiceChainOptions}.\n * @returns The track to publish plus its teardown. On an engine with no Web Audio,\n * and when every stage is off, the source track is handed back untouched and\n * `supported` is `false` — a call without processing beats a call that throws.\n * The idle case is decided **before** a context is opened, because browsers cap\n * how many can be live and one built for a chain that does nothing still counts.\n *\n * @example\n * const floor = await measureNoiseFloor(stream);\n * const chain = createVoiceChain(micTrack, {\n * ...DEFAULT_VOICE_CHAIN,\n * gate: true,\n * gateThreshold: suggestGateThreshold(floor),\n * }, { gain: 1.4 });\n * await mesh.setLocalTrack(\"mic\", chain.track);\n */\nexport function createVoiceChain(\n source: MediaStreamTrack,\n settings: VoiceChainSettings = DEFAULT_VOICE_CHAIN,\n { gain = 1, context: injectedContext, ownsSource = true }: VoiceChainOptions = {},\n): VoiceChain {\n if (isVoiceChainIdle(settings, gain)) return passThrough(source, ownsSource);\n\n const Ctor = audioContextConstructor();\n const context = injectedContext ?? (Ctor ? new Ctor() : null);\n if (!context) return passThrough(source, ownsSource);\n\n const input = context.createMediaStreamSource(\n typeof MediaStream === \"undefined\"\n ? (source as unknown as MediaStream)\n : new MediaStream([source]),\n );\n const built: AudioNode[] = [input];\n const stops: (() => void)[] = [];\n let node: AudioNode = input;\n\n const chain = (next: AudioNode): void => {\n node.connect(next);\n node = next;\n built.push(next);\n };\n\n if (settings.highPass) chain(highPassStage(context));\n\n if (settings.gate) {\n const stage = gateStage(context, () => settings.gateThreshold);\n node.connect(stage.detector);\n built.push(stage.detector);\n stops.push(stage.stop);\n chain(stage.gain);\n }\n\n if (settings.compressor) chain(compressorStage(context));\n if (settings.presence) chain(presenceStage(context));\n if (settings.hissCut) chain(hissCutStage(context));\n\n if (settings.deEsser) {\n const stage = deEsserStage(context);\n node.connect(stage.band);\n built.push(stage.band, stage.detector);\n stops.push(stage.stop);\n chain(stage.shaper);\n }\n\n const output = context.createGain();\n output.gain.value = gain;\n chain(output);\n\n if (settings.limiter) chain(limiterStage(context));\n\n const destination = context.createMediaStreamDestination();\n node.connect(destination);\n built.push(destination);\n const track = destination.stream.getAudioTracks()[0];\n\n const teardown = (): void => {\n for (const stop of stops) stop();\n for (const built_ of built) built_.disconnect();\n };\n\n if (!track) {\n teardown();\n return passThrough(source, ownsSource);\n }\n\n return {\n track,\n release: (): void => {\n teardown();\n track.stop();\n if (ownsSource) source.stop();\n },\n supported: true,\n };\n}\n\n/** A running monitor. */\nexport interface VoiceMonitor {\n /** Stop listening and release the graph. Does **not** stop the source track. */\n stop: () => void;\n}\n\n/**\n * Play your own processed microphone back to you.\n *\n * The only way to hear what these filters do without a second person on the call.\n * Every number the chain is tuned by — how deep the de-esser cuts, where the gate\n * sits, how much presence is too much — is a judgement made by ear, and without\n * this the only available ear belongs to somebody else.\n *\n * **Feedback is the hazard, and the reason this is a held, explicit action rather\n * than a setting:** without headphones the speakers feed the microphone that feeds\n * the speakers. Say so in the UI that offers it.\n *\n * @param source - The raw captured track to listen to. It is never stopped here.\n * @param settings - The stages to hear it through. Default {@link DEFAULT_VOICE_CHAIN}.\n * @param options - See {@link VoiceChainOptions}; `ownsSource` is forced to `false`.\n * @returns A handle that stops the monitor.\n */\nexport function monitorVoiceChain(\n source: MediaStreamTrack,\n settings: VoiceChainSettings = DEFAULT_VOICE_CHAIN,\n options: Omit<VoiceChainOptions, \"ownsSource\"> = {},\n): VoiceMonitor {\n const Ctor = audioContextConstructor();\n const context = options.context ?? (Ctor ? new Ctor() : null);\n if (!context) return { stop: () => undefined };\n\n const chain = createVoiceChain(source, settings, { ...options, context, ownsSource: false });\n const playback = context.createMediaStreamSource(\n typeof MediaStream === \"undefined\"\n ? (chain.track as unknown as MediaStream)\n : new MediaStream([chain.track]),\n );\n playback.connect(context.destination);\n\n return {\n stop: (): void => {\n playback.disconnect();\n chain.release();\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"voice-chain.cjs","names":["highPassStage","gateStage","compressorStage","presenceStage","hissCutStage","deEsserStage","limiterStage","require_voice_chain_stages","DEFAULT_VOICE_CHAIN","audioContextConstructor","isVoiceChainIdle","settings","gain","passThrough","source","ownsSource","createVoiceChain","injectedContext","Ctor","context","input","built","stops","node","chain","next","stage","output","destination","track","teardown","stop","built_","monitorVoiceChain","options","playback"],"sources":["../../src/audio/voice-chain.ts"],"sourcesContent":["import {\n compressorStage,\n deEsserStage,\n gateStage,\n highPassStage,\n hissCutStage,\n limiterStage,\n presenceStage,\n} from \"./voice-chain-stages\";\n\n/** Which stages a voice chain builds. */\nexport interface VoiceChainSettings {\n /** Cut everything below speech: fans, traffic rumble, desk knocks, plosives. */\n highPass: boolean;\n /** Silence the line when nobody is talking. */\n gate: boolean;\n /** RMS below which the gate closes, 0..1. See {@link suggestGateThreshold}. */\n gateThreshold: number;\n /** Even out the distance between a whisper and a shout. */\n compressor: boolean;\n /** Lift the consonant band so speech reads without getting louder. */\n presence: boolean;\n /** Roll off hiss above what speech uses. */\n hissCut: boolean;\n /** Ride down harsh S and CH sounds. */\n deEsser: boolean;\n /**\n * Hard ceiling before the signal leaves.\n *\n * On by default and worth leaving on: the leveller is 3:1, which controls the\n * average and lets a peak through, and `gain` can reach well above 1.\n */\n limiter: boolean;\n}\n\n/**\n * What a call wants before anybody touches a slider.\n *\n * High-pass, leveller and limiter only. Gate, presence, hiss cut and de-esser are\n * off because each has an audible cost when it is not needed — a gate set for the\n * wrong room clips words, presence on a bright microphone is harsh, and a de-esser\n * on a voice that does not hiss just dulls it.\n */\nexport const DEFAULT_VOICE_CHAIN: VoiceChainSettings = {\n highPass: true,\n gate: false,\n gateThreshold: 0.02,\n compressor: true,\n presence: false,\n hissCut: false,\n deEsser: false,\n limiter: true,\n};\n\n/** Options for {@link createVoiceChain}. */\nexport interface VoiceChainOptions {\n /** Output multiplier, applied after every stage and before the limiter. Default `1`. */\n gain?: number;\n /**\n * Reuse an existing `AudioContext` instead of creating one.\n *\n * Browsers cap live contexts (Chrome allows around six), so a page that already\n * has one — a level meter, an audio bus — should hand it over.\n */\n context?: AudioContext;\n /**\n * Whether `release()` also stops `source`. Default `true`.\n *\n * Pass `false` when the track belongs to somebody else: a settings dialog\n * building a second chain over its live preview would otherwise kill the meter\n * the person is watching while they listen.\n */\n ownsSource?: boolean;\n}\n\n/** A running voice chain. */\nexport interface VoiceChain {\n /** The processed track to publish. */\n track: MediaStreamTrack;\n /** Stop the detectors, disconnect the graph, and release the track. */\n release: () => void;\n /**\n * Whether a graph was actually built.\n *\n * `false` when this engine has no Web Audio, and also when the settings asked\n * for nothing — in both cases `track` is `source`, untouched.\n */\n readonly supported: boolean;\n}\n\n/** The `AudioContext` constructor this engine exposes, prefixed or not. */\nfunction audioContextConstructor(): typeof AudioContext | undefined {\n if (typeof AudioContext !== \"undefined\") return AudioContext;\n return (globalThis as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n}\n\n/**\n * Whether any of this would change the signal.\n *\n * A chain that only passes audio through still costs a re-encode and a handful of\n * nodes, so a caller that rebuilds on every settings change should ask first —\n * and {@link createVoiceChain} asks too, handing back `source` rather than\n * building a graph that does nothing.\n *\n * @param settings - The stages under consideration.\n * @param gain - The output multiplier that would be applied. Default `1`.\n * @returns `true` when the chain would be audibly transparent.\n */\nexport function isVoiceChainIdle(settings: VoiceChainSettings, gain = 1): boolean {\n return (\n !settings.highPass &&\n !settings.gate &&\n !settings.compressor &&\n !settings.presence &&\n !settings.hissCut &&\n !settings.deEsser &&\n !settings.limiter &&\n gain === 1\n );\n}\n\n/** A chain that owns nothing, for an engine or a settings object with no work to do. */\nfunction passThrough(source: MediaStreamTrack, ownsSource: boolean): VoiceChain {\n return {\n track: source,\n release: () => {\n if (ownsSource) source.stop();\n },\n supported: false,\n };\n}\n\n/**\n * Build the processing a microphone is published through.\n *\n * Ordered the way a console is: cut what is not speech, gate what is left, level\n * it, shape it, set how loud it goes out, then hold a ceiling. **The order is not\n * cosmetic.** Gating before the high-pass lets a rumble hold the gate open;\n * compressing before the gate lifts the noise floor up to meet the threshold; and\n * a limiter placed before the output gain is a ceiling the gain then walks\n * straight through.\n *\n * This sits *after* the browser's own echo cancellation and noise suppression,\n * which stay on: the two solve different problems. The browser's is trained on\n * stationary noise inside the capture pipeline; this cuts what it leaves behind\n * and decides when the line should be silent at all.\n *\n * @param source - The captured microphone track.\n * @param settings - Which stages to build. Default {@link DEFAULT_VOICE_CHAIN}.\n * @param options - See {@link VoiceChainOptions}.\n * @returns The track to publish plus its teardown. On an engine with no Web Audio,\n * and when every stage is off, the source track is handed back untouched and\n * `supported` is `false` — a call without processing beats a call that throws.\n * The idle case is decided **before** a context is opened, because browsers cap\n * how many can be live and one built for a chain that does nothing still counts.\n *\n * @example\n * const floor = await measureNoiseFloor(stream);\n * const chain = createVoiceChain(micTrack, {\n * ...DEFAULT_VOICE_CHAIN,\n * gate: true,\n * gateThreshold: suggestGateThreshold(floor),\n * }, { gain: 1.4 });\n * await mesh.setLocalTrack(\"mic\", chain.track);\n */\nexport function createVoiceChain(\n source: MediaStreamTrack,\n settings: VoiceChainSettings = DEFAULT_VOICE_CHAIN,\n { gain = 1, context: injectedContext, ownsSource = true }: VoiceChainOptions = {},\n): VoiceChain {\n if (isVoiceChainIdle(settings, gain)) return passThrough(source, ownsSource);\n\n const Ctor = audioContextConstructor();\n const context = injectedContext ?? (Ctor ? new Ctor() : null);\n if (!context) return passThrough(source, ownsSource);\n\n const input = context.createMediaStreamSource(\n typeof MediaStream === \"undefined\"\n ? (source as unknown as MediaStream)\n : new MediaStream([source]),\n );\n const built: AudioNode[] = [input];\n const stops: (() => void)[] = [];\n let node: AudioNode = input;\n\n const chain = (next: AudioNode): void => {\n node.connect(next);\n node = next;\n built.push(next);\n };\n\n if (settings.highPass) chain(highPassStage(context));\n\n if (settings.gate) {\n const stage = gateStage(context, () => settings.gateThreshold);\n node.connect(stage.detector);\n built.push(stage.detector);\n stops.push(stage.stop);\n chain(stage.gain);\n }\n\n if (settings.compressor) chain(compressorStage(context));\n if (settings.presence) chain(presenceStage(context));\n if (settings.hissCut) chain(hissCutStage(context));\n\n if (settings.deEsser) {\n const stage = deEsserStage(context);\n node.connect(stage.band);\n built.push(stage.band, stage.detector);\n stops.push(stage.stop);\n chain(stage.shaper);\n }\n\n const output = context.createGain();\n output.gain.value = gain;\n chain(output);\n\n if (settings.limiter) chain(limiterStage(context));\n\n const destination = context.createMediaStreamDestination();\n node.connect(destination);\n built.push(destination);\n const track = destination.stream.getAudioTracks()[0];\n\n const teardown = (): void => {\n for (const stop of stops) stop();\n for (const built_ of built) built_.disconnect();\n };\n\n if (!track) {\n teardown();\n return passThrough(source, ownsSource);\n }\n\n return {\n track,\n release: (): void => {\n teardown();\n track.stop();\n if (ownsSource) source.stop();\n },\n supported: true,\n };\n}\n\n/** A running monitor. */\nexport interface VoiceMonitor {\n /** Stop listening and release the graph. Does **not** stop the source track. */\n stop: () => void;\n}\n\n/**\n * Play your own processed microphone back to you.\n *\n * The only way to hear what these filters do without a second person on the call.\n * Every number the chain is tuned by — how deep the de-esser cuts, where the gate\n * sits, how much presence is too much — is a judgement made by ear, and without\n * this the only available ear belongs to somebody else.\n *\n * **Feedback is the hazard, and the reason this is a held, explicit action rather\n * than a setting:** without headphones the speakers feed the microphone that feeds\n * the speakers. Say so in the UI that offers it.\n *\n * @param source - The raw captured track to listen to. It is never stopped here.\n * @param settings - The stages to hear it through. Default {@link DEFAULT_VOICE_CHAIN}.\n * @param options - See {@link VoiceChainOptions}; `ownsSource` is forced to `false`.\n * @returns A handle that stops the monitor.\n */\nexport function monitorVoiceChain(\n source: MediaStreamTrack,\n settings: VoiceChainSettings = DEFAULT_VOICE_CHAIN,\n options: Omit<VoiceChainOptions, \"ownsSource\"> = {},\n): VoiceMonitor {\n const Ctor = audioContextConstructor();\n const context = options.context ?? (Ctor ? new Ctor() : null);\n if (!context) return { stop: () => undefined };\n\n const chain = createVoiceChain(source, settings, { ...options, context, ownsSource: false });\n const playback = context.createMediaStreamSource(\n typeof MediaStream === \"undefined\"\n ? (chain.track as unknown as MediaStream)\n : new MediaStream([chain.track]),\n );\n playback.connect(context.destination);\n\n return {\n stop: (): void => {\n playback.disconnect();\n chain.release();\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-chain.js","names":[],"sources":["../../src/audio/voice-chain.ts"],"sourcesContent":["import {\n compressorStage,\n deEsserStage,\n gateStage,\n highPassStage,\n hissCutStage,\n limiterStage,\n presenceStage,\n} from \"./voice-chain-stages\";\n\n/** Which stages a voice chain builds. */\nexport interface VoiceChainSettings {\n /** Cut everything below speech: fans, traffic rumble, desk knocks, plosives. */\n highPass: boolean;\n /** Silence the line when nobody is talking. */\n gate: boolean;\n /** RMS below which the gate closes, 0..1. See {@link suggestGateThreshold}. */\n gateThreshold: number;\n /** Even out the distance between a whisper and a shout. */\n compressor: boolean;\n /** Lift the consonant band so speech reads without getting louder. */\n presence: boolean;\n /** Roll off hiss above what speech uses. */\n hissCut: boolean;\n /** Ride down harsh S and CH sounds. */\n deEsser: boolean;\n /**\n * Hard ceiling before the signal leaves.\n *\n * On by default and worth leaving on: the leveller is 3:1, which controls the\n * average and lets a peak through, and `gain` can reach well above 1.\n */\n limiter: boolean;\n}\n\n/**\n * What a call wants before anybody touches a slider.\n *\n * High-pass, leveller and limiter only. Gate, presence, hiss cut and de-esser are\n * off because each has an audible cost when it is not needed — a gate set for the\n * wrong room clips words, presence on a bright microphone is harsh, and a de-esser\n * on a voice that does not hiss just dulls it.\n */\nexport const DEFAULT_VOICE_CHAIN: VoiceChainSettings = {\n highPass: true,\n gate: false,\n gateThreshold: 0.02,\n compressor: true,\n presence: false,\n hissCut: false,\n deEsser: false,\n limiter: true,\n};\n\n/** Options for {@link createVoiceChain}. */\nexport interface VoiceChainOptions {\n /** Output multiplier, applied after every stage and before the limiter. Default `1`. */\n gain?: number;\n /**\n * Reuse an existing `AudioContext` instead of creating one.\n *\n * Browsers cap live contexts (Chrome allows around six), so a page that already\n * has one — a level meter, an audio bus — should hand it over.\n */\n context?: AudioContext;\n /**\n * Whether `release()` also stops `source`. Default `true`.\n *\n * Pass `false` when the track belongs to somebody else: a settings dialog\n * building a second chain over its live preview would otherwise kill the meter\n * the person is watching while they listen.\n */\n ownsSource?: boolean;\n}\n\n/** A running voice chain. */\nexport interface VoiceChain {\n /** The processed track to publish. */\n track: MediaStreamTrack;\n /** Stop the detectors, disconnect the graph, and release the track. */\n release: () => void;\n /**\n * Whether a graph was actually built.\n *\n * `false` when this engine has no Web Audio, and also when the settings asked\n * for nothing — in both cases `track` is `source`, untouched.\n */\n readonly supported: boolean;\n}\n\n/** The `AudioContext` constructor this engine exposes, prefixed or not. */\nfunction audioContextConstructor(): typeof AudioContext | undefined {\n if (typeof AudioContext !== \"undefined\") return AudioContext;\n return (globalThis as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n}\n\n/**\n * Whether any of this would change the signal.\n *\n * A chain that only passes audio through still costs a re-encode and a handful of\n * nodes, so a caller that rebuilds on every settings change should ask first —\n * and {@link createVoiceChain} asks too, handing back `source` rather than\n * building a graph that does nothing.\n *\n * @param settings - The stages under consideration.\n * @param gain - The output multiplier that would be applied. Default `1`.\n * @returns `true` when the chain would be audibly transparent.\n */\nexport function isVoiceChainIdle(settings: VoiceChainSettings, gain = 1): boolean {\n return (\n !settings.highPass &&\n !settings.gate &&\n !settings.compressor &&\n !settings.presence &&\n !settings.hissCut &&\n !settings.deEsser &&\n !settings.limiter &&\n gain === 1\n );\n}\n\n/** A chain that owns nothing, for an engine or a settings object with no work to do. */\nfunction passThrough(source: MediaStreamTrack, ownsSource: boolean): VoiceChain {\n return {\n track: source,\n release: () => {\n if (ownsSource) source.stop();\n },\n supported: false,\n };\n}\n\n/**\n * Build the processing a microphone is published through.\n *\n * Ordered the way a console is: cut what is not speech, gate what is left, level\n * it, shape it, set how loud it goes out, then hold a ceiling. **The order is not\n * cosmetic.** Gating before the high-pass lets a rumble hold the gate open;\n * compressing before the gate lifts the noise floor up to meet the threshold; and\n * a limiter placed before the output gain is a ceiling the gain then walks\n * straight through.\n *\n * This sits *after* the browser's own echo cancellation and noise suppression,\n * which stay on: the two solve different problems. The browser's is trained on\n * stationary noise inside the capture pipeline; this cuts what it leaves behind\n * and decides when the line should be silent at all.\n *\n * @param source - The captured microphone track.\n * @param settings - Which stages to build. Default {@link DEFAULT_VOICE_CHAIN}.\n * @param options - See {@link VoiceChainOptions}.\n * @returns The track to publish plus its teardown. On an engine with no Web Audio,\n * and when every stage is off, the source track is handed back untouched and\n * `supported` is `false` — a call without processing beats a call that throws.\n * The idle case is decided **before** a context is opened, because browsers cap\n * how many can be live and one built for a chain that does nothing still counts.\n *\n * @example\n * const floor = await measureNoiseFloor(stream);\n * const chain = createVoiceChain(micTrack, {\n * ...DEFAULT_VOICE_CHAIN,\n * gate: true,\n * gateThreshold: suggestGateThreshold(floor),\n * }, { gain: 1.4 });\n * await mesh.setLocalTrack(\"mic\", chain.track);\n */\nexport function createVoiceChain(\n source: MediaStreamTrack,\n settings: VoiceChainSettings = DEFAULT_VOICE_CHAIN,\n { gain = 1, context: injectedContext, ownsSource = true }: VoiceChainOptions = {},\n): VoiceChain {\n if (isVoiceChainIdle(settings, gain)) return passThrough(source, ownsSource);\n\n const Ctor = audioContextConstructor();\n const context = injectedContext ?? (Ctor ? new Ctor() : null);\n if (!context) return passThrough(source, ownsSource);\n\n const input = context.createMediaStreamSource(\n typeof MediaStream === \"undefined\"\n ? (source as unknown as MediaStream)\n : new MediaStream([source]),\n );\n const built: AudioNode[] = [input];\n const stops: (() => void)[] = [];\n let node: AudioNode = input;\n\n const chain = (next: AudioNode): void => {\n node.connect(next);\n node = next;\n built.push(next);\n };\n\n if (settings.highPass) chain(highPassStage(context));\n\n if (settings.gate) {\n const stage = gateStage(context, () => settings.gateThreshold);\n node.connect(stage.detector);\n built.push(stage.detector);\n stops.push(stage.stop);\n chain(stage.gain);\n }\n\n if (settings.compressor) chain(compressorStage(context));\n if (settings.presence) chain(presenceStage(context));\n if (settings.hissCut) chain(hissCutStage(context));\n\n if (settings.deEsser) {\n const stage = deEsserStage(context);\n node.connect(stage.band);\n built.push(stage.band, stage.detector);\n stops.push(stage.stop);\n chain(stage.shaper);\n }\n\n const output = context.createGain();\n output.gain.value = gain;\n chain(output);\n\n if (settings.limiter) chain(limiterStage(context));\n\n const destination = context.createMediaStreamDestination();\n node.connect(destination);\n built.push(destination);\n const track = destination.stream.getAudioTracks()[0];\n\n const teardown = (): void => {\n for (const stop of stops) stop();\n for (const built_ of built) built_.disconnect();\n };\n\n if (!track) {\n teardown();\n return passThrough(source, ownsSource);\n }\n\n return {\n track,\n release: (): void => {\n teardown();\n track.stop();\n if (ownsSource) source.stop();\n },\n supported: true,\n };\n}\n\n/** A running monitor. */\nexport interface VoiceMonitor {\n /** Stop listening and release the graph. Does **not** stop the source track. */\n stop: () => void;\n}\n\n/**\n * Play your own processed microphone back to you.\n *\n * The only way to hear what these filters do without a second person on the call.\n * Every number the chain is tuned by — how deep the de-esser cuts, where the gate\n * sits, how much presence is too much — is a judgement made by ear, and without\n * this the only available ear belongs to somebody else.\n *\n * **Feedback is the hazard, and the reason this is a held, explicit action rather\n * than a setting:** without headphones the speakers feed the microphone that feeds\n * the speakers. Say so in the UI that offers it.\n *\n * @param source - The raw captured track to listen to. It is never stopped here.\n * @param settings - The stages to hear it through. Default {@link DEFAULT_VOICE_CHAIN}.\n * @param options - See {@link VoiceChainOptions}; `ownsSource` is forced to `false`.\n * @returns A handle that stops the monitor.\n */\nexport function monitorVoiceChain(\n source: MediaStreamTrack,\n settings: VoiceChainSettings = DEFAULT_VOICE_CHAIN,\n options: Omit<VoiceChainOptions, \"ownsSource\"> = {},\n): VoiceMonitor {\n const Ctor = audioContextConstructor();\n const context = options.context ?? (Ctor ? new Ctor() : null);\n if (!context) return { stop: () => undefined };\n\n const chain = createVoiceChain(source, settings, { ...options, context, ownsSource: false });\n const playback = context.createMediaStreamSource(\n typeof MediaStream === \"undefined\"\n ? (chain.track as unknown as MediaStream)\n : new MediaStream([chain.track]),\n );\n playback.connect(context.destination);\n\n return {\n stop: (): void => {\n playback.disconnect();\n chain.release();\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"voice-chain.js","names":["DEFAULT_VOICE_CHAIN","audioContextConstructor","isVoiceChainIdle","settings","gain","passThrough","source","ownsSource","createVoiceChain","injectedContext","Ctor","context","input","built","stops","node","chain","next","highPassStage","stage","gateStage","compressorStage","presenceStage","hissCutStage","deEsserStage","output","limiterStage","destination","track","teardown","stop","built_","monitorVoiceChain","options","playback"],"sources":["../../src/audio/voice-chain.ts"],"sourcesContent":["import {\n compressorStage,\n deEsserStage,\n gateStage,\n highPassStage,\n hissCutStage,\n limiterStage,\n presenceStage,\n} from \"./voice-chain-stages\";\n\n/** Which stages a voice chain builds. */\nexport interface VoiceChainSettings {\n /** Cut everything below speech: fans, traffic rumble, desk knocks, plosives. */\n highPass: boolean;\n /** Silence the line when nobody is talking. */\n gate: boolean;\n /** RMS below which the gate closes, 0..1. See {@link suggestGateThreshold}. */\n gateThreshold: number;\n /** Even out the distance between a whisper and a shout. */\n compressor: boolean;\n /** Lift the consonant band so speech reads without getting louder. */\n presence: boolean;\n /** Roll off hiss above what speech uses. */\n hissCut: boolean;\n /** Ride down harsh S and CH sounds. */\n deEsser: boolean;\n /**\n * Hard ceiling before the signal leaves.\n *\n * On by default and worth leaving on: the leveller is 3:1, which controls the\n * average and lets a peak through, and `gain` can reach well above 1.\n */\n limiter: boolean;\n}\n\n/**\n * What a call wants before anybody touches a slider.\n *\n * High-pass, leveller and limiter only. Gate, presence, hiss cut and de-esser are\n * off because each has an audible cost when it is not needed — a gate set for the\n * wrong room clips words, presence on a bright microphone is harsh, and a de-esser\n * on a voice that does not hiss just dulls it.\n */\nexport const DEFAULT_VOICE_CHAIN: VoiceChainSettings = {\n highPass: true,\n gate: false,\n gateThreshold: 0.02,\n compressor: true,\n presence: false,\n hissCut: false,\n deEsser: false,\n limiter: true,\n};\n\n/** Options for {@link createVoiceChain}. */\nexport interface VoiceChainOptions {\n /** Output multiplier, applied after every stage and before the limiter. Default `1`. */\n gain?: number;\n /**\n * Reuse an existing `AudioContext` instead of creating one.\n *\n * Browsers cap live contexts (Chrome allows around six), so a page that already\n * has one — a level meter, an audio bus — should hand it over.\n */\n context?: AudioContext;\n /**\n * Whether `release()` also stops `source`. Default `true`.\n *\n * Pass `false` when the track belongs to somebody else: a settings dialog\n * building a second chain over its live preview would otherwise kill the meter\n * the person is watching while they listen.\n */\n ownsSource?: boolean;\n}\n\n/** A running voice chain. */\nexport interface VoiceChain {\n /** The processed track to publish. */\n track: MediaStreamTrack;\n /** Stop the detectors, disconnect the graph, and release the track. */\n release: () => void;\n /**\n * Whether a graph was actually built.\n *\n * `false` when this engine has no Web Audio, and also when the settings asked\n * for nothing — in both cases `track` is `source`, untouched.\n */\n readonly supported: boolean;\n}\n\n/** The `AudioContext` constructor this engine exposes, prefixed or not. */\nfunction audioContextConstructor(): typeof AudioContext | undefined {\n if (typeof AudioContext !== \"undefined\") return AudioContext;\n return (globalThis as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n}\n\n/**\n * Whether any of this would change the signal.\n *\n * A chain that only passes audio through still costs a re-encode and a handful of\n * nodes, so a caller that rebuilds on every settings change should ask first —\n * and {@link createVoiceChain} asks too, handing back `source` rather than\n * building a graph that does nothing.\n *\n * @param settings - The stages under consideration.\n * @param gain - The output multiplier that would be applied. Default `1`.\n * @returns `true` when the chain would be audibly transparent.\n */\nexport function isVoiceChainIdle(settings: VoiceChainSettings, gain = 1): boolean {\n return (\n !settings.highPass &&\n !settings.gate &&\n !settings.compressor &&\n !settings.presence &&\n !settings.hissCut &&\n !settings.deEsser &&\n !settings.limiter &&\n gain === 1\n );\n}\n\n/** A chain that owns nothing, for an engine or a settings object with no work to do. */\nfunction passThrough(source: MediaStreamTrack, ownsSource: boolean): VoiceChain {\n return {\n track: source,\n release: () => {\n if (ownsSource) source.stop();\n },\n supported: false,\n };\n}\n\n/**\n * Build the processing a microphone is published through.\n *\n * Ordered the way a console is: cut what is not speech, gate what is left, level\n * it, shape it, set how loud it goes out, then hold a ceiling. **The order is not\n * cosmetic.** Gating before the high-pass lets a rumble hold the gate open;\n * compressing before the gate lifts the noise floor up to meet the threshold; and\n * a limiter placed before the output gain is a ceiling the gain then walks\n * straight through.\n *\n * This sits *after* the browser's own echo cancellation and noise suppression,\n * which stay on: the two solve different problems. The browser's is trained on\n * stationary noise inside the capture pipeline; this cuts what it leaves behind\n * and decides when the line should be silent at all.\n *\n * @param source - The captured microphone track.\n * @param settings - Which stages to build. Default {@link DEFAULT_VOICE_CHAIN}.\n * @param options - See {@link VoiceChainOptions}.\n * @returns The track to publish plus its teardown. On an engine with no Web Audio,\n * and when every stage is off, the source track is handed back untouched and\n * `supported` is `false` — a call without processing beats a call that throws.\n * The idle case is decided **before** a context is opened, because browsers cap\n * how many can be live and one built for a chain that does nothing still counts.\n *\n * @example\n * const floor = await measureNoiseFloor(stream);\n * const chain = createVoiceChain(micTrack, {\n * ...DEFAULT_VOICE_CHAIN,\n * gate: true,\n * gateThreshold: suggestGateThreshold(floor),\n * }, { gain: 1.4 });\n * await mesh.setLocalTrack(\"mic\", chain.track);\n */\nexport function createVoiceChain(\n source: MediaStreamTrack,\n settings: VoiceChainSettings = DEFAULT_VOICE_CHAIN,\n { gain = 1, context: injectedContext, ownsSource = true }: VoiceChainOptions = {},\n): VoiceChain {\n if (isVoiceChainIdle(settings, gain)) return passThrough(source, ownsSource);\n\n const Ctor = audioContextConstructor();\n const context = injectedContext ?? (Ctor ? new Ctor() : null);\n if (!context) return passThrough(source, ownsSource);\n\n const input = context.createMediaStreamSource(\n typeof MediaStream === \"undefined\"\n ? (source as unknown as MediaStream)\n : new MediaStream([source]),\n );\n const built: AudioNode[] = [input];\n const stops: (() => void)[] = [];\n let node: AudioNode = input;\n\n const chain = (next: AudioNode): void => {\n node.connect(next);\n node = next;\n built.push(next);\n };\n\n if (settings.highPass) chain(highPassStage(context));\n\n if (settings.gate) {\n const stage = gateStage(context, () => settings.gateThreshold);\n node.connect(stage.detector);\n built.push(stage.detector);\n stops.push(stage.stop);\n chain(stage.gain);\n }\n\n if (settings.compressor) chain(compressorStage(context));\n if (settings.presence) chain(presenceStage(context));\n if (settings.hissCut) chain(hissCutStage(context));\n\n if (settings.deEsser) {\n const stage = deEsserStage(context);\n node.connect(stage.band);\n built.push(stage.band, stage.detector);\n stops.push(stage.stop);\n chain(stage.shaper);\n }\n\n const output = context.createGain();\n output.gain.value = gain;\n chain(output);\n\n if (settings.limiter) chain(limiterStage(context));\n\n const destination = context.createMediaStreamDestination();\n node.connect(destination);\n built.push(destination);\n const track = destination.stream.getAudioTracks()[0];\n\n const teardown = (): void => {\n for (const stop of stops) stop();\n for (const built_ of built) built_.disconnect();\n };\n\n if (!track) {\n teardown();\n return passThrough(source, ownsSource);\n }\n\n return {\n track,\n release: (): void => {\n teardown();\n track.stop();\n if (ownsSource) source.stop();\n },\n supported: true,\n };\n}\n\n/** A running monitor. */\nexport interface VoiceMonitor {\n /** Stop listening and release the graph. Does **not** stop the source track. */\n stop: () => void;\n}\n\n/**\n * Play your own processed microphone back to you.\n *\n * The only way to hear what these filters do without a second person on the call.\n * Every number the chain is tuned by — how deep the de-esser cuts, where the gate\n * sits, how much presence is too much — is a judgement made by ear, and without\n * this the only available ear belongs to somebody else.\n *\n * **Feedback is the hazard, and the reason this is a held, explicit action rather\n * than a setting:** without headphones the speakers feed the microphone that feeds\n * the speakers. Say so in the UI that offers it.\n *\n * @param source - The raw captured track to listen to. It is never stopped here.\n * @param settings - The stages to hear it through. Default {@link DEFAULT_VOICE_CHAIN}.\n * @param options - See {@link VoiceChainOptions}; `ownsSource` is forced to `false`.\n * @returns A handle that stops the monitor.\n */\nexport function monitorVoiceChain(\n source: MediaStreamTrack,\n settings: VoiceChainSettings = DEFAULT_VOICE_CHAIN,\n options: Omit<VoiceChainOptions, \"ownsSource\"> = {},\n): VoiceMonitor {\n const Ctor = audioContextConstructor();\n const context = options.context ?? (Ctor ? new Ctor() : null);\n if (!context) return { stop: () => undefined };\n\n const chain = createVoiceChain(source, settings, { ...options, context, ownsSource: false });\n const playback = context.createMediaStreamSource(\n typeof MediaStream === \"undefined\"\n ? (chain.track as unknown as MediaStream)\n : new MediaStream([chain.track]),\n );\n playback.connect(context.destination);\n\n return {\n stop: (): void => {\n playback.disconnect();\n chain.release();\n },\n };\n}\n"],"mappings":";;AA2CA,IAAaA,IAA0C;CACnD,UAAU;CACV,MAAM;CACN,eAAe;CACf,YAAY;CACZ,UAAU;CACV,SAAS;CACT,SAAS;CACT,SAAS;AACb;AAuCA,SAASC,IAA2D;CAEhE,OADI,OAAO,eAAiB,MAAoB,eACxC,WAA4D;AACxE;AAcA,SAAgBC,EAAiB,GAA8B,IAAO,GAAY;CAC9E,OACI,CAACC,EAAS,YACV,CAACA,EAAS,QACV,CAACA,EAAS,cACV,CAACA,EAAS,YACV,CAACA,EAAS,WACV,CAACA,EAAS,WACV,CAACA,EAAS,WACVC,MAAS;AAEjB;AAGA,SAASC,EAAY,GAA0B,GAAiC;CAC5E,OAAO;EACH,OAAOC;EACP,eAAe;GACX,AAAIC,KAAYD,EAAO,KAAK;EAChC;EACA,WAAW;CACf;AACJ;AAmCA,SAAgBE,EACZ,GACA,IAA+BR,GAC/B,EAAE,UAAO,GAAG,SAASS,GAAiB,gBAAa,OAA4B,CAAC,GACtE;CACV,IAAIP,EAAiBC,GAAUC,CAAI,GAAG,OAAOC,EAAYC,GAAQC,CAAU;CAE3E,IAAMG,IAAOT,EAAwB,GAC/BU,IAAUF,MAAoBC,IAAO,IAAIA,EAAK,IAAI;CACxD,IAAI,CAACC,GAAS,OAAON,EAAYC,GAAQC,CAAU;CAEnD,IAAMK,IAAQD,EAAQ,wBAClB,OAAO,cAAgB,MAChBL,IACD,IAAI,YAAY,CAACA,CAAM,CAAC,CAClC,GACMO,IAAqB,CAACD,CAAK,GAC3BE,IAAwB,CAAC,GAC3BC,IAAkBH,GAEhBI,KAAS,MAA0B;EAGrC,AAFAD,EAAK,QAAQE,CAAI,GACjB,IAAOA,GACPJ,EAAM,KAAKI,CAAI;CACnB;CAIA,IAFId,EAAS,YAAUa,EAAME,EAAcP,CAAO,CAAC,GAE/CR,EAAS,MAAM;EACf,IAAMgB,IAAQC,EAAUT,SAAeR,EAAS,aAAa;EAI7D,AAHAY,EAAK,QAAQI,EAAM,QAAQ,GAC3BN,EAAM,KAAKM,EAAM,QAAQ,GACzBL,EAAM,KAAKK,EAAM,IAAI,GACrBH,EAAMG,EAAM,IAAI;CACpB;CAMA,IAJIhB,EAAS,cAAYa,EAAMK,EAAgBV,CAAO,CAAC,GACnDR,EAAS,YAAUa,EAAMM,EAAcX,CAAO,CAAC,GAC/CR,EAAS,WAASa,EAAMO,EAAaZ,CAAO,CAAC,GAE7CR,EAAS,SAAS;EAClB,IAAMgB,IAAQK,EAAab,CAAO;EAIlC,AAHAI,EAAK,QAAQI,EAAM,IAAI,GACvBN,EAAM,KAAKM,EAAM,MAAMA,EAAM,QAAQ,GACrCL,EAAM,KAAKK,EAAM,IAAI,GACrBH,EAAMG,EAAM,MAAM;CACtB;CAEA,IAAMM,IAASd,EAAQ,WAAW;CAIlC,AAHA,EAAO,KAAK,QAAQP,GACpBY,EAAMS,CAAM,GAERtB,EAAS,WAASa,EAAMU,EAAaf,CAAO,CAAC;CAEjD,IAAMgB,IAAchB,EAAQ,6BAA6B;CAEzD,AADAI,EAAK,QAAQY,CAAW,GACxBd,EAAM,KAAKc,CAAW;CACtB,IAAMC,IAAQD,EAAY,OAAO,eAAe,CAAC,CAAC,IAE5CE,UAAuB;EACzB,KAAK,IAAMC,KAAQhB,GAAO,EAAK;EAC/B,KAAK,IAAMiB,KAAUlB,GAAO,EAAO,WAAW;CAClD;CAOA,OALKe,IAKE;EACH;EACA,eAAqB;GAGjB,AAFAC,EAAS,GACTD,EAAM,KAAK,GACPrB,KAAYD,EAAO,KAAK;EAChC;EACA,WAAW;CACf,KAZIuB,EAAS,GACFxB,EAAYC,GAAQC,CAAU;AAY7C;AAyBA,SAAgByB,EACZ,GACA,IAA+BhC,GAC/B,IAAiD,CAAC,GACtC;CACZ,IAAMU,IAAOT,EAAwB,GAC/BU,IAAUsB,EAAQ,YAAYvB,IAAO,IAAIA,EAAK,IAAI;CACxD,IAAI,CAACC,GAAS,OAAO,EAAE,YAAY,KAAA,EAAU;CAE7C,IAAMK,IAAQR,EAAiBF,GAAQH,GAAU;EAAE,GAAG8B;EAAS;EAAS,YAAY;CAAM,CAAC,GACrFC,IAAWvB,EAAQ,wBACrB,OAAO,cAAgB,MAChBK,EAAM,QACP,IAAI,YAAY,CAACA,EAAM,KAAK,CAAC,CACvC;CAGA,OAFAkB,EAAS,QAAQvB,EAAQ,WAAW,GAE7B,EACH,YAAkB;EAEd,AADAuB,EAAS,WAAW,GACpBlB,EAAM,QAAQ;CAClB,EACJ;AACJ"}
|
package/dist/audio/wav.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wav.cjs","names":[],"sources":["../../src/audio/wav.ts"],"sourcesContent":["/** Raw PCM ready to be wrapped in a RIFF container. */\nexport interface PcmAudio {\n /** One `Float32Array` per channel, samples in −1…1. */\n channels: readonly Float32Array[];\n sampleRate: number;\n}\n\n/** Options for {@link blobToWav}. */\nexport interface WavOptions {\n /**\n * Mix down to one channel. Default `false`.\n *\n * Worth turning on for speech headed to a server: a microphone recorded in stereo\n * carries two nearly identical channels and doubles the upload for nothing.\n */\n mono?: boolean;\n /**\n * Resample to this rate. Default: keep the source rate.\n *\n * 16000 is the rate most speech-to-text APIs want, and dropping 48 kHz to 16 kHz\n * removes two thirds of the bytes with no audible loss on voice.\n */\n sampleRate?: number;\n}\n\n/** Bytes per sample in the output. 16-bit PCM is what every decoder accepts. */\nconst BYTES_PER_SAMPLE = 2;\n\n/**\n * Wrap PCM in a 16-bit RIFF/WAVE container.\n *\n * The header is the 44-byte canonical form: `RIFF` size, `WAVE`, a 16-byte `fmt `\n * chunk declaring format 1 (uncompressed PCM), then `data`. Samples are interleaved\n * and clamped before scaling, because a value even slightly past ±1 wraps around when\n * truncated to 16 bits and turns a loud passage into a burst of noise.\n *\n * Implemented here rather than pulled from a package: it is a fixed header and a\n * scaling loop, roughly forty lines, and a dependency for it would put its own\n * version bounds on every consumer of this SDK to save writing them.\n *\n * @param audio - Channels and sample rate.\n * @returns A `Blob` of type `audio/wav`.\n */\nexport function encodeWav({ channels, sampleRate }: PcmAudio): Blob {\n const channelCount = Math.max(1, channels.length);\n const frameCount = channels[0]?.length ?? 0;\n const dataBytes = frameCount * channelCount * BYTES_PER_SAMPLE;\n const buffer = new ArrayBuffer(44 + dataBytes);\n const view = new DataView(buffer);\n\n const ascii = (offset: number, text: string): void => {\n for (let index = 0; index < text.length; index += 1) {\n view.setUint8(offset + index, text.charCodeAt(index));\n }\n };\n\n const byteRate = sampleRate * channelCount * BYTES_PER_SAMPLE;\n ascii(0, \"RIFF\");\n view.setUint32(4, 36 + dataBytes, true);\n ascii(8, \"WAVE\");\n ascii(12, \"fmt \");\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, channelCount, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, channelCount * BYTES_PER_SAMPLE, true);\n view.setUint16(34, BYTES_PER_SAMPLE * 8, true);\n ascii(36, \"data\");\n view.setUint32(40, dataBytes, true);\n\n let offset = 44;\n for (let frame = 0; frame < frameCount; frame += 1) {\n for (let channel = 0; channel < channelCount; channel += 1) {\n const sample = channels[channel]?.[frame] ?? 0;\n const clamped = sample < -1 ? -1 : sample > 1 ? 1 : sample;\n // Asymmetric scaling: 16-bit PCM runs −32768…32767.\n view.setInt16(offset, clamped < 0 ? clamped * 0x8000 : clamped * 0x7fff, true);\n offset += BYTES_PER_SAMPLE;\n }\n }\n\n return new Blob([buffer], { type: \"audio/wav\" });\n}\n\n/** Average every channel of a decoded buffer into one. */\nfunction mixToMono(buffer: AudioBuffer): Float32Array {\n const frames = buffer.length;\n const out = new Float32Array(frames);\n for (let channel = 0; channel < buffer.numberOfChannels; channel += 1) {\n const data = buffer.getChannelData(channel);\n for (let frame = 0; frame < frames; frame += 1) out[frame] += data[frame];\n }\n if (buffer.numberOfChannels > 1) {\n for (let frame = 0; frame < frames; frame += 1) out[frame] /= buffer.numberOfChannels;\n }\n return out;\n}\n\n/**\n * Convert a recording to 16-bit WAV, with no dependency.\n *\n * `MediaRecorder` cannot produce WAV — it emits Opus on Chromium and Firefox and AAC\n * on Safari — so a backend that insists on WAV has to be served either by a\n * server-side transcode or by this. It decodes through `AudioContext.decodeAudioData`,\n * which is the browser's own decoder for whatever container the recorder chose, then\n * re-encodes the PCM.\n *\n * !!! The cost is real: WAV is uncompressed, so the same voice note that is 40 KB in\n * Opus is roughly 500 KB here at 48 kHz stereo. `{ mono: true, sampleRate: 16000 }`\n * takes that to about 80 KB and is what a speech-to-text endpoint wants anyway.\n *\n * Resampling uses `OfflineAudioContext`, i.e. the browser's own resampler, rather\n * than a hand-rolled one — this is exactly the \"we want the underlying call, not a\n * wrapper\" case.\n *\n * @param blob - A recording from {@link createAudioRecorder}, or any decodable audio.\n * @param options - See {@link WavOptions}.\n * @returns A `Blob` of type `audio/wav`.\n * @throws When the environment has no Web Audio, or the blob cannot be decoded.\n */\nexport async function blobToWav(blob: Blob, options: WavOptions = {}): Promise<Blob> {\n const { mono = false, sampleRate: target } = options;\n\n const Ctor: typeof AudioContext | undefined =\n typeof AudioContext !== \"undefined\"\n ? AudioContext\n : (globalThis as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Ctor) throw new Error(\"Web Audio is not available in this environment.\");\n\n const context = new Ctor();\n let decoded: AudioBuffer;\n try {\n decoded = await context.decodeAudioData(await blob.arrayBuffer());\n } finally {\n void context.close().catch(() => undefined);\n }\n\n const wantedChannels = mono ? 1 : decoded.numberOfChannels;\n const wantedRate = target ?? decoded.sampleRate;\n\n if (wantedRate === decoded.sampleRate) {\n const channels = mono\n ? [mixToMono(decoded)]\n : Array.from({ length: decoded.numberOfChannels }, (_, index) =>\n decoded.getChannelData(index),\n );\n return encodeWav({ channels, sampleRate: decoded.sampleRate });\n }\n\n const Offline: typeof OfflineAudioContext | undefined =\n typeof OfflineAudioContext !== \"undefined\" ? OfflineAudioContext : undefined;\n if (!Offline) throw new Error(\"Resampling requires OfflineAudioContext.\");\n\n const frames = Math.max(1, Math.round((decoded.duration * wantedRate) | 0) || 1);\n const offline = new Offline(wantedChannels, frames, wantedRate);\n const source = offline.createBufferSource();\n source.buffer = decoded;\n source.connect(offline.destination);\n source.start();\n const rendered = await offline.startRendering();\n\n return encodeWav({\n channels: Array.from({ length: rendered.numberOfChannels }, (_, index) =>\n rendered.getChannelData(index),\n ),\n sampleRate: rendered.sampleRate,\n });\n}\n"],"mappings":"AA2CA,
|
|
1
|
+
{"version":3,"file":"wav.cjs","names":["encodeWav","channelCount","channels","frameCount","dataBytes","buffer","view","ascii","index","text","offset","byteRate","sampleRate","frame","channel","sample","clamped","mixToMono","frames","out","data","blobToWav","target","options","Ctor","context","decoded","blob","wantedChannels","mono","wantedRate","Offline","offline","source","rendered"],"sources":["../../src/audio/wav.ts"],"sourcesContent":["/** Raw PCM ready to be wrapped in a RIFF container. */\nexport interface PcmAudio {\n /** One `Float32Array` per channel, samples in −1…1. */\n channels: readonly Float32Array[];\n sampleRate: number;\n}\n\n/** Options for {@link blobToWav}. */\nexport interface WavOptions {\n /**\n * Mix down to one channel. Default `false`.\n *\n * Worth turning on for speech headed to a server: a microphone recorded in stereo\n * carries two nearly identical channels and doubles the upload for nothing.\n */\n mono?: boolean;\n /**\n * Resample to this rate. Default: keep the source rate.\n *\n * 16000 is the rate most speech-to-text APIs want, and dropping 48 kHz to 16 kHz\n * removes two thirds of the bytes with no audible loss on voice.\n */\n sampleRate?: number;\n}\n\n/** Bytes per sample in the output. 16-bit PCM is what every decoder accepts. */\nconst BYTES_PER_SAMPLE = 2;\n\n/**\n * Wrap PCM in a 16-bit RIFF/WAVE container.\n *\n * The header is the 44-byte canonical form: `RIFF` size, `WAVE`, a 16-byte `fmt `\n * chunk declaring format 1 (uncompressed PCM), then `data`. Samples are interleaved\n * and clamped before scaling, because a value even slightly past ±1 wraps around when\n * truncated to 16 bits and turns a loud passage into a burst of noise.\n *\n * Implemented here rather than pulled from a package: it is a fixed header and a\n * scaling loop, roughly forty lines, and a dependency for it would put its own\n * version bounds on every consumer of this SDK to save writing them.\n *\n * @param audio - Channels and sample rate.\n * @returns A `Blob` of type `audio/wav`.\n */\nexport function encodeWav({ channels, sampleRate }: PcmAudio): Blob {\n const channelCount = Math.max(1, channels.length);\n const frameCount = channels[0]?.length ?? 0;\n const dataBytes = frameCount * channelCount * BYTES_PER_SAMPLE;\n const buffer = new ArrayBuffer(44 + dataBytes);\n const view = new DataView(buffer);\n\n const ascii = (offset: number, text: string): void => {\n for (let index = 0; index < text.length; index += 1) {\n view.setUint8(offset + index, text.charCodeAt(index));\n }\n };\n\n const byteRate = sampleRate * channelCount * BYTES_PER_SAMPLE;\n ascii(0, \"RIFF\");\n view.setUint32(4, 36 + dataBytes, true);\n ascii(8, \"WAVE\");\n ascii(12, \"fmt \");\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, channelCount, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, channelCount * BYTES_PER_SAMPLE, true);\n view.setUint16(34, BYTES_PER_SAMPLE * 8, true);\n ascii(36, \"data\");\n view.setUint32(40, dataBytes, true);\n\n let offset = 44;\n for (let frame = 0; frame < frameCount; frame += 1) {\n for (let channel = 0; channel < channelCount; channel += 1) {\n const sample = channels[channel]?.[frame] ?? 0;\n const clamped = sample < -1 ? -1 : sample > 1 ? 1 : sample;\n // Asymmetric scaling: 16-bit PCM runs −32768…32767.\n view.setInt16(offset, clamped < 0 ? clamped * 0x8000 : clamped * 0x7fff, true);\n offset += BYTES_PER_SAMPLE;\n }\n }\n\n return new Blob([buffer], { type: \"audio/wav\" });\n}\n\n/** Average every channel of a decoded buffer into one. */\nfunction mixToMono(buffer: AudioBuffer): Float32Array {\n const frames = buffer.length;\n const out = new Float32Array(frames);\n for (let channel = 0; channel < buffer.numberOfChannels; channel += 1) {\n const data = buffer.getChannelData(channel);\n for (let frame = 0; frame < frames; frame += 1) out[frame] += data[frame];\n }\n if (buffer.numberOfChannels > 1) {\n for (let frame = 0; frame < frames; frame += 1) out[frame] /= buffer.numberOfChannels;\n }\n return out;\n}\n\n/**\n * Convert a recording to 16-bit WAV, with no dependency.\n *\n * `MediaRecorder` cannot produce WAV — it emits Opus on Chromium and Firefox and AAC\n * on Safari — so a backend that insists on WAV has to be served either by a\n * server-side transcode or by this. It decodes through `AudioContext.decodeAudioData`,\n * which is the browser's own decoder for whatever container the recorder chose, then\n * re-encodes the PCM.\n *\n * !!! The cost is real: WAV is uncompressed, so the same voice note that is 40 KB in\n * Opus is roughly 500 KB here at 48 kHz stereo. `{ mono: true, sampleRate: 16000 }`\n * takes that to about 80 KB and is what a speech-to-text endpoint wants anyway.\n *\n * Resampling uses `OfflineAudioContext`, i.e. the browser's own resampler, rather\n * than a hand-rolled one — this is exactly the \"we want the underlying call, not a\n * wrapper\" case.\n *\n * @param blob - A recording from {@link createAudioRecorder}, or any decodable audio.\n * @param options - See {@link WavOptions}.\n * @returns A `Blob` of type `audio/wav`.\n * @throws When the environment has no Web Audio, or the blob cannot be decoded.\n */\nexport async function blobToWav(blob: Blob, options: WavOptions = {}): Promise<Blob> {\n const { mono = false, sampleRate: target } = options;\n\n const Ctor: typeof AudioContext | undefined =\n typeof AudioContext !== \"undefined\"\n ? AudioContext\n : (globalThis as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Ctor) throw new Error(\"Web Audio is not available in this environment.\");\n\n const context = new Ctor();\n let decoded: AudioBuffer;\n try {\n decoded = await context.decodeAudioData(await blob.arrayBuffer());\n } finally {\n void context.close().catch(() => undefined);\n }\n\n const wantedChannels = mono ? 1 : decoded.numberOfChannels;\n const wantedRate = target ?? decoded.sampleRate;\n\n if (wantedRate === decoded.sampleRate) {\n const channels = mono\n ? [mixToMono(decoded)]\n : Array.from({ length: decoded.numberOfChannels }, (_, index) =>\n decoded.getChannelData(index),\n );\n return encodeWav({ channels, sampleRate: decoded.sampleRate });\n }\n\n const Offline: typeof OfflineAudioContext | undefined =\n typeof OfflineAudioContext !== \"undefined\" ? OfflineAudioContext : undefined;\n if (!Offline) throw new Error(\"Resampling requires OfflineAudioContext.\");\n\n const frames = Math.max(1, Math.round((decoded.duration * wantedRate) | 0) || 1);\n const offline = new Offline(wantedChannels, frames, wantedRate);\n const source = offline.createBufferSource();\n source.buffer = decoded;\n source.connect(offline.destination);\n source.start();\n const rendered = await offline.startRendering();\n\n return encodeWav({\n channels: Array.from({ length: rendered.numberOfChannels }, (_, index) =>\n rendered.getChannelData(index),\n ),\n sampleRate: rendered.sampleRate,\n });\n}\n"],"mappings":"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"}
|
package/dist/audio/wav.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wav.js","names":[],"sources":["../../src/audio/wav.ts"],"sourcesContent":["/** Raw PCM ready to be wrapped in a RIFF container. */\nexport interface PcmAudio {\n /** One `Float32Array` per channel, samples in −1…1. */\n channels: readonly Float32Array[];\n sampleRate: number;\n}\n\n/** Options for {@link blobToWav}. */\nexport interface WavOptions {\n /**\n * Mix down to one channel. Default `false`.\n *\n * Worth turning on for speech headed to a server: a microphone recorded in stereo\n * carries two nearly identical channels and doubles the upload for nothing.\n */\n mono?: boolean;\n /**\n * Resample to this rate. Default: keep the source rate.\n *\n * 16000 is the rate most speech-to-text APIs want, and dropping 48 kHz to 16 kHz\n * removes two thirds of the bytes with no audible loss on voice.\n */\n sampleRate?: number;\n}\n\n/** Bytes per sample in the output. 16-bit PCM is what every decoder accepts. */\nconst BYTES_PER_SAMPLE = 2;\n\n/**\n * Wrap PCM in a 16-bit RIFF/WAVE container.\n *\n * The header is the 44-byte canonical form: `RIFF` size, `WAVE`, a 16-byte `fmt `\n * chunk declaring format 1 (uncompressed PCM), then `data`. Samples are interleaved\n * and clamped before scaling, because a value even slightly past ±1 wraps around when\n * truncated to 16 bits and turns a loud passage into a burst of noise.\n *\n * Implemented here rather than pulled from a package: it is a fixed header and a\n * scaling loop, roughly forty lines, and a dependency for it would put its own\n * version bounds on every consumer of this SDK to save writing them.\n *\n * @param audio - Channels and sample rate.\n * @returns A `Blob` of type `audio/wav`.\n */\nexport function encodeWav({ channels, sampleRate }: PcmAudio): Blob {\n const channelCount = Math.max(1, channels.length);\n const frameCount = channels[0]?.length ?? 0;\n const dataBytes = frameCount * channelCount * BYTES_PER_SAMPLE;\n const buffer = new ArrayBuffer(44 + dataBytes);\n const view = new DataView(buffer);\n\n const ascii = (offset: number, text: string): void => {\n for (let index = 0; index < text.length; index += 1) {\n view.setUint8(offset + index, text.charCodeAt(index));\n }\n };\n\n const byteRate = sampleRate * channelCount * BYTES_PER_SAMPLE;\n ascii(0, \"RIFF\");\n view.setUint32(4, 36 + dataBytes, true);\n ascii(8, \"WAVE\");\n ascii(12, \"fmt \");\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, channelCount, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, channelCount * BYTES_PER_SAMPLE, true);\n view.setUint16(34, BYTES_PER_SAMPLE * 8, true);\n ascii(36, \"data\");\n view.setUint32(40, dataBytes, true);\n\n let offset = 44;\n for (let frame = 0; frame < frameCount; frame += 1) {\n for (let channel = 0; channel < channelCount; channel += 1) {\n const sample = channels[channel]?.[frame] ?? 0;\n const clamped = sample < -1 ? -1 : sample > 1 ? 1 : sample;\n // Asymmetric scaling: 16-bit PCM runs −32768…32767.\n view.setInt16(offset, clamped < 0 ? clamped * 0x8000 : clamped * 0x7fff, true);\n offset += BYTES_PER_SAMPLE;\n }\n }\n\n return new Blob([buffer], { type: \"audio/wav\" });\n}\n\n/** Average every channel of a decoded buffer into one. */\nfunction mixToMono(buffer: AudioBuffer): Float32Array {\n const frames = buffer.length;\n const out = new Float32Array(frames);\n for (let channel = 0; channel < buffer.numberOfChannels; channel += 1) {\n const data = buffer.getChannelData(channel);\n for (let frame = 0; frame < frames; frame += 1) out[frame] += data[frame];\n }\n if (buffer.numberOfChannels > 1) {\n for (let frame = 0; frame < frames; frame += 1) out[frame] /= buffer.numberOfChannels;\n }\n return out;\n}\n\n/**\n * Convert a recording to 16-bit WAV, with no dependency.\n *\n * `MediaRecorder` cannot produce WAV — it emits Opus on Chromium and Firefox and AAC\n * on Safari — so a backend that insists on WAV has to be served either by a\n * server-side transcode or by this. It decodes through `AudioContext.decodeAudioData`,\n * which is the browser's own decoder for whatever container the recorder chose, then\n * re-encodes the PCM.\n *\n * !!! The cost is real: WAV is uncompressed, so the same voice note that is 40 KB in\n * Opus is roughly 500 KB here at 48 kHz stereo. `{ mono: true, sampleRate: 16000 }`\n * takes that to about 80 KB and is what a speech-to-text endpoint wants anyway.\n *\n * Resampling uses `OfflineAudioContext`, i.e. the browser's own resampler, rather\n * than a hand-rolled one — this is exactly the \"we want the underlying call, not a\n * wrapper\" case.\n *\n * @param blob - A recording from {@link createAudioRecorder}, or any decodable audio.\n * @param options - See {@link WavOptions}.\n * @returns A `Blob` of type `audio/wav`.\n * @throws When the environment has no Web Audio, or the blob cannot be decoded.\n */\nexport async function blobToWav(blob: Blob, options: WavOptions = {}): Promise<Blob> {\n const { mono = false, sampleRate: target } = options;\n\n const Ctor: typeof AudioContext | undefined =\n typeof AudioContext !== \"undefined\"\n ? AudioContext\n : (globalThis as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Ctor) throw new Error(\"Web Audio is not available in this environment.\");\n\n const context = new Ctor();\n let decoded: AudioBuffer;\n try {\n decoded = await context.decodeAudioData(await blob.arrayBuffer());\n } finally {\n void context.close().catch(() => undefined);\n }\n\n const wantedChannels = mono ? 1 : decoded.numberOfChannels;\n const wantedRate = target ?? decoded.sampleRate;\n\n if (wantedRate === decoded.sampleRate) {\n const channels = mono\n ? [mixToMono(decoded)]\n : Array.from({ length: decoded.numberOfChannels }, (_, index) =>\n decoded.getChannelData(index),\n );\n return encodeWav({ channels, sampleRate: decoded.sampleRate });\n }\n\n const Offline: typeof OfflineAudioContext | undefined =\n typeof OfflineAudioContext !== \"undefined\" ? OfflineAudioContext : undefined;\n if (!Offline) throw new Error(\"Resampling requires OfflineAudioContext.\");\n\n const frames = Math.max(1, Math.round((decoded.duration * wantedRate) | 0) || 1);\n const offline = new Offline(wantedChannels, frames, wantedRate);\n const source = offline.createBufferSource();\n source.buffer = decoded;\n source.connect(offline.destination);\n source.start();\n const rendered = await offline.startRendering();\n\n return encodeWav({\n channels: Array.from({ length: rendered.numberOfChannels }, (_, index) =>\n rendered.getChannelData(index),\n ),\n sampleRate: rendered.sampleRate,\n });\n}\n"],"mappings":"AA2CA,
|
|
1
|
+
{"version":3,"file":"wav.js","names":["encodeWav","channelCount","channels","frameCount","dataBytes","buffer","view","ascii","index","text","offset","byteRate","sampleRate","frame","channel","sample","clamped","mixToMono","frames","out","data","blobToWav","target","options","Ctor","context","decoded","blob","wantedChannels","mono","wantedRate","Offline","offline","source","rendered"],"sources":["../../src/audio/wav.ts"],"sourcesContent":["/** Raw PCM ready to be wrapped in a RIFF container. */\nexport interface PcmAudio {\n /** One `Float32Array` per channel, samples in −1…1. */\n channels: readonly Float32Array[];\n sampleRate: number;\n}\n\n/** Options for {@link blobToWav}. */\nexport interface WavOptions {\n /**\n * Mix down to one channel. Default `false`.\n *\n * Worth turning on for speech headed to a server: a microphone recorded in stereo\n * carries two nearly identical channels and doubles the upload for nothing.\n */\n mono?: boolean;\n /**\n * Resample to this rate. Default: keep the source rate.\n *\n * 16000 is the rate most speech-to-text APIs want, and dropping 48 kHz to 16 kHz\n * removes two thirds of the bytes with no audible loss on voice.\n */\n sampleRate?: number;\n}\n\n/** Bytes per sample in the output. 16-bit PCM is what every decoder accepts. */\nconst BYTES_PER_SAMPLE = 2;\n\n/**\n * Wrap PCM in a 16-bit RIFF/WAVE container.\n *\n * The header is the 44-byte canonical form: `RIFF` size, `WAVE`, a 16-byte `fmt `\n * chunk declaring format 1 (uncompressed PCM), then `data`. Samples are interleaved\n * and clamped before scaling, because a value even slightly past ±1 wraps around when\n * truncated to 16 bits and turns a loud passage into a burst of noise.\n *\n * Implemented here rather than pulled from a package: it is a fixed header and a\n * scaling loop, roughly forty lines, and a dependency for it would put its own\n * version bounds on every consumer of this SDK to save writing them.\n *\n * @param audio - Channels and sample rate.\n * @returns A `Blob` of type `audio/wav`.\n */\nexport function encodeWav({ channels, sampleRate }: PcmAudio): Blob {\n const channelCount = Math.max(1, channels.length);\n const frameCount = channels[0]?.length ?? 0;\n const dataBytes = frameCount * channelCount * BYTES_PER_SAMPLE;\n const buffer = new ArrayBuffer(44 + dataBytes);\n const view = new DataView(buffer);\n\n const ascii = (offset: number, text: string): void => {\n for (let index = 0; index < text.length; index += 1) {\n view.setUint8(offset + index, text.charCodeAt(index));\n }\n };\n\n const byteRate = sampleRate * channelCount * BYTES_PER_SAMPLE;\n ascii(0, \"RIFF\");\n view.setUint32(4, 36 + dataBytes, true);\n ascii(8, \"WAVE\");\n ascii(12, \"fmt \");\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, channelCount, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, channelCount * BYTES_PER_SAMPLE, true);\n view.setUint16(34, BYTES_PER_SAMPLE * 8, true);\n ascii(36, \"data\");\n view.setUint32(40, dataBytes, true);\n\n let offset = 44;\n for (let frame = 0; frame < frameCount; frame += 1) {\n for (let channel = 0; channel < channelCount; channel += 1) {\n const sample = channels[channel]?.[frame] ?? 0;\n const clamped = sample < -1 ? -1 : sample > 1 ? 1 : sample;\n // Asymmetric scaling: 16-bit PCM runs −32768…32767.\n view.setInt16(offset, clamped < 0 ? clamped * 0x8000 : clamped * 0x7fff, true);\n offset += BYTES_PER_SAMPLE;\n }\n }\n\n return new Blob([buffer], { type: \"audio/wav\" });\n}\n\n/** Average every channel of a decoded buffer into one. */\nfunction mixToMono(buffer: AudioBuffer): Float32Array {\n const frames = buffer.length;\n const out = new Float32Array(frames);\n for (let channel = 0; channel < buffer.numberOfChannels; channel += 1) {\n const data = buffer.getChannelData(channel);\n for (let frame = 0; frame < frames; frame += 1) out[frame] += data[frame];\n }\n if (buffer.numberOfChannels > 1) {\n for (let frame = 0; frame < frames; frame += 1) out[frame] /= buffer.numberOfChannels;\n }\n return out;\n}\n\n/**\n * Convert a recording to 16-bit WAV, with no dependency.\n *\n * `MediaRecorder` cannot produce WAV — it emits Opus on Chromium and Firefox and AAC\n * on Safari — so a backend that insists on WAV has to be served either by a\n * server-side transcode or by this. It decodes through `AudioContext.decodeAudioData`,\n * which is the browser's own decoder for whatever container the recorder chose, then\n * re-encodes the PCM.\n *\n * !!! The cost is real: WAV is uncompressed, so the same voice note that is 40 KB in\n * Opus is roughly 500 KB here at 48 kHz stereo. `{ mono: true, sampleRate: 16000 }`\n * takes that to about 80 KB and is what a speech-to-text endpoint wants anyway.\n *\n * Resampling uses `OfflineAudioContext`, i.e. the browser's own resampler, rather\n * than a hand-rolled one — this is exactly the \"we want the underlying call, not a\n * wrapper\" case.\n *\n * @param blob - A recording from {@link createAudioRecorder}, or any decodable audio.\n * @param options - See {@link WavOptions}.\n * @returns A `Blob` of type `audio/wav`.\n * @throws When the environment has no Web Audio, or the blob cannot be decoded.\n */\nexport async function blobToWav(blob: Blob, options: WavOptions = {}): Promise<Blob> {\n const { mono = false, sampleRate: target } = options;\n\n const Ctor: typeof AudioContext | undefined =\n typeof AudioContext !== \"undefined\"\n ? AudioContext\n : (globalThis as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Ctor) throw new Error(\"Web Audio is not available in this environment.\");\n\n const context = new Ctor();\n let decoded: AudioBuffer;\n try {\n decoded = await context.decodeAudioData(await blob.arrayBuffer());\n } finally {\n void context.close().catch(() => undefined);\n }\n\n const wantedChannels = mono ? 1 : decoded.numberOfChannels;\n const wantedRate = target ?? decoded.sampleRate;\n\n if (wantedRate === decoded.sampleRate) {\n const channels = mono\n ? [mixToMono(decoded)]\n : Array.from({ length: decoded.numberOfChannels }, (_, index) =>\n decoded.getChannelData(index),\n );\n return encodeWav({ channels, sampleRate: decoded.sampleRate });\n }\n\n const Offline: typeof OfflineAudioContext | undefined =\n typeof OfflineAudioContext !== \"undefined\" ? OfflineAudioContext : undefined;\n if (!Offline) throw new Error(\"Resampling requires OfflineAudioContext.\");\n\n const frames = Math.max(1, Math.round((decoded.duration * wantedRate) | 0) || 1);\n const offline = new Offline(wantedChannels, frames, wantedRate);\n const source = offline.createBufferSource();\n source.buffer = decoded;\n source.connect(offline.destination);\n source.start();\n const rendered = await offline.startRendering();\n\n return encodeWav({\n channels: Array.from({ length: rendered.numberOfChannels }, (_, index) =>\n rendered.getChannelData(index),\n ),\n sampleRate: rendered.sampleRate,\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthGuard.cjs","names":[],"sources":["../../src/auth/AuthGuard.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nexport interface AuthGuardProps {\n /** Whether the current user is authenticated. */\n isAuthenticated: boolean;\n /** Element to render when authenticated. */\n children: ReactNode;\n /** Element to render when not authenticated. Use this to redirect (e.g. `<Navigate to=\"/login\" />`). */\n fallback: ReactNode;\n}\n\n/**\n * Router-agnostic auth gate. The caller decides what to render in either\n * branch — typically `<Outlet />` for protected layouts and `<Navigate />` for\n * the redirect. Pair with {@link createAuthStore} or any custom auth source.\n *\n * @example\n * <AuthGuard\n * isAuthenticated={useAuthStore((s) => s.isAuthenticated)}\n * fallback={<Navigate to=\"/login\" replace />}\n * >\n * <Outlet />\n * </AuthGuard>\n */\nexport function AuthGuard({ isAuthenticated, children, fallback }: AuthGuardProps) {\n return <>{isAuthenticated ? children : fallback}</>;\n}\n"],"mappings":"mCAwBA,
|
|
1
|
+
{"version":3,"file":"AuthGuard.cjs","names":["_Fragment","react_jsx_runtime","AuthGuard","isAuthenticated","children","fallback"],"sources":["../../src/auth/AuthGuard.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nexport interface AuthGuardProps {\n /** Whether the current user is authenticated. */\n isAuthenticated: boolean;\n /** Element to render when authenticated. */\n children: ReactNode;\n /** Element to render when not authenticated. Use this to redirect (e.g. `<Navigate to=\"/login\" />`). */\n fallback: ReactNode;\n}\n\n/**\n * Router-agnostic auth gate. The caller decides what to render in either\n * branch — typically `<Outlet />` for protected layouts and `<Navigate />` for\n * the redirect. Pair with {@link createAuthStore} or any custom auth source.\n *\n * @example\n * <AuthGuard\n * isAuthenticated={useAuthStore((s) => s.isAuthenticated)}\n * fallback={<Navigate to=\"/login\" replace />}\n * >\n * <Outlet />\n * </AuthGuard>\n */\nexport function AuthGuard({ isAuthenticated, children, fallback }: AuthGuardProps) {\n return <>{isAuthenticated ? children : fallback}</>;\n}\n"],"mappings":"A,mCAwBA,SAAgBE,EAAU,CAAE,kBAAiB,WAAU,YAA4B,CAC/E,OAAO,EAAAD,EAAA,IAAA,CAAAD,EAAAA,SAAA,CAAA,SAAGG,EAAkBC,EAAWC,CAAW,CAAA,CACtD,C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthGuard.js","names":[],"sources":["../../src/auth/AuthGuard.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nexport interface AuthGuardProps {\n /** Whether the current user is authenticated. */\n isAuthenticated: boolean;\n /** Element to render when authenticated. */\n children: ReactNode;\n /** Element to render when not authenticated. Use this to redirect (e.g. `<Navigate to=\"/login\" />`). */\n fallback: ReactNode;\n}\n\n/**\n * Router-agnostic auth gate. The caller decides what to render in either\n * branch — typically `<Outlet />` for protected layouts and `<Navigate />` for\n * the redirect. Pair with {@link createAuthStore} or any custom auth source.\n *\n * @example\n * <AuthGuard\n * isAuthenticated={useAuthStore((s) => s.isAuthenticated)}\n * fallback={<Navigate to=\"/login\" replace />}\n * >\n * <Outlet />\n * </AuthGuard>\n */\nexport function AuthGuard({ isAuthenticated, children, fallback }: AuthGuardProps) {\n return <>{isAuthenticated ? children : fallback}</>;\n}\n"],"mappings":";;AAwBA,
|
|
1
|
+
{"version":3,"file":"AuthGuard.js","names":["_Fragment","AuthGuard","Fragment","isAuthenticated","children","fallback"],"sources":["../../src/auth/AuthGuard.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nexport interface AuthGuardProps {\n /** Whether the current user is authenticated. */\n isAuthenticated: boolean;\n /** Element to render when authenticated. */\n children: ReactNode;\n /** Element to render when not authenticated. Use this to redirect (e.g. `<Navigate to=\"/login\" />`). */\n fallback: ReactNode;\n}\n\n/**\n * Router-agnostic auth gate. The caller decides what to render in either\n * branch — typically `<Outlet />` for protected layouts and `<Navigate />` for\n * the redirect. Pair with {@link createAuthStore} or any custom auth source.\n *\n * @example\n * <AuthGuard\n * isAuthenticated={useAuthStore((s) => s.isAuthenticated)}\n * fallback={<Navigate to=\"/login\" replace />}\n * >\n * <Outlet />\n * </AuthGuard>\n */\nexport function AuthGuard({ isAuthenticated, children, fallback }: AuthGuardProps) {\n return <>{isAuthenticated ? children : fallback}</>;\n}\n"],"mappings":";;AAwBA,SAAgBC,EAAU,EAAE,oBAAiB,aAAU,eAA4B;CAC/E,OAAO,kBAAAD,GAAA,EAAA,UAAGG,IAAkBC,IAAWC,EAAW,CAAA;AACtD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-auth-store.cjs","names":[],"sources":["../../src/auth/create-auth-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\n\nexport interface AuthState<TUser> {\n user: TUser | null;\n token: string | null;\n isAuthenticated: boolean;\n setSession: (session: { user: TUser; token: string }) => void;\n setUser: (user: TUser | null) => void;\n setToken: (token: string | null) => void;\n logout: () => void;\n}\n\nexport interface CreateAuthStoreOptions<TUser> {\n /** Persist key used in storage (default: \"tempest-auth\"). */\n name?: string;\n /** Which storage to persist into (default: localStorage). */\n storage?: \"local\" | \"session\";\n /** Initial user (useful for SSR hydration). */\n initialUser?: TUser | null;\n /** Initial token. */\n initialToken?: string | null;\n}\n\n/**\n * Build a typed Zustand auth store with `persist` middleware. Each app passes\n * its own `TUser` shape so the SDK does not own the user model.\n *\n * @example\n * const useAuthStore = createAuthStore<UserResponse>();\n * const { user, token, logout } = useAuthStore();\n */\nexport function createAuthStore<TUser>(options: CreateAuthStoreOptions<TUser> = {}) {\n const name = options.name ?? \"tempest-auth\";\n const storageImpl = options.storage === \"session\" ? () => sessionStorage : () => localStorage;\n\n return create<AuthState<TUser>>()(\n persist(\n (set) => ({\n user: options.initialUser ?? null,\n token: options.initialToken ?? null,\n isAuthenticated: !!options.initialToken,\n setSession: ({ user, token }) => set({ user, token, isAuthenticated: true }),\n setUser: (user) => set({ user }),\n setToken: (token) => set({ token, isAuthenticated: !!token }),\n logout: () => set({ user: null, token: null, isAuthenticated: false }),\n }),\n {\n name,\n storage: createJSONStorage(storageImpl),\n partialize: (state) => ({ user: state.user, token: state.token }),\n onRehydrateStorage: () => (state) => {\n if (state) state.isAuthenticated = !!state.token;\n },\n },\n ),\n );\n}\n"],"mappings":"yDAgCA,
|
|
1
|
+
{"version":3,"file":"create-auth-store.cjs","names":["create","persist","createJSONStorage","zustand","zustand_middleware","createAuthStore","name","options","storageImpl","set","token","state"],"sources":["../../src/auth/create-auth-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\n\nexport interface AuthState<TUser> {\n user: TUser | null;\n token: string | null;\n isAuthenticated: boolean;\n setSession: (session: { user: TUser; token: string }) => void;\n setUser: (user: TUser | null) => void;\n setToken: (token: string | null) => void;\n logout: () => void;\n}\n\nexport interface CreateAuthStoreOptions<TUser> {\n /** Persist key used in storage (default: \"tempest-auth\"). */\n name?: string;\n /** Which storage to persist into (default: localStorage). */\n storage?: \"local\" | \"session\";\n /** Initial user (useful for SSR hydration). */\n initialUser?: TUser | null;\n /** Initial token. */\n initialToken?: string | null;\n}\n\n/**\n * Build a typed Zustand auth store with `persist` middleware. Each app passes\n * its own `TUser` shape so the SDK does not own the user model.\n *\n * @example\n * const useAuthStore = createAuthStore<UserResponse>();\n * const { user, token, logout } = useAuthStore();\n */\nexport function createAuthStore<TUser>(options: CreateAuthStoreOptions<TUser> = {}) {\n const name = options.name ?? \"tempest-auth\";\n const storageImpl = options.storage === \"session\" ? () => sessionStorage : () => localStorage;\n\n return create<AuthState<TUser>>()(\n persist(\n (set) => ({\n user: options.initialUser ?? null,\n token: options.initialToken ?? null,\n isAuthenticated: !!options.initialToken,\n setSession: ({ user, token }) => set({ user, token, isAuthenticated: true }),\n setUser: (user) => set({ user }),\n setToken: (token) => set({ token, isAuthenticated: !!token }),\n logout: () => set({ user: null, token: null, isAuthenticated: false }),\n }),\n {\n name,\n storage: createJSONStorage(storageImpl),\n partialize: (state) => ({ user: state.user, token: state.token }),\n onRehydrateStorage: () => (state) => {\n if (state) state.isAuthenticated = !!state.token;\n },\n },\n ),\n );\n}\n"],"mappings":"A,yDAgCA,SAAgBK,EAAuB,EAAyC,CAAC,EAAG,CAChF,IAAMC,EAAOC,EAAQ,MAAQ,eACvBC,EAAcD,EAAQ,UAAY,cAAkB,mBAAuB,aAEjF,OAAA,EAAOP,EAAAA,OAAAA,CAAyB,CAAC,EAAA,EAC7BC,EAAAA,QAAAA,CACK,IAAS,CACN,KAAMM,EAAQ,aAAe,KAC7B,MAAOA,EAAQ,cAAgB,KAC/B,gBAAiB,CAAC,CAACA,EAAQ,aAC3B,YAAa,CAAE,OAAM,WAAYE,EAAI,CAAE,OAAM,QAAO,gBAAiB,EAAK,CAAC,EAC3E,QAAU,GAASA,EAAI,CAAE,MAAK,CAAC,EAC/B,SAAW,GAAUA,EAAI,CAAE,QAAO,gBAAiB,CAAC,CAACC,CAAM,CAAC,EAC5D,WAAcD,EAAI,CAAE,KAAM,KAAM,MAAO,KAAM,gBAAiB,EAAM,CAAC,CACzE,GACA,CACI,OACA,SAAA,EAASP,EAAAA,kBAAAA,CAAkBM,CAAW,EACtC,WAAa,IAAW,CAAE,KAAMG,EAAM,KAAM,MAAOA,EAAM,KAAM,GAC/D,uBAA2B,GAAU,CAC7BA,IAAO,EAAM,gBAAkB,CAAC,CAACA,EAAM,MAC/C,CACJ,CACJ,CACJ,CACJ,C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-auth-store.js","names":[],"sources":["../../src/auth/create-auth-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\n\nexport interface AuthState<TUser> {\n user: TUser | null;\n token: string | null;\n isAuthenticated: boolean;\n setSession: (session: { user: TUser; token: string }) => void;\n setUser: (user: TUser | null) => void;\n setToken: (token: string | null) => void;\n logout: () => void;\n}\n\nexport interface CreateAuthStoreOptions<TUser> {\n /** Persist key used in storage (default: \"tempest-auth\"). */\n name?: string;\n /** Which storage to persist into (default: localStorage). */\n storage?: \"local\" | \"session\";\n /** Initial user (useful for SSR hydration). */\n initialUser?: TUser | null;\n /** Initial token. */\n initialToken?: string | null;\n}\n\n/**\n * Build a typed Zustand auth store with `persist` middleware. Each app passes\n * its own `TUser` shape so the SDK does not own the user model.\n *\n * @example\n * const useAuthStore = createAuthStore<UserResponse>();\n * const { user, token, logout } = useAuthStore();\n */\nexport function createAuthStore<TUser>(options: CreateAuthStoreOptions<TUser> = {}) {\n const name = options.name ?? \"tempest-auth\";\n const storageImpl = options.storage === \"session\" ? () => sessionStorage : () => localStorage;\n\n return create<AuthState<TUser>>()(\n persist(\n (set) => ({\n user: options.initialUser ?? null,\n token: options.initialToken ?? null,\n isAuthenticated: !!options.initialToken,\n setSession: ({ user, token }) => set({ user, token, isAuthenticated: true }),\n setUser: (user) => set({ user }),\n setToken: (token) => set({ token, isAuthenticated: !!token }),\n logout: () => set({ user: null, token: null, isAuthenticated: false }),\n }),\n {\n name,\n storage: createJSONStorage(storageImpl),\n partialize: (state) => ({ user: state.user, token: state.token }),\n onRehydrateStorage: () => (state) => {\n if (state) state.isAuthenticated = !!state.token;\n },\n },\n ),\n );\n}\n"],"mappings":";;;AAgCA,
|
|
1
|
+
{"version":3,"file":"create-auth-store.js","names":["createAuthStore","name","options","storageImpl","create","persist","set","token","createJSONStorage","state"],"sources":["../../src/auth/create-auth-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\n\nexport interface AuthState<TUser> {\n user: TUser | null;\n token: string | null;\n isAuthenticated: boolean;\n setSession: (session: { user: TUser; token: string }) => void;\n setUser: (user: TUser | null) => void;\n setToken: (token: string | null) => void;\n logout: () => void;\n}\n\nexport interface CreateAuthStoreOptions<TUser> {\n /** Persist key used in storage (default: \"tempest-auth\"). */\n name?: string;\n /** Which storage to persist into (default: localStorage). */\n storage?: \"local\" | \"session\";\n /** Initial user (useful for SSR hydration). */\n initialUser?: TUser | null;\n /** Initial token. */\n initialToken?: string | null;\n}\n\n/**\n * Build a typed Zustand auth store with `persist` middleware. Each app passes\n * its own `TUser` shape so the SDK does not own the user model.\n *\n * @example\n * const useAuthStore = createAuthStore<UserResponse>();\n * const { user, token, logout } = useAuthStore();\n */\nexport function createAuthStore<TUser>(options: CreateAuthStoreOptions<TUser> = {}) {\n const name = options.name ?? \"tempest-auth\";\n const storageImpl = options.storage === \"session\" ? () => sessionStorage : () => localStorage;\n\n return create<AuthState<TUser>>()(\n persist(\n (set) => ({\n user: options.initialUser ?? null,\n token: options.initialToken ?? null,\n isAuthenticated: !!options.initialToken,\n setSession: ({ user, token }) => set({ user, token, isAuthenticated: true }),\n setUser: (user) => set({ user }),\n setToken: (token) => set({ token, isAuthenticated: !!token }),\n logout: () => set({ user: null, token: null, isAuthenticated: false }),\n }),\n {\n name,\n storage: createJSONStorage(storageImpl),\n partialize: (state) => ({ user: state.user, token: state.token }),\n onRehydrateStorage: () => (state) => {\n if (state) state.isAuthenticated = !!state.token;\n },\n },\n ),\n );\n}\n"],"mappings":";;;AAgCA,SAAgBA,EAAuB,IAAyC,CAAC,GAAG;CAChF,IAAMC,IAAOC,EAAQ,QAAQ,gBACvBC,IAAcD,EAAQ,YAAY,kBAAkB,uBAAuB;CAEjF,OAAOE,EAAyB,CAAC,CAC7BC,GACK,OAAS;EACN,MAAMH,EAAQ,eAAe;EAC7B,OAAOA,EAAQ,gBAAgB;EAC/B,iBAAiB,CAAC,CAACA,EAAQ;EAC3B,aAAa,EAAE,SAAM,eAAYI,EAAI;GAAE;GAAM;GAAO,iBAAiB;EAAK,CAAC;EAC3E,UAAU,MAASA,EAAI,EAAE,QAAK,CAAC;EAC/B,WAAW,MAAUA,EAAI;GAAE;GAAO,iBAAiB,CAAC,CAACC;EAAM,CAAC;EAC5D,cAAcD,EAAI;GAAE,MAAM;GAAM,OAAO;GAAM,iBAAiB;EAAM,CAAC;CACzE,IACA;EACI;EACA,SAASE,EAAkBL,CAAW;EACtC,aAAa,OAAW;GAAE,MAAMM,EAAM;GAAM,OAAOA,EAAM;EAAM;EAC/D,2BAA2B,MAAU;GACjC,AAAIA,MAAO,EAAM,kBAAkB,CAAC,CAACA,EAAM;EAC/C;CACJ,CACJ,CACJ;AACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../utils/dev-mode.cjs"),t=require("../http/errors.cjs"),n=require("../http/api-client.cjs"),r=require("./create-auth-store.cjs"),i=require("./refresh-queue.cjs");var a=1e4;function o(e,t){return e.length===0?Promise.resolve():new Promise(n=>{let r=setTimeout(n,t);Promise.allSettled(e).then(()=>{clearTimeout(r),n()})})}function s(e){let t=e??{};return{token:t.access_token,refreshToken:t.refresh_token}}function c(c){let{baseURL:l,prefix:u,loginPath:d=`/api/auth/login`,refreshPath:ee=`/api/auth/refresh`,logoutPath:f,onLogoutError:te,mePath:p,storeName:m=`tempest-auth`,storage:h=`local`,withCredentials:g=!1,csrf:_,fetcher:v,parseTokens:y=s,parseUser:b,refreshBody:x=e=>e?{refresh_token:e}:void 0,retry:ne,redirectTo:S,onSessionEnd:C}=c,w=r.createAuthStore({name:m,storage:h}),T=`${m}-refresh`;function E(){return typeof window>`u`?null:h===`session`?window.sessionStorage:window.localStorage}function D(){return E()?.getItem(T)??null}function O(e){let t=E();t&&(e?t.setItem(T,e):t.removeItem(T))}let k=()=>w.getState(),A=()=>k().token;async function j(){if(!p)return k().user;let e=M,n;try{n=await $.get(p,{skipAuthRetry:!0})}catch(n){throw M===e?n:new t.SessionSupersededError}if(M!==e)throw new t.SessionSupersededError;return k().setUser(n),n}let M=0,N=Promise.resolve(),P=0,F=new Set,I=new Set,L=new Set,R=new Set;function z(e){let t=e.then(()=>void 0,()=>void 0);return L.add(t),t.then(()=>L.delete(t)),e}function B(t){e.isDevBuild()&&console.warn(`[tempest-react-sdk] createTempestAuth: logoutPath revocation failed`,t);try{te?.(t)}catch{return}}function V(e){let t=(async()=>{try{await e()}catch(e){B(e)}})();return I.add(t),t.then(()=>I.delete(t)),t}function H(e,t){f&&!g&&t&&V(()=>U(e).post(f,{body:x(t)}))}function U(e){return n.createApiClient({baseURL:l,prefix:u,withCredentials:g,csrf:_,fetcher:v,getToken:()=>e})}function W(){M+=1,K=i.createRefreshQueue(G)}async function G(){let e=M;R.add(e);try{let n;try{n=await z($.post(ee,{body:x(D()),skipAuth:!0}))}catch(n){throw M===e?n:new t.SessionSupersededError}let{token:r,refreshToken:i}=y(n);if(M!==e)throw H(r??null,i),new t.SessionSupersededError;k().setToken(r),i&&O(i)}finally{R.delete(e)}}let K=i.createRefreshQueue(G);function q(e){let t=A();return t===null?!1:e.token===null||e.token!==t}function J(){return A()===null&&D()===null}async function Y(){if(!(F.size>0&&P===0&&(await Promise.allSettled([...F]),A()!==null)))throw Error(`anonymous 401 with no session`)}async function X(e){if(e.epoch!==null&&e.epoch!==M)throw new t.SessionSupersededError;if(q(e)){if(e.token===null)throw Error(`anonymous 401 under a live session`)
|
|
1
|
+
const e=require("../utils/dev-mode.cjs"),t=require("../http/errors.cjs"),n=require("../http/api-client.cjs"),r=require("./create-auth-store.cjs"),i=require("./refresh-queue.cjs");var a=1e4;function o(e,t){return e.length===0?Promise.resolve():new Promise(n=>{let r=setTimeout(n,t);Promise.allSettled(e).then(()=>{clearTimeout(r),n()})})}function s(e){let t=e??{};return{token:t.access_token,refreshToken:t.refresh_token}}function c(c){let{baseURL:l,prefix:u,loginPath:d=`/api/auth/login`,refreshPath:ee=`/api/auth/refresh`,logoutPath:f,onLogoutError:te,mePath:p,storeName:m=`tempest-auth`,storage:h=`local`,withCredentials:g=!1,csrf:_,fetcher:v,parseTokens:y=s,parseUser:b,refreshBody:x=e=>e?{refresh_token:e}:void 0,retry:ne,redirectTo:S,onSessionEnd:C}=c,w=r.createAuthStore({name:m,storage:h}),T=`${m}-refresh`;function E(){return typeof window>`u`?null:h===`session`?window.sessionStorage:window.localStorage}function D(){return E()?.getItem(T)??null}function O(e){let t=E();t&&(e?t.setItem(T,e):t.removeItem(T))}let k=()=>w.getState(),A=()=>k().token;async function j(){if(!p)return k().user;let e=M,n;try{n=await $.get(p,{skipAuthRetry:!0})}catch(n){throw M===e?n:new t.SessionSupersededError}if(M!==e)throw new t.SessionSupersededError;return k().setUser(n),n}let M=0,N=Promise.resolve(),P=0,F=new Set,I=new Set,L=new Set,R=new Set;function z(e){let t=e.then(()=>void 0,()=>void 0);return L.add(t),t.then(()=>L.delete(t)),e}function B(t){e.isDevBuild()&&console.warn(`[tempest-react-sdk] createTempestAuth: logoutPath revocation failed`,t);try{te?.(t)}catch{return}}function V(e){let t=(async()=>{try{await e()}catch(e){B(e)}})();return I.add(t),t.then(()=>I.delete(t)),t}function H(e,t){f&&!g&&t&&V(()=>U(e).post(f,{body:x(t)}))}function U(e){return n.createApiClient({baseURL:l,prefix:u,withCredentials:g,csrf:_,fetcher:v,getToken:()=>e})}function W(){M+=1,K=i.createRefreshQueue(G)}async function G(){let e=M;R.add(e);try{let n;try{n=await z($.post(ee,{body:x(D()),skipAuth:!0}))}catch(n){throw M===e?n:new t.SessionSupersededError}let{token:r,refreshToken:i}=y(n);if(M!==e)throw H(r??null,i),new t.SessionSupersededError;k().setToken(r),i&&O(i)}finally{R.delete(e)}}let K=i.createRefreshQueue(G);function q(e){let t=A();return t===null?!1:e.token===null||e.token!==t}function J(){return A()===null&&D()===null}async function Y(){if(!(F.size>0&&P===0&&(await Promise.allSettled([...F]),A()!==null)))throw Error(`anonymous 401 with no session`)}async function X(e){if(e.epoch!==null&&e.epoch!==M)throw new t.SessionSupersededError;if(q(e)){if(e.token===null)throw Error(`anonymous 401 under a live session`)}else return e.token===null&&J()?R.has(M)?K():Y():K()}async function Z(e,n){if(n.epoch!==null&&n.epoch!==M)throw new t.SessionSupersededError;q(n)||n.token===null&&J()||await se()}async function re(e){I.size>0&&await o([...I],a);let t=ie(e);F.add(t);try{return await t}finally{F.delete(t)}}async function ie(e){let n=M,r=await z($.post(d,{body:e,skipAuth:!0})),{token:i,refreshToken:a}=y(r);if(M!==n)throw H(i??null,a),new t.SessionSupersededError;let{done:o}=Q(`switch`),s=M;if(await o.catch(()=>void 0),M!==s)throw new t.SessionSupersededError;k().setToken(i),O(a??null);let c=b?.(r);return c==null?j():(k().setUser(c),c)}function Q(e){let{user:t,token:n}=k(),r=n!==null||t!==null||D()!==null;if((r||e!==`expired`)&&W(),k().logout(),O(null),!r)return{live:r,done:e===`switch`||P===0?N:Promise.resolve()};if(!C)return{live:r,done:Promise.resolve()};let i=U(n);P+=1;let a=(async()=>{try{await C({reason:e,user:t,token:n,api:i})}finally{--P}})();return N=a.then(()=>void 0,()=>void 0),{live:r,done:a.then(()=>void 0)}}function ae(){if(!f)return null;let e=f,{token:t,user:n}=k(),r=D();if(!g&&t===null&&n===null&&r===null)return null;let i=g?[...L]:[];return V(async()=>(await o(i,a),U(t).post(e,{body:x(r)})))}function oe(){let e=ae(),{done:t}=Q(`logout`);return e?Promise.all([e,t]).then(()=>void 0):t}async function se(){let{live:e,done:t}=Q(`expired`);await t.catch(()=>void 0),e&&S&&typeof window<`u`&&window.location.assign(S)}let $=n.createApiClient({baseURL:l,prefix:u,withCredentials:g,csrf:_,fetcher:v,getToken:A,getSessionEpoch:()=>M,refresh:X,retry:ne,onUnauthorized:Z});return{useAuthStore:w,api:$,login:re,logout:oe,refresh:()=>K(),getToken:A}}exports.createTempestAuth=c;
|
|
2
2
|
//# sourceMappingURL=create-tempest-auth.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-tempest-auth.cjs","names":[],"sources":["../../src/auth/create-tempest-auth.ts"],"sourcesContent":["import { createApiClient, SessionSupersededError } from \"../http\";\nimport { isDevBuild } from \"../utils/dev-mode\";\nimport type { ApiClient, CsrfOptions, RequestAuthContext, RetryOptions } from \"../http\";\nimport { createAuthStore } from \"./create-auth-store\";\nimport type { AuthState } from \"./create-auth-store\";\nimport { createRefreshQueue } from \"./refresh-queue\";\n\n/** The token envelope returned by a Tempest FastAPI SDK login/refresh route. */\nexport interface TempestTokenResponse {\n /** The bearer access token. */\n access_token: string;\n /** Token type — always `\"bearer\"` for the SDK. */\n token_type?: string;\n /** Optional refresh token (when the API returns it in the body, not a cookie). */\n refresh_token?: string;\n}\n\n/**\n * What {@link CreateTempestAuthOptions.onSessionEnd} receives about the session\n * that just ended.\n */\nexport interface TempestSessionEnd<TUser> {\n /**\n * Why it ended: `\"logout\"` for an explicit `logout()`, `\"expired\"` when the\n * client ended it on its own — the refresh failed, or the request replayed\n * after a refresh still came back `401` — and `\"switch\"` when `login()`\n * succeeded for another set of credentials while this session was live.\n */\n reason: \"logout\" | \"expired\" | \"switch\";\n /** The user of the ended session, as the store held it. */\n user: TUser | null;\n /** The access token of the ended session, or `null` when it had none. */\n token: string | null;\n /**\n * A client that sends the ended session's access token — the store has\n * already been cleared, so `auth.api` no longer does.\n *\n * Use it for the server-side half of the teardown, such as deleting this\n * browser's push subscription. It never refreshes and never ends a session:\n * a `401` here just rejects. On `\"logout\"` the token is usually still\n * valid; on `\"expired\"` the server has already refused it, so expect the\n * call to fail and keep the local half of the teardown independent of it.\n */\n api: ApiClient;\n}\n\nexport interface CreateTempestAuthOptions<TUser> {\n /** Base URL of the API. Required. */\n baseURL: string;\n /**\n * Path segment the API is mounted under, such as `\"/api\"`, forwarded to\n * every client this preset builds — see the `prefix` option of\n * `createApiClient`.\n *\n * Safe to combine with the default paths below: the prefix is applied at\n * most once, so `\"/api/auth/login\"` under a `\"/api\"` prefix stays\n * `\"/api/auth/login\"`.\n */\n prefix?: string;\n /** Login route (`POST`). Default: `\"/api/auth/login\"`. */\n loginPath?: string;\n /** Refresh route (`POST`). Default: `\"/api/auth/refresh\"`. */\n refreshPath?: string;\n /**\n * Server-side logout route (`POST`), called by an explicit `logout()` to\n * revoke the ending session. Off by default.\n *\n * `logout()` alone only clears client state, and an HttpOnly refresh\n * cookie is out of its reach: with `withCredentials`, the cookie stays\n * redeemable until the backend revokes it. The tempest-fastapi-sdk mounts\n * `POST /auth/logout` (`204`, idempotent) when it stores refresh tokens in\n * the database — at `/auth/cookie/logout` when it delivers tokens both in\n * the body and in cookies — and stateless refresh tokens cannot be\n * revoked, so the route is absent there.\n *\n * The request goes out through the ended session's client (the same one\n * `onSessionEnd` receives, carrying its access token), with the body that\n * `refreshBody` builds from the refresh token stored before the clear:\n * `{ refresh_token }` in body mode, no body in cookie mode, which is what\n * that route reads. The local clear does not wait on it — `isAuthenticated`\n * is still `false` when `logout()` returns — but the promise `logout()`\n * returns does, so a navigation after `await auth.logout()` does not abort\n * it. A failure (network, `4xx`, `5xx`) is swallowed: revocation is\n * best-effort and never keeps the user signed in. A `login()` started\n * while it is in flight waits for it, because the logout response clears\n * the auth cookies and would otherwise clear the new account's.\n *\n * Only an explicit `logout()` revokes. On `\"switch\"` the cookie jar already\n * holds the new account's cookie, and on `\"expired\"` the server has already\n * refused the session.\n *\n * Overlapping sign-outs each send their own revocation, and `login()` waits\n * for all of them — at most 10 seconds, so one that\n * never answers delays the sign-in instead of blocking it. In cookie mode,\n * a revocation also waits (within the same bound) for the login and\n * refresh responses that were in flight when `logout()` ran: the server\n * sets the ended session's cookie when that response arrives, and a\n * revocation sent earlier would leave it behind. In body mode, the refresh\n * token such a superseded response brings back is revoked on its own.\n */\n logoutPath?: string;\n /**\n * Called with the error when a `logoutPath` revocation fails — network\n * failure, `4xx` or `5xx`. `logout()` still resolves, since the local\n * sign-out already happened; this is where the app logs or retries. A dev\n * build also warns in the console. Errors it throws are swallowed.\n */\n onLogoutError?: (error: unknown) => void;\n /** Optional current-user route (`GET`) called after login/refresh. */\n mePath?: string;\n /** Persist key for the store. Default: `\"tempest-auth\"`. */\n storeName?: string;\n /** Storage backend. Default: `\"local\"`. */\n storage?: \"local\" | \"session\";\n /** Send cookies (needed when the refresh token lives in an httpOnly cookie). */\n withCredentials?: boolean;\n /**\n * CSRF token for every write, forwarded to `createApiClient`'s `csrf`. Off\n * by default; `true` echoes the `csrf_token` cookie as `X-CSRF-Token`.\n *\n * The pair to `withCredentials`: once the refresh token lives in a cookie,\n * the browser attaches it to a `POST /auth/refresh` that another site\n * triggers. Login and refresh carry the header too — they go out with\n * `skipAuth`, and the CSRF header does not follow auth.\n */\n csrf?: boolean | CsrfOptions;\n /** Custom fetch implementation (testing / SSR). Defaults to `globalThis.fetch`. */\n fetcher?: typeof fetch;\n /**\n * Extract tokens from a login/refresh response. Default reads\n * `access_token` + `refresh_token`.\n */\n parseTokens?: (data: unknown) => { token: string; refreshToken?: string };\n /** Pull the user out of the login response, when the API embeds it. */\n parseUser?: (data: unknown) => TUser | null;\n /**\n * Build the refresh request body. Default sends `{ refresh_token }` when a\n * refresh token is stored, else `undefined` (cookie-based refresh).\n */\n refreshBody?: (refreshToken: string | null) => unknown;\n /**\n * Retry policy for `api`, forwarded to {@link createApiClient}. Off by\n * default; `true` enables the conservative built-in policy, which never\n * replays a write.\n */\n retry?: boolean | RetryOptions;\n /**\n * Where to send the browser when the session ends — a hard navigation via\n * `window.location.assign`, after the store is cleared.\n *\n * **Prefer leaving this unset.** `logout()` already clears the store, so a\n * `<RouteGuard when={isAuthenticated} redirectTo=\"/login\">` wrapped around\n * the protected area navigates on its own, which keeps the SPA alive and the\n * router history intact. Reach for this only when the expiry can happen\n * outside any guarded subtree and a full reload is acceptable.\n */\n redirectTo?: string;\n /**\n * Tear down per-user state when a session ends — on an explicit `logout()`,\n * when the client ends the session on its own after a failed refresh, and\n * when a successful `login()` replaces a live session. Without it, the automatic path had no place to hook into, and\n * state derived from the user (a push subscription, a persisted query\n * cache, a background-sync queue) survived into the next account.\n *\n * Order, on every path:\n *\n * 1. any refresh still in flight is superseded, so it cannot reinstall the\n * ending session's tokens;\n * 2. the store and the stored refresh token are cleared — synchronously, so\n * `isAuthenticated` is already `false` when `logout()` returns;\n * 3. this hook runs with a {@link TempestSessionEnd} whose `api` still sends\n * the ended session's access token;\n * 4. on `\"expired\"` with `redirectTo` set, the browser navigates — after the\n * hook settled, even if it threw; on `\"switch\"`, the new session is\n * installed — after the hook settled, even if it threw.\n *\n * It may be async. `logout()` resolves when it settles and rejects with what\n * it threw; on the automatic path the error is swallowed, because the\n * request that ended the session already rejects with its own `401`. It\n * runs once per session: a second `logout()`, or the other requests of a\n * burst of `401`s, find nothing left to end and skip it.\n *\n * On `\"switch\"` the hook runs only **after** the new credentials were\n * accepted, so a failed `login()` leaves the live session untouched. It runs\n * **before** the new session is installed: teardown of the previous\n * account's state (clearing a persisted cache, a background-sync queue) has\n * to finish before the new account's requests start filling them, or it\n * would wipe — or race with — the new account's data. The cost is that\n * `isAuthenticated` is `false` while the hook runs, which a route guard may\n * act on; keep the hook short. Its error is swallowed there, since the\n * login itself succeeded.\n *\n * Inside the hook, reach the backend only through `session.api`. `auth.api`\n * goes out with no token; `auth.login()` waits on the teardown in progress —\n * this hook — and never settles. Telling the hook apart from an outside\n * caller would need an async context the browser does not have, so this is\n * a documented rule, not a runtime check.\n */\n onSessionEnd?: (session: TempestSessionEnd<TUser>) => void | Promise<void>;\n}\n\nexport interface TempestAuth<TUser, TCredentials> {\n /** The persisted Zustand auth store hook (compatible with `<AuthGuard>`). */\n useAuthStore: ReturnType<typeof createAuthStore<TUser>>;\n /** A `createApiClient` wired with bearer auth + 401 → refresh → retry. */\n api: ApiClient;\n /** Authenticate, store the session, and resolve the user (or null). */\n login: (credentials: TCredentials) => Promise<TUser | null>;\n /**\n * End the session: supersede any refresh in flight, clear the store and the\n * stored refresh token, then run `onSessionEnd`.\n *\n * The store is cleared synchronously, before the first `await`, so callers\n * that ignore the promise see the signed-out state at once. The promise\n * resolves when `onSessionEnd` settled — and, with `logoutPath`, when the\n * server-side revocation settled too — and rejects with the hook's error;\n * a failed revocation never rejects it.\n */\n logout: () => Promise<void>;\n /**\n * Refresh the access token (deduplicated across concurrent callers).\n *\n * @throws {SessionSupersededError} When the session ended or changed while\n * the refresh was in flight; its tokens were discarded.\n */\n refresh: () => Promise<void>;\n /** The current access token, or null. */\n getToken: () => string | null;\n}\n\n/**\n * How long `login()` waits for pending `logoutPath` revocations, and how long a\n * cookie-mode revocation waits for the login or refresh responses still in\n * flight when `logout()` ran, before going ahead anyway.\n *\n * A bound, not a guess at latency: a fetcher that ignores abort, or a backend\n * that never answers, would otherwise leave the next `login()` waiting forever.\n * Ten seconds is past any healthy round trip and short enough that a stuck\n * revocation costs a slow sign-in, not a dead one.\n */\nconst LOGOUT_REVOCATION_WAIT_MS = 10_000;\n\n/**\n * Wait until every promise settled, or until `ms` passed, whichever is first.\n *\n * @param pending - What to wait for; rejections count as settled.\n * @param ms - The bound.\n * @returns A promise that always resolves.\n */\nfunction settleWithin(pending: readonly Promise<unknown>[], ms: number): Promise<void> {\n if (pending.length === 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, ms);\n void Promise.allSettled(pending).then(() => {\n clearTimeout(timer);\n resolve();\n });\n });\n}\n\nfunction defaultParseTokens(data: unknown): { token: string; refreshToken?: string } {\n const d = (data ?? {}) as TempestTokenResponse;\n return { token: d.access_token, refreshToken: d.refresh_token };\n}\n\n/**\n * Turn-key auth preset wiring `createAuthStore` + `createRefreshQueue` +\n * `createApiClient` to the Tempest FastAPI SDK auth contract: login returns\n * `{ access_token, token_type }`, requests carry `Authorization: Bearer`, and a\n * `401` triggers a single deduplicated refresh + replay.\n *\n * The session is cleared whenever that path ends unauthorized anyway — the\n * refresh threw, or the replay came back `401` — so a refresh token that the\n * backend has revoked cannot leave the app holding a dead session. With\n * `redirectTo` set, the browser also leaves the page; without it, clearing the\n * store is enough for a `<RouteGuard>` to navigate on its own.\n *\n * Every refresh is bound to a **session epoch**, bumped by `logout()` and by a\n * `login()` whose credentials were accepted. A refresh that resolves after its epoch ended discards\n * what it brought back and rejects with {@link SessionSupersededError}, which\n * the client passes through without ending the new session. Before 0.77.0 a\n * refresh that was in flight during `logout()` reinstalled the old tokens —\n * and after `logout()` + `login(B)`, replaced B's token with A's while the UI\n * showed B (#435). A `401` from a request sent under an ended session is\n * refused the same way, instead of being replayed as the account signed in now.\n *\n * A `401` from a request that carried no token, while no session is installed,\n * surfaces without a refresh: in cookie mode that refresh would be answered by\n * the HttpOnly cookie `logout()` cannot clear, signing the previous account\n * back in (#474). With a `login()` in flight, that `401` waits for it and\n * replays with the token it installed, or surfaces when it failed — still\n * without a refresh (#496). Restore a session at boot with an explicit\n * `refresh()`, and pass `logoutPath` so the backend revokes the cookie:\n * without a working revocation, that boot `refresh()` restores the previous\n * account for whoever uses the browser next.\n *\n * Refreshes are collapsed per rotation, not just per overlap: a `401` whose\n * request left with a token that has since been replaced in the same session\n * replays with the new token and refreshes nothing. Three expired requests take\n * one refresh whether their `401`s arrive together or one after another.\n *\n * @example\n * const auth = createTempestAuth<User, { email: string; password: string }>({\n * baseURL: import.meta.env.VITE_API_URL,\n * mePath: \"/api/auth/me\",\n * });\n *\n * await auth.login({ email, password }); // stores session, returns the user\n * const orders = await auth.api.get(\"/api/orders\"); // sends the bearer token\n * await auth.logout(); // clears, then runs onSessionEnd\n *\n * @param options - The auth configuration.\n * @returns The store hook, a wired API client, and login/logout/refresh helpers.\n */\nexport function createTempestAuth<TUser, TCredentials = { email: string; password: string }>(\n options: CreateTempestAuthOptions<TUser>,\n): TempestAuth<TUser, TCredentials> {\n const {\n baseURL,\n prefix,\n loginPath = \"/api/auth/login\",\n refreshPath = \"/api/auth/refresh\",\n logoutPath,\n onLogoutError,\n mePath,\n storeName = \"tempest-auth\",\n storage = \"local\",\n withCredentials = false,\n csrf,\n fetcher,\n parseTokens = defaultParseTokens,\n parseUser,\n refreshBody = (rt) => (rt ? { refresh_token: rt } : undefined),\n retry,\n redirectTo,\n onSessionEnd,\n } = options;\n\n const useAuthStore = createAuthStore<TUser>({ name: storeName, storage });\n const refreshKey = `${storeName}-refresh`;\n\n function storageImpl(): Storage | null {\n if (typeof window === \"undefined\") return null;\n return storage === \"session\" ? window.sessionStorage : window.localStorage;\n }\n function readRefreshToken(): string | null {\n return storageImpl()?.getItem(refreshKey) ?? null;\n }\n function writeRefreshToken(token: string | null): void {\n const s = storageImpl();\n if (!s) return;\n if (token) s.setItem(refreshKey, token);\n else s.removeItem(refreshKey);\n }\n\n const state = (): AuthState<TUser> => useAuthStore.getState();\n const getToken = (): string | null => state().token;\n\n /**\n * Load the current user, without letting a `401` end the session.\n *\n * `skipAuthRetry` is what keeps this one client honest: the call is\n * authenticated, but a `401` here surfaces instead of triggering a refresh\n * and `onUnauthorized`. That was the behaviour of the throwaway client this\n * used to build per invocation, and it is the reason the preset needed\n * three clients before 0.66.0.\n *\n * Bound to the epoch current when the request leaves, like `rotate()`: a\n * profile that resolves after `logout()` — or after `logout()` +\n * `login(B)` — is discarded instead of written next to B's token, and the\n * `login()` that asked for it rejects as superseded (#473).\n *\n * @throws {SessionSupersededError} When the epoch changed while the\n * request was in flight, on success and on failure alike.\n */\n async function fetchUser(): Promise<TUser | null> {\n if (!mePath) return state().user;\n const started = epoch;\n let user: TUser;\n try {\n user = await api.get<TUser>(mePath, { skipAuthRetry: true });\n } catch (error) {\n if (epoch !== started) throw new SessionSupersededError();\n throw error;\n }\n if (epoch !== started) throw new SessionSupersededError();\n state().setUser(user);\n return user;\n }\n\n let epoch = 0;\n let ending: Promise<void> = Promise.resolve();\n let hooksRunning = 0;\n const loginsInFlight = new Set<Promise<unknown>>();\n const revocations = new Set<Promise<void>>();\n const credentialRequests = new Set<Promise<unknown>>();\n const rotatingEpochs = new Set<number>();\n\n /**\n * Track a login or refresh request until it settles, so a cookie-mode\n * revocation can wait for the response that sets the cookie it revokes.\n *\n * @param request - The request in flight.\n * @returns The same request.\n */\n function trackCredentialRequest<T>(request: Promise<T>): Promise<T> {\n const settled = request.then(\n () => undefined,\n () => undefined,\n );\n credentialRequests.add(settled);\n void settled.then(() => credentialRequests.delete(settled));\n return request;\n }\n\n /**\n * Surface a failed revocation: `onLogoutError`, plus a console warning in a\n * dev build.\n *\n * @param error - What the revocation rejected with.\n */\n function reportLogoutError(error: unknown): void {\n if (isDevBuild()) {\n console.warn(\n \"[tempest-react-sdk] createTempestAuth: logoutPath revocation failed\",\n error,\n );\n }\n try {\n onLogoutError?.(error);\n } catch {\n return;\n }\n }\n\n /**\n * Send one `logoutPath` revocation and track it until it settles. Never\n * rejects.\n *\n * @param build - Produces the request, after whatever wait it needs.\n * @returns A promise settling when the revocation settled.\n */\n function trackRevocation(build: () => Promise<unknown>): Promise<void> {\n const tracked = (async () => {\n try {\n await build();\n } catch (error) {\n reportLogoutError(error);\n }\n })();\n revocations.add(tracked);\n void tracked.then(() => revocations.delete(tracked));\n return tracked;\n }\n\n /**\n * Revoke, in body mode, the refresh token a superseded login or refresh\n * brought back. Nobody can use it — the client discarded it — but it would\n * stay valid on the server until it expires.\n *\n * Cookie mode is left to `revoke()`, which waits for these responses\n * instead: re-sending a cookie revocation here could carry whichever\n * account's cookie the browser holds by then.\n *\n * @param token - The superseded response's access token.\n * @param refreshToken - The superseded response's refresh token.\n */\n function revokeDiscarded(token: string | null, refreshToken: string | undefined): void {\n if (!logoutPath || withCredentials || !refreshToken) return;\n void trackRevocation(() =>\n endedSessionApi(token).post<unknown>(logoutPath, {\n body: refreshBody(refreshToken),\n }),\n );\n }\n\n /**\n * A client that sends an ended session's access token, and never refreshes\n * or ends a session: a `401` there just rejects.\n *\n * @param token - The ended session's access token.\n * @returns The client `onSessionEnd` and the `logoutPath` call go through.\n */\n function endedSessionApi(token: string | null): ApiClient {\n return createApiClient({\n baseURL,\n prefix,\n withCredentials,\n csrf,\n fetcher,\n getToken: () => token,\n });\n }\n\n /**\n * Start a new session epoch: supersede the refresh in flight and give the\n * new epoch a refresh queue of its own, so a `401` under the new session\n * never joins the old session's refresh.\n *\n * The client reads the epoch when each attempt leaves (`getSessionEpoch`),\n * which is what ties a `401` to the session that sent it — no set of\n * tokens to keep, and nothing that grows with the session's length.\n */\n function nextEpoch(): void {\n epoch += 1;\n queue = createRefreshQueue(rotate);\n }\n\n /**\n * Perform one refresh for the epoch that is current when it starts.\n *\n * Checked again after the round trip, on success and on failure alike: a\n * refresh that fails after `logout()` + `login(B)` must not reach\n * `onUnauthorized` either, or it would end B's session.\n *\n * @throws {SessionSupersededError} When the epoch changed during the call.\n */\n async function rotate(): Promise<void> {\n const started = epoch;\n rotatingEpochs.add(started);\n try {\n let data: unknown;\n try {\n data = await trackCredentialRequest(\n api.post<unknown>(refreshPath, {\n body: refreshBody(readRefreshToken()),\n skipAuth: true,\n }),\n );\n } catch (error) {\n if (epoch !== started) throw new SessionSupersededError();\n throw error;\n }\n const { token, refreshToken } = parseTokens(data);\n if (epoch !== started) {\n revokeDiscarded(token ?? null, refreshToken);\n throw new SessionSupersededError();\n }\n state().setToken(token);\n if (refreshToken) writeRefreshToken(refreshToken);\n } finally {\n rotatingEpochs.delete(started);\n }\n }\n\n let queue = createRefreshQueue(rotate);\n\n /**\n * Whether a `401` must be left alone: no refresh, no ending the session.\n *\n * Two cases, both of the current epoch: an attempt that carried no token\n * while a session is now installed (it left before the sign-in, and\n * refreshing or ending the new session for it would punish the wrong\n * session); and an attempt whose token the session has since rotated past\n * (the rotation it would trigger already happened).\n *\n * @param context - The attempt's credential.\n * @returns Whether the attempt is stale within the current session.\n */\n function staleWithinSession(context: RequestAuthContext): boolean {\n const current = getToken();\n if (current === null) return false;\n return context.token === null || context.token !== current;\n }\n\n /**\n * Whether nothing could make a tokenless `401` worth a refresh: no access\n * token and no stored refresh token.\n *\n * With `withCredentials`, the refresh would otherwise be answered by an\n * HttpOnly cookie the client cannot see or clear — after `logout()`, that\n * signed the previous account back in for whoever used the browser next\n * (#474). A `login()` in flight does not count as a session either: the\n * cookie a refresh would present during it is still the ended account's\n * (#496). Restoring a session from the cookie is an explicit `refresh()`,\n * typically at boot, which this does not touch.\n *\n * @returns Whether no session is installed.\n */\n function signedOut(): boolean {\n return getToken() === null && readRefreshToken() === null;\n }\n\n /**\n * Settle a tokenless `401` that arrived with no session installed while a\n * `login()` is in flight: wait for every login in flight, then let the\n * request replay when one of them installed a token.\n *\n * No refresh is sent. In cookie mode one would present whatever cookie\n * the browser holds — after `logout()` without a working `logoutPath`,\n * the previous account's: a failed login then left the client signed in\n * as that account, and a successful one could have its cookie overwritten\n * by the refresh's late `Set-Cookie` (#496).\n *\n * Skipped while an `onSessionEnd` hook runs: an account switch waits on\n * that hook, so a hook awaiting `auth.api` would wait on itself.\n *\n * @throws {Error} When no login is in flight, a hook is running, or every\n * login settled without installing a token — the `401` surfaces as is.\n */\n async function awaitLogins(): Promise<void> {\n if (loginsInFlight.size > 0 && hooksRunning === 0) {\n await Promise.allSettled([...loginsInFlight]);\n if (getToken() !== null) return;\n }\n throw new Error(\"anonymous 401 with no session\");\n }\n\n /**\n * The `refresh` the client calls on a `401`.\n *\n * - An attempt from an ended epoch is refused rather than replayed as\n * whoever is signed in now.\n * - A token this session already rotated past: the replay just picks up the\n * new one — no second refresh.\n * - No token, while a session is now installed: the `401` surfaces as is.\n * - No token, and no session installed: joins this epoch's refresh when\n * one is already in flight — the boot `refresh()` restoring a session\n * from the cookie, which the request then replays under; otherwise waits\n * for a `login()` in flight and replays with its token, see\n * `awaitLogins`; otherwise surfaces the `401` as is, see `signedOut`.\n * - Otherwise: join or start this epoch's refresh.\n *\n * @param context - The credential and epoch the rejected attempt left with.\n * @throws {SessionSupersededError} When the attempt belongs to an ended\n * session, or the session changed during the refresh.\n */\n async function refreshFor(context: RequestAuthContext): Promise<void> {\n if (context.epoch !== null && context.epoch !== epoch) throw new SessionSupersededError();\n if (staleWithinSession(context)) {\n if (context.token === null) throw new Error(\"anonymous 401 under a live session\");\n return;\n }\n if (context.token === null && signedOut()) {\n if (rotatingEpochs.has(epoch)) return queue();\n return awaitLogins();\n }\n return queue();\n }\n\n /**\n * The client's `onUnauthorized`: end the session only when the `401` is the\n * current session's own.\n *\n * The replay after a refresh is checked here too — it can come back `401`\n * after the user signed out and another account signed in, and ending the\n * session then would sign the new account out. A tokenless `401` with no\n * session installed ends nothing either: there is no session to end, and\n * ending \"nothing\" used to move the epoch and supersede the boot\n * `refresh()` in flight.\n *\n * @param context - The credential and epoch of the attempt that failed.\n * @throws {SessionSupersededError} When that attempt belongs to an ended\n * session.\n */\n async function onUnauthorized(_response: Response, context: RequestAuthContext): Promise<void> {\n if (context.epoch !== null && context.epoch !== epoch) throw new SessionSupersededError();\n if (staleWithinSession(context)) return;\n if (context.token === null && signedOut()) return;\n await endSession();\n }\n\n /**\n * Authenticate and install the new session.\n *\n * The previous session ends only once the new credentials are accepted: a\n * login that fails leaves it, and the refresh it may have in flight,\n * untouched. When one was live, `onSessionEnd` runs with `\"switch\"` before\n * the new tokens are installed — see the option for why that order. A\n * `logout()` still tearing down is awaited the same way, so the new\n * account's state never starts before the previous one's teardown ended.\n * Never call it from inside `onSessionEnd`: it would wait on that hook.\n * Every `logoutPath` revocation still in flight is awaited before the\n * request leaves — at most {@link LOGOUT_REVOCATION_WAIT_MS} — so a\n * cookie-clearing response cannot land after the new account's cookies.\n *\n * @param credentials - Sent as the login body.\n * @returns The user of the new session, or null.\n * @throws {SessionSupersededError} When a `logout()` or another `login()`\n * moved the session while this one was in flight — during its request,\n * during the teardown or while `mePath` was loading the user; the new\n * tokens, or the user, are discarded.\n */\n async function login(credentials: TCredentials): Promise<TUser | null> {\n if (revocations.size > 0) await settleWithin([...revocations], LOGOUT_REVOCATION_WAIT_MS);\n const run = signIn(credentials);\n loginsInFlight.add(run);\n try {\n return await run;\n } finally {\n loginsInFlight.delete(run);\n }\n }\n\n /**\n * The body of `login()`, tracked in `loginsInFlight` by its caller so a\n * tokenless `401` during the sign-in waits for it and replays with the\n * token it installs, see `awaitLogins`. Tracking starts only after a\n * pending revocation settled.\n *\n * @param credentials - Sent as the login body.\n * @returns The user of the new session, or null.\n * @throws {SessionSupersededError} See `login()`.\n */\n async function signIn(credentials: TCredentials): Promise<TUser | null> {\n const started = epoch;\n const data = await trackCredentialRequest(\n api.post<unknown>(loginPath, {\n body: credentials,\n skipAuth: true,\n }),\n );\n const { token, refreshToken } = parseTokens(data);\n if (epoch !== started) {\n revokeDiscarded(token ?? null, refreshToken);\n throw new SessionSupersededError();\n }\n const { done } = finish(\"switch\");\n const mine = epoch;\n await done.catch(() => undefined);\n if (epoch !== mine) throw new SessionSupersededError();\n state().setToken(token);\n writeRefreshToken(refreshToken ?? null);\n const embedded = parseUser?.(data);\n if (embedded != null) {\n state().setUser(embedded);\n return embedded;\n }\n return fetchUser();\n }\n\n /**\n * End the current session, synchronously up to the hook.\n *\n * The epoch moves first, so a refresh resolving between here and the hook\n * is already superseded. The hook only runs when there was a session to\n * end — a token, a stored refresh token or a user — which is what keeps a\n * burst of `401`s from running it once per request.\n *\n * With nothing left to end, the caller waits on the teardown already under\n * way (`ending`, which never rejects, so a later no-op does not inherit an\n * old error). Two callers skip that wait while a hook is running, because\n * they may be the hook itself and would wait on their own teardown forever:\n * `endSession` — a hook awaiting `auth.api` goes out tokenless, gets a\n * `401`, and lands here — and a `logout()` with no session left. `login()`\n * (`\"switch\"`) always waits: the new account's state must not start while\n * the previous one's teardown is still clearing it. A hook awaiting\n * `auth.login()` therefore never settles — misuse the option documents.\n *\n * `\"expired\"` with nothing live leaves the epoch alone: there is nothing to\n * supersede but a refresh with no session behind it — the boot `refresh()`\n * restoring one from the cookie — and moving the epoch would throw that\n * away. `logout()` and `login()` always move it, since they must supersede\n * such a refresh.\n *\n * The returned `done` is a fresh promise derived from the hook, not the one\n * `ending` holds: a `logout()` nobody awaits still surfaces the hook's error\n * as an unhandled rejection instead of losing it.\n *\n * @param reason - Whether the caller signed out, the client gave up, or a\n * login replaced the session.\n * @returns Whether a live session ended here, and a promise settling with\n * the hook.\n */\n function finish(reason: TempestSessionEnd<TUser>[\"reason\"]): {\n live: boolean;\n done: Promise<void>;\n } {\n const { user, token } = state();\n const live = token !== null || user !== null || readRefreshToken() !== null;\n if (live || reason !== \"expired\") nextEpoch();\n state().logout();\n writeRefreshToken(null);\n if (!live) {\n const wait = reason === \"switch\" || hooksRunning === 0;\n return { live, done: wait ? ending : Promise.resolve() };\n }\n if (!onSessionEnd) return { live, done: Promise.resolve() };\n const sessionApi = endedSessionApi(token);\n hooksRunning += 1;\n const run = (async () => {\n try {\n await onSessionEnd({ reason, user, token, api: sessionApi });\n } finally {\n hooksRunning -= 1;\n }\n })();\n ending = run.then(\n () => undefined,\n () => undefined,\n );\n return { live, done: run.then(() => undefined) };\n }\n\n /**\n * Send the `logoutPath` revocation for the session about to end, with the\n * credentials it holds now — read before `finish` clears them.\n *\n * Skipped without `logoutPath`, and in body mode when there is nothing to\n * revoke; in cookie mode it always goes out, since the cookie it revokes is\n * invisible to the client. Never rejects.\n *\n * In cookie mode it first waits — at most\n * {@link LOGOUT_REVOCATION_WAIT_MS} — for the login and refresh responses\n * in flight right now: each one sets a cookie for the session this\n * `logout()` ends, and a revocation that left before it would leave that\n * cookie behind.\n *\n * @returns A promise settling when the revocation settled, or null when\n * none was sent.\n */\n function revoke(): Promise<void> | null {\n if (!logoutPath) return null;\n const path = logoutPath;\n const { token, user } = state();\n const refreshToken = readRefreshToken();\n if (!withCredentials && token === null && user === null && refreshToken === null) {\n return null;\n }\n const inFlight = withCredentials ? [...credentialRequests] : [];\n return trackRevocation(async () => {\n await settleWithin(inFlight, LOGOUT_REVOCATION_WAIT_MS);\n return endedSessionApi(token).post<unknown>(path, { body: refreshBody(refreshToken) });\n });\n }\n\n function logout(): Promise<void> {\n const revocation = revoke();\n const { done } = finish(\"logout\");\n if (!revocation) return done;\n return Promise.all([revocation, done]).then(() => undefined);\n }\n\n /**\n * End the session after the client gave up on it and, when `redirectTo`\n * is set, leave the page once `onSessionEnd` settled.\n *\n * Separate from `logout` so an explicit sign-out stays a pure state change:\n * a caller that already navigates itself would otherwise get a second,\n * competing navigation. The hook is awaited before navigating because a\n * hard navigation aborts whatever request it still had in flight. Only the\n * call that ended a live session navigates; the rest of a burst of `401`s\n * waits on the same teardown, or returns at once inside a hook.\n */\n async function endSession(): Promise<void> {\n const { live, done } = finish(\"expired\");\n await done.catch(() => undefined);\n if (live && redirectTo && typeof window !== \"undefined\") {\n window.location.assign(redirectTo);\n }\n }\n\n const api = createApiClient({\n baseURL,\n prefix,\n withCredentials,\n csrf,\n fetcher,\n getToken,\n getSessionEpoch: () => epoch,\n refresh: refreshFor,\n retry,\n onUnauthorized,\n });\n\n const refresh = (): Promise<void> => queue();\n\n return { useAuthStore, api, login, logout, refresh, getToken };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"create-tempest-auth.cjs","names":["createAuthStore","SessionSupersededError","isDevBuild","createApiClient","createRefreshQueue","require_dev_mode","require_errors","require_api_client","require_create_auth_store","require_refresh_queue","LOGOUT_REVOCATION_WAIT_MS","settleWithin","pending","timer","resolve","ms","defaultParseTokens","d","data","createTempestAuth","rt","options","useAuthStore","storeName","refreshKey","storageImpl","storage","readRefreshToken","writeRefreshToken","s","token","state","getToken","fetchUser","mePath","started","epoch","user","api","error","ending","hooksRunning","loginsInFlight","revocations","credentialRequests","rotatingEpochs","trackCredentialRequest","settled","request","reportLogoutError","onLogoutError","trackRevocation","tracked","build","revokeDiscarded","logoutPath","refreshToken","endedSessionApi","refreshBody","nextEpoch","rotate","refreshPath","parseTokens","queue","staleWithinSession","current","context","signedOut","awaitLogins","refreshFor","onUnauthorized","endSession","login","run","signIn","credentials","loginPath","finish","mine","done","embedded","parseUser","live","reason","onSessionEnd","sessionApi","revoke","path","withCredentials","inFlight","logout","revocation","redirectTo"],"sources":["../../src/auth/create-tempest-auth.ts"],"sourcesContent":["import { createApiClient, SessionSupersededError } from \"../http\";\nimport { isDevBuild } from \"../utils/dev-mode\";\nimport type { ApiClient, CsrfOptions, RequestAuthContext, RetryOptions } from \"../http\";\nimport { createAuthStore } from \"./create-auth-store\";\nimport type { AuthState } from \"./create-auth-store\";\nimport { createRefreshQueue } from \"./refresh-queue\";\n\n/** The token envelope returned by a Tempest FastAPI SDK login/refresh route. */\nexport interface TempestTokenResponse {\n /** The bearer access token. */\n access_token: string;\n /** Token type — always `\"bearer\"` for the SDK. */\n token_type?: string;\n /** Optional refresh token (when the API returns it in the body, not a cookie). */\n refresh_token?: string;\n}\n\n/**\n * What {@link CreateTempestAuthOptions.onSessionEnd} receives about the session\n * that just ended.\n */\nexport interface TempestSessionEnd<TUser> {\n /**\n * Why it ended: `\"logout\"` for an explicit `logout()`, `\"expired\"` when the\n * client ended it on its own — the refresh failed, or the request replayed\n * after a refresh still came back `401` — and `\"switch\"` when `login()`\n * succeeded for another set of credentials while this session was live.\n */\n reason: \"logout\" | \"expired\" | \"switch\";\n /** The user of the ended session, as the store held it. */\n user: TUser | null;\n /** The access token of the ended session, or `null` when it had none. */\n token: string | null;\n /**\n * A client that sends the ended session's access token — the store has\n * already been cleared, so `auth.api` no longer does.\n *\n * Use it for the server-side half of the teardown, such as deleting this\n * browser's push subscription. It never refreshes and never ends a session:\n * a `401` here just rejects. On `\"logout\"` the token is usually still\n * valid; on `\"expired\"` the server has already refused it, so expect the\n * call to fail and keep the local half of the teardown independent of it.\n */\n api: ApiClient;\n}\n\nexport interface CreateTempestAuthOptions<TUser> {\n /** Base URL of the API. Required. */\n baseURL: string;\n /**\n * Path segment the API is mounted under, such as `\"/api\"`, forwarded to\n * every client this preset builds — see the `prefix` option of\n * `createApiClient`.\n *\n * Safe to combine with the default paths below: the prefix is applied at\n * most once, so `\"/api/auth/login\"` under a `\"/api\"` prefix stays\n * `\"/api/auth/login\"`.\n */\n prefix?: string;\n /** Login route (`POST`). Default: `\"/api/auth/login\"`. */\n loginPath?: string;\n /** Refresh route (`POST`). Default: `\"/api/auth/refresh\"`. */\n refreshPath?: string;\n /**\n * Server-side logout route (`POST`), called by an explicit `logout()` to\n * revoke the ending session. Off by default.\n *\n * `logout()` alone only clears client state, and an HttpOnly refresh\n * cookie is out of its reach: with `withCredentials`, the cookie stays\n * redeemable until the backend revokes it. The tempest-fastapi-sdk mounts\n * `POST /auth/logout` (`204`, idempotent) when it stores refresh tokens in\n * the database — at `/auth/cookie/logout` when it delivers tokens both in\n * the body and in cookies — and stateless refresh tokens cannot be\n * revoked, so the route is absent there.\n *\n * The request goes out through the ended session's client (the same one\n * `onSessionEnd` receives, carrying its access token), with the body that\n * `refreshBody` builds from the refresh token stored before the clear:\n * `{ refresh_token }` in body mode, no body in cookie mode, which is what\n * that route reads. The local clear does not wait on it — `isAuthenticated`\n * is still `false` when `logout()` returns — but the promise `logout()`\n * returns does, so a navigation after `await auth.logout()` does not abort\n * it. A failure (network, `4xx`, `5xx`) is swallowed: revocation is\n * best-effort and never keeps the user signed in. A `login()` started\n * while it is in flight waits for it, because the logout response clears\n * the auth cookies and would otherwise clear the new account's.\n *\n * Only an explicit `logout()` revokes. On `\"switch\"` the cookie jar already\n * holds the new account's cookie, and on `\"expired\"` the server has already\n * refused the session.\n *\n * Overlapping sign-outs each send their own revocation, and `login()` waits\n * for all of them — at most 10 seconds, so one that\n * never answers delays the sign-in instead of blocking it. In cookie mode,\n * a revocation also waits (within the same bound) for the login and\n * refresh responses that were in flight when `logout()` ran: the server\n * sets the ended session's cookie when that response arrives, and a\n * revocation sent earlier would leave it behind. In body mode, the refresh\n * token such a superseded response brings back is revoked on its own.\n */\n logoutPath?: string;\n /**\n * Called with the error when a `logoutPath` revocation fails — network\n * failure, `4xx` or `5xx`. `logout()` still resolves, since the local\n * sign-out already happened; this is where the app logs or retries. A dev\n * build also warns in the console. Errors it throws are swallowed.\n */\n onLogoutError?: (error: unknown) => void;\n /** Optional current-user route (`GET`) called after login/refresh. */\n mePath?: string;\n /** Persist key for the store. Default: `\"tempest-auth\"`. */\n storeName?: string;\n /** Storage backend. Default: `\"local\"`. */\n storage?: \"local\" | \"session\";\n /** Send cookies (needed when the refresh token lives in an httpOnly cookie). */\n withCredentials?: boolean;\n /**\n * CSRF token for every write, forwarded to `createApiClient`'s `csrf`. Off\n * by default; `true` echoes the `csrf_token` cookie as `X-CSRF-Token`.\n *\n * The pair to `withCredentials`: once the refresh token lives in a cookie,\n * the browser attaches it to a `POST /auth/refresh` that another site\n * triggers. Login and refresh carry the header too — they go out with\n * `skipAuth`, and the CSRF header does not follow auth.\n */\n csrf?: boolean | CsrfOptions;\n /** Custom fetch implementation (testing / SSR). Defaults to `globalThis.fetch`. */\n fetcher?: typeof fetch;\n /**\n * Extract tokens from a login/refresh response. Default reads\n * `access_token` + `refresh_token`.\n */\n parseTokens?: (data: unknown) => { token: string; refreshToken?: string };\n /** Pull the user out of the login response, when the API embeds it. */\n parseUser?: (data: unknown) => TUser | null;\n /**\n * Build the refresh request body. Default sends `{ refresh_token }` when a\n * refresh token is stored, else `undefined` (cookie-based refresh).\n */\n refreshBody?: (refreshToken: string | null) => unknown;\n /**\n * Retry policy for `api`, forwarded to {@link createApiClient}. Off by\n * default; `true` enables the conservative built-in policy, which never\n * replays a write.\n */\n retry?: boolean | RetryOptions;\n /**\n * Where to send the browser when the session ends — a hard navigation via\n * `window.location.assign`, after the store is cleared.\n *\n * **Prefer leaving this unset.** `logout()` already clears the store, so a\n * `<RouteGuard when={isAuthenticated} redirectTo=\"/login\">` wrapped around\n * the protected area navigates on its own, which keeps the SPA alive and the\n * router history intact. Reach for this only when the expiry can happen\n * outside any guarded subtree and a full reload is acceptable.\n */\n redirectTo?: string;\n /**\n * Tear down per-user state when a session ends — on an explicit `logout()`,\n * when the client ends the session on its own after a failed refresh, and\n * when a successful `login()` replaces a live session. Without it, the automatic path had no place to hook into, and\n * state derived from the user (a push subscription, a persisted query\n * cache, a background-sync queue) survived into the next account.\n *\n * Order, on every path:\n *\n * 1. any refresh still in flight is superseded, so it cannot reinstall the\n * ending session's tokens;\n * 2. the store and the stored refresh token are cleared — synchronously, so\n * `isAuthenticated` is already `false` when `logout()` returns;\n * 3. this hook runs with a {@link TempestSessionEnd} whose `api` still sends\n * the ended session's access token;\n * 4. on `\"expired\"` with `redirectTo` set, the browser navigates — after the\n * hook settled, even if it threw; on `\"switch\"`, the new session is\n * installed — after the hook settled, even if it threw.\n *\n * It may be async. `logout()` resolves when it settles and rejects with what\n * it threw; on the automatic path the error is swallowed, because the\n * request that ended the session already rejects with its own `401`. It\n * runs once per session: a second `logout()`, or the other requests of a\n * burst of `401`s, find nothing left to end and skip it.\n *\n * On `\"switch\"` the hook runs only **after** the new credentials were\n * accepted, so a failed `login()` leaves the live session untouched. It runs\n * **before** the new session is installed: teardown of the previous\n * account's state (clearing a persisted cache, a background-sync queue) has\n * to finish before the new account's requests start filling them, or it\n * would wipe — or race with — the new account's data. The cost is that\n * `isAuthenticated` is `false` while the hook runs, which a route guard may\n * act on; keep the hook short. Its error is swallowed there, since the\n * login itself succeeded.\n *\n * Inside the hook, reach the backend only through `session.api`. `auth.api`\n * goes out with no token; `auth.login()` waits on the teardown in progress —\n * this hook — and never settles. Telling the hook apart from an outside\n * caller would need an async context the browser does not have, so this is\n * a documented rule, not a runtime check.\n */\n onSessionEnd?: (session: TempestSessionEnd<TUser>) => void | Promise<void>;\n}\n\nexport interface TempestAuth<TUser, TCredentials> {\n /** The persisted Zustand auth store hook (compatible with `<AuthGuard>`). */\n useAuthStore: ReturnType<typeof createAuthStore<TUser>>;\n /** A `createApiClient` wired with bearer auth + 401 → refresh → retry. */\n api: ApiClient;\n /** Authenticate, store the session, and resolve the user (or null). */\n login: (credentials: TCredentials) => Promise<TUser | null>;\n /**\n * End the session: supersede any refresh in flight, clear the store and the\n * stored refresh token, then run `onSessionEnd`.\n *\n * The store is cleared synchronously, before the first `await`, so callers\n * that ignore the promise see the signed-out state at once. The promise\n * resolves when `onSessionEnd` settled — and, with `logoutPath`, when the\n * server-side revocation settled too — and rejects with the hook's error;\n * a failed revocation never rejects it.\n */\n logout: () => Promise<void>;\n /**\n * Refresh the access token (deduplicated across concurrent callers).\n *\n * @throws {SessionSupersededError} When the session ended or changed while\n * the refresh was in flight; its tokens were discarded.\n */\n refresh: () => Promise<void>;\n /** The current access token, or null. */\n getToken: () => string | null;\n}\n\n/**\n * How long `login()` waits for pending `logoutPath` revocations, and how long a\n * cookie-mode revocation waits for the login or refresh responses still in\n * flight when `logout()` ran, before going ahead anyway.\n *\n * A bound, not a guess at latency: a fetcher that ignores abort, or a backend\n * that never answers, would otherwise leave the next `login()` waiting forever.\n * Ten seconds is past any healthy round trip and short enough that a stuck\n * revocation costs a slow sign-in, not a dead one.\n */\nconst LOGOUT_REVOCATION_WAIT_MS = 10_000;\n\n/**\n * Wait until every promise settled, or until `ms` passed, whichever is first.\n *\n * @param pending - What to wait for; rejections count as settled.\n * @param ms - The bound.\n * @returns A promise that always resolves.\n */\nfunction settleWithin(pending: readonly Promise<unknown>[], ms: number): Promise<void> {\n if (pending.length === 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, ms);\n void Promise.allSettled(pending).then(() => {\n clearTimeout(timer);\n resolve();\n });\n });\n}\n\nfunction defaultParseTokens(data: unknown): { token: string; refreshToken?: string } {\n const d = (data ?? {}) as TempestTokenResponse;\n return { token: d.access_token, refreshToken: d.refresh_token };\n}\n\n/**\n * Turn-key auth preset wiring `createAuthStore` + `createRefreshQueue` +\n * `createApiClient` to the Tempest FastAPI SDK auth contract: login returns\n * `{ access_token, token_type }`, requests carry `Authorization: Bearer`, and a\n * `401` triggers a single deduplicated refresh + replay.\n *\n * The session is cleared whenever that path ends unauthorized anyway — the\n * refresh threw, or the replay came back `401` — so a refresh token that the\n * backend has revoked cannot leave the app holding a dead session. With\n * `redirectTo` set, the browser also leaves the page; without it, clearing the\n * store is enough for a `<RouteGuard>` to navigate on its own.\n *\n * Every refresh is bound to a **session epoch**, bumped by `logout()` and by a\n * `login()` whose credentials were accepted. A refresh that resolves after its epoch ended discards\n * what it brought back and rejects with {@link SessionSupersededError}, which\n * the client passes through without ending the new session. Before 0.77.0 a\n * refresh that was in flight during `logout()` reinstalled the old tokens —\n * and after `logout()` + `login(B)`, replaced B's token with A's while the UI\n * showed B (#435). A `401` from a request sent under an ended session is\n * refused the same way, instead of being replayed as the account signed in now.\n *\n * A `401` from a request that carried no token, while no session is installed,\n * surfaces without a refresh: in cookie mode that refresh would be answered by\n * the HttpOnly cookie `logout()` cannot clear, signing the previous account\n * back in (#474). With a `login()` in flight, that `401` waits for it and\n * replays with the token it installed, or surfaces when it failed — still\n * without a refresh (#496). Restore a session at boot with an explicit\n * `refresh()`, and pass `logoutPath` so the backend revokes the cookie:\n * without a working revocation, that boot `refresh()` restores the previous\n * account for whoever uses the browser next.\n *\n * Refreshes are collapsed per rotation, not just per overlap: a `401` whose\n * request left with a token that has since been replaced in the same session\n * replays with the new token and refreshes nothing. Three expired requests take\n * one refresh whether their `401`s arrive together or one after another.\n *\n * @example\n * const auth = createTempestAuth<User, { email: string; password: string }>({\n * baseURL: import.meta.env.VITE_API_URL,\n * mePath: \"/api/auth/me\",\n * });\n *\n * await auth.login({ email, password }); // stores session, returns the user\n * const orders = await auth.api.get(\"/api/orders\"); // sends the bearer token\n * await auth.logout(); // clears, then runs onSessionEnd\n *\n * @param options - The auth configuration.\n * @returns The store hook, a wired API client, and login/logout/refresh helpers.\n */\nexport function createTempestAuth<TUser, TCredentials = { email: string; password: string }>(\n options: CreateTempestAuthOptions<TUser>,\n): TempestAuth<TUser, TCredentials> {\n const {\n baseURL,\n prefix,\n loginPath = \"/api/auth/login\",\n refreshPath = \"/api/auth/refresh\",\n logoutPath,\n onLogoutError,\n mePath,\n storeName = \"tempest-auth\",\n storage = \"local\",\n withCredentials = false,\n csrf,\n fetcher,\n parseTokens = defaultParseTokens,\n parseUser,\n refreshBody = (rt) => (rt ? { refresh_token: rt } : undefined),\n retry,\n redirectTo,\n onSessionEnd,\n } = options;\n\n const useAuthStore = createAuthStore<TUser>({ name: storeName, storage });\n const refreshKey = `${storeName}-refresh`;\n\n function storageImpl(): Storage | null {\n if (typeof window === \"undefined\") return null;\n return storage === \"session\" ? window.sessionStorage : window.localStorage;\n }\n function readRefreshToken(): string | null {\n return storageImpl()?.getItem(refreshKey) ?? null;\n }\n function writeRefreshToken(token: string | null): void {\n const s = storageImpl();\n if (!s) return;\n if (token) s.setItem(refreshKey, token);\n else s.removeItem(refreshKey);\n }\n\n const state = (): AuthState<TUser> => useAuthStore.getState();\n const getToken = (): string | null => state().token;\n\n /**\n * Load the current user, without letting a `401` end the session.\n *\n * `skipAuthRetry` is what keeps this one client honest: the call is\n * authenticated, but a `401` here surfaces instead of triggering a refresh\n * and `onUnauthorized`. That was the behaviour of the throwaway client this\n * used to build per invocation, and it is the reason the preset needed\n * three clients before 0.66.0.\n *\n * Bound to the epoch current when the request leaves, like `rotate()`: a\n * profile that resolves after `logout()` — or after `logout()` +\n * `login(B)` — is discarded instead of written next to B's token, and the\n * `login()` that asked for it rejects as superseded (#473).\n *\n * @throws {SessionSupersededError} When the epoch changed while the\n * request was in flight, on success and on failure alike.\n */\n async function fetchUser(): Promise<TUser | null> {\n if (!mePath) return state().user;\n const started = epoch;\n let user: TUser;\n try {\n user = await api.get<TUser>(mePath, { skipAuthRetry: true });\n } catch (error) {\n if (epoch !== started) throw new SessionSupersededError();\n throw error;\n }\n if (epoch !== started) throw new SessionSupersededError();\n state().setUser(user);\n return user;\n }\n\n let epoch = 0;\n let ending: Promise<void> = Promise.resolve();\n let hooksRunning = 0;\n const loginsInFlight = new Set<Promise<unknown>>();\n const revocations = new Set<Promise<void>>();\n const credentialRequests = new Set<Promise<unknown>>();\n const rotatingEpochs = new Set<number>();\n\n /**\n * Track a login or refresh request until it settles, so a cookie-mode\n * revocation can wait for the response that sets the cookie it revokes.\n *\n * @param request - The request in flight.\n * @returns The same request.\n */\n function trackCredentialRequest<T>(request: Promise<T>): Promise<T> {\n const settled = request.then(\n () => undefined,\n () => undefined,\n );\n credentialRequests.add(settled);\n void settled.then(() => credentialRequests.delete(settled));\n return request;\n }\n\n /**\n * Surface a failed revocation: `onLogoutError`, plus a console warning in a\n * dev build.\n *\n * @param error - What the revocation rejected with.\n */\n function reportLogoutError(error: unknown): void {\n if (isDevBuild()) {\n console.warn(\n \"[tempest-react-sdk] createTempestAuth: logoutPath revocation failed\",\n error,\n );\n }\n try {\n onLogoutError?.(error);\n } catch {\n return;\n }\n }\n\n /**\n * Send one `logoutPath` revocation and track it until it settles. Never\n * rejects.\n *\n * @param build - Produces the request, after whatever wait it needs.\n * @returns A promise settling when the revocation settled.\n */\n function trackRevocation(build: () => Promise<unknown>): Promise<void> {\n const tracked = (async () => {\n try {\n await build();\n } catch (error) {\n reportLogoutError(error);\n }\n })();\n revocations.add(tracked);\n void tracked.then(() => revocations.delete(tracked));\n return tracked;\n }\n\n /**\n * Revoke, in body mode, the refresh token a superseded login or refresh\n * brought back. Nobody can use it — the client discarded it — but it would\n * stay valid on the server until it expires.\n *\n * Cookie mode is left to `revoke()`, which waits for these responses\n * instead: re-sending a cookie revocation here could carry whichever\n * account's cookie the browser holds by then.\n *\n * @param token - The superseded response's access token.\n * @param refreshToken - The superseded response's refresh token.\n */\n function revokeDiscarded(token: string | null, refreshToken: string | undefined): void {\n if (!logoutPath || withCredentials || !refreshToken) return;\n void trackRevocation(() =>\n endedSessionApi(token).post<unknown>(logoutPath, {\n body: refreshBody(refreshToken),\n }),\n );\n }\n\n /**\n * A client that sends an ended session's access token, and never refreshes\n * or ends a session: a `401` there just rejects.\n *\n * @param token - The ended session's access token.\n * @returns The client `onSessionEnd` and the `logoutPath` call go through.\n */\n function endedSessionApi(token: string | null): ApiClient {\n return createApiClient({\n baseURL,\n prefix,\n withCredentials,\n csrf,\n fetcher,\n getToken: () => token,\n });\n }\n\n /**\n * Start a new session epoch: supersede the refresh in flight and give the\n * new epoch a refresh queue of its own, so a `401` under the new session\n * never joins the old session's refresh.\n *\n * The client reads the epoch when each attempt leaves (`getSessionEpoch`),\n * which is what ties a `401` to the session that sent it — no set of\n * tokens to keep, and nothing that grows with the session's length.\n */\n function nextEpoch(): void {\n epoch += 1;\n queue = createRefreshQueue(rotate);\n }\n\n /**\n * Perform one refresh for the epoch that is current when it starts.\n *\n * Checked again after the round trip, on success and on failure alike: a\n * refresh that fails after `logout()` + `login(B)` must not reach\n * `onUnauthorized` either, or it would end B's session.\n *\n * @throws {SessionSupersededError} When the epoch changed during the call.\n */\n async function rotate(): Promise<void> {\n const started = epoch;\n rotatingEpochs.add(started);\n try {\n let data: unknown;\n try {\n data = await trackCredentialRequest(\n api.post<unknown>(refreshPath, {\n body: refreshBody(readRefreshToken()),\n skipAuth: true,\n }),\n );\n } catch (error) {\n if (epoch !== started) throw new SessionSupersededError();\n throw error;\n }\n const { token, refreshToken } = parseTokens(data);\n if (epoch !== started) {\n revokeDiscarded(token ?? null, refreshToken);\n throw new SessionSupersededError();\n }\n state().setToken(token);\n if (refreshToken) writeRefreshToken(refreshToken);\n } finally {\n rotatingEpochs.delete(started);\n }\n }\n\n let queue = createRefreshQueue(rotate);\n\n /**\n * Whether a `401` must be left alone: no refresh, no ending the session.\n *\n * Two cases, both of the current epoch: an attempt that carried no token\n * while a session is now installed (it left before the sign-in, and\n * refreshing or ending the new session for it would punish the wrong\n * session); and an attempt whose token the session has since rotated past\n * (the rotation it would trigger already happened).\n *\n * @param context - The attempt's credential.\n * @returns Whether the attempt is stale within the current session.\n */\n function staleWithinSession(context: RequestAuthContext): boolean {\n const current = getToken();\n if (current === null) return false;\n return context.token === null || context.token !== current;\n }\n\n /**\n * Whether nothing could make a tokenless `401` worth a refresh: no access\n * token and no stored refresh token.\n *\n * With `withCredentials`, the refresh would otherwise be answered by an\n * HttpOnly cookie the client cannot see or clear — after `logout()`, that\n * signed the previous account back in for whoever used the browser next\n * (#474). A `login()` in flight does not count as a session either: the\n * cookie a refresh would present during it is still the ended account's\n * (#496). Restoring a session from the cookie is an explicit `refresh()`,\n * typically at boot, which this does not touch.\n *\n * @returns Whether no session is installed.\n */\n function signedOut(): boolean {\n return getToken() === null && readRefreshToken() === null;\n }\n\n /**\n * Settle a tokenless `401` that arrived with no session installed while a\n * `login()` is in flight: wait for every login in flight, then let the\n * request replay when one of them installed a token.\n *\n * No refresh is sent. In cookie mode one would present whatever cookie\n * the browser holds — after `logout()` without a working `logoutPath`,\n * the previous account's: a failed login then left the client signed in\n * as that account, and a successful one could have its cookie overwritten\n * by the refresh's late `Set-Cookie` (#496).\n *\n * Skipped while an `onSessionEnd` hook runs: an account switch waits on\n * that hook, so a hook awaiting `auth.api` would wait on itself.\n *\n * @throws {Error} When no login is in flight, a hook is running, or every\n * login settled without installing a token — the `401` surfaces as is.\n */\n async function awaitLogins(): Promise<void> {\n if (loginsInFlight.size > 0 && hooksRunning === 0) {\n await Promise.allSettled([...loginsInFlight]);\n if (getToken() !== null) return;\n }\n throw new Error(\"anonymous 401 with no session\");\n }\n\n /**\n * The `refresh` the client calls on a `401`.\n *\n * - An attempt from an ended epoch is refused rather than replayed as\n * whoever is signed in now.\n * - A token this session already rotated past: the replay just picks up the\n * new one — no second refresh.\n * - No token, while a session is now installed: the `401` surfaces as is.\n * - No token, and no session installed: joins this epoch's refresh when\n * one is already in flight — the boot `refresh()` restoring a session\n * from the cookie, which the request then replays under; otherwise waits\n * for a `login()` in flight and replays with its token, see\n * `awaitLogins`; otherwise surfaces the `401` as is, see `signedOut`.\n * - Otherwise: join or start this epoch's refresh.\n *\n * @param context - The credential and epoch the rejected attempt left with.\n * @throws {SessionSupersededError} When the attempt belongs to an ended\n * session, or the session changed during the refresh.\n */\n async function refreshFor(context: RequestAuthContext): Promise<void> {\n if (context.epoch !== null && context.epoch !== epoch) throw new SessionSupersededError();\n if (staleWithinSession(context)) {\n if (context.token === null) throw new Error(\"anonymous 401 under a live session\");\n return;\n }\n if (context.token === null && signedOut()) {\n if (rotatingEpochs.has(epoch)) return queue();\n return awaitLogins();\n }\n return queue();\n }\n\n /**\n * The client's `onUnauthorized`: end the session only when the `401` is the\n * current session's own.\n *\n * The replay after a refresh is checked here too — it can come back `401`\n * after the user signed out and another account signed in, and ending the\n * session then would sign the new account out. A tokenless `401` with no\n * session installed ends nothing either: there is no session to end, and\n * ending \"nothing\" used to move the epoch and supersede the boot\n * `refresh()` in flight.\n *\n * @param context - The credential and epoch of the attempt that failed.\n * @throws {SessionSupersededError} When that attempt belongs to an ended\n * session.\n */\n async function onUnauthorized(_response: Response, context: RequestAuthContext): Promise<void> {\n if (context.epoch !== null && context.epoch !== epoch) throw new SessionSupersededError();\n if (staleWithinSession(context)) return;\n if (context.token === null && signedOut()) return;\n await endSession();\n }\n\n /**\n * Authenticate and install the new session.\n *\n * The previous session ends only once the new credentials are accepted: a\n * login that fails leaves it, and the refresh it may have in flight,\n * untouched. When one was live, `onSessionEnd` runs with `\"switch\"` before\n * the new tokens are installed — see the option for why that order. A\n * `logout()` still tearing down is awaited the same way, so the new\n * account's state never starts before the previous one's teardown ended.\n * Never call it from inside `onSessionEnd`: it would wait on that hook.\n * Every `logoutPath` revocation still in flight is awaited before the\n * request leaves — at most {@link LOGOUT_REVOCATION_WAIT_MS} — so a\n * cookie-clearing response cannot land after the new account's cookies.\n *\n * @param credentials - Sent as the login body.\n * @returns The user of the new session, or null.\n * @throws {SessionSupersededError} When a `logout()` or another `login()`\n * moved the session while this one was in flight — during its request,\n * during the teardown or while `mePath` was loading the user; the new\n * tokens, or the user, are discarded.\n */\n async function login(credentials: TCredentials): Promise<TUser | null> {\n if (revocations.size > 0) await settleWithin([...revocations], LOGOUT_REVOCATION_WAIT_MS);\n const run = signIn(credentials);\n loginsInFlight.add(run);\n try {\n return await run;\n } finally {\n loginsInFlight.delete(run);\n }\n }\n\n /**\n * The body of `login()`, tracked in `loginsInFlight` by its caller so a\n * tokenless `401` during the sign-in waits for it and replays with the\n * token it installs, see `awaitLogins`. Tracking starts only after a\n * pending revocation settled.\n *\n * @param credentials - Sent as the login body.\n * @returns The user of the new session, or null.\n * @throws {SessionSupersededError} See `login()`.\n */\n async function signIn(credentials: TCredentials): Promise<TUser | null> {\n const started = epoch;\n const data = await trackCredentialRequest(\n api.post<unknown>(loginPath, {\n body: credentials,\n skipAuth: true,\n }),\n );\n const { token, refreshToken } = parseTokens(data);\n if (epoch !== started) {\n revokeDiscarded(token ?? null, refreshToken);\n throw new SessionSupersededError();\n }\n const { done } = finish(\"switch\");\n const mine = epoch;\n await done.catch(() => undefined);\n if (epoch !== mine) throw new SessionSupersededError();\n state().setToken(token);\n writeRefreshToken(refreshToken ?? null);\n const embedded = parseUser?.(data);\n if (embedded != null) {\n state().setUser(embedded);\n return embedded;\n }\n return fetchUser();\n }\n\n /**\n * End the current session, synchronously up to the hook.\n *\n * The epoch moves first, so a refresh resolving between here and the hook\n * is already superseded. The hook only runs when there was a session to\n * end — a token, a stored refresh token or a user — which is what keeps a\n * burst of `401`s from running it once per request.\n *\n * With nothing left to end, the caller waits on the teardown already under\n * way (`ending`, which never rejects, so a later no-op does not inherit an\n * old error). Two callers skip that wait while a hook is running, because\n * they may be the hook itself and would wait on their own teardown forever:\n * `endSession` — a hook awaiting `auth.api` goes out tokenless, gets a\n * `401`, and lands here — and a `logout()` with no session left. `login()`\n * (`\"switch\"`) always waits: the new account's state must not start while\n * the previous one's teardown is still clearing it. A hook awaiting\n * `auth.login()` therefore never settles — misuse the option documents.\n *\n * `\"expired\"` with nothing live leaves the epoch alone: there is nothing to\n * supersede but a refresh with no session behind it — the boot `refresh()`\n * restoring one from the cookie — and moving the epoch would throw that\n * away. `logout()` and `login()` always move it, since they must supersede\n * such a refresh.\n *\n * The returned `done` is a fresh promise derived from the hook, not the one\n * `ending` holds: a `logout()` nobody awaits still surfaces the hook's error\n * as an unhandled rejection instead of losing it.\n *\n * @param reason - Whether the caller signed out, the client gave up, or a\n * login replaced the session.\n * @returns Whether a live session ended here, and a promise settling with\n * the hook.\n */\n function finish(reason: TempestSessionEnd<TUser>[\"reason\"]): {\n live: boolean;\n done: Promise<void>;\n } {\n const { user, token } = state();\n const live = token !== null || user !== null || readRefreshToken() !== null;\n if (live || reason !== \"expired\") nextEpoch();\n state().logout();\n writeRefreshToken(null);\n if (!live) {\n const wait = reason === \"switch\" || hooksRunning === 0;\n return { live, done: wait ? ending : Promise.resolve() };\n }\n if (!onSessionEnd) return { live, done: Promise.resolve() };\n const sessionApi = endedSessionApi(token);\n hooksRunning += 1;\n const run = (async () => {\n try {\n await onSessionEnd({ reason, user, token, api: sessionApi });\n } finally {\n hooksRunning -= 1;\n }\n })();\n ending = run.then(\n () => undefined,\n () => undefined,\n );\n return { live, done: run.then(() => undefined) };\n }\n\n /**\n * Send the `logoutPath` revocation for the session about to end, with the\n * credentials it holds now — read before `finish` clears them.\n *\n * Skipped without `logoutPath`, and in body mode when there is nothing to\n * revoke; in cookie mode it always goes out, since the cookie it revokes is\n * invisible to the client. Never rejects.\n *\n * In cookie mode it first waits — at most\n * {@link LOGOUT_REVOCATION_WAIT_MS} — for the login and refresh responses\n * in flight right now: each one sets a cookie for the session this\n * `logout()` ends, and a revocation that left before it would leave that\n * cookie behind.\n *\n * @returns A promise settling when the revocation settled, or null when\n * none was sent.\n */\n function revoke(): Promise<void> | null {\n if (!logoutPath) return null;\n const path = logoutPath;\n const { token, user } = state();\n const refreshToken = readRefreshToken();\n if (!withCredentials && token === null && user === null && refreshToken === null) {\n return null;\n }\n const inFlight = withCredentials ? [...credentialRequests] : [];\n return trackRevocation(async () => {\n await settleWithin(inFlight, LOGOUT_REVOCATION_WAIT_MS);\n return endedSessionApi(token).post<unknown>(path, { body: refreshBody(refreshToken) });\n });\n }\n\n function logout(): Promise<void> {\n const revocation = revoke();\n const { done } = finish(\"logout\");\n if (!revocation) return done;\n return Promise.all([revocation, done]).then(() => undefined);\n }\n\n /**\n * End the session after the client gave up on it and, when `redirectTo`\n * is set, leave the page once `onSessionEnd` settled.\n *\n * Separate from `logout` so an explicit sign-out stays a pure state change:\n * a caller that already navigates itself would otherwise get a second,\n * competing navigation. The hook is awaited before navigating because a\n * hard navigation aborts whatever request it still had in flight. Only the\n * call that ended a live session navigates; the rest of a burst of `401`s\n * waits on the same teardown, or returns at once inside a hook.\n */\n async function endSession(): Promise<void> {\n const { live, done } = finish(\"expired\");\n await done.catch(() => undefined);\n if (live && redirectTo && typeof window !== \"undefined\") {\n window.location.assign(redirectTo);\n }\n }\n\n const api = createApiClient({\n baseURL,\n prefix,\n withCredentials,\n csrf,\n fetcher,\n getToken,\n getSessionEpoch: () => epoch,\n refresh: refreshFor,\n retry,\n onUnauthorized,\n });\n\n const refresh = (): Promise<void> => queue();\n\n return { useAuthStore, api, login, logout, refresh, getToken };\n}\n"],"mappings":"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"}
|
|
@@ -121,9 +121,7 @@ function c(c) {
|
|
|
121
121
|
if (e.epoch !== null && e.epoch !== M) throw new t();
|
|
122
122
|
if (q(e)) {
|
|
123
123
|
if (e.token === null) throw Error("anonymous 401 under a live session");
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
return e.token === null && J() ? R.has(M) ? K() : Y() : K();
|
|
124
|
+
} else return e.token === null && J() ? R.has(M) ? K() : Y() : K();
|
|
127
125
|
}
|
|
128
126
|
async function Z(e, n) {
|
|
129
127
|
if (n.epoch !== null && n.epoch !== M) throw new t();
|