shortcutxl 0.3.76 → 0.3.78

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 (480) hide show
  1. package/BINARY-INVENTORY.json +813 -813
  2. package/CHANGELOG.md +14 -0
  3. package/dist/ai/models.d.ts +10 -6
  4. package/dist/ai/models.generated.d.ts +72 -0
  5. package/dist/ai/models.generated.js +33 -0
  6. package/dist/ai/models.js +36 -13
  7. package/dist/ai/providers/anthropic.d.ts +4 -2
  8. package/dist/ai/providers/anthropic.js +59 -29
  9. package/dist/ai/providers/azure-openai-responses.d.ts +2 -1
  10. package/dist/ai/providers/azure-openai-responses.js +4 -20
  11. package/dist/ai/providers/google-vertex.d.ts +8 -2
  12. package/dist/ai/providers/google-vertex.js +66 -54
  13. package/dist/ai/providers/google.js +15 -48
  14. package/dist/ai/providers/openai-completions.d.ts +4 -1
  15. package/dist/ai/providers/openai-completions.js +26 -10
  16. package/dist/ai/providers/openai-reasoning.d.ts +5 -0
  17. package/dist/ai/providers/openai-reasoning.js +13 -0
  18. package/dist/ai/providers/openai-responses-shared.js +1 -1
  19. package/dist/ai/providers/openai-responses.d.ts +6 -2
  20. package/dist/ai/providers/openai-responses.js +28 -23
  21. package/dist/ai/providers/simple-options.d.ts +2 -3
  22. package/dist/ai/providers/simple-options.js +4 -6
  23. package/dist/ai/types.d.ts +19 -2
  24. package/dist/app/agent-session-runtime-adapter.js +7 -6
  25. package/dist/app/agent-session.d.ts +0 -15
  26. package/dist/app/agent-session.js +0 -37
  27. package/dist/app/approvals/approval-errors.d.ts +15 -0
  28. package/dist/app/approvals/approval-errors.js +14 -0
  29. package/dist/app/approvals/runtime-approval-gates.d.ts +1 -2
  30. package/dist/app/approvals/tool-approval.d.ts +1 -0
  31. package/dist/app/approvals/tool-approval.js +11 -23
  32. package/dist/app/approvals/types.d.ts +0 -14
  33. package/dist/app/approvals/types.js +0 -2
  34. package/dist/app/background/tool-summaries.js +0 -2
  35. package/dist/app/extensions/slash-commands.js +1 -3
  36. package/dist/app/local/tools/execute-code/executor.d.ts +1 -1
  37. package/dist/app/local/tools/execute-code/executor.js +20 -10
  38. package/dist/app/local/tools/execute-code/tool.js +21 -2
  39. package/dist/app/modes/action/agent.js +4 -5
  40. package/dist/app/modes/ask/agent.js +4 -6
  41. package/dist/app/modes/index.js +20 -10
  42. package/dist/app/modes/installation/agent.js +4 -5
  43. package/dist/app/modes/manage/agent.js +3 -4
  44. package/dist/app/modes/manage/prompt.js +1 -2
  45. package/dist/app/modes/metadata.d.ts +31 -0
  46. package/dist/app/modes/metadata.js +35 -0
  47. package/dist/app/modes/plan/agent.js +5 -8
  48. package/dist/app/modes/plan/prompt.js +3 -3
  49. package/dist/app/permissions/runtime-approval-flow.d.ts +1 -5
  50. package/dist/app/permissions/runtime-approval-flow.js +0 -28
  51. package/dist/app/permissions/tool-gates.d.ts +2 -3
  52. package/dist/app/permissions/tool-gates.js +3 -81
  53. package/dist/app/prompts/com-api.js +1 -1
  54. package/dist/app/prompts/shared-guidelines.js +7 -3
  55. package/dist/app/providers/google-vertex-gateway.js +4 -51
  56. package/dist/app/providers/model-registry.d.ts +1 -0
  57. package/dist/app/providers/model-registry.js +39 -0
  58. package/dist/app/providers/model-resolver.js +10 -7
  59. package/dist/app/providers/register-shortcut-provider.js +6 -6
  60. package/dist/app/providers/shortcut-api-v2-transport.js +4 -0
  61. package/dist/app/providers/shortcut-llm-proxy-client.d.ts +2 -2
  62. package/dist/app/providers/shortcut-llm-proxy-client.js +1 -0
  63. package/dist/app/resources/package-manager.js +2 -123
  64. package/dist/app/resources/resource-patterns.d.ts +9 -0
  65. package/dist/app/resources/resource-patterns.js +120 -0
  66. package/dist/app/session/forked-agent.d.ts +2 -2
  67. package/dist/app/session/model-manager.js +3 -2
  68. package/dist/app/session/session-lifecycle.js +3 -2
  69. package/dist/app/session/session-models.d.ts +2 -0
  70. package/dist/app/session/session-models.js +14 -6
  71. package/dist/app/settings-manager.d.ts +3 -3
  72. package/dist/app/shared/tools/execute-code/mog.d.ts +3 -1
  73. package/dist/app/shared/tools/execute-code/mog.js +18 -2
  74. package/dist/app/tools/bash.js +3 -3
  75. package/dist/app/tools/execute-code/index.d.ts +1 -0
  76. package/dist/app/tools/execute-code/index.js +3 -1
  77. package/dist/app/tools/find-ignore-files.d.ts +3 -0
  78. package/dist/app/tools/find-ignore-files.js +33 -0
  79. package/dist/app/tools/find.js +2 -21
  80. package/dist/app/tools/llm-analysis.js +0 -2
  81. package/dist/app/tools/refresh-context.d.ts +1 -1
  82. package/dist/app/tools/session-settings/update-session-settings.js +2 -2
  83. package/dist/app/tools/switch-mode.js +2 -2
  84. package/dist/app/tools/take-screenshot.js +0 -2
  85. package/dist/app/tools/task/runner.js +1 -2
  86. package/dist/app/tools/task/task.d.ts +1 -3
  87. package/dist/app/tools/task/task.js +20 -40
  88. package/dist/app/tools/web-search.js +12 -2
  89. package/dist/autocomplete/core.d.ts +33 -0
  90. package/dist/autocomplete/core.js +113 -0
  91. package/dist/{tui → autocomplete}/fuzzy.d.ts +0 -1
  92. package/dist/{tui → autocomplete}/fuzzy.js +0 -1
  93. package/dist/{tui/autocomplete.d.ts → autocomplete/index.d.ts} +0 -1
  94. package/dist/{tui/autocomplete.js → autocomplete/index.js} +62 -158
  95. package/dist/cli/args.js +1 -1
  96. package/dist/cli/list-models.js +1 -1
  97. package/dist/cli.js +26227 -30480
  98. package/dist/constants.d.ts +0 -2
  99. package/dist/constants.js +0 -3
  100. package/dist/contracts/agent-session-store.d.ts +1 -1
  101. package/dist/contracts/agent-settings.d.ts +1 -0
  102. package/dist/contracts/agent-settings.js +2 -1
  103. package/dist/contracts/model-call.d.ts +1 -2
  104. package/dist/contracts/model-call.js +1 -2
  105. package/dist/contracts/model-stream.d.ts +1 -0
  106. package/dist/contracts/model-stream.js +1 -0
  107. package/dist/contracts/wire.d.ts +10 -7
  108. package/dist/contracts/wire.js +8 -0
  109. package/dist/core/agent-engine.js +1 -1
  110. package/dist/core/core-types.d.ts +1 -21
  111. package/dist/core/defaults.d.ts +1 -1
  112. package/dist/core/defaults.js +9 -1
  113. package/dist/core/prompts/agent-guidelines.d.ts +4 -5
  114. package/dist/core/prompts/agent-guidelines.js +28 -42
  115. package/dist/core/session-schema.d.ts +1 -1
  116. package/dist/embedded-agent/ai-invoke.d.ts +1 -2
  117. package/dist/embedded-agent/ai-invoke.js +2 -70
  118. package/dist/embedded-agent/anthropic-messages-transport.d.ts +4 -20
  119. package/dist/embedded-agent/anthropic-messages-transport.js +23 -786
  120. package/dist/embedded-agent/bootstrap.js +2 -2
  121. package/dist/embedded-agent/compose.js +8 -7
  122. package/dist/embedded-agent/gateway-base-url.d.ts +3 -0
  123. package/dist/embedded-agent/gateway-base-url.js +35 -0
  124. package/dist/embedded-agent/google-vertex-transport.d.ts +2 -11
  125. package/dist/embedded-agent/google-vertex-transport.js +20 -117
  126. package/dist/embedded-agent/host-runtime-options.d.ts +0 -8
  127. package/dist/embedded-agent/host-runtime-options.js +10 -2
  128. package/dist/embedded-agent/host-tools/invoke-llm/contract.d.ts +2 -2
  129. package/dist/embedded-agent/host-tools/invoke-llm/contract.js +4 -2
  130. package/dist/embedded-agent/host-tools/sandbox-llm/contract.d.ts +8 -3
  131. package/dist/embedded-agent/host-tools/sandbox-llm/contract.js +12 -8
  132. package/dist/embedded-agent/host-tools/sandbox-llm/index.d.ts +1 -1
  133. package/dist/embedded-agent/host-tools/sandbox-llm/index.js +1 -1
  134. package/dist/embedded-agent/host-tools/shared/index.d.ts +1 -1
  135. package/dist/embedded-agent/host-tools/shared/index.js +1 -1
  136. package/dist/embedded-agent/host-tools/shared/llm-stream-error.d.ts +7 -0
  137. package/dist/embedded-agent/host-tools/shared/llm-stream-error.js +10 -0
  138. package/dist/embedded-agent/host-tools/take-screenshot/contract.d.ts +0 -2
  139. package/dist/embedded-agent/host-tools/take-screenshot/contract.js +1 -3
  140. package/dist/embedded-agent/host-tools/take-screenshot/index.d.ts +1 -1
  141. package/dist/embedded-agent/host-tools/take-screenshot/index.js +1 -1
  142. package/dist/embedded-agent/host-tools/task/agents/clone.js +1 -2
  143. package/dist/embedded-agent/host-tools/task/contract.js +1 -1
  144. package/dist/embedded-agent/index.d.ts +3 -4
  145. package/dist/embedded-agent/index.js +2 -3
  146. package/dist/embedded-agent/model-selection.d.ts +0 -6
  147. package/dist/embedded-agent/model-selection.js +15 -8
  148. package/dist/embedded-agent/openai-completions-transport.d.ts +2 -11
  149. package/dist/embedded-agent/openai-completions-transport.js +20 -120
  150. package/dist/embedded-agent/openai-responses-transport.d.ts +2 -11
  151. package/dist/embedded-agent/openai-responses-transport.js +15 -170
  152. package/dist/embedded-agent/prompt/build.js +1 -2
  153. package/dist/embedded-agent/prompt/sections.d.ts +1 -1
  154. package/dist/embedded-agent/prompt/sections.js +2 -0
  155. package/dist/embedded-agent/provider-native-request.d.ts +29 -0
  156. package/dist/embedded-agent/provider-native-request.js +65 -0
  157. package/dist/embedded-agent/run-stats.d.ts +1 -1
  158. package/dist/embedded-agent/stream.d.ts +10 -96
  159. package/dist/embedded-agent/stream.js +31 -556
  160. package/dist/embedded-agent/suggestions.d.ts +1 -1
  161. package/dist/embedded-agent/suggestions.js +1 -1
  162. package/dist/embedded-agent/worker-bridge/host-client.d.ts +7 -0
  163. package/dist/embedded-agent/worker-bridge/host-client.js +15 -0
  164. package/dist/embedded-agent/worker-bridge/worker-port.js +17 -1
  165. package/dist/main.js +18 -14
  166. package/dist/rpc/index.d.ts +1 -1
  167. package/dist/rpc/rpc-approval-gates.js +1 -2
  168. package/dist/rpc/rpc-host.d.ts +4 -0
  169. package/dist/rpc/rpc-host.js +13 -24
  170. package/dist/rpc/rpc-types.d.ts +6 -17
  171. package/dist/shared/platform/open.d.ts +4 -1
  172. package/dist/shared/platform/open.js +14 -2
  173. package/dist/shell/approvals/runtime-approval-gates.js +1 -3
  174. package/dist/shell/components/selectors/model-selector.js +2 -1
  175. package/dist/shell/components/selectors/session-selector-search.js +1 -1
  176. package/dist/shell/components/selectors/thinking-selector.js +7 -6
  177. package/dist/shell/index.d.ts +0 -1
  178. package/dist/shell/index.js +0 -1
  179. package/dist/shell/interactive/interactive-mode-options.d.ts +10 -0
  180. package/dist/shell/interactive/interactive-mode.d.ts +6 -12
  181. package/dist/shell/interactive/interactive-mode.js +39 -158
  182. package/dist/shell/interactive/slash-commands.d.ts +0 -34
  183. package/dist/shell/interactive/slash-commands.js +0 -2
  184. package/dist/shell/links/local-file-link-input.d.ts +15 -0
  185. package/dist/shell/links/local-file-link-input.js +38 -0
  186. package/dist/shell/links/local-file-link.d.ts +7 -0
  187. package/dist/shell/links/local-file-link.js +35 -0
  188. package/dist/shell/links/local-file-opener.d.ts +16 -0
  189. package/dist/shell/links/local-file-opener.js +90 -0
  190. package/dist/shell/session-client.d.ts +0 -1
  191. package/dist/shell/session-client.js +0 -6
  192. package/dist/shell/status/update-check-scheduler.d.ts +30 -0
  193. package/dist/shell/status/update-check-scheduler.js +71 -0
  194. package/dist/shell/status/update-reminder-controller.d.ts +22 -0
  195. package/dist/shell/status/update-reminder-controller.js +40 -0
  196. package/dist/shell/status/update-reminder.d.ts +6 -0
  197. package/dist/shell/status/update-reminder.js +4 -0
  198. package/dist/shell/theme/dark.json +1 -2
  199. package/dist/shell/theme/light.json +1 -2
  200. package/dist/shell/theme/runtime.d.ts +2 -3
  201. package/dist/shell/theme/runtime.js +2 -6
  202. package/dist/shell/theme/theme-schema.json +1 -6
  203. package/dist/shortcut-model-catalog.d.ts +7 -2
  204. package/dist/shortcut-model-catalog.js +134 -10
  205. package/dist/startup/update-manager.d.ts +6 -6
  206. package/dist/startup/update-manager.js +23 -38
  207. package/dist/startup/update-prompt.d.ts +1 -0
  208. package/dist/startup/update-prompt.js +2 -2
  209. package/dist/tui/components/editor.d.ts +1 -1
  210. package/dist/tui/components/editor.js +1 -1
  211. package/dist/tui/components/markdown.js +15 -6
  212. package/dist/tui/components/settings-list.js +1 -1
  213. package/dist/tui/editor-component.d.ts +1 -1
  214. package/dist/tui/index.d.ts +7 -8
  215. package/dist/tui/index.js +7 -6
  216. package/dist/tui/tui.d.ts +2 -0
  217. package/dist/tui/tui.js +16 -1
  218. package/dist/tui/utils.d.ts +7 -0
  219. package/dist/tui/utils.js +58 -8
  220. package/native-app/main/index.js +3200 -48
  221. package/native-app/main/{photon_rs-SoVDgheu.js → photon_rs-XkYyYTVA.js} +2 -2
  222. package/native-app/package.json +1 -1
  223. package/native-app/preload/index.cjs +2 -0
  224. package/native-app/renderer/assets/{index-CFc-oZuS.js → index-B4jz6epR.js} +825 -155
  225. package/native-app/renderer/assets/{index-OhGE0UY4.css → index-C__g36ft.css} +412 -162
  226. package/native-app/renderer/assets/shortcutagent-DxFsGSkw.png +0 -0
  227. package/native-app/renderer/index.html +2 -2
  228. package/package.json +17 -3
  229. package/skills/add-custom-model/SKILL.md +52 -0
  230. package/user-docs/dist/index.html +1 -1
  231. package/user-docs/dist/shortcutxl-docs.pdf +0 -0
  232. package/xll/ShortcutXL.xll +0 -0
  233. package/xll/modules/shortcut_xl/_managed.py +26 -7
  234. package/xll/modules/shortcut_xl/_tracking.py +141 -251
  235. package/xll/modules/shortcut_xl/api/__init__.py +60 -0
  236. package/xll/modules/shortcut_xl/api/workbook.py +9 -1
  237. package/xll/modules/shortcut_xl/api/worksheet.py +4 -1
  238. package/xll/python/Lib/site-packages/httpx-0.28.1.dist-info/RECORD +1 -1
  239. package/xll/python/Lib/site-packages/idna-3.18.dist-info/RECORD +1 -1
  240. package/xll/python/Lib/site-packages/pip/__init__.py +1 -1
  241. package/xll/python/Lib/site-packages/pip/_internal/build_env/__init__.py +25 -0
  242. package/xll/python/Lib/site-packages/pip/_internal/build_env/base.py +103 -0
  243. package/xll/python/Lib/site-packages/pip/_internal/build_env/installer.py +333 -0
  244. package/xll/python/Lib/site-packages/pip/_internal/build_env/noop.py +42 -0
  245. package/xll/python/Lib/site-packages/pip/_internal/build_env/venv.py +145 -0
  246. package/xll/python/Lib/site-packages/pip/_internal/build_env/virtual.py +139 -0
  247. package/xll/python/Lib/site-packages/pip/_internal/cache.py +4 -1
  248. package/xll/python/Lib/site-packages/pip/_internal/cli/autocompletion.py +4 -1
  249. package/xll/python/Lib/site-packages/pip/_internal/cli/base_command.py +11 -14
  250. package/xll/python/Lib/site-packages/pip/_internal/cli/cmdoptions.py +133 -22
  251. package/xll/python/Lib/site-packages/pip/_internal/cli/index_command.py +11 -6
  252. package/xll/python/Lib/site-packages/pip/_internal/cli/main.py +2 -1
  253. package/xll/python/Lib/site-packages/pip/_internal/cli/main_parser.py +1 -2
  254. package/xll/python/Lib/site-packages/pip/_internal/cli/parser.py +10 -2
  255. package/xll/python/Lib/site-packages/pip/_internal/cli/progress_bars.py +2 -2
  256. package/xll/python/Lib/site-packages/pip/_internal/cli/req_command.py +57 -25
  257. package/xll/python/Lib/site-packages/pip/_internal/cli/status_codes.py +1 -0
  258. package/xll/python/Lib/site-packages/pip/_internal/commands/cache.py +7 -6
  259. package/xll/python/Lib/site-packages/pip/_internal/commands/configuration.py +2 -1
  260. package/xll/python/Lib/site-packages/pip/_internal/commands/debug.py +4 -5
  261. package/xll/python/Lib/site-packages/pip/_internal/commands/download.py +4 -0
  262. package/xll/python/Lib/site-packages/pip/_internal/commands/freeze.py +1 -1
  263. package/xll/python/Lib/site-packages/pip/_internal/commands/index.py +10 -3
  264. package/xll/python/Lib/site-packages/pip/_internal/commands/inspect.py +1 -1
  265. package/xll/python/Lib/site-packages/pip/_internal/commands/install.py +6 -6
  266. package/xll/python/Lib/site-packages/pip/_internal/commands/list.py +11 -6
  267. package/xll/python/Lib/site-packages/pip/_internal/commands/lock.py +4 -6
  268. package/xll/python/Lib/site-packages/pip/_internal/commands/show.py +14 -8
  269. package/xll/python/Lib/site-packages/pip/_internal/commands/wheel.py +7 -1
  270. package/xll/python/Lib/site-packages/pip/_internal/configuration.py +2 -3
  271. package/xll/python/Lib/site-packages/pip/_internal/distributions/base.py +5 -3
  272. package/xll/python/Lib/site-packages/pip/_internal/distributions/installed.py +3 -2
  273. package/xll/python/Lib/site-packages/pip/_internal/distributions/sdist.py +45 -20
  274. package/xll/python/Lib/site-packages/pip/_internal/distributions/wheel.py +3 -2
  275. package/xll/python/Lib/site-packages/pip/_internal/exceptions.py +174 -0
  276. package/xll/python/Lib/site-packages/pip/_internal/index/collector.py +70 -48
  277. package/xll/python/Lib/site-packages/pip/_internal/index/package_finder.py +46 -7
  278. package/xll/python/Lib/site-packages/pip/_internal/index/sources.py +1 -2
  279. package/xll/python/Lib/site-packages/pip/_internal/locations/_sysconfig.py +1 -1
  280. package/xll/python/Lib/site-packages/pip/_internal/locations/base.py +1 -1
  281. package/xll/python/Lib/site-packages/pip/_internal/metadata/base.py +10 -5
  282. package/xll/python/Lib/site-packages/pip/_internal/metadata/importlib/_dists.py +1 -1
  283. package/xll/python/Lib/site-packages/pip/_internal/metadata/importlib/_envs.py +8 -3
  284. package/xll/python/Lib/site-packages/pip/_internal/models/candidate.py +5 -1
  285. package/xll/python/Lib/site-packages/pip/_internal/models/link.py +53 -13
  286. package/xll/python/Lib/site-packages/pip/_internal/network/auth.py +38 -21
  287. package/xll/python/Lib/site-packages/pip/_internal/network/cache.py +2 -2
  288. package/xll/python/Lib/site-packages/pip/_internal/network/download.py +58 -11
  289. package/xll/python/Lib/site-packages/pip/_internal/network/session.py +35 -40
  290. package/xll/python/Lib/site-packages/pip/_internal/network/utils.py +109 -1
  291. package/xll/python/Lib/site-packages/pip/_internal/operations/check.py +2 -3
  292. package/xll/python/Lib/site-packages/pip/_internal/operations/install/wheel.py +23 -18
  293. package/xll/python/Lib/site-packages/pip/_internal/operations/prepare.py +152 -7
  294. package/xll/python/Lib/site-packages/pip/_internal/req/__init__.py +2 -0
  295. package/xll/python/Lib/site-packages/pip/_internal/req/constructors.py +54 -69
  296. package/xll/python/Lib/site-packages/pip/_internal/req/req_file.py +6 -6
  297. package/xll/python/Lib/site-packages/pip/_internal/req/req_install.py +25 -21
  298. package/xll/python/Lib/site-packages/pip/_internal/req/req_uninstall.py +13 -8
  299. package/xll/python/Lib/site-packages/pip/_internal/resolution/base.py +2 -2
  300. package/xll/python/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py +2 -2
  301. package/xll/python/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py +1 -1
  302. package/xll/python/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py +0 -1
  303. package/xll/python/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py +39 -52
  304. package/xll/python/Lib/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py +3 -3
  305. package/xll/python/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py +3 -0
  306. package/xll/python/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py +9 -0
  307. package/xll/python/Lib/site-packages/pip/_internal/utils/compat.py +15 -9
  308. package/xll/python/Lib/site-packages/pip/_internal/utils/deprecation.py +6 -1
  309. package/xll/python/Lib/site-packages/pip/_internal/utils/filesystem.py +2 -2
  310. package/xll/python/Lib/site-packages/pip/_internal/utils/logging.py +11 -3
  311. package/xll/python/Lib/site-packages/pip/_internal/utils/misc.py +48 -4
  312. package/xll/python/Lib/site-packages/pip/_internal/utils/pylock.py +18 -2
  313. package/xll/python/Lib/site-packages/pip/_internal/utils/retry.py +2 -1
  314. package/xll/python/Lib/site-packages/pip/_internal/utils/subprocess.py +4 -3
  315. package/xll/python/Lib/site-packages/pip/_internal/utils/temp_dir.py +1 -2
  316. package/xll/python/Lib/site-packages/pip/_internal/utils/unpacking.py +30 -4
  317. package/xll/python/Lib/site-packages/pip/_internal/utils/urls.py +5 -2
  318. package/xll/python/Lib/site-packages/pip/_internal/utils/virtualenv.py +2 -36
  319. package/xll/python/Lib/site-packages/pip/_internal/vcs/git.py +8 -5
  320. package/xll/python/Lib/site-packages/pip/_internal/vcs/versioncontrol.py +1 -2
  321. package/xll/python/Lib/site-packages/pip/_internal/wheel_builder.py +5 -1
  322. package/xll/python/Lib/site-packages/pip/_vendor/bom.cdx.json +231 -0
  323. package/xll/python/Lib/site-packages/pip/_vendor/certifi/__init__.py +1 -1
  324. package/xll/python/Lib/site-packages/pip/_vendor/certifi/cacert.pem +0 -631
  325. package/xll/python/Lib/site-packages/pip/_vendor/distlib/__init__.py +1 -1
  326. package/xll/python/Lib/site-packages/pip/_vendor/distlib/compat.py +34 -64
  327. package/xll/python/Lib/site-packages/pip/_vendor/distlib/resources.py +28 -14
  328. package/xll/python/Lib/site-packages/pip/_vendor/distlib/scripts.py +7 -3
  329. package/xll/python/Lib/site-packages/pip/_vendor/distlib/util.py +48 -11
  330. package/xll/python/Lib/site-packages/pip/_vendor/idna/LICENSE.md +1 -1
  331. package/xll/python/Lib/site-packages/pip/_vendor/idna/__main__.py +6 -0
  332. package/xll/python/Lib/site-packages/pip/_vendor/idna/cli.py +128 -0
  333. package/xll/python/Lib/site-packages/pip/_vendor/idna/codec.py +50 -13
  334. package/xll/python/Lib/site-packages/pip/_vendor/idna/compat.py +26 -0
  335. package/xll/python/Lib/site-packages/pip/_vendor/idna/core.py +352 -141
  336. package/xll/python/Lib/site-packages/pip/_vendor/idna/idnadata.py +571 -2983
  337. package/xll/python/Lib/site-packages/pip/_vendor/idna/intranges.py +5 -7
  338. package/xll/python/Lib/site-packages/pip/_vendor/idna/package_data.py +1 -1
  339. package/xll/python/Lib/site-packages/pip/_vendor/idna/uts46data.py +16887 -8832
  340. package/xll/python/Lib/site-packages/pip/_vendor/platformdirs/__init__.py +352 -66
  341. package/xll/python/Lib/site-packages/pip/_vendor/platformdirs/__main__.py +11 -0
  342. package/xll/python/Lib/site-packages/pip/_vendor/platformdirs/_xdg.py +171 -0
  343. package/xll/python/Lib/site-packages/pip/_vendor/platformdirs/android.py +92 -41
  344. package/xll/python/Lib/site-packages/pip/_vendor/platformdirs/api.py +213 -49
  345. package/xll/python/Lib/site-packages/pip/_vendor/platformdirs/macos.py +128 -51
  346. package/xll/python/Lib/site-packages/pip/_vendor/platformdirs/unix.py +176 -127
  347. package/xll/python/Lib/site-packages/pip/_vendor/platformdirs/version.py +8 -18
  348. package/xll/python/Lib/site-packages/pip/_vendor/platformdirs/windows.py +185 -68
  349. package/xll/python/Lib/site-packages/pip/_vendor/pygments/__init__.py +2 -2
  350. package/xll/python/Lib/site-packages/pip/_vendor/pygments/__main__.py +1 -1
  351. package/xll/python/Lib/site-packages/pip/_vendor/pygments/console.py +1 -1
  352. package/xll/python/Lib/site-packages/pip/_vendor/pygments/filter.py +1 -1
  353. package/xll/python/Lib/site-packages/pip/_vendor/pygments/filters/__init__.py +10 -8
  354. package/xll/python/Lib/site-packages/pip/_vendor/pygments/formatter.py +1 -1
  355. package/xll/python/Lib/site-packages/pip/_vendor/pygments/formatters/__init__.py +1 -1
  356. package/xll/python/Lib/site-packages/pip/_vendor/pygments/lexer.py +7 -5
  357. package/xll/python/Lib/site-packages/pip/_vendor/pygments/lexers/__init__.py +1 -1
  358. package/xll/python/Lib/site-packages/pip/_vendor/pygments/lexers/_mapping.py +9 -8
  359. package/xll/python/Lib/site-packages/pip/_vendor/pygments/lexers/python.py +35 -32
  360. package/xll/python/Lib/site-packages/pip/_vendor/pygments/modeline.py +1 -1
  361. package/xll/python/Lib/site-packages/pip/_vendor/pygments/plugin.py +3 -1
  362. package/xll/python/Lib/site-packages/pip/_vendor/pygments/regexopt.py +13 -2
  363. package/xll/python/Lib/site-packages/pip/_vendor/pygments/scanner.py +1 -1
  364. package/xll/python/Lib/site-packages/pip/_vendor/pygments/sphinxext.py +1 -1
  365. package/xll/python/Lib/site-packages/pip/_vendor/pygments/style.py +1 -1
  366. package/xll/python/Lib/site-packages/pip/_vendor/pygments/styles/__init__.py +1 -1
  367. package/xll/python/Lib/site-packages/pip/_vendor/pygments/token.py +1 -1
  368. package/xll/python/Lib/site-packages/pip/_vendor/pygments/unistring.py +1 -1
  369. package/xll/python/Lib/site-packages/pip/_vendor/pygments/util.py +2 -2
  370. package/xll/python/Lib/site-packages/pip/_vendor/requests/__init__.py +52 -16
  371. package/xll/python/Lib/site-packages/pip/_vendor/requests/__version__.py +2 -2
  372. package/xll/python/Lib/site-packages/pip/_vendor/requests/_internal_utils.py +2 -2
  373. package/xll/python/Lib/site-packages/pip/_vendor/requests/_types.py +183 -0
  374. package/xll/python/Lib/site-packages/pip/_vendor/requests/adapters.py +101 -50
  375. package/xll/python/Lib/site-packages/pip/_vendor/requests/api.py +31 -8
  376. package/xll/python/Lib/site-packages/pip/_vendor/requests/auth.py +80 -40
  377. package/xll/python/Lib/site-packages/pip/_vendor/requests/compat.py +9 -3
  378. package/xll/python/Lib/site-packages/pip/_vendor/requests/cookies.py +143 -79
  379. package/xll/python/Lib/site-packages/pip/_vendor/requests/exceptions.py +15 -5
  380. package/xll/python/Lib/site-packages/pip/_vendor/requests/help.py +13 -10
  381. package/xll/python/Lib/site-packages/pip/_vendor/requests/hooks.py +23 -9
  382. package/xll/python/Lib/site-packages/pip/_vendor/requests/models.py +265 -126
  383. package/xll/python/Lib/site-packages/pip/_vendor/requests/py.typed +0 -0
  384. package/xll/python/Lib/site-packages/pip/_vendor/requests/sessions.py +164 -78
  385. package/xll/python/Lib/site-packages/pip/_vendor/requests/status_codes.py +2 -2
  386. package/xll/python/Lib/site-packages/pip/_vendor/requests/structures.py +51 -20
  387. package/xll/python/Lib/site-packages/pip/_vendor/requests/utils.py +161 -89
  388. package/xll/python/Lib/site-packages/pip/_vendor/tomli/__init__.py +1 -1
  389. package/xll/python/Lib/site-packages/pip/_vendor/tomli/_parser.py +9 -4
  390. package/xll/python/Lib/site-packages/pip/_vendor/tomli/_re.py +13 -9
  391. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/_base_connection.py +3 -1
  392. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/_collections.py +0 -1
  393. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/_version.py +8 -18
  394. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/connection.py +4 -4
  395. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py +23 -10
  396. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/contrib/emscripten/response.py +5 -1
  397. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py +7 -8
  398. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py +1 -1
  399. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/exceptions.py +2 -2
  400. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/fields.py +14 -14
  401. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py +8 -6
  402. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/response.py +35 -22
  403. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/util/retry.py +8 -0
  404. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py +11 -61
  405. package/xll/python/Lib/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py +5 -11
  406. package/xll/python/Lib/site-packages/pip/_vendor/vendor.txt +8 -8
  407. package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/METADATA +3 -2
  408. package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/RECORD +208 -188
  409. package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/AUTHORS.txt +20 -0
  410. package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/idna/LICENSE.md +1 -1
  411. package/xll/python/Lib/site-packages/pywin32-311.dist-info/RECORD +2 -2
  412. package/xll/python/Scripts/httpx.exe +0 -0
  413. package/xll/python/Scripts/idna.exe +0 -0
  414. package/xll/python/Scripts/pip.exe +0 -0
  415. package/xll/python/Scripts/pip3.13.exe +0 -0
  416. package/xll/python/Scripts/pip3.exe +0 -0
  417. package/xll/python/Scripts/pywin32_postinstall.exe +0 -0
  418. package/xll/python/Scripts/pywin32_testall.exe +0 -0
  419. package/dist/app/collab/awareness.d.ts +0 -32
  420. package/dist/app/collab/awareness.js +0 -52
  421. package/dist/app/collab/collab-client.d.ts +0 -37
  422. package/dist/app/collab/collab-client.js +0 -208
  423. package/dist/app/collab/collab-manager.d.ts +0 -91
  424. package/dist/app/collab/collab-manager.js +0 -218
  425. package/dist/app/collab/collab-sync.d.ts +0 -62
  426. package/dist/app/collab/collab-sync.js +0 -215
  427. package/dist/app/collab/index.d.ts +0 -9
  428. package/dist/app/collab/index.js +0 -6
  429. package/dist/app/collab/super-fight.d.ts +0 -90
  430. package/dist/app/collab/super-fight.js +0 -310
  431. package/dist/app/collab/sync-bridge.d.ts +0 -35
  432. package/dist/app/collab/sync-bridge.js +0 -109
  433. package/dist/app/collab/types.d.ts +0 -75
  434. package/dist/app/collab/types.js +0 -5
  435. package/dist/embedded-agent/ai-content.d.ts +0 -52
  436. package/dist/embedded-agent/ai-content.js +0 -101
  437. package/dist/embedded-agent/host-tools/shared/llm-stream.d.ts +0 -31
  438. package/dist/embedded-agent/host-tools/shared/llm-stream.js +0 -136
  439. package/dist/embedded-agent/model-call-orchestrator.d.ts +0 -22
  440. package/dist/embedded-agent/model-call-orchestrator.js +0 -22
  441. package/dist/embedded-agent/stream-errors.d.ts +0 -10
  442. package/dist/embedded-agent/stream-errors.js +0 -73
  443. package/dist/embedded-agent/stream-message-state.d.ts +0 -14
  444. package/dist/embedded-agent/stream-message-state.js +0 -271
  445. package/dist/embedded-agent/stream-sse.d.ts +0 -78
  446. package/dist/embedded-agent/stream-sse.js +0 -73
  447. package/dist/shell/approvals/task-spawn-approval.d.ts +0 -20
  448. package/dist/shell/approvals/task-spawn-approval.js +0 -93
  449. package/dist/shell/interactive/collab-command.d.ts +0 -9
  450. package/dist/shell/interactive/collab-command.js +0 -115
  451. package/dist/shell/interactive/super-fight-ui.d.ts +0 -80
  452. package/dist/shell/interactive/super-fight-ui.js +0 -484
  453. package/dist/shell/status/collab-status.d.ts +0 -18
  454. package/dist/shell/status/collab-status.js +0 -46
  455. package/xll/python/Lib/site-packages/pip/_internal/build_env.py +0 -606
  456. /package/dist/{tui → autocomplete}/skill-autocomplete.js +0 -0
  457. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/INSTALLER +0 -0
  458. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/REQUESTED +0 -0
  459. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/WHEEL +0 -0
  460. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/entry_points.txt +0 -0
  461. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/LICENSE.txt +0 -0
  462. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +0 -0
  463. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/certifi/LICENSE +0 -0
  464. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/distlib/LICENSE.txt +0 -0
  465. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/distro/LICENSE +0 -0
  466. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/msgpack/COPYING +0 -0
  467. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE +0 -0
  468. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +0 -0
  469. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.BSD +0 -0
  470. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/pkg_resources/LICENSE +0 -0
  471. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/platformdirs/LICENSE +0 -0
  472. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/pygments/LICENSE +0 -0
  473. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +0 -0
  474. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/requests/LICENSE +0 -0
  475. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/resolvelib/LICENSE +0 -0
  476. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/rich/LICENSE +0 -0
  477. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/tomli/LICENSE +0 -0
  478. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/tomli_w/LICENSE +0 -0
  479. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/truststore/LICENSE +0 -0
  480. /package/xll/python/Lib/site-packages/{pip-26.1.2.dist-info → pip-26.2.dist-info}/licenses/src/pip/_vendor/urllib3/LICENSE.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  import bisect
