streamlit-nightly 1.53.1.dev20260114__py3-none-any.whl → 1.53.1.dev20260116__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. streamlit/__init__.py +0 -26
  2. streamlit/auth_util.py +90 -1
  3. streamlit/cli_util.py +2 -1
  4. streamlit/commands/echo.py +2 -2
  5. streamlit/commands/execution_control.py +1 -1
  6. streamlit/commands/navigation.py +1 -1
  7. streamlit/components/types/base_custom_component.py +0 -2
  8. streamlit/components/v1/custom_component.py +0 -2
  9. streamlit/components/v2/component_path_utils.py +1 -1
  10. streamlit/components/v2/manifest_scanner.py +8 -3
  11. streamlit/components/v2/presentation.py +1 -1
  12. streamlit/config.py +44 -13
  13. streamlit/config_util.py +5 -5
  14. streamlit/dataframe_util.py +5 -5
  15. streamlit/elements/arrow.py +11 -6
  16. streamlit/elements/deck_gl_json_chart.py +1 -1
  17. streamlit/elements/exception.py +4 -2
  18. streamlit/elements/form.py +1 -1
  19. streamlit/elements/layouts.py +1 -1
  20. streamlit/elements/lib/built_in_chart_utils.py +7 -7
  21. streamlit/elements/lib/column_config_utils.py +6 -6
  22. streamlit/elements/lib/dialog.py +1 -1
  23. streamlit/elements/lib/image_utils.py +4 -4
  24. streamlit/elements/lib/layout_utils.py +1 -1
  25. streamlit/elements/lib/policies.py +1 -1
  26. streamlit/elements/lib/streamlit_plotly_theme.py +9 -11
  27. streamlit/elements/lib/utils.py +1 -1
  28. streamlit/elements/map.py +6 -6
  29. streamlit/elements/plotly_chart.py +2 -2
  30. streamlit/elements/toast.py +1 -1
  31. streamlit/elements/vega_charts.py +2 -2
  32. streamlit/elements/widgets/button.py +3 -3
  33. streamlit/elements/widgets/button_group.py +3 -3
  34. streamlit/elements/widgets/chat.py +1 -1
  35. streamlit/elements/widgets/data_editor.py +6 -6
  36. streamlit/elements/widgets/number_input.py +1 -1
  37. streamlit/elements/widgets/slider.py +5 -5
  38. streamlit/elements/widgets/time_widgets.py +91 -10
  39. streamlit/elements/write.py +1 -1
  40. streamlit/env_util.py +1 -1
  41. streamlit/errors.py +0 -14
  42. streamlit/proto/NewSession_pb2.py +18 -18
  43. streamlit/proto/NewSession_pb2.pyi +5 -1
  44. streamlit/runtime/app_session.py +78 -59
  45. streamlit/runtime/caching/cache_data_api.py +3 -3
  46. streamlit/runtime/caching/cache_errors.py +0 -2
  47. streamlit/runtime/caching/cache_resource_api.py +1 -1
  48. streamlit/runtime/caching/cache_utils.py +2 -2
  49. streamlit/runtime/caching/hashing.py +1 -3
  50. streamlit/runtime/caching/storage/cache_storage_protocol.py +0 -3
  51. streamlit/runtime/connection_factory.py +1 -1
  52. streamlit/runtime/runtime.py +6 -6
  53. streamlit/runtime/scriptrunner_utils/exceptions.py +0 -4
  54. streamlit/runtime/scriptrunner_utils/script_run_context.py +0 -21
  55. streamlit/runtime/secrets.py +2 -3
  56. streamlit/runtime/state/query_params.py +0 -19
  57. streamlit/runtime/state/session_state.py +1 -1
  58. streamlit/runtime/stats.py +0 -7
  59. streamlit/static/index.html +1 -1
  60. streamlit/static/manifest.json +299 -299
  61. streamlit/static/static/js/{ErrorOutline.esm.BAZUU4id.js → ErrorOutline.esm.CiSfK8ht.js} +1 -1
  62. streamlit/static/static/js/{FileDownload.esm.C6tTTniR.js → FileDownload.esm.ItNjcEfs.js} +1 -1
  63. streamlit/static/static/js/{FileHelper.BOHlwlc9.js → FileHelper.xS7f19FE.js} +1 -1
  64. streamlit/static/static/js/{FormClearHelper.D4lty7rT.js → FormClearHelper.N8_BCinn.js} +1 -1
  65. streamlit/static/static/js/{InputInstructions.BWw9lhud.js → InputInstructions.CrsdK7CQ.js} +1 -1
  66. streamlit/static/static/js/Particles.BDlTHC3I.js +1 -0
  67. streamlit/static/static/js/{ProgressBar.DCHh4N3P.js → ProgressBar.Bk2qeHfS.js} +2 -2
  68. streamlit/static/static/js/{StreamlitSyntaxHighlighter.BRydQwEj.js → StreamlitSyntaxHighlighter.BcuPrPcw.js} +1 -1
  69. streamlit/static/static/js/{TableChart.esm.7KFX5I_G.js → TableChart.esm.CP7XPz8I.js} +1 -1
  70. streamlit/static/static/js/Toolbar.DCJcLwve.js +1 -0
  71. streamlit/static/static/js/{WidgetLabelHelpIconInline.CzodezAH.js → WidgetLabelHelpIconInline.BPqxu1b-.js} +1 -1
  72. streamlit/static/static/js/{base-input.Cg7NpsfS.js → base-input.x4muJJ13.js} +4 -4
  73. streamlit/static/static/js/{checkbox.Bs20OTna.js → checkbox.C5r4AIaH.js} +1 -1
  74. streamlit/static/static/js/{createDownloadLinkElement.DSqCyu38.js → createDownloadLinkElement.C83kkEeT.js} +1 -1
  75. streamlit/static/static/js/{data-grid-overlay-editor.Ch4SqNfY.js → data-grid-overlay-editor.0sOWm4IB.js} +1 -1
  76. streamlit/static/static/js/{downloader.Oj5CTGJA.js → downloader.C_6FfoEv.js} +1 -1
  77. streamlit/static/static/js/{embed.CqzzUq73.js → embed.BnJ6-SK4.js} +14 -14
  78. streamlit/static/static/js/{es6.CYrlw0Vn.js → es6.44_BWfFd.js} +2 -2
  79. streamlit/static/static/js/{formatNumber.CT_v8e75.js → formatNumber.DhHZAosz.js} +1 -1
  80. streamlit/static/static/js/{iconPosition.DPAvXTmd.js → iconPosition.shOoQuRR.js} +1 -1
  81. streamlit/static/static/js/{iframeResizer.contentWindow.T4DvQsIf.js → iframeResizer.contentWindow.savEE5Vs.js} +1 -1
  82. streamlit/static/static/js/{index.BoWBzl6h.js → index.AdijUi9Z.js} +1 -1
  83. streamlit/static/static/js/{index.Dtd5z2rM.js → index.B5HlOnvq.js} +1 -1
  84. streamlit/static/static/js/{index.MtwRNvlS.js → index.B6ewTTej.js} +1 -1
  85. streamlit/static/static/js/{index.D7KbBAWb.js → index.B8Of3XXR.js} +1 -1
  86. streamlit/static/static/js/{index.xCV2qwtw.js → index.BCaCSVj6.js} +1 -1
  87. streamlit/static/static/js/index.BE7Rhig1.js +1 -0
  88. streamlit/static/static/js/{index.DL_Ooizi.js → index.BHpLZ7X6.js} +2 -2
  89. streamlit/static/static/js/{index.BPxuKK0S.js → index.BNvP8E5Q.js} +1 -1
  90. streamlit/static/static/js/{index.LOA31DFn.js → index.BNzuyXeN.js} +2 -2
  91. streamlit/static/static/js/{index.Cfe-tCQJ.js → index.BT7o4A9V.js} +1 -1
  92. streamlit/static/static/js/{index.BZ8xp-w9.js → index.BYY3iuVE.js} +1 -1
  93. streamlit/static/static/js/index.BZI7jTDf.js +1 -0
  94. streamlit/static/static/js/{index.CGRIbGGV.js → index.BZXk3Qxq.js} +1 -1
  95. streamlit/static/static/js/{index.CVXiBeDI.js → index.BhQvq6YD.js} +1 -1
  96. streamlit/static/static/js/{index.CnXxoVEM.js → index.BkIReLGJ.js} +3 -3
  97. streamlit/static/static/js/{index.AGYZDQZm.js → index.Bl7SMZUw.js} +1 -1
  98. streamlit/static/static/js/{index.b-MrPulo.js → index.BqtgAGnG.js} +2 -2
  99. streamlit/static/static/js/{index.B3HdcUdo.js → index.BwnLQnAk.js} +1 -1
  100. streamlit/static/static/js/index.C1coXTgf.js +2 -0
  101. streamlit/static/static/js/{index.DXRGd--0.js → index.C6uOmY9O.js} +1 -1
  102. streamlit/static/static/js/index.CF88nImM.js +1 -0
  103. streamlit/static/static/js/{index.B9Oowxt8.js → index.CFlGfFvq.js} +1 -1
  104. streamlit/static/static/js/{index.wngeYhKj.js → index.CQgiNpC7.js} +1 -1
  105. streamlit/static/static/js/{index.D84XHt50.js → index.CZJkiRFm.js} +1 -1
  106. streamlit/static/static/js/index.Ch1TdQMG.js +1 -0
  107. streamlit/static/static/js/{index.Yj6vcyFD.js → index.Ck2mQvBi.js} +1 -1
  108. streamlit/static/static/js/{index.DPlPEuq6.js → index.CrD2lwZG.js} +1 -1
  109. streamlit/static/static/js/index.CySNwKoN.js +2 -0
  110. streamlit/static/static/js/index.D3LEkXqN.js +1 -0
  111. streamlit/static/static/js/{index.BTA0d5oq.js → index.D8BQH06Z.js} +1 -1
  112. streamlit/static/static/js/{index.B8ZZdPqF.js → index.DBtWixG1.js} +1 -1
  113. streamlit/static/static/js/{index.CVKk1nkB.js → index.DIsNrJpC.js} +1 -1
  114. streamlit/static/static/js/{index.CPYB1awG.js → index.DO3HICut.js} +1 -1
  115. streamlit/static/static/js/{index.D6HCANv6.js → index.D_ziFad8.js} +1 -1
  116. streamlit/static/static/js/{index.BTqav7_K.js → index.Dblsdn8d.js} +1 -1
  117. streamlit/static/static/js/{index.Dg9k4R8B.js → index.DcdD0ROn.js} +2 -2
  118. streamlit/static/static/js/{index.tLZuZM89.js → index.DfHm3P22.js} +51 -51
  119. streamlit/static/static/js/{index.CXVpBAvU.js → index.DiwhD0Ic.js} +46 -46
  120. streamlit/static/static/js/index.DvZDNiBV.js +11 -0
  121. streamlit/static/static/js/{index.DQzRwgrT.js → index.HjydQ4bj.js} +1 -1
  122. streamlit/static/static/js/{index.XHtvnZ0-.js → index.HsxzYHNf.js} +1 -1
  123. streamlit/static/static/js/{index.DesNeXSA.js → index.SVJGpnpy.js} +1 -1
  124. streamlit/static/static/js/index.SjKkVSL7.js +1 -0
  125. streamlit/static/static/js/index.hX8_3tMP.js +1 -0
  126. streamlit/static/static/js/{index.BEzgNZOm.js → index.jZin0w6_.js} +1 -1
  127. streamlit/static/static/js/{index.DzldU3Hy.js → index.pQvPlPcA.js} +4 -4
  128. streamlit/static/static/js/index.pzQTLx9j.js +1 -0
  129. streamlit/static/static/js/{index.ClFk8x0U.js → index.s44_OVd6.js} +33 -33
  130. streamlit/static/static/js/{input.CYXuTqoa.js → input.jAD-v9D6.js} +2 -2
  131. streamlit/static/static/js/{main.BE-siVKv.js → main.Cas0fuOs.js} +1 -1
  132. streamlit/static/static/js/{memory.V554ztRg.js → memory.ENruL8Qk.js} +1 -1
  133. streamlit/static/static/js/{number-overlay-editor.BiUTOXIl.js → number-overlay-editor.DAOTdulh.js} +2 -2
  134. streamlit/static/static/js/{pandasStylerUtils.Dej3Tstq.js → pandasStylerUtils.DChYgcPq.js} +1 -1
  135. streamlit/static/static/js/{sandbox.gpd7KGMo.js → sandbox.DWZYRCm0.js} +1 -1
  136. streamlit/static/static/js/{styled-components.Coj4dr6D.js → styled-components.3lwVqgRW.js} +1 -1
  137. streamlit/static/static/js/{throttle.gZUdtYp7.js → throttle.DfOW1Cns.js} +1 -1
  138. streamlit/static/static/js/{timepicker.B-Y4aU15.js → timepicker.CXHgL5f9.js} +1 -1
  139. streamlit/static/static/js/{toConsumableArray.CcKcKvEd.js → toConsumableArray.DiW0GSDV.js} +1 -1
  140. streamlit/static/static/js/uniqueId.Dfi3SGKZ.js +1 -0
  141. streamlit/static/static/js/{useBasicWidgetState.D-fc_aIL.js → useBasicWidgetState.DnPEt_f3.js} +1 -1
  142. streamlit/static/static/js/{useIntlLocale.CY32IeNt.js → useIntlLocale.BZEGSOVD.js} +8 -8
  143. streamlit/static/static/js/{useTextInputAutoExpand.C0jK7TwF.js → useTextInputAutoExpand.MGVZMqyt.js} +1 -1
  144. streamlit/static/static/js/useUpdateUiValue.CPl6CEx3.js +1 -0
  145. streamlit/static/static/js/{useWaveformController.BLEIAIDo.js → useWaveformController.DOpzDyZu.js} +1 -1
  146. streamlit/static/static/js/{withCalculatedWidth.B3oSlRC0.js → withCalculatedWidth.DgKFTeif.js} +1 -1
  147. streamlit/static/static/js/{withFullScreenWrapper.Cw_ebNmr.js → withFullScreenWrapper.K1jkwhPu.js} +1 -1
  148. streamlit/string_util.py +2 -2
  149. streamlit/testing/v1/element_tree.py +8 -10
  150. streamlit/type_util.py +2 -2
  151. streamlit/url_util.py +2 -2
  152. streamlit/user_info.py +2 -2
  153. streamlit/util.py +1 -1
  154. streamlit/watcher/path_watcher.py +1 -1
  155. streamlit/web/cli.py +1 -4
  156. streamlit/web/server/app_discovery.py +2 -1
  157. streamlit/web/server/oauth_authlib_routes.py +14 -42
  158. streamlit/web/server/server.py +1 -1
  159. streamlit/web/server/server_util.py +1 -1
  160. streamlit/web/server/starlette/starlette_auth_routes.py +94 -16
  161. streamlit/web/server/starlette/starlette_routes.py +9 -3
  162. streamlit/web/server/starlette/starlette_server.py +2 -2
  163. {streamlit_nightly-1.53.1.dev20260114.dist-info → streamlit_nightly-1.53.1.dev20260116.dist-info}/METADATA +1 -1
  164. {streamlit_nightly-1.53.1.dev20260114.dist-info → streamlit_nightly-1.53.1.dev20260116.dist-info}/RECORD +168 -169
  165. streamlit/commands/experimental_query_params.py +0 -169
  166. streamlit/static/static/js/Particles.BDi7fIn-.js +0 -1
  167. streamlit/static/static/js/Toolbar.guIuiwEF.js +0 -1
  168. streamlit/static/static/js/index.BJnWg9Jq.js +0 -1
  169. streamlit/static/static/js/index.Bqgt60FU.js +0 -1
  170. streamlit/static/static/js/index.BsYYrijt.js +0 -11
  171. streamlit/static/static/js/index.CJQq5LcR.js +0 -2
  172. streamlit/static/static/js/index.Cw4eSvJ7.js +0 -1
  173. streamlit/static/static/js/index.DNyw7S7Z.js +0 -1
  174. streamlit/static/static/js/index.DZDt5hYD.js +0 -1
  175. streamlit/static/static/js/index.Do9A7QCt.js +0 -1
  176. streamlit/static/static/js/index.DrSH3pK3.js +0 -2
  177. streamlit/static/static/js/index.GZi6GTJa.js +0 -1
  178. streamlit/static/static/js/index.hcUYvTqs.js +0 -1
  179. streamlit/static/static/js/uniqueId.PRn3V1WU.js +0 -1
  180. streamlit/static/static/js/useUpdateUiValue.CDQloDgB.js +0 -1
  181. {streamlit_nightly-1.53.1.dev20260114.data → streamlit_nightly-1.53.1.dev20260116.data}/scripts/streamlit.cmd +0 -0
  182. {streamlit_nightly-1.53.1.dev20260114.dist-info → streamlit_nightly-1.53.1.dev20260116.dist-info}/WHEEL +0 -0
  183. {streamlit_nightly-1.53.1.dev20260114.dist-info → streamlit_nightly-1.53.1.dev20260116.dist-info}/entry_points.txt +0 -0
  184. {streamlit_nightly-1.53.1.dev20260114.dist-info → streamlit_nightly-1.53.1.dev20260116.dist-info}/top_level.txt +0 -0
