vercel-runtime 0.6.0__tar.gz → 0.7.0__tar.gz

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 (162) hide show
  1. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/PKG-INFO +1 -1
  2. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/pyproject.toml +1 -1
  3. vercel_runtime-0.7.0/src/vercel_runtime/dev.py +421 -0
  4. vercel_runtime-0.7.0/src/vercel_runtime/resolver.py +92 -0
  5. vercel_runtime-0.7.0/src/vercel_runtime/routing.py +177 -0
  6. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/vc_init.py +45 -182
  7. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/__init__.py +0 -0
  8. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/__init__.py +0 -0
  9. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/LICENSE.txt +0 -0
  10. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/__init__.py +0 -0
  11. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/_compat.py +0 -0
  12. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/_termui_impl.py +0 -0
  13. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/_textwrap.py +0 -0
  14. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/_winconsole.py +0 -0
  15. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/core.py +0 -0
  16. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/decorators.py +0 -0
  17. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/exceptions.py +0 -0
  18. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/formatting.py +0 -0
  19. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/globals.py +0 -0
  20. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/parser.py +0 -0
  21. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/py.typed +0 -0
  22. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/shell_completion.py +0 -0
  23. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/termui.py +0 -0
  24. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/testing.py +0 -0
  25. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/types.py +0 -0
  26. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/click/utils.py +0 -0
  27. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/LICENSE.txt +0 -0
  28. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/__init__.py +0 -0
  29. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/ansi.py +0 -0
  30. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/ansitowin32.py +0 -0
  31. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/initialise.py +0 -0
  32. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/win32.py +0 -0
  33. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama/winterm.py +0 -0
  34. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/colorama.pyi +0 -0
  35. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/LICENSE.txt +0 -0
  36. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/__init__.py +0 -0
  37. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_abnf.py +0 -0
  38. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_connection.py +0 -0
  39. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_events.py +0 -0
  40. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_headers.py +0 -0
  41. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_readers.py +0 -0
  42. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_receivebuffer.py +0 -0
  43. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_state.py +0 -0
  44. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_util.py +0 -0
  45. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_version.py +0 -0
  46. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/_writers.py +0 -0
  47. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/h11/py.typed +0 -0
  48. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/LICENSE.txt +0 -0
  49. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/__init__.py +0 -0
  50. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/_native.py +0 -0
  51. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/_speedups.c +0 -0
  52. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/_speedups.pyi +0 -0
  53. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/markupsafe/py.typed +0 -0
  54. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/LICENSE.md +0 -0
  55. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/__init__.py +0 -0
  56. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/__main__.py +0 -0
  57. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/_compat.py +0 -0
  58. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/_subprocess.py +0 -0
  59. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/_types.py +0 -0
  60. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/config.py +0 -0
  61. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/importer.py +0 -0
  62. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/lifespan/__init__.py +0 -0
  63. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/lifespan/off.py +0 -0
  64. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/lifespan/on.py +0 -0
  65. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/logging.py +0 -0
  66. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/loops/__init__.py +0 -0
  67. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/loops/asyncio.py +0 -0
  68. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/loops/auto.py +0 -0
  69. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/loops/uvloop.py +0 -0
  70. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/main.py +0 -0
  71. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/middleware/__init__.py +0 -0
  72. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/middleware/asgi2.py +0 -0
  73. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/middleware/message_logger.py +0 -0
  74. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/middleware/proxy_headers.py +0 -0
  75. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/middleware/wsgi.py +0 -0
  76. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/__init__.py +0 -0
  77. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/http/__init__.py +0 -0
  78. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/http/auto.py +0 -0
  79. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/http/flow_control.py +0 -0
  80. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/http/h11_impl.py +0 -0
  81. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/http/httptools_impl.py +0 -0
  82. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/utils.py +0 -0
  83. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/websockets/__init__.py +0 -0
  84. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/websockets/auto.py +0 -0
  85. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/websockets/websockets_impl.py +0 -0
  86. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/websockets/websockets_sansio_impl.py +0 -0
  87. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/protocols/websockets/wsproto_impl.py +0 -0
  88. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/py.typed +0 -0
  89. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/server.py +0 -0
  90. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/supervisors/__init__.py +0 -0
  91. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/supervisors/basereload.py +0 -0
  92. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/supervisors/multiprocess.py +0 -0
  93. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/supervisors/statreload.py +0 -0
  94. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/supervisors/watchfilesreload.py +0 -0
  95. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/uvicorn/workers.py +0 -0
  96. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/vendor.txt +0 -0
  97. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/ICON_LICENSE.md +0 -0
  98. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/LICENSE.txt +0 -0
  99. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/__init__.py +0 -0
  100. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/_internal.py +0 -0
  101. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/_reloader.py +0 -0
  102. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/__init__.py +0 -0
  103. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/accept.py +0 -0
  104. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/auth.py +0 -0
  105. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/cache_control.py +0 -0
  106. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/csp.py +0 -0
  107. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/etag.py +0 -0
  108. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/file_storage.py +0 -0
  109. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/headers.py +0 -0
  110. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/mixins.py +0 -0
  111. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/range.py +0 -0
  112. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/datastructures/structures.py +0 -0
  113. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/__init__.py +0 -0
  114. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/console.py +0 -0
  115. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/repr.py +0 -0
  116. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/ICON_LICENSE.md +0 -0
  117. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/console.png +0 -0
  118. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/debugger.js +0 -0
  119. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/less.png +0 -0
  120. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/more.png +0 -0
  121. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/shared/style.css +0 -0
  122. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/debug/tbtools.py +0 -0
  123. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/exceptions.py +0 -0
  124. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/formparser.py +0 -0
  125. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/http.py +0 -0
  126. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/local.py +0 -0
  127. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/__init__.py +0 -0
  128. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/dispatcher.py +0 -0
  129. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/http_proxy.py +0 -0
  130. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/lint.py +0 -0
  131. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/profiler.py +0 -0
  132. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/proxy_fix.py +0 -0
  133. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/middleware/shared_data.py +0 -0
  134. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/py.typed +0 -0
  135. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/__init__.py +0 -0
  136. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/converters.py +0 -0
  137. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/exceptions.py +0 -0
  138. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/map.py +0 -0
  139. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/matcher.py +0 -0
  140. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/routing/rules.py +0 -0
  141. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/__init__.py +0 -0
  142. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/http.py +0 -0
  143. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/multipart.py +0 -0
  144. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/request.py +0 -0
  145. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/response.py +0 -0
  146. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/sansio/utils.py +0 -0
  147. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/security.py +0 -0
  148. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/serving.py +0 -0
  149. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/test.py +0 -0
  150. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/testapp.py +0 -0
  151. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/urls.py +0 -0
  152. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/user_agent.py +0 -0
  153. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/utils.py +0 -0
  154. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/wrappers/__init__.py +0 -0
  155. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/wrappers/request.py +0 -0
  156. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/wrappers/response.py +0 -0
  157. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/_vendor/werkzeug/wsgi.py +0 -0
  158. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/asgi.py +0 -0
  159. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/crons.py +0 -0
  160. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/headers.py +0 -0
  161. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/utils.py +0 -0
  162. {vercel_runtime-0.6.0 → vercel_runtime-0.7.0}/src/vercel_runtime/workers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vercel-runtime
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  License-Expression: Apache-2.0
5
5
  License-File: src/vercel_runtime/_vendor/__init__.py