2
2
  import re
3
3
  import unicodedata
4
+ import warnings
4
5
  from typing import Optional, Union
5
6
 
6
7
  from . import idnadata
@@ -8,38 +9,49 @@ from .intranges import intranges_contain
8
9
 
9
10
  _virama_combining_class = 9
10
11
  _alabel_prefix = b"xn--"
12
+ _max_input_length = 1024
11
13
  _unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]")
12
14
 
13
15
 
16
+ # Bidi category sets from RFC 5893, hoisted out of the per-codepoint loop
17
+ _bidi_rtl_first = frozenset({"R", "AL"})
18
+ _bidi_rtl_categories = frozenset({"R", "AL", "AN"})
19
+ _bidi_rtl_allowed = frozenset({"R", "AL", "AN", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"})
20
+ _bidi_rtl_valid_ending = frozenset({"R", "AL", "EN", "AN"})
21
+ _bidi_rtl_numeric = frozenset({"AN", "EN"})
22
+ _bidi_ltr_allowed = frozenset({"L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"})
23
+ _bidi_ltr_valid_ending = frozenset({"L", "EN"})
24
+ _bidi_joiner_l_or_d = frozenset({"L", "D"})
25
+ _bidi_joiner_r_or_d = frozenset({"R", "D"})
26
+
27
+
28
+ def _joining_type(cp: int) -> Optional[str]:
29
+ for jt, ranges in idnadata.joining_types.items():
30
+ if intranges_contain(cp, ranges):
31
+ return jt
32
+ return None
33
+
34
+
14
35
  class IDNAError(UnicodeError):
15
36
  """Base exception for all IDNA-encoding related problems"""
16
37
 
17
- pass
18
-
19
38
 
20
39
  class IDNABidiError(IDNAError):
21
40
  """Exception when bidirectional requirements are not satisfied"""
22
41
 
23
- pass
24
-
25
42
 
26
43
  class InvalidCodepoint(IDNAError):
27
44
  """Exception when a disallowed or unallocated codepoint is used"""
28
45
 
29
- pass
30
-
31
46
 
32
47
  class InvalidCodepointContext(IDNAError):
33
48
  """Exception when the codepoint is not valid in the context it is used"""
34
49
 
35
- pass
36
-
37
50
 
38
51
  def _combining_class(cp: int) -> int:
39
52
  v = unicodedata.combining(chr(cp))
40
- if v == 0:
41
- if not unicodedata.name(chr(cp)):
42
- raise ValueError("Unknown character in unicodedata")
53
+ if v == 0 and not unicodedata.name(chr(cp)):
54
+ raise ValueError("Unknown character in unicodedata")
43
55
  return v
44
56
 
45
57
 
@@ -52,42 +64,76 @@ def _punycode(s: str) -> bytes:
52
64
 
53
65
 
54
66
  def _unot(s: int) -> str:
55
- return "U+{:04X}".format(s)
67
+ return f"U+{s:04X}"
56
68
 
57
69
 
58
70
  def valid_label_length(label: Union[bytes, str]) -> bool:
59
- if len(label) > 63:
60
- return False
61
- return True
71
+ """Check that a label does not exceed the maximum permitted length.
62
72
 
73
+ Per :rfc:`1035` (and :rfc:`5891` §4.2.4) a DNS label must not exceed
74
+ 63 octets. The argument may be either a :class:`str` (a U-label, where
75
+ length is measured in characters) or :class:`bytes` (an A-label, where
76
+ length is measured in octets).
63
77
 
64
- def valid_string_length(label: Union[bytes, str], trailing_dot: bool) -> bool:
65
- if len(label) > (254 if trailing_dot else 253):
66
- return False
67
- return True
78
+ :param label: The label to check.
79
+ :returns: ``True`` if the label is within the length limit, otherwise
80
+ ``False``.
81
+ """
82
+ return len(label) <= 63
83
+
84
+
85
+ def valid_string_length(domain: Union[bytes, str], trailing_dot: bool) -> bool:
86
+ """Check that a full domain name does not exceed the maximum length.
87
+
88
+ Per :rfc:`1035`, a domain name is limited to 253 octets when no trailing
89
+ dot is present, or 254 octets when one is included.
90
+
91
+ :param domain: The full (possibly multi-label) domain name.
92
+ :param trailing_dot: ``True`` if ``domain`` includes a trailing ``.``.
93
+ :returns: ``True`` if the domain is within the length limit, otherwise
94
+ ``False``.
95
+ """
96
+ return len(domain) <= (254 if trailing_dot else 253)
68
97
 
69
98
 
70
99
  def check_bidi(label: str, check_ltr: bool = False) -> bool:
100
+ """Validate the Bidi Rule from :rfc:`5893` for a single label.
101
+
102
+ The Bidi Rule constrains how bidirectional characters (Hebrew, Arabic,
103
+ etc.) may appear within a label. By default the check is only applied
104
+ when the label contains at least one right-to-left character (Unicode
105
+ bidirectional categories ``R``, ``AL``, or ``AN``); set ``check_ltr``
106
+ to ``True`` to apply it to LTR-only labels as well.
107
+
108
+ :param label: The label to validate, as a Unicode string.
109
+ :param check_ltr: If ``True``, apply the rules even when the label
110
+ contains no RTL characters.
111
+ :returns: ``True`` if the label satisfies the Bidi Rule.
112
+ :raises IDNABidiError: If any of Bidi Rule conditions 1-6 are violated,
113
+ or if the directional category of a codepoint cannot be determined.
114
+ """
115
+ if len(label) > _max_input_length:
116
+ raise IDNAError("Label too long")
71
117
  # Bidi rules should only be applied if string contains RTL characters
72
118
  bidi_label = False
73
119
  for idx, cp in enumerate(label, 1):
74
120
  direction = unicodedata.bidirectional(cp)
75
121
  if direction == "":
76
122
  # String likely comes from a newer version of Unicode
77
- raise IDNABidiError("Unknown directionality in label {} at position {}".format(repr(label), idx))
78
- if direction in ["R", "AL", "AN"]:
123
+ raise IDNABidiError(f"Unknown directionality in label {label!r} at position {idx}")
124
+ if direction in _bidi_rtl_categories:
79
125
  bidi_label = True
80
126
  if not bidi_label and not check_ltr:
81
127
  return True
82
128
 
83
129
  # Bidi rule 1
84
130
  direction = unicodedata.bidirectional(label[0])
85
- if direction in ["R", "AL"]:
131
+ if direction in _bidi_rtl_first:
86
132
  rtl = True
87
133
  elif direction == "L":
88
134
  rtl = False
89
135
  else:
90
- raise IDNABidiError("First codepoint in label {} must be directionality L, R or AL".format(repr(label)))
136
+ raise IDNABidiError(f"First codepoint in label {label!r} must be directionality L, R or AL")
91
137
 
92
138
  valid_ending = False
93
139
  number_type: Optional[str] = None
@@ -96,37 +142,25 @@ def check_bidi(label: str, check_ltr: bool = False) -> bool:
96
142
 
97
143
  if rtl:
98
144
  # Bidi rule 2
99
- if direction not in [
100
- "R",
101
- "AL",
102
- "AN",
103
- "EN",
104
- "ES",
105
- "CS",
106
- "ET",
107
- "ON",
108
- "BN",
109
- "NSM",
110
- ]:
111
- raise IDNABidiError("Invalid direction for codepoint at position {} in a right-to-left label".format(idx))
145
+ if direction not in _bidi_rtl_allowed:
146
+ raise IDNABidiError(f"Invalid direction for codepoint at position {idx} in a right-to-left label")
112
147
  # Bidi rule 3
113
- if direction in ["R", "AL", "EN", "AN"]:
148
+ if direction in _bidi_rtl_valid_ending:
114
149
  valid_ending = True
115
150
  elif direction != "NSM":
116
151
  valid_ending = False
117
152
  # Bidi rule 4
118
- if direction in ["AN", "EN"]:
153
+ if direction in _bidi_rtl_numeric:
119
154
  if not number_type:
120
155
  number_type = direction
121
- else:
122
- if number_type != direction:
123
- raise IDNABidiError("Can not mix numeral types in a right-to-left label")
156
+ elif number_type != direction:
157
+ raise IDNABidiError("Can not mix numeral types in a right-to-left label")
124
158
  else:
125
159
  # Bidi rule 5
126
- if direction not in ["L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"]:
127
- raise IDNABidiError("Invalid direction for codepoint at position {} in a left-to-right label".format(idx))
160
+ if direction not in _bidi_ltr_allowed:
161
+ raise IDNABidiError(f"Invalid direction for codepoint at position {idx} in a left-to-right label")
128
162
  # Bidi rule 6
129
- if direction in ["L", "EN"]:
163
+ if direction in _bidi_ltr_valid_ending:
130
164
  valid_ending = True
131
165
  elif direction != "NSM":
132
166
  valid_ending = False
@@ -138,12 +172,31 @@ def check_bidi(label: str, check_ltr: bool = False) -> bool:
138
172
 
139
173
 
140
174
  def check_initial_combiner(label: str) -> bool:
175
+ """Reject labels that begin with a combining mark.
176
+
177
+ Per :rfc:`5891` §4.2.3.2 a label must not start with a character of
178
+ Unicode general category ``M`` (Mark).
179
+
180
+ :param label: The label to check.
181
+ :returns: ``True`` if the first character is not a combining mark.
182
+ :raises IDNAError: If the label begins with a combining character.
183
+ """
141
184
  if unicodedata.category(label[0])[0] == "M":
142
185
  raise IDNAError("Label begins with an illegal combining character")
143
186
  return True
144
187
 
145
188
 
146
189
  def check_hyphen_ok(label: str) -> bool:
190
+ """Validate the hyphen restrictions for a label.
191
+
192
+ Per :rfc:`5891` §4.2.3.1 a label must not start or end with a hyphen
193
+ (``U+002D``), and must not have hyphens in both the third and fourth
194
+ positions (the prefix reserved for A-labels).
195
+
196
+ :param label: The label to check.
197
+ :returns: ``True`` if the hyphen restrictions are satisfied.
198
+ :raises IDNAError: If any of the hyphen restrictions are violated.
199
+ """
147
200
  if label[2:4] == "--":
148
201
  raise IDNAError("Label has disallowed hyphens in 3rd and 4th position")
149
202
  if label[0] == "-" or label[-1] == "-":
@@ -152,74 +205,104 @@ def check_hyphen_ok(label: str) -> bool:
152
205
 
153
206
 
154
207
  def check_nfc(label: str) -> None:
208
+ """Require that a label is in Unicode Normalization Form C.
209
+
210
+ :param label: The label to check.
211
+ :raises IDNAError: If ``label`` differs from its NFC normalisation.
212
+ """
213
+ if len(label) > _max_input_length:
214
+ raise IDNAError("Label too long")
155
215
  if unicodedata.normalize("NFC", label) != label:
156
216
  raise IDNAError("Label must be in Normalization Form C")
157
217
 
158
218
 
159
219
  def valid_contextj(label: str, pos: int) -> bool:
220
+ """Validate the CONTEXTJ rules from :rfc:`5892` Appendix A.
221
+
222
+ These rules govern the contextual use of the joiner codepoints
223
+ ``U+200C`` (ZERO WIDTH NON-JOINER, Appendix A.1) and ``U+200D``
224
+ (ZERO WIDTH JOINER, Appendix A.2) within a label.
225
+
226
+ :param label: The label containing the codepoint.
227
+ :param pos: Index of the joiner codepoint within ``label``.
228
+ :returns: ``True`` if the codepoint at ``pos`` satisfies its CONTEXTJ
229
+ rule, ``False`` otherwise (including when the codepoint at
230
+ ``pos`` is not a recognised joiner).
231
+ :raises ValueError: If an adjacent codepoint has no Unicode name when
232
+ determining its combining class.
233
+ :raises IDNAError: If ``label`` exceeds the defensive input length limit.
234
+ """
235
+ if len(label) > _max_input_length:
236
+ raise IDNAError("Label too long")
160
237
  cp_value = ord(label[pos])
161
238
 
162
239
  if cp_value == 0x200C:
163
- if pos > 0:
164
- if _combining_class(ord(label[pos - 1])) == _virama_combining_class:
165
- return True
240
+ if pos > 0 and _combining_class(ord(label[pos - 1])) == _virama_combining_class:
241
+ return True
166
242
 
167
243
  ok = False
168
244
  for i in range(pos - 1, -1, -1):
169
- joining_type = idnadata.joining_types.get(ord(label[i]))
170
- if joining_type == ord("T"):
245
+ joining_type = _joining_type(ord(label[i]))
246
+ if joining_type == "T":
171
247
  continue
172
- elif joining_type in [ord("L"), ord("D")]:
248
+ if joining_type in _bidi_joiner_l_or_d:
173
249
  ok = True
174
250
  break
175
- else:
176
- break
251
+ break
177
252
 
178
253
  if not ok:
179
254
  return False
180
255
 
181
256
  ok = False
182
257
  for i in range(pos + 1, len(label)):
183
- joining_type = idnadata.joining_types.get(ord(label[i]))
184
- if joining_type == ord("T"):
258
+ joining_type = _joining_type(ord(label[i]))
259
+ if joining_type == "T":
185
260
  continue
186
- elif joining_type in [ord("R"), ord("D")]:
261
+ if joining_type in _bidi_joiner_r_or_d:
187
262
  ok = True
188
263
  break
189
- else:
190
- break
264
+ break
191
265
  return ok
192
266
 
193
267
  if cp_value == 0x200D:
194
- if pos > 0:
195
- if _combining_class(ord(label[pos - 1])) == _virama_combining_class:
196
- return True
197
- return False
268
+ return pos > 0 and _combining_class(ord(label[pos - 1])) == _virama_combining_class
198
269
 
199
- else:
200
- return False
270
+ return False
201
271
 
202
272
 
203
273
  def valid_contexto(label: str, pos: int, exception: bool = False) -> bool:
274
+ """Validate the CONTEXTO rules from :rfc:`5892` Appendix A.
275
+
276
+ Covers the contextual rules for codepoints such as MIDDLE DOT
277
+ (``U+00B7``), Greek lower numeral sign, Hebrew punctuation, Katakana
278
+ middle dot, and the Arabic-Indic / Extended Arabic-Indic digit ranges.
279
+
280
+ :param label: The label containing the codepoint.
281
+ :param pos: Index of the codepoint within ``label``.
282
+ :param exception: Reserved for forward compatibility; currently unused.
283
+ :returns: ``True`` if the codepoint at ``pos`` satisfies its CONTEXTO
284
+ rule, ``False`` otherwise (including when the codepoint is not a
285
+ recognised CONTEXTO codepoint).
286
+ :raises IDNAError: If ``label`` exceeds the defensive input length limit.
287
+ """
288
+ if len(label) > _max_input_length:
289
+ raise IDNAError("Label too long")
204
290
  cp_value = ord(label[pos])
205
291
 
206
292
  if cp_value == 0x00B7:
207
- if 0 < pos < len(label) - 1:
208
- if ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C:
209
- return True
210
- return False
293
+ return 0 < pos < len(label) - 1 and ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C
211
294
 
212
- elif cp_value == 0x0375:
295
+ if cp_value == 0x0375:
213
296
  if pos < len(label) - 1 and len(label) > 1:
214
297
  return _is_script(label[pos + 1], "Greek")
215
298
  return False
216
299
 
217
- elif cp_value == 0x05F3 or cp_value == 0x05F4:
300
+ if cp_value in {0x05F3, 0x05F4}:
218
301
  if pos > 0:
219
302
  return _is_script(label[pos - 1], "Hebrew")
220
303
  return False
221
304
 
222
- elif cp_value == 0x30FB:
305
+ if cp_value == 0x30FB:
223
306
  for cp in label:
224
307
  if cp == "\u30fb":
225
308
  continue
@@ -227,27 +310,45 @@ def valid_contexto(label: str, pos: int, exception: bool = False) -> bool:
227
310
  return True
228
311
  return False
229
312
 
230
- elif 0x660 <= cp_value <= 0x669:
231
- for cp in label:
232
- if 0x6F0 <= ord(cp) <= 0x06F9:
233
- return False
234
- return True
313
+ if 0x660 <= cp_value <= 0x669:
314
+ return not any(0x6F0 <= ord(cp) <= 0x06F9 for cp in label)
235
315
 
236
- elif 0x6F0 <= cp_value <= 0x6F9:
237
- for cp in label:
238
- if 0x660 <= ord(cp) <= 0x0669:
239
- return False
240
- return True
316
+ if 0x6F0 <= cp_value <= 0x6F9:
317
+ return not any(0x660 <= ord(cp) <= 0x0669 for cp in label)
241
318
 
242
319
  return False
243
320
 
244
321
 
245
322
  def check_label(label: Union[str, bytes, bytearray]) -> None:
323
+ """Run the full set of IDNA 2008 validity checks on a single label.
324
+
325
+ Applies, in order: NFC normalisation (:func:`check_nfc`), hyphen
326
+ restrictions (:func:`check_hyphen_ok`), the no-leading-combiner rule
327
+ (:func:`check_initial_combiner`), per-codepoint validity (PVALID,
328
+ CONTEXTJ, CONTEXTO classes from :rfc:`5892`), and the Bidi Rule
329
+ (:func:`check_bidi`).
330
+
331
+ :param label: The label to validate. ``bytes`` or ``bytearray`` input
332
+ is decoded as UTF-8 first.
333
+ :raises IDNAError: If the label is empty or fails a structural rule.
334
+ :raises InvalidCodepoint: If the label contains a DISALLOWED or
335
+ UNASSIGNED codepoint.
336
+ :raises InvalidCodepointContext: If a CONTEXTJ or CONTEXTO codepoint
337
+ is not valid in its context.
338
+ :raises IDNABidiError: If the Bidi Rule is violated.
339
+ """
340
+ if len(label) > _max_input_length:
341
+ raise IDNAError("Label too long")
246
342
  if isinstance(label, (bytes, bytearray)):
247
343
  label = label.decode("utf-8")
248
344
  if len(label) == 0:
249
345
  raise IDNAError("Empty Label")
250
346
 
347
+ # Reject on domain length rather than label length so support some UTS 46
348
+ # use cases, still reducing processing of label contextual rules
349
+ if not valid_string_length(label, trailing_dot=True):
350
+ raise IDNAError("Label too long")
351
+
251
352
  check_nfc(label)
252
353
  check_hyphen_ok(label)
253
354
  check_initial_combiner(label)
@@ -256,40 +357,47 @@ def check_label(label: Union[str, bytes, bytearray]) -> None:
256
357
  cp_value = ord(cp)
257
358
  if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]):