@@ -1 +1 @@
1
- import{l as Z,r as i,aR as M,ab as $,aS as O}from"./index.CXVpBAvU.js";async function q(l,e=16e3){if(!l||l.size===0)throw new Error("Invalid or empty blob provided");if(!window.AudioContext)throw new Error("AudioContext not supported in this browser");const o=new AudioContext;try{const n=await l.arrayBuffer(),r=await o.decodeAudioData(n),d=e??r.sampleRate,s=await J(r,d);return K(s,d)}finally{o.close()}}async function J(l,e){const{duration:o,numberOfChannels:n,sampleRate:r}=l,d=Math.ceil(o*e);if(!window.OfflineAudioContext)throw new Error("OfflineAudioContext not supported");const s=new OfflineAudioContext(1,d,e),p=s.createBufferSource();if(p.buffer=l,n>1){const h=s.createChannelSplitter(n),m=s.createChannelMerger(1);p.connect(h);for(let c=0;c<n;c++){const u=s.createGain();u.gain.value=1/n,h.connect(u,c),u.connect(m,0,0)}m.connect(s.destination)}else p.connect(s.destination);p.start(0);try{return await s.startRendering()}catch(h){throw new Error(`Failed to resample audio from ${r}Hz to ${e}Hz: ${h instanceof Error?h.message:String(h)}`)}}function K(l,e){const n=l.length,r=n*2+44,d=new ArrayBuffer(r),s=new DataView(d),p=l.getChannelData(0),h=(c,u)=>{for(let y=0;y<u.length;y++)s.setUint8(c+y,u.charCodeAt(y))};h(0,"RIFF"),s.setUint32(4,r-8,!0),h(8,"WAVE"),h(12,"fmt "),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,1,!0),s.setUint32(24,e,!0),s.setUint32(28,e*2,!0),s.setUint16(32,2,!0),s.setUint16(34,16,!0),h(36,"data"),s.setUint32(40,n*2,!0);let m=44;for(let c=0;c<n;c++){const u=Math.max(-1,Math.min(1,p[c]));s.setInt16(m,u*32767,!0),m+=2}return new Blob([d],{type:"audio/wav"})}class W{constructor(){this.wavesurfer=null,this.currentBlobUrl=null,this.events={},this.isPlaying=!1}initialize(e){this.wavesurfer=e,this.setupEventListeners()}setupEventListeners(){this.wavesurfer&&(this.teardownEventListeners(),this.handleTimeUpdate=e=>{this.events.onTimeUpdate?.(e*1e3)},this.handlePause=()=>{this.isPlaying=!1,this.events.onPause?.()},this.handlePlay=()=>{this.isPlaying=!0,this.events.onPlay?.()},this.handleFinish=()=>{this.isPlaying=!1,this.events.onFinish?.()},this.handleReady=()=>{this.events.onReady?.()},this.handleError=e=>{const o=e instanceof Error?e:new Error(String(e));this.events.onError?.(o)},this.wavesurfer.on("timeupdate",this.handleTimeUpdate),this.wavesurfer.on("pause",this.handlePause),this.wavesurfer.on("play",this.handlePlay),this.wavesurfer.on("finish",this.handleFinish),this.wavesurfer.on("ready",this.handleReady),this.wavesurfer.on("error",this.handleError))}setEventHandlers(e){this.events=e}async load(e){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");this.cleanupPreviousUrl();let o,n=null;try{if(e instanceof Blob)n=URL.createObjectURL(e),o=n;else if(e instanceof ArrayBuffer){const r=new Blob([e]);n=URL.createObjectURL(r),o=n}else o=e;this.currentBlobUrl=n,await this.wavesurfer.load(o)}catch(r){throw this.cleanupPreviousUrl(),r}}async play(){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");await this.wavesurfer.play()}pause(){this.wavesurfer&&this.wavesurfer.pause()}getDuration(){return this.wavesurfer?this.wavesurfer.getDuration()*1e3:0}getCurrentTime(){return this.wavesurfer?this.wavesurfer.getCurrentTime()*1e3:0}getIsPlaying(){return this.isPlaying}seekToStart(){this.wavesurfer&&this.wavesurfer.seekTo(0)}cleanupPreviousUrl(){this.currentBlobUrl&&(URL.revokeObjectURL(this.currentBlobUrl),this.currentBlobUrl=null)}destroy(){this.pause(),this.cleanupPreviousUrl(),this.wavesurfer&&(this.teardownEventListeners(),this.wavesurfer.empty(),this.wavesurfer=null),this.events={},this.isPlaying=!1,this.handleTimeUpdate=void 0,this.handlePause=void 0,this.handlePlay=void 0,this.handleFinish=void 0,this.handleReady=void 0,this.handleError=void 0}teardownEventListeners(){this.wavesurfer&&(this.handleTimeUpdate&&(this.wavesurfer.un("timeupdate",this.handleTimeUpdate),this.handleTimeUpdate=void 0),this.handlePause&&(this.wavesurfer.un("pause",this.handlePause),this.handlePause=void 0),this.handlePlay&&(this.wavesurfer.un("play",this.handlePlay),this.handlePlay=void 0),this.handleFinish&&(this.wavesurfer.un("finish",this.handleFinish),this.handleFinish=void 0),this.handleReady&&(this.wavesurfer.un("ready",this.handleReady),this.handleReady=void 0),this.handleError&&(this.wavesurfer.un("error",this.handleError),this.handleError=void 0))}}function I(l){return l.name==="NotAllowedError"||l.name==="PermissionDeniedError"||l.message?.toLowerCase().includes("permission denied")}class Q{constructor(e={}){this.wavesurfer=null,this.recordPlugin=null,this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null,this.events={},this.options=e}initialize(e,o){this.wavesurfer=e;try{const n={renderRecordedAudio:!1,mimeType:"audio/webm"};this.recordPlugin=e.registerPlugin(o.create(n)),this.setupEventListeners()}catch(n){const r=n instanceof Error?n:new Error(String(n));throw I(r)?(this.events.onPermissionDenied?.(),new Error("Microphone permission denied")):(this.events.onError?.(r),r)}}setupEventListeners(){this.recordPlugin&&(this.recordPlugin.on("record-start",()=>{this.isRecording=!0,this.events.onRecordStart?.()}),this.recordPlugin.on("record-end",e=>{this.isRecording=!1,this.events.onRecordEnd?.(e),this.recordEndResolve&&e&&e.size>0?(this.recordEndResolve(e),this.recordEndResolve=null,this.recordEndReject=null):this.recordEndReject?(this.recordEndReject(new Error("Invalid or empty recording")),this.recordEndResolve=null,this.recordEndReject=null):(this.recordEndResolve=null,this.recordEndReject=null)}),this.recordPlugin.on("record-progress",e=>{this.events.onRecordProgress?.(e)}))}setEventHandlers(e){this.events=e}async startRecording(){if(!this.recordPlugin)throw new Error("Record plugin not initialized");if(this.isRecording)return;const e=typeof this.options.sampleRate=="number"?this.options.sampleRate:void 0,o={};e!==void 0&&(o.sampleRate={ideal:e}),await this.startRecordingWithConstraints(o,e!==void 0)}async startRecordingWithConstraints(e,o){if(!this.recordPlugin)throw new Error("Record plugin not initialized");try{const n=Object.keys(e).length?e:void 0;await this.recordPlugin.startRecording(n)}catch(n){const r=n instanceof Error?n:new Error(String(n));if(I(r))throw this.events.onPermissionDenied?.(),new Error("Microphone permission denied");if(o&&(r.name==="OverconstrainedError"||r.name==="NotReadableError")){this.options.sampleRate=void 0,await this.startRecordingWithConstraints({},!1);return}throw this.events.onError?.(r),r}}async stopRecording(){if(!this.recordPlugin||!this.isRecording)throw new Error("Not currently recording");try{return await new Promise((e,o)=>{this.recordEndResolve=e,this.recordEndReject=o,this.recordPlugin?.stopRecording()})}catch(e){const o=e instanceof Error?e:new Error(String(e));throw this.events.onError?.(o),o}}cancelRecording(){this.recordPlugin&&this.isRecording&&(this.recordPlugin.stopRecording(),this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null)}destroy(){this.cancelRecording(),this.recordPlugin&&(this.recordPlugin.destroy(),this.recordPlugin=null),this.wavesurfer=null,this.events={}}}const X=4,Y=4,ee=8,re=0,te=16e3;function se({containerRef:l,sampleRate:e,events:o,waveformPadding:n=0}){const r=Z(),[d,s]=i.useState("idle"),[p,h]=i.useState(null),[m,c]=i.useState(!1),u=i.useRef(null),y=i.useRef(null),a=i.useRef(null),f=i.useRef(o),U=i.useRef(!1),D=i.useRef(!1),E=i.useRef(new Set),P=i.useRef(!1),b=e===void 0?te:e,A=i.useCallback(()=>{E.current.clear(),c(!1),y.current&&(y.current.destroy(),y.current=null),a.current&&(a.current.destroy(),a.current=null),u.current&&(u.current.destroy(),u.current=null),U.current=!1,P.current=!1,s("idle"),h(null)},[]),x=i.useCallback(()=>{const t=Array.from(E.current);E.current.clear(),t.forEach(w=>{w.resolve()})},[]),_=i.useCallback(t=>{c(!1);const w=Array.from(E.current);E.current.clear(),w.forEach(v=>{v.reject(t)})},[]),T=i.useCallback(t=>{const w={onPlay:()=>{c(!0),f.current.onPlaybackPlay?.()},onPause:()=>{c(!1),f.current.onPlaybackPause?.()},onFinish:()=>{c(!1),f.current.onPlaybackFinish?.()},onReady:()=>{x()},onError:v=>{c(!1),_(v),f.current.onError?.(v)}};t.setEventHandlers(w)},[x,_]);i.useEffect(()=>{f.current=o,a.current&&T(a.current)},[o,T]);const S=i.useCallback(async()=>{if(!(U.current||D.current||!l.current)){D.current=!0;try{const[t,w]=await Promise.all([M(()=>import("./wavesurfer.esm.D1Sty35j.js"),[],import.meta.url),M(()=>import("./record.DytFsBUt.js"),[],import.meta.url)]),v=t.default,B=w.default,k=v.create({container:l.current,waveColor:r.colors.primary,progressColor:r.colors.bodyText,height:n>0?$(r.sizes.largestElementHeight)-2*n:"auto",barWidth:X,barGap:Y,barRadius:ee,cursorWidth:re,interact:!0});u.current=k,P.current=!1;const C=new Q({sampleRate:b});C.initialize(k,B),C.setEventHandlers({onRecordProgress:R=>{f.current.onProgressMs?.(R)},onPermissionDenied:()=>{f.current.onPermissionDenied(),s("idle")},onError:R=>{f.current.onError(R),s("idle")}}),y.current=C;const g=new W;g.initialize(k),a.current=g,T(g),U.current=!0}catch(t){const w=t instanceof Error?t:new Error(String(t));f.current.onError?.(w)}finally{D.current=!1}}},[l,r,b,T,n]);i.useEffect(()=>(S(),()=>{A()}),[A,S]),i.useEffect(()=>{const t=u.current;if(t){if(d==="recording"){t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary});return}if(P.current){t.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText});return}t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.bodyText})}},[d,r.colors.bodyText,r.colors.fadedText40,r.colors.primary,r.colors.secondaryBg]);const F=i.useCallback(async()=>{if(d!=="recording"){if(U.current||await S(),!y.current)throw new Error("Record backend not initialized");u.current&&u.current.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary}),P.current=!1,await y.current.startRecording(),s("recording"),h(null),c(!1),f.current.onRecordStart?.()}},[d,S,r.colors.primary]),L=i.useCallback(()=>{a.current&&u.current&&(a.current.destroy(),a.current=new W,a.current.initialize(u.current),E.current.clear(),c(!1),T(a.current)),P.current=!1},[T]),z=i.useCallback(()=>{a.current?.seekToStart(),c(!1),P.current=!0,u.current&&u.current.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText})},[r.colors.bodyText,r.colors.fadedText40,r.colors.secondaryBg]),H=i.useCallback(async()=>{if(d!=="recording")throw new Error("Not currently recording");if(!y.current||!a.current)throw new Error("Backends not initialized");try{const t=await y.current.stopRecording();h(t),await new Promise((k,C)=>{if(!a.current){C(new Error("Player not initialized"));return}const g={resolve:()=>{E.current.delete(g),k()},reject:R=>{E.current.delete(g),C(R)}};E.current.add(g),a.current.load(t).catch(R=>{E.current.delete(g),C(R instanceof Error?R:new Error(String(R)))})}),s("idle"),c(!1),z();const v={durationMs:a.current?.getDuration()??0,sampleRate:typeof b=="number"?b:null,mimeType:t.type||"audio/webm",size:t.size},B={blob:t,meta:v};return f.current.onRecordReady?.(t),B}catch(t){const w=t instanceof Error?t:new Error(String(t));return c(!1),s("idle"),f.current.onError(w),{blob:new Blob,meta:{durationMs:0,sampleRate:null,mimeType:"audio/webm",size:0}}}},[d,z,b]),j=i.useCallback(async t=>{const w=t??p;if(!w){const v=new Error("No recorded audio to approve");f.current.onError(v);return}try{const v=await q(w,b);await f.current.onApprove?.(v),h(null),s("idle")}catch(v){const B=v instanceof Error?v:new Error(String(v));f.current.onError(B)}},[p,b]),N=i.useCallback(()=>{d==="recording"&&y.current?.cancelRecording(),L(),h(null),s("idle"),c(!1),P.current=!1,f.current.onCancel?.()},[d,L]),V=i.useMemo(()=>({isPlaying:()=>a.current?.getIsPlaying()??!1,play:async()=>{if(!a.current)throw new Error("Player not initialized");await a.current.play()},pause:()=>{a.current?.pause()},load:async t=>{if(U.current||await S(),!a.current)throw new Error("Player not initialized");await a.current.load(t),z()},getCurrentTimeMs:()=>a.current?.getCurrentTime()??0,getDurationMs:()=>a.current?.getDuration()??0}),[z,S]),G=i.useCallback(t=>{f.current=t},[]);return i.useEffect(()=>()=>{A()},[A]),{state:d,isPlaybackPlaying:m,mountRef:l,start:F,stop:H,approve:j,cancel:N,destroy:A,playback:V,setEventHandlers:G}}export{se as u};
1
+ import{l as Z,r as i,aP as M,aa as $,aQ as O}from"./index.DiwhD0Ic.js";async function q(l,e=16e3){if(!l||l.size===0)throw new Error("Invalid or empty blob provided");if(!window.AudioContext)throw new Error("AudioContext not supported in this browser");const o=new AudioContext;try{const n=await l.arrayBuffer(),r=await o.decodeAudioData(n),d=e??r.sampleRate,s=await Q(r,d);return J(s,d)}finally{o.close()}}async function Q(l,e){const{duration:o,numberOfChannels:n,sampleRate:r}=l,d=Math.ceil(o*e);if(!window.OfflineAudioContext)throw new Error("OfflineAudioContext not supported");const s=new OfflineAudioContext(1,d,e),p=s.createBufferSource();if(p.buffer=l,n>1){const h=s.createChannelSplitter(n),m=s.createChannelMerger(1);p.connect(h);for(let c=0;c<n;c++){const u=s.createGain();u.gain.value=1/n,h.connect(u,c),u.connect(m,0,0)}m.connect(s.destination)}else p.connect(s.destination);p.start(0);try{return await s.startRendering()}catch(h){throw new Error(`Failed to resample audio from ${r}Hz to ${e}Hz: ${h instanceof Error?h.message:String(h)}`)}}function J(l,e){const n=l.length,r=n*2+44,d=new ArrayBuffer(r),s=new DataView(d),p=l.getChannelData(0),h=(c,u)=>{for(let y=0;y<u.length;y++)s.setUint8(c+y,u.charCodeAt(y))};h(0,"RIFF"),s.setUint32(4,r-8,!0),h(8,"WAVE"),h(12,"fmt "),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,1,!0),s.setUint32(24,e,!0),s.setUint32(28,e*2,!0),s.setUint16(32,2,!0),s.setUint16(34,16,!0),h(36,"data"),s.setUint32(40,n*2,!0);let m=44;for(let c=0;c<n;c++){const u=Math.max(-1,Math.min(1,p[c]));s.setInt16(m,u*32767,!0),m+=2}return new Blob([d],{type:"audio/wav"})}class W{constructor(){this.wavesurfer=null,this.currentBlobUrl=null,this.events={},this.isPlaying=!1}initialize(e){this.wavesurfer=e,this.setupEventListeners()}setupEventListeners(){this.wavesurfer&&(this.teardownEventListeners(),this.handleTimeUpdate=e=>{this.events.onTimeUpdate?.(e*1e3)},this.handlePause=()=>{this.isPlaying=!1,this.events.onPause?.()},this.handlePlay=()=>{this.isPlaying=!0,this.events.onPlay?.()},this.handleFinish=()=>{this.isPlaying=!1,this.events.onFinish?.()},this.handleReady=()=>{this.events.onReady?.()},this.handleError=e=>{const o=e instanceof Error?e:new Error(String(e));this.events.onError?.(o)},this.wavesurfer.on("timeupdate",this.handleTimeUpdate),this.wavesurfer.on("pause",this.handlePause),this.wavesurfer.on("play",this.handlePlay),this.wavesurfer.on("finish",this.handleFinish),this.wavesurfer.on("ready",this.handleReady),this.wavesurfer.on("error",this.handleError))}setEventHandlers(e){this.events=e}async load(e){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");this.cleanupPreviousUrl();let o,n=null;try{if(e instanceof Blob)n=URL.createObjectURL(e),o=n;else if(e instanceof ArrayBuffer){const r=new Blob([e]);n=URL.createObjectURL(r),o=n}else o=e;this.currentBlobUrl=n,await this.wavesurfer.load(o)}catch(r){throw this.cleanupPreviousUrl(),r}}async play(){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");await this.wavesurfer.play()}pause(){this.wavesurfer&&this.wavesurfer.pause()}getDuration(){return this.wavesurfer?this.wavesurfer.getDuration()*1e3:0}getCurrentTime(){return this.wavesurfer?this.wavesurfer.getCurrentTime()*1e3:0}getIsPlaying(){return this.isPlaying}seekToStart(){this.wavesurfer&&this.wavesurfer.seekTo(0)}cleanupPreviousUrl(){this.currentBlobUrl&&(URL.revokeObjectURL(this.currentBlobUrl),this.currentBlobUrl=null)}destroy(){this.pause(),this.cleanupPreviousUrl(),this.wavesurfer&&(this.teardownEventListeners(),this.wavesurfer.empty(),this.wavesurfer=null),this.events={},this.isPlaying=!1,this.handleTimeUpdate=void 0,this.handlePause=void 0,this.handlePlay=void 0,this.handleFinish=void 0,this.handleReady=void 0,this.handleError=void 0}teardownEventListeners(){this.wavesurfer&&(this.handleTimeUpdate&&(this.wavesurfer.un("timeupdate",this.handleTimeUpdate),this.handleTimeUpdate=void 0),this.handlePause&&(this.wavesurfer.un("pause",this.handlePause),this.handlePause=void 0),this.handlePlay&&(this.wavesurfer.un("play",this.handlePlay),this.handlePlay=void 0),this.handleFinish&&(this.wavesurfer.un("finish",this.handleFinish),this.handleFinish=void 0),this.handleReady&&(this.wavesurfer.un("ready",this.handleReady),this.handleReady=void 0),this.handleError&&(this.wavesurfer.un("error",this.handleError),this.handleError=void 0))}}function I(l){return l.name==="NotAllowedError"||l.name==="PermissionDeniedError"||l.message?.toLowerCase().includes("permission denied")}class K{constructor(e={}){this.wavesurfer=null,this.recordPlugin=null,this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null,this.events={},this.options=e}initialize(e,o){this.wavesurfer=e;try{const n={renderRecordedAudio:!1,mimeType:"audio/webm"};this.recordPlugin=e.registerPlugin(o.create(n)),this.setupEventListeners()}catch(n){const r=n instanceof Error?n:new Error(String(n));throw I(r)?(this.events.onPermissionDenied?.(),new Error("Microphone permission denied")):(this.events.onError?.(r),r)}}setupEventListeners(){this.recordPlugin&&(this.recordPlugin.on("record-start",()=>{this.isRecording=!0,this.events.onRecordStart?.()}),this.recordPlugin.on("record-end",e=>{this.isRecording=!1,this.events.onRecordEnd?.(e),this.recordEndResolve&&e&&e.size>0?(this.recordEndResolve(e),this.recordEndResolve=null,this.recordEndReject=null):this.recordEndReject?(this.recordEndReject(new Error("Invalid or empty recording")),this.recordEndResolve=null,this.recordEndReject=null):(this.recordEndResolve=null,this.recordEndReject=null)}),this.recordPlugin.on("record-progress",e=>{this.events.onRecordProgress?.(e)}))}setEventHandlers(e){this.events=e}async startRecording(){if(!this.recordPlugin)throw new Error("Record plugin not initialized");if(this.isRecording)return;const e=typeof this.options.sampleRate=="number"?this.options.sampleRate:void 0,o={};e!==void 0&&(o.sampleRate={ideal:e}),await this.startRecordingWithConstraints(o,e!==void 0)}async startRecordingWithConstraints(e,o){if(!this.recordPlugin)throw new Error("Record plugin not initialized");try{const n=Object.keys(e).length?e:void 0;await this.recordPlugin.startRecording(n)}catch(n){const r=n instanceof Error?n:new Error(String(n));if(I(r))throw this.events.onPermissionDenied?.(),new Error("Microphone permission denied");if(o&&(r.name==="OverconstrainedError"||r.name==="NotReadableError")){this.options.sampleRate=void 0,await this.startRecordingWithConstraints({},!1);return}throw this.events.onError?.(r),r}}async stopRecording(){if(!this.recordPlugin||!this.isRecording)throw new Error("Not currently recording");try{return await new Promise((e,o)=>{this.recordEndResolve=e,this.recordEndReject=o,this.recordPlugin?.stopRecording()})}catch(e){const o=e instanceof Error?e:new Error(String(e));throw this.events.onError?.(o),o}}cancelRecording(){this.recordPlugin&&this.isRecording&&(this.recordPlugin.stopRecording(),this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null)}destroy(){this.cancelRecording(),this.recordPlugin&&(this.recordPlugin.destroy(),this.recordPlugin=null),this.wavesurfer=null,this.events={}}}const X=4,Y=4,ee=8,re=0,te=16e3;function se({containerRef:l,sampleRate:e,events:o,waveformPadding:n=0}){const r=Z(),[d,s]=i.useState("idle"),[p,h]=i.useState(null),[m,c]=i.useState(!1),u=i.useRef(null),y=i.useRef(null),a=i.useRef(null),f=i.useRef(o),S=i.useRef(!1),D=i.useRef(!1),E=i.useRef(new Set),P=i.useRef(!1),b=e===void 0?te:e,A=i.useCallback(()=>{E.current.clear(),c(!1),y.current&&(y.current.destroy(),y.current=null),a.current&&(a.current.destroy(),a.current=null),u.current&&(u.current.destroy(),u.current=null),S.current=!1,P.current=!1,s("idle"),h(null)},[]),x=i.useCallback(()=>{const t=Array.from(E.current);E.current.clear(),t.forEach(w=>{w.resolve()})},[]),_=i.useCallback(t=>{c(!1);const w=Array.from(E.current);E.current.clear(),w.forEach(v=>{v.reject(t)})},[]),T=i.useCallback(t=>{const w={onPlay:()=>{c(!0),f.current.onPlaybackPlay?.()},onPause:()=>{c(!1),f.current.onPlaybackPause?.()},onFinish:()=>{c(!1),f.current.onPlaybackFinish?.()},onReady:()=>{x()},onError:v=>{c(!1),_(v),f.current.onError?.(v)}};t.setEventHandlers(w)},[x,_]);i.useEffect(()=>{f.current=o,a.current&&T(a.current)},[o,T]);const U=i.useCallback(async()=>{if(!(S.current||D.current||!l.current)){D.current=!0;try{const[t,w]=await Promise.all([M(()=>import("./wavesurfer.esm.D1Sty35j.js"),[],import.meta.url),M(()=>import("./record.DytFsBUt.js"),[],import.meta.url)]),v=t.default,B=w.default,k=v.create({container:l.current,waveColor:r.colors.primary,progressColor:r.colors.bodyText,height:n>0?$(r.sizes.largestElementHeight)-2*n:"auto",barWidth:X,barGap:Y,barRadius:ee,cursorWidth:re,interact:!0});u.current=k,P.current=!1;const C=new K({sampleRate:b});C.initialize(k,B),C.setEventHandlers({onRecordProgress:R=>{f.current.onProgressMs?.(R)},onPermissionDenied:()=>{f.current.onPermissionDenied(),s("idle")},onError:R=>{f.current.onError(R),s("idle")}}),y.current=C;const g=new W;g.initialize(k),a.current=g,T(g),S.current=!0}catch(t){const w=t instanceof Error?t:new Error(String(t));f.current.onError?.(w)}finally{D.current=!1}}},[l,r,b,T,n]);i.useEffect(()=>(U(),()=>{A()}),[A,U]),i.useEffect(()=>{const t=u.current;if(t){if(d==="recording"){t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary});return}if(P.current){t.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText});return}t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.bodyText})}},[d,r.colors.bodyText,r.colors.fadedText40,r.colors.primary,r.colors.secondaryBg]);const F=i.useCallback(async()=>{if(d!=="recording"){if(S.current||await U(),!y.current)throw new Error("Record backend not initialized");u.current&&u.current.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary}),P.current=!1,await y.current.startRecording(),s("recording"),h(null),c(!1),f.current.onRecordStart?.()}},[d,U,r.colors.primary]),L=i.useCallback(()=>{a.current&&u.current&&(a.current.destroy(),a.current=new W,a.current.initialize(u.current),E.current.clear(),c(!1),T(a.current)),P.current=!1},[T]),z=i.useCallback(()=>{a.current?.seekToStart(),c(!1),P.current=!0,u.current&&u.current.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText})},[r.colors.bodyText,r.colors.fadedText40,r.colors.secondaryBg]),H=i.useCallback(async()=>{if(d!=="recording")throw new Error("Not currently recording");if(!y.current||!a.current)throw new Error("Backends not initialized");try{const t=await y.current.stopRecording();h(t),await new Promise((k,C)=>{if(!a.current){C(new Error("Player not initialized"));return}const g={resolve:()=>{E.current.delete(g),k()},reject:R=>{E.current.delete(g),C(R)}};E.current.add(g),a.current.load(t).catch(R=>{E.current.delete(g),C(R instanceof Error?R:new Error(String(R)))})}),s("idle"),c(!1),z();const v={durationMs:a.current?.getDuration()??0,sampleRate:typeof b=="number"?b:null,mimeType:t.type||"audio/webm",size:t.size},B={blob:t,meta:v};return f.current.onRecordReady?.(t),B}catch(t){const w=t instanceof Error?t:new Error(String(t));return c(!1),s("idle"),f.current.onError(w),{blob:new Blob,meta:{durationMs:0,sampleRate:null,mimeType:"audio/webm",size:0}}}},[d,z,b]),j=i.useCallback(async t=>{const w=t??p;if(!w){const v=new Error("No recorded audio to approve");f.current.onError(v);return}try{const v=await q(w,b);await f.current.onApprove?.(v),h(null),s("idle")}catch(v){const B=v instanceof Error?v:new Error(String(v));f.current.onError(B)}},[p,b]),N=i.useCallback(()=>{d==="recording"&&y.current?.cancelRecording(),L(),h(null),s("idle"),c(!1),P.current=!1,f.current.onCancel?.()},[d,L]),V=i.useMemo(()=>({isPlaying:()=>a.current?.getIsPlaying()??!1,play:async()=>{if(!a.current)throw new Error("Player not initialized");await a.current.play()},pause:()=>{a.current?.pause()},load:async t=>{if(S.current||await U(),!a.current)throw new Error("Player not initialized");await a.current.load(t),z()},getCurrentTimeMs:()=>a.current?.getCurrentTime()??0,getDurationMs:()=>a.current?.getDuration()??0}),[z,U]),G=i.useCallback(t=>{f.current=t},[]);return i.useEffect(()=>()=>{A()},[A]),{state:d,isPlaybackPlaying:m,mountRef:l,start:F,stop:H,approve:j,cancel:N,destroy:A,playback:V,setEventHandlers:G}}export{se as u};
@@ -1 +1 @@
1
- import{h as l,k as h,j as s,d as n}from"./index.CXVpBAvU.js";const o=t=>{const a=e=>{const{width:i,elementRef:c}=h();return s(n,{ref:c,children:s(t,{...e,width:i})})};return a.displayName=`withCalculatedWidth(${t.displayName||t.name})`,l(a,t)};export{o as w};
1
+ import{h as l,k as h,j as s,d as n}from"./index.DiwhD0Ic.js";const o=t=>{const a=e=>{const{width:i,elementRef:c}=h();return s(n,{ref:c,children:s(t,{...e,width:i})})};return a.displayName=`withCalculatedWidth(${t.displayName||t.name})`,l(a,t)};export{o as w};
@@ -1 +1 @@
1
- import{r as n,F as f,b7 as p,b8 as h,l as x,k as y,j as i,h as g}from"./index.CXVpBAvU.js";const m=n.createContext(null);m.displayName="ElementFullscreenContext";const w=f("div",{target:"e5bcvgj0"})(({theme:e,isExpanded:t})=>({width:"100%",height:"100%",...t?{position:"fixed",top:0,left:0,bottom:0,right:0,background:e.colors.bgColor,zIndex:e.zIndices.fullscreenWrapper,padding:e.spacing.md,paddingTop:e.sizes.fullScreenHeaderHeight,overflow:"auto",display:"flex",alignItems:"center",justifyContent:"center"}:{}})),C=()=>{const{setFullScreen:e}=n.useContext(p),[t,s]=n.useState(!1),{fullHeight:a,fullWidth:c}=h(),l=n.useCallback(r=>{s(r),e(r)},[e]),u=n.useCallback(()=>{document.body.style.overflow="hidden",l(!0)},[l]),o=n.useCallback(()=>{document.body.style.overflow="unset",l(!1)},[l]),d=n.useCallback(r=>{r.keyCode===27&&t&&o()},[o,t]);return n.useEffect(()=>(document.addEventListener("keydown",d,!1),()=>{document.removeEventListener("keydown",d,!1)}),[d]),n.useMemo(()=>({expanded:t,zoomIn:u,zoomOut:o,fullHeight:a,fullWidth:c}),[t,u,o,a,c])},F=({children:e})=>{const t=x(),{expanded:s,fullHeight:a,fullWidth:c,zoomIn:l,zoomOut:u}=C(),{width:o,elementRef:d}=y(),r=n.useMemo(()=>({width:s?c:o,height:s?a:void 0,expanded:s,expand:l,collapse:u}),[s,a,c,o,l,u]);return i(m.Provider,{value:r,children:i(w,{ref:d,isExpanded:s,"data-testid":"stFullScreenFrame",theme:t,children:e})})};function S(e){const t=s=>i(F,{children:i(e,{...s})});return t.displayName=`withFullScreenWrapper(${e.displayName||e.name})`,g(t,e)}export{m as E,S as w};
1
+ import{r as n,z as f,b5 as p,b6 as h,l as x,k as y,j as i,h as g}from"./index.DiwhD0Ic.js";const m=n.createContext(null);m.displayName="ElementFullscreenContext";const w=f("div",{target:"e5bcvgj0"})(({theme:e,isExpanded:t})=>({width:"100%",height:"100%",...t?{position:"fixed",top:0,left:0,bottom:0,right:0,background:e.colors.bgColor,zIndex:e.zIndices.fullscreenWrapper,padding:e.spacing.md,paddingTop:e.sizes.fullScreenHeaderHeight,overflow:"auto",display:"flex",alignItems:"center",justifyContent:"center"}:{}})),C=()=>{const{setFullScreen:e}=n.useContext(p),[t,s]=n.useState(!1),{fullHeight:a,fullWidth:c}=h(),l=n.useCallback(r=>{s(r),e(r)},[e]),u=n.useCallback(()=>{document.body.style.overflow="hidden",l(!0)},[l]),o=n.useCallback(()=>{document.body.style.overflow="unset",l(!1)},[l]),d=n.useCallback(r=>{r.keyCode===27&&t&&o()},[o,t]);return n.useEffect(()=>(document.addEventListener("keydown",d,!1),()=>{document.removeEventListener("keydown",d,!1)}),[d]),n.useMemo(()=>({expanded:t,zoomIn:u,zoomOut:o,fullHeight:a,fullWidth:c}),[t,u,o,a,c])},b=({children:e})=>{const t=x(),{expanded:s,fullHeight:a,fullWidth:c,zoomIn:l,zoomOut:u}=C(),{width:o,elementRef:d}=y(),r=n.useMemo(()=>({width:s?c:o,height:s?a:void 0,expanded:s,expand:l,collapse:u}),[s,a,c,o,l,u]);return i(m.Provider,{value:r,children:i(w,{ref:d,isExpanded:s,"data-testid":"stFullScreenFrame",theme:t,children:e})})};function S(e){const t=s=>i(b,{children:i(e,{...s})});return t.displayName=`withFullScreenWrapper(${e.displayName||e.name})`,g(t,e)}export{m as E,S as w};
streamlit/string_util.py CHANGED
@@ -209,8 +209,8 @@ def to_snake_case(camel_case_str: str) -> str:
209
209
  BazBang -> baz_bang