6
6
  License-File: src/vercel_runtime/_vendor/click/LICENSE.txt
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vercel-runtime"
3
- version = "0.6.0"
3
+ version = "0.7.0"
4
4
  license = "Apache-2.0"
5
5
  license-files = [
6
6
  "src/vercel_runtime/_vendor/*/LICEN[CS]E*",
@@ -0,0 +1,421 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import logging.config
5
+ import mimetypes
6
+ import os
7
+ import sys
8
+ from typing import TYPE_CHECKING, Any, cast
9
+
10
+ from vercel_runtime._vendor import uvicorn as vendored_uvicorn
11
+ from vercel_runtime._vendor.uvicorn.config import (
12
+ LOGGING_CONFIG as UVICORN_LOGGING_CONFIG,
13
+ )
14
+ from vercel_runtime._vendor.werkzeug.serving import run_simple
15
+ from vercel_runtime.resolver import detect_app_type, import_module, resolve_app
16
+ from vercel_runtime.routing import (
17
+ apply_service_route_prefix_to_asgi_scope,
18
+ strip_service_route_prefix,
19
+ )
20
+
21
+ if TYPE_CHECKING:
22
+ from collections.abc import Awaitable, Callable
23
+ from wsgiref.types import WSGIApplication
24
+
25
+ from vercel_runtime.asgi import ASGI
26
+
27
+ _NO_COLOR = "NO_COLOR" in os.environ
28
+
29
+ _log = logging.getLogger("vercel.dev")
30
+
31
+
32
+ # Used to split DEBUG-WARNING -> stdout and ERROR-CRITICAL -> stderr
33
+ class _MaxLevelFilter(logging.Filter):
34
+ def __init__(self, max_level: int) -> None:
35
+ super().__init__()
36
+ self.max_level = max_level
37
+
38
+ def filter(self, record: logging.LogRecord) -> bool:
39
+ return record.levelno <= self.max_level
40
+
41
+
42
+ def _build_log_config(
43
+ loggers: dict[str, Any],
44
+ _filter_ref: object | None = None,
45
+ ) -> dict[str, Any]:
46
+ if _filter_ref is None:
47
+ _filter_ref = "vercel_runtime.dev._MaxLevelFilter"
48
+
49
+ return {
50
+ "version": 1,
51
+ "disable_existing_loggers": False,
52
+ "filters": {
53
+ "max_warning": {
54
+ "()": _filter_ref,
55
+ "max_level": logging.WARNING,
56
+ }
57
+ },
58
+ "handlers": {
59
+ "stdout": {
60
+ "class": "logging.StreamHandler",
61
+ "stream": "ext://sys.stdout",
62
+ "filters": ["max_warning"],
63
+ },
64
+ "stderr": {
65
+ "class": "logging.StreamHandler",
66
+ "stream": "ext://sys.stderr",
67
+ "level": "ERROR",
68
+ },
69
+ },
70
+ "loggers": loggers,
71
+ "root": {
72
+ "handlers": ["stdout", "stderr"],
73
+ "level": "INFO",
74
+ },
75
+ }
76
+
77
+
78
+ def _setup_server_log_routing(logger_name: str | None = None) -> None:
79
+ loggers: dict[str, Any] = {}
80
+
81
+ if logger_name:
82
+ loggers[logger_name] = {
83
+ "handlers": ["stdout", "stderr"],
84
+ "level": "INFO",
85
+ "propagate": False,
86
+ }
87
+
88
+ logging.config.dictConfig(
89
+ _build_log_config(
90
+ loggers=loggers,
91
+ _filter_ref=_MaxLevelFilter,
92
+ ),
93
+ )
94
+
95
+
96
+ def _build_uvicorn_log_config(
97
+ default_fmt: dict[str, Any] | None = None,
98
+ access_fmt: dict[str, Any] | None = None,
99
+ ) -> dict[str, Any]:
100
+ uvicorn_fmts: dict[str, Any] = UVICORN_LOGGING_CONFIG["formatters"]
101
+
102
+ cfg = _build_log_config(
103
+ loggers={
104
+ "uvicorn": {
105
+ "handlers": ["stdout", "stderr"],
106
+ "level": "INFO",
107
+ "propagate": False,
108
+ },
109
+ "uvicorn.error": {"level": "INFO"},
110
+ "uvicorn.access": {
111
+ "handlers": ["access"],
112
+ "level": "INFO",
113
+ "propagate": False,
114
+ },
115
+ },
116
+ )
117
+
118
+ if default_fmt is None:
119
+ default_fmt = {
120
+ **uvicorn_fmts["default"],
121
+ "use_colors": not _NO_COLOR,
122
+ }
123
+
124
+ if access_fmt is None:
125
+ access_fmt = {
126
+ **uvicorn_fmts["access"],
127
+ "use_colors": not _NO_COLOR,
128
+ }
129
+
130
+ cfg["formatters"] = {"default": default_fmt, "access": access_fmt}
131
+ cfg["handlers"]["stdout"]["formatter"] = "default"
132
+ cfg["handlers"]["stderr"]["formatter"] = "default"
133
+ cfg["handlers"]["access"] = {
134
+ "class": "logging.StreamHandler",
135
+ "stream": "ext://sys.stdout",
136
+ "formatter": "access",
137
+ }
138
+
139
+ return cfg
140
+
141
+
142
+ def _patch_fastapi_cli_log_config() -> None:
143
+ try:
144
+ import fastapi_cli.cli as _fcli_cli # type: ignore[import-not-found] # noqa: PLC0415 # pyright: ignore[reportMissingImports]
145
+ import fastapi_cli.utils.cli as _fcli # type: ignore[import-not-found] # noqa: PLC0415 # pyright: ignore[reportMissingImports]
146
+
147
+ orig_get_config = cast(
148
+ "Callable[[], dict[str, Any]]",
149
+ _fcli.get_uvicorn_log_config, # pyright: ignore[reportUnknownMemberType]
150
+ )
151
+
152
+ # we want to ensure that attr exists
153
+ # because we're going to patch it as well
154
+ _fcli_cli.get_uvicorn_log_config # noqa: B018 # pyright: ignore[reportUnknownMemberType]
155
+ except (ImportError, AttributeError):
156
+ return
157
+
158
+ def _get_routed_config() -> dict[str, Any]:
159
+ orig = orig_get_config()
160
+ return _build_uvicorn_log_config(
161
+ default_fmt={
162
+ "()": "fastapi_cli.utils.cli.CustomFormatter",
163
+ "fmt": orig["formatters"]["default"].get(
164
+ "fmt", "%(levelprefix)s %(message)s"
165
+ ),
166
+ "use_colors": orig["formatters"]["default"].get("use_colors"),
167
+ },
168
+ access_fmt={
169
+ "()": "fastapi_cli.utils.cli.CustomFormatter",
170
+ "fmt": orig["formatters"]["access"].get(
171
+ "fmt",
172
+ "%(levelprefix)s %(client_addr)s - "
173
+ "'%(request_line)s' %(status_code)s",
174
+ ),
175
+ },
176
+ )
177
+
178
+ _fcli.get_uvicorn_log_config = _get_routed_config
179
+ _fcli_cli.get_uvicorn_log_config = _get_routed_config
180
+
181
+
182
+ PUBLIC_DIR = "public"
183
+
184
+
185
+ def _build_static_asgi_app() -> ASGI | None:
186
+ static_files_cls: type[Any] | None = None
187
+ try:
188
+ from fastapi.staticfiles import ( # type: ignore[import-not-found] # noqa: PLC0415 # pyright: ignore[reportMissingImports]
189
+ StaticFiles, # pyright: ignore[reportUnknownVariableType]
190
+ )
191
+
192
+ static_files_cls = StaticFiles # pyright: ignore[reportUnknownVariableType]
193
+ except Exception:
194
+ try:
195
+ from starlette.staticfiles import ( # type: ignore[import-not-found] # noqa: PLC0415 # pyright: ignore[reportMissingImports]
196
+ StaticFiles, # pyright: ignore[reportUnknownVariableType]
197
+ )
198
+
199
+ static_files_cls = StaticFiles # pyright: ignore[reportUnknownVariableType]
200
+ except Exception:
201
+ return None
202
+
203
+ if static_files_cls is not None:
204
+ try:
205
+ try:
206
+ return cast(
207
+ "ASGI",
208
+ static_files_cls(directory=PUBLIC_DIR, check_dir=False),
209
+ )
210
+ except TypeError:
211
+ return cast("ASGI", static_files_cls(directory=PUBLIC_DIR))
212
+ except Exception:
213
+ return None
214
+
215
+ return None
216
+
217
+
218
+ def _is_safe_file(base_dir: str, target: str) -> bool:
219
+ try:
220
+ base = os.path.realpath(base_dir)
221
+ tgt = os.path.realpath(target)
222
+ return (
223
+ tgt == base or tgt.startswith(base + os.sep)
224
+ ) and os.path.isfile(tgt)
225
+ except Exception:
226
+ return False
227
+
228
+
229
+ def _static_wsgi_app(
230
+ environ: dict[str, Any],
231
+ start_response: Callable[..., Any],
232
+ ) -> list[bytes]:
233
+ if environ.get("REQUEST_METHOD", "GET") not in ("GET", "HEAD"):
234
+ return _not_found(start_response)
235
+
236
+ req_path = environ.get("PATH_INFO", "/") or "/"
237
+ safe = os.path.normpath(req_path).lstrip("/")
238
+ full = os.path.join(PUBLIC_DIR, safe)
239
+ if not _is_safe_file(PUBLIC_DIR, full):
240
+ return _not_found(start_response)
241
+
242
+ ctype, _ = mimetypes.guess_type(full)
243
+ headers = [("Content-Type", ctype or "application/octet-stream")]
244
+ try:
245
+ if environ.get("REQUEST_METHOD") == "HEAD":
246
+ start_response("200 OK", headers)
247
+ return []
248
+ with open(full, "rb") as f:
249
+ data = f.read()
250
+ headers.append(("Content-Length", str(len(data))))
251
+ start_response("200 OK", headers)
252
+ return [data]
253
+ except Exception:
254
+ return _not_found(start_response)
255
+
256
+
257
+ def _not_found(
258
+ start_response: Callable[..., Any],
259
+ ) -> list[bytes]:
260
+ start_response(
261
+ "404 Not Found",
262
+ [("Content-Type", "text/plain; charset=utf-8")],
263
+ )
264
+ return [b"Not Found"]
265
+
266
+
267
+ static_asgi = _build_static_asgi_app()
268
+ _asgi_user_app: ASGI | None = None
269
+ _wsgi_user_app: WSGIApplication | None = None
270
+
271
+
272
+ async def asgi_app(
273
+ scope: dict[str, Any],
274
+ receive: Callable[[], Awaitable[Any]],
275
+ send: Callable[[Any], Awaitable[None]],
276
+ ) -> None:
277
+ effective_scope = dict(scope)
278
+ apply_service_route_prefix_to_asgi_scope(effective_scope)
279
+
280
+ if static_asgi is not None and effective_scope.get("type") == "http":
281
+ req_path = effective_scope.get("path", "/") or "/"
282
+ safe = os.path.normpath(req_path).lstrip("/")
283
+ full = os.path.join(PUBLIC_DIR, safe)
284
+ try:
285
+ base = os.path.realpath(PUBLIC_DIR)
286
+ target = os.path.realpath(full)
287
+ if (
288
+ target == base or target.startswith(base + os.path.sep)
289
+ ) and os.path.isfile(target):
290
+ await static_asgi(effective_scope, receive, send)
291
+ return
292
+ except Exception:
293
+ pass
294
+
295
+ assert _asgi_user_app is not None
296
+ await _asgi_user_app(effective_scope, receive, send)
297
+
298
+
299
+ def wsgi_app(
300
+ environ: dict[str, Any],
301
+ start_response: Callable[..., Any],
302
+ ) -> Any:
303
+ path_info, matched_prefix = strip_service_route_prefix(
304
+ environ.get("PATH_INFO", "/") or "/"
305
+ )
306
+ environ["PATH_INFO"] = path_info
307
+ if matched_prefix:
308
+ script_name = environ.get("SCRIPT_NAME", "") or ""
309
+ if script_name and script_name != "/":
310
+ script_name = script_name.rstrip("/")
311
+ else:
312
+ script_name = ""
313
+ environ["SCRIPT_NAME"] = f"{script_name}{matched_prefix}"
314
+
315
+ # Try static first; if 404 then delegate to user app
316
+ captured_status = ""
317
+ captured_headers: tuple[tuple[str, str], ...] = ()
318
+ body_chunks: list[bytes] = []
319
+
320
+ def capture_start_response(
321
+ status: str,
322
+ headers: list[tuple[str, str]],
323
+ exc_info: Any = None,
324
+ ) -> Callable[[bytes], None]:
325
+ nonlocal captured_status, captured_headers
326
+ captured_status = status
327
+ captured_headers = tuple(headers)
328
+
329
+ def write(chunk: bytes) -> None:
330
+ body_chunks.append(chunk)
331
+
332
+ return write
333
+
334
+ result = _static_wsgi_app(environ, capture_start_response)
335
+ # If static handler produced 200, forward its response
336
+ if captured_status.startswith("200 "):
337
+ # Send headers and any chunks collected
338
+ writer = start_response(captured_status, list(captured_headers))
339
+ for chunk in body_chunks:
340
+ writer(chunk)
341
+ return result
342
+
343
+ # Otherwise, delegate to user's WSGI app
344
+ assert _wsgi_user_app is not None
345
+ return _wsgi_user_app(environ, start_response)
346
+
347
+
348
+ def _start_wsgi(host: str, port: int) -> None:
349
+ _setup_server_log_routing("werkzeug")
350
+ run_simple(host, port, wsgi_app, use_reloader=True, threaded=True)
351
+
352
+
353
+ def _start_asgi(host: str, port: int) -> None:
354
+ # Prefer user-installed fastapi-cli when available
355
+ try:
356
+ from fastapi_cli.cli import ( # noqa: PLC0415 # pyright: ignore[reportMissingImports]
357
+ dev as fastapi_dev, # pyright: ignore[reportUnknownVariableType]
358
+ )
359
+
360
+ _patch_fastapi_cli_log_config()
361
+ fastapi_dev(
362
+ entrypoint="vercel_runtime.dev:asgi_app",
363
+ host=host,
364
+ port=port,
365
+ reload=True,
366
+ )
367
+ sys.exit(0)
368
+ except (ImportError, AttributeError):
369
+ pass
370
+
371
+ vendored_uvicorn.run(
372
+ "vercel_runtime.dev:asgi_app",
373
+ host=host,
374
+ port=port,
375
+ use_colors=not _NO_COLOR,
376
+ reload=True,
377
+ log_config=_build_uvicorn_log_config(),
378
+ )
379
+
380
+
381
+ def _setup_apps() -> None:
382
+ global _asgi_user_app, _wsgi_user_app # noqa: PLW0603
383
+
384
+ module_name = os.environ["VERCEL_DEV_MODULE_NAME"]
385
+ entry_abs = os.environ["VERCEL_DEV_ENTRY_ABS"]
386
+
387
+ _setup_server_log_routing()
388
+
389
+ mod = import_module(module_name, entry_abs)
390
+ app_name, user_app = resolve_app(mod, module_name)
391
+ try:
392
+ result = detect_app_type(user_app, module_name, app_name)
393
+ except RuntimeError:
394
+ _log.exception("could not detect application type")
395
+ sys.exit(1)
396
+
397
+ if result[0] == "asgi":
398
+ _asgi_user_app = result[1]
399
+ else:
400
+ _wsgi_user_app = result[1]
401
+
402
+
403
+ # Setup apps at import time so that servers reloader can pick them up.
404
+ if os.environ.get("VERCEL_DEV_MODULE_NAME") and os.environ.get(
405
+ "VERCEL_DEV_ENTRY_ABS"
406
+ ):
407
+ _setup_apps()
408
+
409
+
410
+ def main() -> None:
411
+ host = "127.0.0.1"
412
+ raw_port = os.environ.get("PORT")
413
+ if not raw_port:
414
+ _log.error("PORT environment variable is required")
415
+ sys.exit(1)
416
+ port = int(raw_port)
417
+
418
+ if _wsgi_user_app is not None:
419
+ _start_wsgi(host, port)
420
+ else:
421
+ _start_asgi(host, port)
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ import sys
5
+ from importlib import util as _importlib_util
6
+ from typing import TYPE_CHECKING, Any, Literal, cast
7
+
8
+ if TYPE_CHECKING:
9
+ from types import ModuleType
10
+ from wsgiref.types import WSGIApplication
11
+
12
+ from vercel_runtime.asgi import ASGI
13
+
14
+
15
+ # Import relative path
16
+ # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
17
+ def import_module(name: str, path: str) -> ModuleType:
18
+ spec = _importlib_util.spec_from_file_location(name, path)
19
+ if spec is None or spec.loader is None:
20
+ msg = f'could not load module spec for "{name}" at {path}'
21
+ raise RuntimeError(msg)
22
+ mod = _importlib_util.module_from_spec(spec)
23
+ sys.modules[name] = mod
24
+ spec.loader.exec_module(mod)
25
+ return mod
26
+
27
+
28
+ def resolve_app(mod: ModuleType, module_name: str) -> tuple[str, Any]:
29
+ for app_var in ("app", "application"):
30
+ if hasattr(mod, app_var):
31
+ return app_var, getattr(mod, app_var)
32
+
33
+ raise RuntimeError(
34
+ f"Missing 'app' or 'application' in module '{module_name}': "
35
+ "define `app = ...` or `application = ...` in your entrypoint"
36
+ )
37
+
38
+
39
+ def _get_positional_param_count(obj: object) -> int | None:
40
+ try:
41
+ sig = inspect.signature(obj) # type: ignore[arg-type]
42
+ return sum(
43
+ 1
44
+ for p in sig.parameters.values()
45
+ if p.default is inspect.Parameter.empty
46
+ and p.kind
47
+ in (
48
+ inspect.Parameter.POSITIONAL_ONLY,
49
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
50
+ )
51
+ )
52
+ except (ValueError, TypeError):
53
+ return None
54
+
55
+
56
+ def detect_app_type(
57
+ app_obj: object,
58
+ module_name: str,
59
+ app_name: str,
60
+ ) -> tuple[Literal["asgi"], ASGI] | tuple[Literal["wsgi"], WSGIApplication]:
61
+ # Try .asgi attribute if it's available and is callable
62
+ asgi_attr = getattr(app_obj, "asgi", None)
63
+ if asgi_attr is not None and callable(asgi_attr):
64
+ return "asgi", cast("ASGI", asgi_attr)
65
+
66
+ # For async detection, check the object itself first
67
+ # (works for plain functions/methods).
68
+ # For class instances iscoroutinefunction(obj) is False,
69
+ # so fall back to __call__.
70
+ is_async = inspect.iscoroutinefunction(app_obj)
71
+ if not is_async and callable(app_obj):
72
+ is_async = inspect.iscoroutinefunction(
73
+ app_obj.__call__ # pyright: ignore[reportFunctionMemberAccess]
74
+ )
75
+
76
+ param_count = _get_positional_param_count(app_obj)
77
+
78
+ # that's ASGI (scope, receive, send)
79
+ if is_async and param_count == 3:
80
+ return "asgi", cast("ASGI", app_obj)
81
+
82
+ # that's WSGI (environ, start_response)
83
+ if param_count == 2:
84
+ return "wsgi", cast("WSGIApplication", app_obj)
85
+
86
+ raise RuntimeError(
87
+ f"Could not determine the application interface for "
88
+ f"'{module_name}:{app_name}'\n"
89
+ f"Expected either:\n"
90
+ f" - an ASGI app: async callable(scope, receive, send)\n"
91
+ f" - a WSGI app: callable(environ, start_response)"
92
+ )
@@ -0,0 +1,177 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import Any
5
+ from urllib.parse import urlsplit
6
+
7
+
8
+ def normalize_service_route_prefix(raw_prefix: str | None) -> str:
9
+ """
10
+ Normalize a configured service prefix into canonical mount-path form.
11
+
12
+ Returns an empty string when unset/blank/root ("/"), otherwise:
13
+ - always starts with "/"
14
+ - has no trailing slash
15
+ """
16
+ if not raw_prefix:
17
+ return ""
18
+
19
+ prefix = raw_prefix.strip()
20
+ if not prefix:
21
+ return ""
22
+
23
+ if not prefix.startswith("/"):
24
+ prefix = f"/{prefix}"
25
+
26
+ # Treat "/" as an unset mount prefix.
27
+ return "" if prefix == "/" else prefix.rstrip("/")
28
+
29
+
30
+ def is_service_route_prefix_strip_enabled() -> bool:
31
+ raw = os.environ.get("VERCEL_SERVICE_ROUTE_PREFIX_STRIP")
32
+ if not raw:
33
+ return False
34
+ return raw.lower() in ("1", "true")
35
+
36
+
37
+ def get_service_route_prefix() -> str:
38
+ if not is_service_route_prefix_strip_enabled():
39
+ return ""
40
+
41
+ return normalize_service_route_prefix(
42
+ os.environ.get("VERCEL_SERVICE_ROUTE_PREFIX")
43
+ )
44
+
45
+
46
+ _service_route_prefix = get_service_route_prefix()
47
+
48
+
49
+ def split_request_target(target: str) -> tuple[str, str]:
50
+ """
51
+ Split an HTTP request-target into (path, query).
52
+
53
+ Supports:
54
+ - origin-form: "/a/b?x=1"
55
+ - absolute-form: "https://example.com/a/b?x=1"
56
+ - asterisk-form: "*"
57
+ """
58
+ if not target:
59
+ return "/", ""
60
+
61
+ parsed = urlsplit(target)
62
+ path = parsed.path
63
+ query = parsed.query
64
+
65
+ # Absolute-form request-target (RFC 7230 section 5.3.2).
66
+ if parsed.scheme in ("http", "https") and parsed.netloc:
67
+ return path or "/", query
68
+
69
+ # Asterisk-form request-target (RFC 7230 section 5.3.4).
70
+ if path == "*":
71
+ return "*", query
72
+
73
+ if not path:
74
+ path = "/"
75
+ elif not path.startswith("/"):
76
+ path = f"/{path}"
77
+
78
+ return path, query
79
+
80
+
81
+ def strip_service_route_prefix(
82
+ path: str, prefix: str | None = None
83
+ ) -> tuple[str, str]:
84
+ """
85
+ Strip a service route prefix from a request path.
86
+
87
+ Returns a tuple of:
88
+ - stripped path passed to the user app
89
+ - matched mount prefix (empty string when no prefix matched)
90
+
91
+ When *prefix* is ``None`` (the default), reads the current value
92
+ from ``get_service_route_prefix()``.
93
+
94
+ Example with prefix "/_/backend":
95
+ "/_/backend/ping" -> ("/ping", "/_/backend")
96
+ "/foo" -> ("/foo", "")
97
+ """
98
+ if prefix is None:
99
+ prefix = _service_route_prefix
100
+
101
+ if path == "*":
102
+ return path, ""
103
+ if not path:
104
+ path = "/"
105
+ elif not path.startswith("/"):
106
+ path = f"/{path}"
107
+
108
+ if not prefix:
109
+ return path, ""
110
+
111
+ if path == prefix:
112
+ return "/", prefix
113
+
114
+ if path.startswith(f"{prefix}/"):
115
+ stripped = path[len(prefix) :]
116
+ return stripped if stripped else "/", prefix
117
+
118
+ return path, ""
119
+
120
+
121
+ def apply_service_route_prefix_to_asgi_scope(
122
+ scope: dict[str, Any], prefix: str | None = None
123
+ ) -> None:
124
+ if prefix is None:
125
+ prefix = _service_route_prefix
126
+
127
+ if not prefix:
128
+ return
129
+
130
+ path = scope.get("path", "/") or "/"
131
+ stripped_path, matched_prefix = strip_service_route_prefix(path, prefix)
132
+ if not matched_prefix:
133
+ return
134
+
135
+ scope["path"] = stripped_path
136
+
137
+ raw_path = scope.get("raw_path")
138
+ if isinstance(raw_path, (bytes, bytearray)):
139
+ try:
140
+ decoded = bytes(raw_path).decode("utf-8", "surrogateescape")
141
+ stripped_raw, _ = strip_service_route_prefix(decoded, prefix)
142
+ scope["raw_path"] = stripped_raw.encode("utf-8", "surrogateescape")
143
+ except Exception:
144
+ pass
145
+
146
+ existing_root = scope.get("root_path", "") or ""
147
+ if existing_root and existing_root != "/":
148
+ existing_root = existing_root.rstrip("/")
149
+ else:
150
+ existing_root = ""
151
+
152
+ scope["root_path"] = f"{existing_root}{matched_prefix}"
153
+
154
+
155
+ def apply_service_route_prefix_to_target(
156
+ target: str,
157
+ prefix: str | None = None,
158
+ ) -> tuple[str, str]:
159
+ """
160
+ Apply service-prefix stripping to a full request target.
161
+
162
+ When *prefix* is ``None`` (the default), reads the current value
163
+ from ``get_service_route_prefix()``.
164
+
165
+ Returns:
166
+ - updated request target (path + optional query)
167
+ - matched mount prefix for framework metadata (or "")
168
+ """
169
+ if prefix is None:
170
+ prefix = _service_route_prefix
171
+
172
+ path, query = split_request_target(target)
173
+ path, root_path = strip_service_route_prefix(path, prefix)
174
+ if query:
175
+ return f"{path}?{query}", root_path
176
+
177
+ return path, root_path