258
359
  continue
259
- elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]):
360
+ if intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]):
260
361
  try:
261
362
  if not valid_contextj(label, pos):
262
- raise InvalidCodepointContext(
263
- "Joiner {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label))
264
- )
265
- except ValueError:
363
+ raise InvalidCodepointContext(f"Joiner {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}")
364
+ except ValueError as err:
266
365
  raise IDNAError(
267
- "Unknown codepoint adjacent to joiner {} at position {} in {}".format(
268
- _unot(cp_value), pos + 1, repr(label)
269
- )
270
- )
366
+ f"Unknown codepoint adjacent to joiner {_unot(cp_value)} at position {pos + 1} in {label!r}"
367
+ ) from err
271
368
  elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]):
272
369
  if not valid_contexto(label, pos):
273
- raise InvalidCodepointContext(
274
- "Codepoint {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label))
275
- )
370
+ raise InvalidCodepointContext(f"Codepoint {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}")
276
371
  else:
277
- raise InvalidCodepoint(
278
- "Codepoint {} at position {} of {} not allowed".format(_unot(cp_value), pos + 1, repr(label))
279
- )
372
+ raise InvalidCodepoint(f"Codepoint {_unot(cp_value)} at position {pos + 1} of {label!r} not allowed")
280
373
 
281
374
  check_bidi(label)