210
210
 
211
211
  """
212
- s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", camel_case_str)
213
- return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
212
+ s1 = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", camel_case_str)
213
+ return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
214
214
 
215
215
 
216
216
  AnyNumber: TypeAlias = Union[
@@ -90,8 +90,6 @@ T = TypeVar("T")
90
90
  class InitialValue:
91
91
  """Used to represent the initial value of a widget."""
92
92
 
93
- pass
94
-
95
93
 
96
94
  # TODO: This class serves as a fallback option for elements that have not
97
95
  # been implemented yet, as well as providing implementations of some
@@ -515,7 +513,7 @@ DateValue: TypeAlias = SingleDateValue | Sequence[SingleDateValue] | None
515
513
  class DateInput(Widget):
516
514
  """A representation of ``st.date_input``."""
517
515
 
518
- _value: DateValue | None | InitialValue
516
+ _value: DateValue | InitialValue | None
519
517
  proto: DateInputProto = field(repr=False)
520
518
  label: str
521
519
  min: date
@@ -884,7 +882,7 @@ Number: TypeAlias = int | float
884
882
  class NumberInput(Widget):
885
883
  """A representation of ``st.number_input``."""
886
884
 
887
- _value: Number | None | InitialValue
885
+ _value: Number | InitialValue | None
888
886
  proto: NumberInputProto = field(repr=False)
889
887
  label: str
890
888
  min: Number | None
@@ -945,7 +943,7 @@ class NumberInput(Widget):
945
943
  class Radio(Widget, Generic[T]):
946
944
  """A representation of ``st.radio``."""
947
945
 
948
- _value: T | None | InitialValue
946
+ _value: T | InitialValue | None
949
947
 
950
948
  proto: RadioProto = field(repr=False)
951
949
  label: str
@@ -1004,7 +1002,7 @@ class Radio(Widget, Generic[T]):
1004
1002
  class Selectbox(Widget, Generic[T]):
1005
1003
  """A representation of ``st.selectbox``."""
1006
1004
 
1007
- _value: T | None | InitialValue
1005
+ _value: T | InitialValue | None
1008
1006
 
1009
1007
  proto: SelectboxProto = field(repr=False)
1010
1008
  label: str
@@ -1224,7 +1222,7 @@ class Text(Element):
1224
1222
  class TextArea(Widget):
1225
1223
  """A representation of ``st.text_area``."""
1226
1224
 
1227
- _value: str | None | InitialValue
1225
+ _value: str | InitialValue | None
1228
1226
 
1229
1227
  proto: TextAreaProto = field(repr=False)
1230
1228
  label: str
@@ -1276,7 +1274,7 @@ class TextArea(Widget):
1276
1274
  class TextInput(Widget):
1277
1275
  """A representation of ``st.text_input``."""
1278
1276
 
1279
- _value: str | None | InitialValue
1277
+ _value: str | InitialValue | None
1280
1278
  proto: TextInputProto = field(repr=False)
1281
1279
  label: str
1282
1280
  max_chars: int
@@ -1332,7 +1330,7 @@ DateTimeWidgetValue: TypeAlias = datetime
1332
1330
  class TimeInput(Widget):
1333
1331
  """A representation of ``st.time_input``."""
1334
1332
 
1335
- _value: TimeValue | None | InitialValue
1333
+ _value: TimeValue | InitialValue | None
1336
1334
  proto: TimeInputProto = field(repr=False)
1337
1335
  label: str
1338
1336
  step: int
@@ -1389,7 +1387,7 @@ class TimeInput(Widget):
1389
1387
  class DateTimeInput(Widget):
1390
1388
  """A representation of ``st.datetime_input``."""
1391
1389
 
1392
- _value: DateTimeWidgetValue | None | InitialValue
1390
+ _value: DateTimeWidgetValue | InitialValue | None
1393
1391
  proto: DateTimeInputProto = field(repr=False)
1394
1392
  label: str
1395
1393
  format: str
streamlit/type_util.py CHANGED
@@ -268,7 +268,7 @@ def _is_probably_plotly_dict(obj: object) -> TypeGuard[dict[str, Any]]:
268
268
  if len(obj.keys()) == 0:
269
269
  return False
270
270
 
271
- if any(k not in ["config", "data", "frames", "layout"] for k in obj):
271
+ if any(k not in {"config", "data", "frames", "layout"} for k in obj):
272
272
  return False
273
273
 
274
274
  if any(_is_plotly_obj(v) for v in obj.values()):
@@ -472,7 +472,7 @@ def async_generator_to_sync(
472
472
  try:
473
473
  # Iterate over the async generator until it raises StopAsyncIteration
474
474
  while True:
475
- yield loop.run_until_complete(async_gen.__anext__())
475
+ yield loop.run_until_complete(anext(async_gen))
476
476
  except StopAsyncIteration:
477
477
  # The async generator has finished
478
478
  pass
streamlit/url_util.py CHANGED
@@ -87,9 +87,9 @@ def is_url(
87
87
  if result.scheme not in allowed_schemas:
88
88
  return False
89
89
 
90
- if result.scheme in ["http", "https"]:
90
+ if result.scheme in {"http", "https"}:
91
91
  return bool(result.netloc)
92
- if result.scheme in ["mailto", "data"]:
92
+ if result.scheme in {"mailto", "data"}:
93
93
  return bool(result.path)
94
94
 
95
95
  except ValueError:
streamlit/user_info.py CHANGED
@@ -652,7 +652,7 @@ class UserInfoProxy(Mapping[str, str | bool | TokensProxy | None]):
652
652
  }
653
653
  """