282
375
 
283
376
 
284
377
  def alabel(label: str) -> bytes:
378
+ """Convert a single U-label into its A-label form.
379
+
380
+ The result is the ASCII-Compatible Encoding (ACE) form per :rfc:`5891`
381
+ §4: the label is validated, Punycode-encoded, and prefixed with
382
+ ``xn--``. Pure ASCII labels that are already valid IDNA labels are
383
+ returned unchanged (as :class:`bytes`).
384
+
385
+ :param label: The label to convert, as a Unicode string.
386
+ :returns: The A-label as ASCII-encoded :class:`bytes`.
387
+ :raises IDNAError: If the label is invalid or the resulting A-label
388
+ exceeds 63 octets.
389
+ """
390
+ if len(label) > _max_input_length:
391
+ raise IDNAError("Label too long")
285
392
  try:
286
393
  label_bytes = label.encode("ascii")
394
+ except UnicodeEncodeError:
395
+ pass
396
+ else:
287
397
  ulabel(label_bytes)
288
398
  if not valid_label_length(label_bytes):
289
399
  raise IDNAError("Label too long")
290
400
  return label_bytes
291
- except UnicodeEncodeError:
292
- pass
293
401
 
294
402
  check_label(label)
295
403
  label_bytes = _alabel_prefix + _punycode(label)
@@ -301,6 +409,20 @@ def alabel(label: str) -> bytes:
301
409
 
302
410
 
303
411
  def ulabel(label: Union[str, bytes, bytearray]) -> str:
412
+ """Convert a single A-label into its U-label form.
413
+
414
+ Performs the inverse of :func:`alabel`: an ``xn--``-prefixed label is
415
+ Punycode-decoded and validated. Labels that are already Unicode (or
416
+ plain ASCII without the ACE prefix) are validated and returned as a
417
+ Unicode string.
418
+
419
+ :param label: The label to convert. ``bytes`` or ``bytearray`` input
420
+ is treated as ASCII.
421
+ :returns: The U-label as a Unicode string.
422
+ :raises IDNAError: If the label is malformed or fails validation.
423
+ """
424
+ if len(label) > _max_input_length:
425
+ raise IDNAError("Label too long")
304
426
  if not isinstance(label, (bytes, bytearray)):
305
427
  try:
306
428
  label_bytes = label.encode("ascii")
@@ -315,7 +437,7 @@ def ulabel(label: Union[str, bytes, bytearray]) -> str:
315
437
  label_bytes = label_bytes[len(_alabel_prefix) :]
316
438
  if not label_bytes:
317
439
  raise IDNAError("Malformed A-label, no Punycode eligible content found")