654
654
 
655
- def __getitem__(self, key: str) -> str | bool | None | TokensProxy:
655
+ def __getitem__(self, key: str) -> str | bool | TokensProxy | None:
656
656
  if key == "tokens":
657
657
  return self.tokens
658
658
  try:
@@ -660,7 +660,7 @@ class UserInfoProxy(Mapping[str, str | bool | TokensProxy | None]):
660
660
  except KeyError:
661
661
  raise KeyError(f'st.user has no key "{key}".')
662
662
 
663
- def __getattr__(self, key: str) -> str | bool | None | TokensProxy:
663
+ def __getattr__(self, key: str) -> str | bool | TokensProxy | None:
664
664
  if key == "tokens":
665
665
  return self.tokens
666
666
  try:
streamlit/util.py CHANGED
@@ -79,7 +79,7 @@ def calc_md5(s: bytes | str) -> str:
79
79
  return h.hexdigest()
80
80
 
81
81
 
82
- class AttributeDictionary(dict[Any, Any]):
82
+ class AttributeDictionary(dict[Any, Any]): # noqa: FURB189
83
83
  """
84
84
  A dictionary subclass that supports attribute-style access.
85
85
 
@@ -66,7 +66,7 @@ def _is_watchdog_available() -> bool:
66
66
 
67
67
  def report_watchdog_availability() -> None:
68
68
  if (
69
- config.get_option("server.fileWatcherType") not in ["poll", "none"]
69
+ config.get_option("server.fileWatcherType") not in {"poll", "none"}
70
70
  and not _is_watchdog_available()
71
71
  ):
72
72
  msg = "\n $ xcode-select --install" if env_util.IS_DARWIN else ""
streamlit/web/cli.py CHANGED
@@ -267,7 +267,7 @@ def _check_extension_or_raise(path_str: str) -> None:
267
267
 
268
268
 
269
269
  def _get_command_line_as_string() -> str | None:
270
- import subprocess
270
+ import subprocess # noqa: S404
271
271
 
272
272
  parent = click.get_current_context().parent
273
273
  if parent is None:
@@ -331,7 +331,6 @@ def _main_run(
331
331
  @main.group("cache")
332
332
  def cache() -> None:
333
333
  """Manage the Streamlit cache."""
334
- pass
335
334
 
336
335
 
337
336
  @cache.command("clear")
@@ -354,7 +353,6 @@ def cache_clear() -> None:
354
353
  @main.group("config")
355
354
  def config() -> None:
356
355
  """Manage Streamlit's config settings."""
357
- pass
358
356
 
359
357
 
360
358
  @config.command("show")
@@ -393,7 +391,6 @@ def test() -> None:
393
391
 
394
392
  These commands are not included in the output of `streamlit help`.
395
393
  """
396
- pass
397
394
 
398
395
 
399
396
  @test.command("prog_name")
@@ -31,6 +31,7 @@ the source code without executing it.
31
31
  from __future__ import annotations
32
32
 
33
33
  import ast
34
+ import operator
34
35
  from dataclasses import dataclass
35
36
  from typing import TYPE_CHECKING, Final
36
37
 
@@ -404,7 +405,7 @@ def discover_asgi_app(
404
405
  )
405
406
 
406
407
  # Fall back to the first discovered app (by line number)
407
- first_app = min(app_assignments.items(), key=lambda x: x[1])
408
+ first_app = min(app_assignments.items(), key=operator.itemgetter(1))
408
409
  _LOGGER.debug(
409
410
  "Found ASGI app at %s:%s (fallback, line %d)",
410
411
  module_str,
@@ -15,18 +15,20 @@ from __future__ import annotations
15
15
 
16
16
  import json
17
17
  from typing import Any, Final, cast
18
- from urllib.parse import urlencode, urlparse
19
18
 
20
19
  import tornado.web
21
20
 
22
21
  from streamlit.auth_util import (
23
22
  AuthCache,
23
+ build_logout_url,
24
24
  clear_cookie_and_chunks,
25
25
  decode_provider_token,
26
26
  generate_default_provider_section,
27
27
  get_cookie_with_chunks,
28
+ get_origin_from_redirect_uri,
28
29
  get_redirect_uri,
29
30
  get_secrets_auth_section,
31
+ get_validated_redirect_uri,
30
32
  set_cookie_with_chunks,
31
33
  )
32
34
  from streamlit.errors import StreamlitAuthError
@@ -179,21 +181,6 @@ class AuthLogoutHandler(AuthHandlerMixin, tornado.web.RequestHandler):
179
181
  else:
180
182
  self.redirect_to_base()
181
183
 
182
- def _get_redirect_uri(self) -> str | None:
183
- auth_section = get_secrets_auth_section()
184
- if not auth_section:
185
- return None
186
-
187
- redirect_uri = get_redirect_uri(auth_section)
188
- if not redirect_uri:
189
- return None
190
-
191
- if not redirect_uri.endswith("/oauth2callback"):
192
- _LOGGER.warning("Redirect URI does not end with /oauth2callback")
193
- return None
194
-
195
- return redirect_uri
196
-
197
184
  def _get_provider_logout_url(self) -> str | None:
198
185
  """Get the OAuth provider's logout URL from OIDC metadata."""
199
186
  cookie_value = get_cookie_with_chunks(self._get_signed_cookie, AUTH_COOKIE_NAME)
@@ -219,17 +206,13 @@ class AuthLogoutHandler(AuthHandlerMixin, tornado.web.RequestHandler):
219
206
  # Use redirect_uri (i.e. /oauth2callback) for post_logout_redirect_uri
220
207
  # This is safer than redirecting to root as some providers seem to
221
208
  # require url to be in a whitelist /oauth2callback should be whitelisted
222
- redirect_uri = self._get_redirect_uri()
209
+ redirect_uri = get_validated_redirect_uri()
223
210
  if redirect_uri is None:
224
211
  _LOGGER.info("Redirect url could not be determined")
225
212
  return None
226
213
 
227
- logout_params = {
228
- "client_id": client.client_id,
229
- "post_logout_redirect_uri": redirect_uri,
230
- }
231
-
232
- # Add id_token_hint to logout params if it is available
214
+ # Get id_token_hint from tokens cookie if available
215
+ id_token: str | None = None
233
216
  tokens_cookie_value = get_cookie_with_chunks(
234
217
  self._get_signed_cookie, TOKENS_COOKIE_NAME
235
218
  )
@@ -237,15 +220,16 @@ class AuthLogoutHandler(AuthHandlerMixin, tornado.web.RequestHandler):
237
220
  try:
238
221
  tokens = json.loads(tokens_cookie_value)
239
222
  id_token = tokens.get("id_token")
240
- if id_token:
241
- logout_params["id_token_hint"] = id_token
242
223
  except (json.JSONDecodeError, TypeError):
243
- _LOGGER.exception(
244
- "Error, invalid tokens cookie value.",
245
- )
224
+ _LOGGER.exception("Error, invalid tokens cookie value.")
246
225
  return None
247
226
 
248
- return f"{end_session_endpoint}?{urlencode(logout_params)}"
227
+ return build_logout_url(
228
+ end_session_endpoint=end_session_endpoint,
229
+ client_id=client.client_id,
230
+ post_logout_redirect_uri=redirect_uri,
231
+ id_token=id_token,
232
+ )
249
233
 
250
234
  except Exception as e:
251
235
  _LOGGER.warning("Failed to get provider logout URL: %s", e)
@@ -327,16 +311,4 @@ class AuthCallbackHandler(AuthHandlerMixin, tornado.web.RequestHandler):
327
311
  return provider
328
312
 
329
313
  def _get_origin_from_secrets(self) -> str | None:
330
- redirect_uri = None
331
- auth_section = get_secrets_auth_section()
332
- if auth_section:
333
- redirect_uri = get_redirect_uri(auth_section)
334
-
335
- if not redirect_uri:
336
- return None
337
-
338
- redirect_uri_parsed = urlparse(redirect_uri)
339
- origin_from_redirect_uri: str = (
340
- redirect_uri_parsed.scheme + "://" + redirect_uri_parsed.netloc
341
- )
342
- return origin_from_redirect_uri
314
+ return get_origin_from_redirect_uri()
@@ -263,7 +263,7 @@ def start_listening_tcp_socket(http_server: HTTPServer) -> None:
263
263
  except OSError as e:
264
264
  # EADDRINUSE: port in use by another process
265
265
  # EACCES: port reserved by system (common on Windows, see #13521)
266
- if e.errno in (errno.EADDRINUSE, errno.EACCES):
266
+ if e.errno in {errno.EADDRINUSE, errno.EACCES}:
267
267
  if server_port_is_manually_set():
268
268
  _LOGGER.error("Port %s is not available", port) # noqa: TRY400
269
269
  sys.exit(1)
@@ -82,7 +82,7 @@ def is_url_from_allowed_origins(url: str) -> bool:
82
82
  url_util.get_hostname(origin) for origin in allowlisted_origins()
83
83
  ]
84
84
 
85
- allowed_domains: list[str | None | Callable[[], str | None]] = [
85
+ allowed_domains: list[str | Callable[[], str | None] | None] = [
86
86
  # Check localhost first.
87
87
  "localhost",
88
88
  "0.0.0.0", # noqa: S104
@@ -12,19 +12,26 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ # ruff: noqa: RUF029 # Async route handlers are idiomatic even without await
16
+
15
17
  """Starlette app authentication routes."""
16
18
 
17
19
  from __future__ import annotations
18
20
 
21
+ import json
19
22
  import time
20
23
  from typing import TYPE_CHECKING, Any, Final, cast
21
- from urllib.parse import urlparse
22
24
 
23
25
  from streamlit.auth_util import (
26
+ build_logout_url,
24
27
  clear_cookie_and_chunks,
25
28
  decode_provider_token,
26
29
  generate_default_provider_section,
30
+ get_cookie_with_chunks,
31
+ get_origin_from_redirect_uri,
32
+ get_redirect_uri,
27
33
  get_secrets_auth_section,
34
+ get_validated_redirect_uri,
28
35
  set_cookie_with_chunks,
29
36
  )
30
37
  from streamlit.errors import StreamlitAuthError
@@ -139,7 +146,7 @@ def _looks_like_provider_section(value: dict[str, Any]) -> bool:
139
146
  return any(key in value for key in provider_keys)
140
147
 
141
148
 
142
- class _AuthlibConfig(dict[str, Any]):
149
+ class _AuthlibConfig(dict[str, Any]): # noqa: FURB189
143
150
  """Config adapter that exposes provider data via Authlib's flat lookup.
144
151
 
145
152
  Authlib expects a flat configuration dictionary (e.g. "GOOGLE_CLIENT_ID").
@@ -307,7 +314,7 @@ def _create_oauth_client(provider: str) -> tuple[Any, str]:
307
314
 
308
315
  auth_section = get_secrets_auth_section()
309
316
  if auth_section:
310
- redirect_uri = auth_section.get("redirect_uri", "/")
317
+ redirect_uri = get_redirect_uri(auth_section) or "/"
311
318
  config = auth_section.to_dict()
312
319
  else:
313
320
  config = {}
@@ -385,20 +392,80 @@ def _get_provider_by_state(state_code_from_url: str | None) -> str | None:
385
392
 
386
393
  def _get_origin_from_secrets() -> str | None:
387
394
  """Extract the origin from the redirect URI in the secrets."""
395
+ return get_origin_from_redirect_uri()
388
396
 
389
- redirect_uri = None
390
- auth_section = get_secrets_auth_section()
391
- if auth_section:
392
- redirect_uri = auth_section.get("redirect_uri", None)
393
397
 
394
- if not redirect_uri:
398
+ def _get_cookie_value_from_request(request: Request, cookie_name: str) -> bytes | None:
399
+ """Get a signed cookie value from the request, handling chunked cookies."""
400
+
401
+ def get_single_cookie(name: str) -> bytes | None:
402
+ return _get_signed_cookie_from_request(request, name)
403
+
404
+ return get_cookie_with_chunks(get_single_cookie, cookie_name)
405
+
406
+
407
+ def _get_provider_logout_url(request: Request) -> str | None:
408
+ """Get the OAuth provider's logout URL from OIDC metadata.
409
+
410
+ Returns the end_session_endpoint URL with proper parameters for OIDC logout,
411
+ or None if the provider doesn't support it or required data is unavailable.
412
+
413
+ This function returns None (rather than raising exceptions) to allow graceful
414
+ fallback to a simple base URL redirect when OIDC logout isn't possible.
415
+ """
416
+ cookie_value = _get_cookie_value_from_request(request, USER_COOKIE_NAME)
417
+
418
+ if not cookie_value:
395
419
  return None
396
420
 
397
- redirect_uri_parsed = urlparse(redirect_uri)
398
- origin_from_redirect_uri: str = (
399
- redirect_uri_parsed.scheme + "://" + redirect_uri_parsed.netloc
400
- )
401
- return origin_from_redirect_uri
421
+ try:
422
+ user_info = json.loads(cookie_value)
423
+ provider = user_info.get("provider")
424
+ if not provider:
425
+ return None
426
+
427
+ client, _ = _create_oauth_client(provider)
428
+
429
+ # Load OIDC metadata - Authlib's Starlette client uses async methods
430
+ # but load_server_metadata is synchronous in both implementations
431
+ metadata = client.load_server_metadata()
432
+ end_session_endpoint = metadata.get("end_session_endpoint")
433
+
434
+ if not end_session_endpoint:
435
+ _LOGGER.info("No end_session_endpoint found for provider %s", provider)
436
+ return None
437
+
438
+ # Use redirect_uri (i.e. /oauth2callback) for post_logout_redirect_uri
439
+ # This is safer than redirecting to root as some providers seem to
440
+ # require URL to be in a whitelist - /oauth2callback should be whitelisted
441
+ redirect_uri = get_validated_redirect_uri()
442
+ if redirect_uri is None:
443
+ _LOGGER.info("Redirect url could not be determined")
444
+ return None
445
+
446
+ # Get id_token_hint from tokens cookie if available
447
+ id_token: str | None = None
448
+ tokens_cookie_value = _get_cookie_value_from_request(
449
+ request, TOKENS_COOKIE_NAME
450
+ )
451
+ if tokens_cookie_value:
452
+ try:
453
+ tokens = json.loads(tokens_cookie_value)
454
+ id_token = tokens.get("id_token")
455
+ except (json.JSONDecodeError, TypeError):
456
+ _LOGGER.exception("Error, invalid tokens cookie value.")
457
+ return None
458
+
459
+ return build_logout_url(
460
+ end_session_endpoint=end_session_endpoint,
461
+ client_id=client.client_id,
462
+ post_logout_redirect_uri=redirect_uri,
463
+ id_token=id_token,
464
+ )
465
+
466
+ except Exception as e:
467
+ _LOGGER.warning("Failed to get provider logout URL: %s", e)
468
+ return None
402
469
 
403
470
 
404
471
  async def _auth_login(request: Request, base_url: str) -> Response:
@@ -421,9 +488,20 @@ async def _auth_login(request: Request, base_url: str) -> Response:
421
488
 
422
489
 
423
490
  async def _auth_logout(request: Request, base_url: str) -> Response:
424
- """Logout the user by clearing the auth cookie and redirecting to the base URL."""
491
+ """Logout the user by clearing the auth cookie and redirecting.
492
+
493
+ If the OAuth provider supports end_session_endpoint, redirects there for
494
+ proper OIDC logout. Otherwise, redirects to the base URL.
495
+ """
496
+ from starlette.responses import RedirectResponse
497
+
498
+ provider_logout_url = _get_provider_logout_url(request)
499
+
500
+ if provider_logout_url:
501
+ response = RedirectResponse(provider_logout_url, status_code=302)
502
+ else:
503
+ response = await _redirect_to_base(base_url)
425
504
 
426
- response = await _redirect_to_base(base_url)
427
505
  _clear_auth_cookie(response, request)
428
506
  return response
429
507
 
@@ -471,7 +549,7 @@ async def _auth_callback(request: Request, base_url: str) -> Response:
471
549
 
472
550
  response = await _redirect_to_base(base_url)
473
551
 
474
- cookie_value = dict(user, origin=origin, is_logged_in=True)
552
+ cookie_value = dict(user, origin=origin, is_logged_in=True, provider=provider)
475
553
  tokens = {k: token[k] for k in ["id_token", "access_token"] if k in token}
476
554
  if user:
477
555
  await _set_auth_cookie(response, cookie_value, tokens)
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ # ruff: noqa: RUF029 # Async route handlers are idiomatic even without await
16
+
15
17
  """Route handlers for the Starlette server."""
16
18
 
17
19
  from __future__ import annotations
@@ -741,6 +743,7 @@ def create_bidi_component_routes(
741
743
  ) -> list[BaseRoute]:
742
744
  """Create bidirectional component route handlers."""
743
745
  import anyio
746
+ from anyio import Path as AsyncPath
744
747
  from starlette.responses import PlainTextResponse, Response
745
748
  from starlette.routing import Route
746
749
 
@@ -771,7 +774,7 @@ def create_bidi_component_routes(
771
774
  if abspath is None:
772
775
  return await _text_response("forbidden", 403)
773
776
 
774
- if os.path.isdir(abspath):
777
+ if await AsyncPath(abspath).is_dir():
775
778
  return await _text_response("not found", 404)
776
779
 
777
780
  try:
@@ -817,6 +820,7 @@ def create_app_static_serving_routes(
817
820
  main_script_path: str | None, base_url: str | None
818
821
  ) -> list[BaseRoute]:
819
822
  """Create app static serving file route handlers."""
823
+ from anyio import Path as AsyncPath
820
824
  from starlette.exceptions import HTTPException
821
825
  from starlette.responses import FileResponse, Response
822
826
  from starlette.routing import Route
@@ -836,10 +840,12 @@ def create_app_static_serving_routes(
836
840
  if safe_path is None:
837
841
  raise HTTPException(status_code=404, detail="File not found")
838
842
 
839
- if not os.path.exists(safe_path) or os.path.isdir(safe_path):
843
+ async_path = AsyncPath(safe_path)
844
+ if not await async_path.exists() or await async_path.is_dir():
840
845
  raise HTTPException(status_code=404, detail="File not found")
841
846
 
842
- if os.path.getsize(safe_path) > MAX_APP_STATIC_FILE_SIZE:
847
+ file_stat = await async_path.stat()
848
+ if file_stat.st_size > MAX_APP_STATIC_FILE_SIZE:
843
849
  raise HTTPException(
844
850
  status_code=404,
845
851
  detail="File is too large",
@@ -288,7 +288,7 @@ class UvicornServer:
288
288
  last_exception = exc
289
289
  # EADDRINUSE: port in use by another process
290
290
  # EACCES: port reserved by system (common on Windows, see #13521)
291
- if exc.errno in (errno.EADDRINUSE, errno.EACCES):
291
+ if exc.errno in {errno.EADDRINUSE, errno.EACCES}:
292
292
  if _is_port_manually_set():
293
293
  _LOGGER.error("Port %s is not available", port) # noqa: TRY400
294
294
  sys.exit(1)
@@ -476,7 +476,7 @@ class UvicornRunner:
476
476
  except OSError as exc:
477
477
  # EADDRINUSE: port in use by another process
478
478
  # EACCES: port reserved by system (common on Windows)
479
- if exc.errno in (errno.EADDRINUSE, errno.EACCES):
479
+ if exc.errno in {errno.EADDRINUSE, errno.EACCES}:
480
480
  if _is_port_manually_set():
481
481
  _LOGGER.error("Port %s is not available", port) # noqa: TRY400
482
482
  sys.exit(1)