318
- if label_bytes.decode("ascii")[-1] == "-":
440
+ if label_bytes.endswith(b"-"):
319
441
  raise IDNAError("A-label must not end with a hyphen")
320
442
  else:
321
443
  check_label(label_bytes)
@@ -323,42 +445,64 @@ def ulabel(label: Union[str, bytes, bytearray]) -> str:
323
445
 
324
446
  try:
325
447
  label = label_bytes.decode("punycode")
326
- except UnicodeError:
327
- raise IDNAError("Invalid A-label")
448
+ except UnicodeError as err:
449
+ raise IDNAError("Invalid A-label") from err
328
450
  check_label(label)
329
451
  return label
330
452
 
331
453
 
332
454
  def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str:
333
- """Re-map the characters in the string according to UTS46 processing."""
334
- from .uts46data import uts46data
455
+ """Apply the UTS #46 character mapping to a domain string.
456
+
457
+ Implements the mapping table from `UTS #46 §4
458
+ <https://www.unicode.org/reports/tr46/>`_: each character is kept,
459
+ replaced, or rejected based on its status (``V``, ``M``, ``D``, ``3``,
460
+ ``I``). The result is returned in Normalisation Form C.
461
+
462
+ :param domain: The full domain name to remap.
463
+ :param std3_rules: If ``True``, apply the stricter STD3 ASCII rules
464
+ (status ``3`` codepoints raise instead of being kept or mapped).
465
+ :param transitional: If ``True``, use transitional processing (status
466
+ ``D`` codepoints are mapped instead of kept). Transitional
467
+ processing has been removed from UTS #46 and this option is
468
+ retained only for backwards compatibility.
469
+ :returns: The remapped domain, in Normalisation Form C.
470
+ :raises InvalidCodepoint: If the domain contains a disallowed
471
+ codepoint under the chosen rules.
472
+ :raises IDNAError: If ``domain`` exceeds the defensive input length limit.
473
+ """
474
+ if len(domain) > _max_input_length:
475
+ raise IDNAError("Domain too long")
476
+ from .uts46data import uts46_replacements, uts46_starts, uts46_statuses
335
477
 
336
478
  output = ""
337
479
 
338
480
  for pos, char in enumerate(domain):
339
481
  code_point = ord(char)
340
- try:
341
- uts46row = uts46data[code_point if code_point < 256 else bisect.bisect_left(uts46data, (code_point, "Z")) - 1]
342
- status = uts46row[1]
343
- replacement: Optional[str] = None
344
- if len(uts46row) == 3:
345
- replacement = uts46row[2]
346
- if (
347
- status == "V"
348
- or (status == "D" and not transitional)
349
- or (status == "3" and not std3_rules and replacement is None)
350
- ):
351
- output += char
352
- elif replacement is not None and (
353
- status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional)
354
- ):
355
- output += replacement
356
- elif status != "I":
357
- raise IndexError()
358
- except IndexError:
359
- raise InvalidCodepoint(
360
- "Codepoint {} not allowed at position {} in {}".format(_unot(code_point), pos + 1, repr(domain))
361
- )
482
+ i = code_point if code_point < 256 else bisect.bisect_right(uts46_starts, code_point) - 1
483
+ status = chr(uts46_statuses[i])
484
+ replacement: Optional[str] = uts46_replacements[i]
485
+
486
+ # UTS #46 §4: V is always valid, D is deviation (kept unless transitional),
487
+ # 3 is disallowed-STD3 (kept unmapped if std3_rules is off and no mapping).
488
+ keep_as_is = (
489
+ status == "V" or (status == "D" and not transitional) or (status == "3" and not std3_rules and replacement is None)
490
+ )
491
+ # M is mapped, 3-with-replacement and transitional D fall through to the
492
+ # same replacement output path.
493
+ use_replacement = replacement is not None and (
494
+ status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional)
495
+ )
496
+
497
+ if keep_as_is:
498
+ output += char
499
+ elif use_replacement:
500
+ assert replacement is not None # narrowed by use_replacement
501
+ output += replacement
502
+ elif status == "I":
503
+ continue
504
+ else:
505
+ raise InvalidCodepoint(f"Codepoint {_unot(code_point)} not allowed at position {pos + 1} in {domain!r}")
362
506
 
363
507
  return unicodedata.normalize("NFC", output)
364
508
 
@@ -370,19 +514,52 @@ def encode(
370
514
  std3_rules: bool = False,
371
515
  transitional: bool = False,
372
516
  ) -> bytes:
517
+ """Encode a Unicode domain name into its ASCII (A-label) form.
518
+
519
+ Splits the input on label separators (only ``U+002E`` if ``strict`` is
520
+ set; otherwise also IDEOGRAPHIC FULL STOP ``U+3002``, FULLWIDTH FULL
521
+ STOP ``U+FF0E``, and HALFWIDTH IDEOGRAPHIC FULL STOP ``U+FF61``),
522
+ encodes each label with :func:`alabel`, and rejoins them with ``.``.
523
+ Optionally pre-processes the input through :func:`uts46_remap`.
524
+
525
+ :param s: The domain name to encode.
526
+ :param strict: If ``True``, only ``U+002E`` is recognised as a label
527
+ separator.
528
+ :param uts46: If ``True``, apply UTS #46 mapping before encoding.
529
+ :param std3_rules: Forwarded to :func:`uts46_remap` when ``uts46`` is
530
+ ``True``.
531
+ :param transitional: Forwarded to :func:`uts46_remap` when ``uts46``
532
+ is ``True``. Deprecated: emits a :class:`DeprecationWarning` and
533
+ will be removed in a future version.
534
+ :returns: The encoded domain as ASCII :class:`bytes`.
535
+ :raises IDNAError: If the domain is empty, contains an invalid label,
536
+ or exceeds the maximum domain length.
537
+ """
538
+ if transitional:
539
+ warnings.warn(
540
+ "Transitional processing has been removed from UTS #46. "
541
+ "The transitional argument will be removed in a future version.",
542
+ DeprecationWarning,
543
+ stacklevel=2,
544
+ )
373
545
  if not isinstance(s, str):
374
546
  try:
375
547
  s = str(s, "ascii")
376
- except UnicodeDecodeError:
377
- raise IDNAError("should pass a unicode string to the function rather than a byte string.")
548
+ except (UnicodeDecodeError, TypeError) as err:
549
+ raise IDNAError("should pass a unicode string to the function rather than a byte string.") from err
550
+ if len(s) > _max_input_length:
551
+ raise IDNAError("Domain too long")
378
552
  if uts46:
379
553
  s = uts46_remap(s, std3_rules, transitional)
554
+
555
+ # Reject inputs that exceed the maximum DNS domain length up-front
556
+ # to avoid expensive computation on long inputs.
557
+ if not valid_string_length(s, trailing_dot=True):
558
+ raise IDNAError("Domain too long")
559
+
380
560
  trailing_dot = False
381
561
  result = []
382
- if strict:
383
- labels = s.split(".")
384
- else:
385
- labels = _unicode_dots_re.split(s)
562
+ labels = s.split(".") if strict else _unicode_dots_re.split(s)
386
563
  if not labels or labels == [""]:
387
564
  raise IDNAError("Empty domain")
388
565
  if labels[-1] == "":
@@ -407,29 +584,63 @@ def decode(
407
584
  strict: bool = False,
408
585
  uts46: bool = False,
409
586
  std3_rules: bool = False,
587
+ display: bool = False,
410
588
  ) -> str:
411
- try:
412
- if not isinstance(s, str):
589
+ """Decode an A-label-encoded domain name back to Unicode.
590
+
591
+ Splits the input on label separators (see :func:`encode` for the
592
+ rules), decodes each label with :func:`ulabel`, and rejoins them
593
+ with ``.``. Optionally pre-processes the input through
594
+ :func:`uts46_remap`.
595
+
596
+ :param s: The domain name to decode.
597
+ :param strict: If ``True``, only ``U+002E`` is recognised as a label
598
+ separator.
599
+ :param uts46: If ``True``, apply UTS #46 mapping before decoding.
600
+ :param std3_rules: Forwarded to :func:`uts46_remap` when ``uts46`` is
601
+ ``True``.
602
+ :param display: If ``True``, any ``xn--`` label that fails IDNA
603
+ validation is passed through unchanged (lowercased) rather than
604
+ aborting the whole call. Intended for "decode for display"
605
+ consumers (e.g. URL libraries, HTTP clients) that want to show
606
+ the user the label as it appears on the wire when it cannot be
607
+ rendered as Unicode. Matches the per-label recovery prescribed
608
+ by UTS #46 §4 and the WHATWG URL "domain to Unicode" algorithm.
609
+ :returns: The decoded domain as a Unicode string.
610
+ :raises IDNAError: If the input is not valid ASCII, contains an
611
+ invalid label, or is empty.
612
+ """
613
+ if not isinstance(s, str):
614
+ try:
413
615
  s = str(s, "ascii")
414
- except UnicodeDecodeError:
415
- raise IDNAError("Invalid ASCII in A-label")
616
+ except (UnicodeDecodeError, TypeError) as err:
617
+ raise IDNAError("Invalid ASCII in A-label") from err
618
+ if len(s) > _max_input_length:
619
+ raise IDNAError("Domain too long")
416
620
  if uts46:
417
621
  s = uts46_remap(s, std3_rules, False)
622
+ # Reject inputs that exceed the maximum DNS domain length up-front
623
+ # to avoid expensive computation on long inputs.
624
+ if not valid_string_length(s, trailing_dot=True):
625
+ raise IDNAError("Domain too long")
418
626
  trailing_dot = False
419
627
  result = []
420
- if not strict:
421
- labels = _unicode_dots_re.split(s)
422
- else:
423
- labels = s.split(".")
628
+ labels = s.split(".") if strict else _unicode_dots_re.split(s)
424
629
  if not labels or labels == [""]:
425
630
  raise IDNAError("Empty domain")
426
631
  if not labels[-1]:
427
632
  del labels[-1]
428
633
  trailing_dot = True
429
634
  for label in labels:
430
- s = ulabel(label)
431
- if s:
432
- result.append(s)
635
+ try:
636
+ u = ulabel(label)
637
+ except IDNAError:
638
+ if display and label[:4].lower() == "xn--":
639
+ u = label.lower()
640
+ else:
641
+ raise
642
+ if u:
643
+ result.append(u)
433
644
  else:
434
645
  raise IDNAError("Empty label")
435
646
  if trailing_dot: