xlwings-server 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. xlwings_server/.env.template +145 -0
  2. xlwings_server/__init__.py +12 -0
  3. xlwings_server/_version.py +34 -0
  4. xlwings_server/auth/__init__.py +0 -0
  5. xlwings_server/auth/custom/__init__.py +26 -0
  6. xlwings_server/auth/entraid/__init__.py +131 -0
  7. xlwings_server/auth/entraid/jwks.py +10 -0
  8. xlwings_server/azure_functions_templates/.funcignore +28 -0
  9. xlwings_server/azure_functions_templates/function_app.py +28 -0
  10. xlwings_server/azure_functions_templates/host.json +22 -0
  11. xlwings_server/azure_functions_templates/local.settings.json +8 -0
  12. xlwings_server/build_utils/__init__.py +9 -0
  13. xlwings_server/build_utils/static_file_hasher.py +212 -0
  14. xlwings_server/cli.py +1592 -0
  15. xlwings_server/config.py +228 -0
  16. xlwings_server/custom_functions/__init__.py +8 -0
  17. xlwings_server/custom_functions/examples.py +177 -0
  18. xlwings_server/custom_scripts/__init__.py +8 -0
  19. xlwings_server/custom_scripts/examples.py +94 -0
  20. xlwings_server/databases.py +19 -0
  21. xlwings_server/dependencies.py +126 -0
  22. xlwings_server/docker_templates/.dockerignore +15 -0
  23. xlwings_server/docker_templates/Dockerfile +60 -0
  24. xlwings_server/docker_templates/docker-compose.yaml +32 -0
  25. xlwings_server/hotreload.py +59 -0
  26. xlwings_server/main.py +242 -0
  27. xlwings_server/models/__init__.py +14 -0
  28. xlwings_server/models/user.py +53 -0
  29. xlwings_server/object_handles.py +142 -0
  30. xlwings_server/routers/__init__.py +0 -0
  31. xlwings_server/routers/manifest.py +82 -0
  32. xlwings_server/routers/root.py +16 -0
  33. xlwings_server/routers/socketio.py +69 -0
  34. xlwings_server/routers/taskpane.py +12 -0
  35. xlwings_server/routers/xlwings.py +197 -0
  36. xlwings_server/security_headers.json +53 -0
  37. xlwings_server/serializers/__init__.py +25 -0
  38. xlwings_server/serializers/default_serializer.py +19 -0
  39. xlwings_server/serializers/dictionary_serializer.py +25 -0
  40. xlwings_server/serializers/framework.py +50 -0
  41. xlwings_server/serializers/numpy_serializer.py +26 -0
  42. xlwings_server/serializers/pandas_serializer.py +95 -0
  43. xlwings_server/static/css/core.css +28 -0
  44. xlwings_server/static/css/style.css +0 -0
  45. xlwings_server/static/images/favicon.png +0 -0
  46. xlwings_server/static/images/xlwings-16.png +0 -0
  47. xlwings_server/static/images/xlwings-32.png +0 -0
  48. xlwings_server/static/images/xlwings-64.png +0 -0
  49. xlwings_server/static/images/xlwings-80.png +0 -0
  50. xlwings_server/static/js/auth.js +13 -0
  51. xlwings_server/static/js/config.js +4 -0
  52. xlwings_server/static/js/core/alpinejs-csp-boilerplate.js +11 -0
  53. xlwings_server/static/js/core/bootstrap-customizations.js +7 -0
  54. xlwings_server/static/js/core/custom-functions-code.js +296 -0
  55. xlwings_server/static/js/core/examples.js +62 -0
  56. xlwings_server/static/js/core/hotreload.js +3 -0
  57. xlwings_server/static/js/core/htmx-handlers.js +86 -0
  58. xlwings_server/static/js/core/officejs-history-fix-part1.js +3 -0
  59. xlwings_server/static/js/core/officejs-history-fix-part2.js +2 -0
  60. xlwings_server/static/js/core/reload-custom-functions.js +79 -0
  61. xlwings_server/static/js/core/socketio-handlers.js +34 -0
  62. xlwings_server/static/js/core/xlwings-alert.js +22 -0
  63. xlwings_server/static/js/core/xlwingsjs/alert.js +85 -0
  64. xlwings_server/static/js/core/xlwingsjs/auth.js +63 -0
  65. xlwings_server/static/js/core/xlwingsjs/sheet-buttons.js +133 -0
  66. xlwings_server/static/js/core/xlwingsjs/utils.js +119 -0
  67. xlwings_server/static/js/core/xlwingsjs/wasm.js +131 -0
  68. xlwings_server/static/js/core/xlwingsjs/xlwings.js +1060 -0
  69. xlwings_server/static/js/main.js +0 -0
  70. xlwings_server/static/js/ribbon.js +17 -0
  71. xlwings_server/static/vendor/@alpinejs/LICENSE +21 -0
  72. xlwings_server/static/vendor/@alpinejs/csp/dist/cdn.min.js +7 -0
  73. xlwings_server/static/vendor/@microsoft/office-js/LICENSE.md +76 -0
  74. xlwings_server/static/vendor/@microsoft/office-js/dist/af-za/office_strings.js +8 -0
  75. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/agaveerrorux.js +18 -0
  76. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/agavedefaulticon32x32.png +0 -0
  77. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/agavedefaulticon96x96.png +0 -0
  78. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/businessbarclose_16x16x32.png +0 -0
  79. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/dropdownarrow_16x16x32.png +0 -0
  80. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/ellipsis_16x16x32.png +0 -0
  81. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/miniinfoblue_16x16x32.png +0 -0
  82. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/moe_default_icon.png +0 -0
  83. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/moe_status_icons.png +0 -0
  84. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/office.png +0 -0
  85. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/images/refresh_16x16x32.png +0 -0
  86. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/index.html +16 -0
  87. xlwings_server/static/vendor/@microsoft/office-js/dist/agaveerrorux/style/agaveerrorux.css +482 -0
  88. xlwings_server/static/vendor/@microsoft/office-js/dist/am-et/office_strings.js +1 -0
  89. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ae/office_strings.js +8 -0
  90. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-bh/office_strings.js +8 -0
  91. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-dz/office_strings.js +8 -0
  92. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-eg/office_strings.js +8 -0
  93. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-iq/office_strings.js +8 -0
  94. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-jo/office_strings.js +8 -0
  95. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-kw/office_strings.js +8 -0
  96. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-lb/office_strings.js +8 -0
  97. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ly/office_strings.js +8 -0
  98. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ma/office_strings.js +8 -0
  99. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-om/office_strings.js +8 -0
  100. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-qa/office_strings.js +8 -0
  101. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-sa/office_strings.js +1 -0
  102. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-sy/office_strings.js +8 -0
  103. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-tn/office_strings.js +8 -0
  104. xlwings_server/static/vendor/@microsoft/office-js/dist/ar-ye/office_strings.js +8 -0
  105. xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry-2.8.0.min.js +2 -0
  106. xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry-2.9.0.min.js +2 -0
  107. xlwings_server/static/vendor/@microsoft/office-js/dist/ariatelemetry/aria-web-telemetry.js +1 -0
  108. xlwings_server/static/vendor/@microsoft/office-js/dist/az-latn-az/office_strings.js +8 -0
  109. xlwings_server/static/vendor/@microsoft/office-js/dist/be-by/office_strings.js +8 -0
  110. xlwings_server/static/vendor/@microsoft/office-js/dist/bg-bg/office_strings.js +1 -0
  111. xlwings_server/static/vendor/@microsoft/office-js/dist/bn-in/office_strings.js +1 -0
  112. xlwings_server/static/vendor/@microsoft/office-js/dist/bs-latn-ba/office_strings.js +8 -0
  113. xlwings_server/static/vendor/@microsoft/office-js/dist/ca-es/office_strings.js +1 -0
  114. xlwings_server/static/vendor/@microsoft/office-js/dist/cs-cz/office_strings.js +1 -0
  115. xlwings_server/static/vendor/@microsoft/office-js/dist/cy-gb/office_strings.js +1 -0
  116. xlwings_server/static/vendor/@microsoft/office-js/dist/da-dk/office_strings.js +1 -0
  117. xlwings_server/static/vendor/@microsoft/office-js/dist/de-at/office_strings.js +8 -0
  118. xlwings_server/static/vendor/@microsoft/office-js/dist/de-ch/office_strings.js +8 -0
  119. xlwings_server/static/vendor/@microsoft/office-js/dist/de-de/office_strings.js +1 -0
  120. xlwings_server/static/vendor/@microsoft/office-js/dist/de-li/office_strings.js +8 -0
  121. xlwings_server/static/vendor/@microsoft/office-js/dist/de-lu/office_strings.js +8 -0
  122. xlwings_server/static/vendor/@microsoft/office-js/dist/el-gr/office_strings.js +1 -0
  123. xlwings_server/static/vendor/@microsoft/office-js/dist/en-029/office_strings.js +8 -0
  124. xlwings_server/static/vendor/@microsoft/office-js/dist/en-au/office_strings.js +8 -0
  125. xlwings_server/static/vendor/@microsoft/office-js/dist/en-bz/office_strings.js +8 -0
  126. xlwings_server/static/vendor/@microsoft/office-js/dist/en-ca/office_strings.js +8 -0
  127. xlwings_server/static/vendor/@microsoft/office-js/dist/en-gb/office_strings.js +8 -0
  128. xlwings_server/static/vendor/@microsoft/office-js/dist/en-ie/office_strings.js +8 -0
  129. xlwings_server/static/vendor/@microsoft/office-js/dist/en-in/office_strings.js +8 -0
  130. xlwings_server/static/vendor/@microsoft/office-js/dist/en-jm/office_strings.js +8 -0
  131. xlwings_server/static/vendor/@microsoft/office-js/dist/en-my/office_strings.js +8 -0
  132. xlwings_server/static/vendor/@microsoft/office-js/dist/en-nz/office_strings.js +8 -0
  133. xlwings_server/static/vendor/@microsoft/office-js/dist/en-ph/office_strings.js +8 -0
  134. xlwings_server/static/vendor/@microsoft/office-js/dist/en-sg/office_strings.js +8 -0
  135. xlwings_server/static/vendor/@microsoft/office-js/dist/en-tt/office_strings.js +8 -0
  136. xlwings_server/static/vendor/@microsoft/office-js/dist/en-us/office_strings.js +8 -0
  137. xlwings_server/static/vendor/@microsoft/office-js/dist/en-za/office_strings.js +8 -0
  138. xlwings_server/static/vendor/@microsoft/office-js/dist/en-zw/office_strings.js +8 -0
  139. xlwings_server/static/vendor/@microsoft/office-js/dist/es-ar/office_strings.js +8 -0
  140. xlwings_server/static/vendor/@microsoft/office-js/dist/es-bo/office_strings.js +8 -0
  141. xlwings_server/static/vendor/@microsoft/office-js/dist/es-cl/office_strings.js +8 -0
  142. xlwings_server/static/vendor/@microsoft/office-js/dist/es-co/office_strings.js +8 -0
  143. xlwings_server/static/vendor/@microsoft/office-js/dist/es-cr/office_strings.js +8 -0
  144. xlwings_server/static/vendor/@microsoft/office-js/dist/es-do/office_strings.js +8 -0
  145. xlwings_server/static/vendor/@microsoft/office-js/dist/es-ec/office_strings.js +8 -0
  146. xlwings_server/static/vendor/@microsoft/office-js/dist/es-es/office_strings.js +1 -0
  147. xlwings_server/static/vendor/@microsoft/office-js/dist/es-gt/office_strings.js +8 -0
  148. xlwings_server/static/vendor/@microsoft/office-js/dist/es-hn/office_strings.js +8 -0
  149. xlwings_server/static/vendor/@microsoft/office-js/dist/es-mx/office_strings.js +1 -0
  150. xlwings_server/static/vendor/@microsoft/office-js/dist/es-ni/office_strings.js +8 -0
  151. xlwings_server/static/vendor/@microsoft/office-js/dist/es-pa/office_strings.js +8 -0
  152. xlwings_server/static/vendor/@microsoft/office-js/dist/es-pe/office_strings.js +8 -0
  153. xlwings_server/static/vendor/@microsoft/office-js/dist/es-pr/office_strings.js +8 -0
  154. xlwings_server/static/vendor/@microsoft/office-js/dist/es-py/office_strings.js +8 -0
  155. xlwings_server/static/vendor/@microsoft/office-js/dist/es-sv/office_strings.js +8 -0
  156. xlwings_server/static/vendor/@microsoft/office-js/dist/es-us/office_strings.js +8 -0
  157. xlwings_server/static/vendor/@microsoft/office-js/dist/es-uy/office_strings.js +8 -0
  158. xlwings_server/static/vendor/@microsoft/office-js/dist/es-ve/office_strings.js +8 -0
  159. xlwings_server/static/vendor/@microsoft/office-js/dist/es6-promise.js +5 -0
  160. xlwings_server/static/vendor/@microsoft/office-js/dist/et-ee/office_strings.js +1 -0
  161. xlwings_server/static/vendor/@microsoft/office-js/dist/eu-es/office_strings.js +1 -0
  162. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.01.js +11 -0
  163. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.02.js +11 -0
  164. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-15.js +11 -0
  165. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-mac-16.00-core.js +11 -0
  166. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-mac-16.00.js +25 -0
  167. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-web-16.00-core.js +11 -0
  168. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-web-16.00.js +25 -0
  169. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.00.js +19 -0
  170. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.01-core.js +11 -0
  171. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-win32-16.01.js +25 -0
  172. xlwings_server/static/vendor/@microsoft/office-js/dist/excel-winrt-16.00.js +25 -0
  173. xlwings_server/static/vendor/@microsoft/office-js/dist/excelios-15.js +11 -0
  174. xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.01.js +11 -0
  175. xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.02.js +11 -0
  176. xlwings_server/static/vendor/@microsoft/office-js/dist/excelwebapp-15.js +11 -0
  177. xlwings_server/static/vendor/@microsoft/office-js/dist/fa-ir/office_strings.js +1 -0
  178. xlwings_server/static/vendor/@microsoft/office-js/dist/fi-fi/office_strings.js +1 -0
  179. xlwings_server/static/vendor/@microsoft/office-js/dist/fil-ph/office_strings.js +1 -0
  180. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-be/office_strings.js +8 -0
  181. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-ca/office_strings.js +1 -0
  182. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-ch/office_strings.js +8 -0
  183. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-fr/office_strings.js +1 -0
  184. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-lu/office_strings.js +8 -0
  185. xlwings_server/static/vendor/@microsoft/office-js/dist/fr-mc/office_strings.js +8 -0
  186. xlwings_server/static/vendor/@microsoft/office-js/dist/ga-ie/office_strings.js +8 -0
  187. xlwings_server/static/vendor/@microsoft/office-js/dist/gl-es/office_strings.js +1 -0
  188. xlwings_server/static/vendor/@microsoft/office-js/dist/gu-in/office_strings.js +1 -0
  189. xlwings_server/static/vendor/@microsoft/office-js/dist/he-il/office_strings.js +1 -0
  190. xlwings_server/static/vendor/@microsoft/office-js/dist/hi-in/office_strings.js +1 -0
  191. xlwings_server/static/vendor/@microsoft/office-js/dist/hr-ba/office_strings.js +8 -0
  192. xlwings_server/static/vendor/@microsoft/office-js/dist/hr-hr/office_strings.js +1 -0
  193. xlwings_server/static/vendor/@microsoft/office-js/dist/html2canvas.js +8 -0
  194. xlwings_server/static/vendor/@microsoft/office-js/dist/hu-hu/office_strings.js +1 -0
  195. xlwings_server/static/vendor/@microsoft/office-js/dist/hy-am/office_strings.js +8 -0
  196. xlwings_server/static/vendor/@microsoft/office-js/dist/id-id/office_strings.js +1 -0
  197. xlwings_server/static/vendor/@microsoft/office-js/dist/is-is/office_strings.js +1 -0
  198. xlwings_server/static/vendor/@microsoft/office-js/dist/it-ch/office_strings.js +8 -0
  199. xlwings_server/static/vendor/@microsoft/office-js/dist/it-it/office_strings.js +1 -0
  200. xlwings_server/static/vendor/@microsoft/office-js/dist/ja-jp/office_strings.js +1 -0
  201. xlwings_server/static/vendor/@microsoft/office-js/dist/ka-ge/office_strings.js +8 -0
  202. xlwings_server/static/vendor/@microsoft/office-js/dist/kk-kz/office_strings.js +1 -0
  203. xlwings_server/static/vendor/@microsoft/office-js/dist/km-kh/office_strings.js +8 -0
  204. xlwings_server/static/vendor/@microsoft/office-js/dist/kn-in/office_strings.js +1 -0
  205. xlwings_server/static/vendor/@microsoft/office-js/dist/ko-kr/office_strings.js +1 -0
  206. xlwings_server/static/vendor/@microsoft/office-js/dist/lb-lu/office_strings.js +8 -0
  207. xlwings_server/static/vendor/@microsoft/office-js/dist/lo-la/office_strings.js +1 -0
  208. xlwings_server/static/vendor/@microsoft/office-js/dist/lt-lt/office_strings.js +1 -0
  209. xlwings_server/static/vendor/@microsoft/office-js/dist/lv-lv/office_strings.js +1 -0
  210. xlwings_server/static/vendor/@microsoft/office-js/dist/mk-mk/office_strings.js +8 -0
  211. xlwings_server/static/vendor/@microsoft/office-js/dist/ml-in/office_strings.js +1 -0
  212. xlwings_server/static/vendor/@microsoft/office-js/dist/mn-mn/office_strings.js +8 -0
  213. xlwings_server/static/vendor/@microsoft/office-js/dist/mr-in/office_strings.js +1 -0
  214. xlwings_server/static/vendor/@microsoft/office-js/dist/ms-bn/office_strings.js +8 -0
  215. xlwings_server/static/vendor/@microsoft/office-js/dist/ms-my/office_strings.js +1 -0
  216. xlwings_server/static/vendor/@microsoft/office-js/dist/mt-mt/office_strings.js +8 -0
  217. xlwings_server/static/vendor/@microsoft/office-js/dist/nb-no/office_strings.js +1 -0
  218. xlwings_server/static/vendor/@microsoft/office-js/dist/ne-np/office_strings.js +8 -0
  219. xlwings_server/static/vendor/@microsoft/office-js/dist/nl-be/office_strings.js +8 -0
  220. xlwings_server/static/vendor/@microsoft/office-js/dist/nl-nl/office_strings.js +1 -0
  221. xlwings_server/static/vendor/@microsoft/office-js/dist/nn-no/office_strings.js +1 -0
  222. xlwings_server/static/vendor/@microsoft/office-js/dist/o15apptofilemappingtable.js +11 -0
  223. xlwings_server/static/vendor/@microsoft/office-js/dist/office-vsdoc.js +28596 -0
  224. xlwings_server/static/vendor/@microsoft/office-js/dist/office.js +84 -0
  225. xlwings_server/static/vendor/@microsoft/office-js/dist/pl-pl/office_strings.js +1 -0
  226. xlwings_server/static/vendor/@microsoft/office-js/dist/pt-br/office_strings.js +1 -0
  227. xlwings_server/static/vendor/@microsoft/office-js/dist/pt-pt/office_strings.js +1 -0
  228. xlwings_server/static/vendor/@microsoft/office-js/dist/ro-ro/office_strings.js +1 -0
  229. xlwings_server/static/vendor/@microsoft/office-js/dist/ru-ru/office_strings.js +1 -0
  230. xlwings_server/static/vendor/@microsoft/office-js/dist/si-lk/office_strings.js +8 -0
  231. xlwings_server/static/vendor/@microsoft/office-js/dist/sk-sk/office_strings.js +1 -0
  232. xlwings_server/static/vendor/@microsoft/office-js/dist/sl-si/office_strings.js +1 -0
  233. xlwings_server/static/vendor/@microsoft/office-js/dist/sq-al/office_strings.js +8 -0
  234. xlwings_server/static/vendor/@microsoft/office-js/dist/sr-cyrl-cs/office_strings.js +1 -0
  235. xlwings_server/static/vendor/@microsoft/office-js/dist/sr-cyrl-rs/office_strings.js +1 -0
  236. xlwings_server/static/vendor/@microsoft/office-js/dist/sr-latn-cs/office_strings.js +1 -0
  237. xlwings_server/static/vendor/@microsoft/office-js/dist/sr-latn-rs/office_strings.js +1 -0
  238. xlwings_server/static/vendor/@microsoft/office-js/dist/sv-fi/office_strings.js +8 -0
  239. xlwings_server/static/vendor/@microsoft/office-js/dist/sv-se/office_strings.js +1 -0
  240. xlwings_server/static/vendor/@microsoft/office-js/dist/sw-ke/office_strings.js +1 -0
  241. xlwings_server/static/vendor/@microsoft/office-js/dist/ta-in/office_strings.js +1 -0
  242. xlwings_server/static/vendor/@microsoft/office-js/dist/te-in/office_strings.js +1 -0
  243. xlwings_server/static/vendor/@microsoft/office-js/dist/telemetry/oteljs.js +1 -0
  244. xlwings_server/static/vendor/@microsoft/office-js/dist/telemetry/oteljs_agave.js +1 -0
  245. xlwings_server/static/vendor/@microsoft/office-js/dist/th-th/office_strings.js +1 -0
  246. xlwings_server/static/vendor/@microsoft/office-js/dist/tr-tr/office_strings.js +1 -0
  247. xlwings_server/static/vendor/@microsoft/office-js/dist/uk-ua/office_strings.js +1 -0
  248. xlwings_server/static/vendor/@microsoft/office-js/dist/ur-pk/office_strings.js +1 -0
  249. xlwings_server/static/vendor/@microsoft/office-js/dist/vi-vn/office_strings.js +1 -0
  250. xlwings_server/static/vendor/@microsoft/office-js/dist/webauth/webauth.browserauth.js +77 -0
  251. xlwings_server/static/vendor/@microsoft/office-js/dist/webauth/webauth.implicit.js +35 -0
  252. xlwings_server/static/vendor/@microsoft/office-js/dist/zh-cn/office_strings.js +1 -0
  253. xlwings_server/static/vendor/@microsoft/office-js/dist/zh-hk/office_strings.js +8 -0
  254. xlwings_server/static/vendor/@microsoft/office-js/dist/zh-mo/office_strings.js +8 -0
  255. xlwings_server/static/vendor/@microsoft/office-js/dist/zh-sg/office_strings.js +8 -0
  256. xlwings_server/static/vendor/@microsoft/office-js/dist/zh-tw/office_strings.js +1 -0
  257. xlwings_server/static/vendor/axios/dist/axios.min.js +3 -0
  258. xlwings_server/static/vendor/axios/dist/axios.min.js.map +1 -0
  259. xlwings_server/static/vendor/bootstrap/LICENSE +21 -0
  260. xlwings_server/static/vendor/bootstrap/dist/js/bootstrap.bundle.min.js +7 -0
  261. xlwings_server/static/vendor/bootstrap/dist/js/bootstrap.bundle.min.js.map +1 -0
  262. xlwings_server/static/vendor/bootstrap-xlwings/dist/bootstrap-xlwings.min.css +12 -0
  263. xlwings_server/static/vendor/bootstrap-xlwings/dist/bootstrap-xlwings.min.css.map +1 -0
  264. xlwings_server/static/vendor/htmx-ext-head-support/head-support.js +144 -0
  265. xlwings_server/static/vendor/htmx-ext-loading-states/loading-states.js +184 -0
  266. xlwings_server/static/vendor/htmx.org/LICENSE +13 -0
  267. xlwings_server/static/vendor/htmx.org/dist/htmx.min.js +1 -0
  268. xlwings_server/static/vendor/socket.io/LICENSE +22 -0
  269. xlwings_server/static/vendor/socket.io/client-dist/socket.io.min.js +7 -0
  270. xlwings_server/static/vendor/socket.io/client-dist/socket.io.min.js.map +1 -0
  271. xlwings_server/templates/_book.html +8 -0
  272. xlwings_server/templates/alert_base.html +16 -0
  273. xlwings_server/templates/base.html +117 -0
  274. xlwings_server/templates/examples/alpine/README.md +26 -0
  275. xlwings_server/templates/examples/alpine/taskpane.html +47 -0
  276. xlwings_server/templates/examples/auth/README.md +38 -0
  277. xlwings_server/templates/examples/auth/protected.html +8 -0
  278. xlwings_server/templates/examples/auth/public.html +11 -0
  279. xlwings_server/templates/examples/excel_object_model/README.md +49 -0
  280. xlwings_server/templates/examples/excel_object_model/add_name_form.html +27 -0
  281. xlwings_server/templates/examples/hello_world/README.md +9 -0
  282. xlwings_server/templates/examples/hello_world/taskpane_hello.html +24 -0
  283. xlwings_server/templates/examples/htmx_form/README.md +44 -0
  284. xlwings_server/templates/examples/htmx_form/_greeting.html +6 -0
  285. xlwings_server/templates/examples/htmx_form/taskpane_htmx_form.html +21 -0
  286. xlwings_server/templates/examples/live_form_validation/README.md +60 -0
  287. xlwings_server/templates/examples/live_form_validation/add_name_form.html +33 -0
  288. xlwings_server/templates/examples/multi_app/README.md +34 -0
  289. xlwings_server/templates/examples/multi_app/taskpane1.html +7 -0
  290. xlwings_server/templates/examples/multi_app/taskpane2.html +7 -0
  291. xlwings_server/templates/examples/multi_app/taskpane_loader.html +5 -0
  292. xlwings_server/templates/examples/navigation/README.md +28 -0
  293. xlwings_server/templates/examples/navigation/_navigation.html +16 -0
  294. xlwings_server/templates/examples/navigation/taskpane_one.html +8 -0
  295. xlwings_server/templates/examples/navigation/taskpane_three.html +8 -0
  296. xlwings_server/templates/examples/navigation/taskpane_two.html +8 -0
  297. xlwings_server/templates/examples/pictures/README.md +42 -0
  298. xlwings_server/templates/examples/pictures/_picture.html +4 -0
  299. xlwings_server/templates/examples/pictures/taskpane_pictures.html +26 -0
  300. xlwings_server/templates/manifest.xml +155 -0
  301. xlwings_server/templates/taskpane.html +1 -0
  302. xlwings_server/templates/xlwings_alert.html +27 -0
  303. xlwings_server/templates.py +61 -0
  304. xlwings_server/utils.py +32 -0
  305. xlwings_server/wasm/__init__.py +0 -0
  306. xlwings_server/wasm/config.py +24 -0
  307. xlwings_server/wasm/main.py +236 -0
  308. xlwings_server/wasm/requirements.txt +5 -0
  309. xlwings_server-1.1.0.dist-info/METADATA +61 -0
  310. xlwings_server-1.1.0.dist-info/RECORD +313 -0
  311. xlwings_server-1.1.0.dist-info/WHEEL +4 -0
  312. xlwings_server-1.1.0.dist-info/entry_points.txt +2 -0
  313. xlwings_server-1.1.0.dist-info/licenses/LICENSE.md +223 -0
@@ -0,0 +1,145 @@
1
+ # Get a free trial key from https://www.xlwings.org/trial
2
+ XLWINGS_LICENSE_KEY="your_license_key"
3
+
4
+ # Use one of "dev", "qa", "uat", "staging", or "prod". "dev" will activate hotreload for
5
+ # the task pane and other dev-specific features. This setting also takes care of loading
6
+ # the correct ID in the manifest. Except for "prod", the environment name will show up
7
+ # in the add-in name (ProjectName [dev]) and custom functions (NAMESPACE_DEV.MYFUNC)
8
+ XLWINGS_ENVIRONMENT="dev"
9
+
10
+ # Secret key is generated by "xlwings-server init"
11
+ XLWINGS_SECRET_KEY=""
12
+
13
+ # This sets the HTTP response headers recommended by OWASP. Some of the headers have to
14
+ # be less restrictive if XLWINGS_ENABLE_EXCEL_ONLINE=true. Note that this only applies
15
+ # to xlwings Server, not xlwings Wasm.
16
+ # XLWINGS_ADD_SECURITY_HEADERS=true
17
+
18
+ # If you mount xlwings Server on a non-root path (e.g., https://my.domain.com/myapp)
19
+ # via a reverse proxy such as nginx, you need to set this to: "/myapp". You most likely
20
+ # also need to set the XLWINGS_STATIC_URL_PATH="/myapp/static"
21
+ # XLWINGS_APP_PATH=""
22
+
23
+ # To authenticate users, provide the Auth providers as list.
24
+ # E.g, to enable Entra ID SSO auth: XLWINGS_AUTH_PROVIDERS=["entraid"]. If you want
25
+ # to accept multipe authentication methods, you will need the name as
26
+ # Auth-Provider header from the client.
27
+ # XLWINGS_AUTH_PROVIDERS=[]
28
+
29
+ # Enable Single Sign-On (SSO) via Microsoft Entra ID (previously Azure AD)
30
+ # XLWINGS_AUTH_ENTRAID_CLIENT_ID=
31
+ # XLWINGS_AUTH_ENTRAID_TENANT_ID=
32
+
33
+ # RBAC (role-based access control)
34
+ # If your auth provider supports roles, you can list the required roles here.
35
+ # E.g.: XLWINGS_AUTH_REQUIRED_ROLES=["xlwings.user"]
36
+ # XLWINGS_AUTH_REQUIRED_ROLES=[]
37
+
38
+ # Set this to true if you have users from external organizations
39
+ # XLWINGS_AUTH_ENTRAID_MULTITENANT=false
40
+
41
+ # If the add-in will be distributed via Microsoft's public add-in store, set this to
42
+ # true to load office.js via their CDN
43
+ # XLWINGS_CDN_OFFICEJS=false
44
+
45
+ # By default, xlwings Wasm uses the CDN for Pyodide. If you set this to false,
46
+ # Excel won't require any connection to the public Internet as everything will be served
47
+ # from the static file server. This, however, requires you to provide all Python wheels
48
+ # via the app/static/vendors/pyodide folder.
49
+ # XLWINGS_CDN_PYODIDE=true
50
+
51
+ # If you use the Office Scripts integration or custom functions in Excel on the web,
52
+ # you need to set this to ["*"]. Otherwise you should disable it by setting it to [].
53
+ XLWINGS_CORS_ALLOW_ORIGINS=["*"]
54
+
55
+ # Maximum number of retry attempts for failed custom function requests
56
+ # XLWINGS_CUSTOM_FUNCTIONS_MAX_RETRIES=3
57
+
58
+ # HTTP status codes that should trigger retries for custom function requests
59
+ # XLWINGS_CUSTOM_FUNCTIONS_RETRY_CODES=[500, 502, 504]
60
+
61
+ # This allows you to override the date format for custom functions globally.
62
+ # Example: XLWINGS_DATE_FORMAT="m/d/yyyy"
63
+ # XLWINGS_DATE_FORMAT=
64
+
65
+ # Task pane HTML file
66
+ # XLWINGS_TASKPANE_HTML="taskpane.html"
67
+
68
+ # This loads Alpine.js (CSP build) for client-side interactions
69
+ # see: https://alpinejs.dev/advanced/csp
70
+ # XLWINGS_ENABLE_ALPINEJS_CSP=true
71
+
72
+ # This loads Bootstrap with the xlwings theme
73
+ # XLWINGS_ENABLE_BOOTSTRAP=true
74
+
75
+ # Excel on the web requires less strict security headers
76
+ # XLWINGS_ENABLE_EXCEL_ONLINE=true
77
+
78
+ # Enables hot reloading of Office.js add-ins.
79
+ # Requires XLWINGS_ENVIRONMENT="dev" and XLWINGS_ENABLE_SOCKETIO=true.
80
+ # XLWINGS_ENABLE_HOTRELOAD=true
81
+
82
+ # This loads htmx for client-server interaction, see https://htmx.org
83
+ # XLWINGS_ENABLE_HTMX=true
84
+
85
+ # If true, it uses xlwings Wasm instead of xlwings Server. This will use Python
86
+ # via WASM and won't require a Python installation on the backend. This allows you to
87
+ # deploy the add-in to a static file server such as GitHub Pages or Cloudflare Pages.
88
+ # XLWINGS_ENABLE_WASM=false
89
+
90
+ # This loads Socket.io, which is required for streaming custom functions
91
+ # and can be used for realtime interaction on the task pane. Note that this must also
92
+ # be true to enable hotreloading of the taskpane during development.
93
+ # XLWINGS_ENABLE_SOCKETIO=true
94
+
95
+ # Provide custom headers in the form {"header1": "value1", "header2": "value2"}. E.g. for CSP header:
96
+ # {"Content-Security-Policy": "default-src 'self'; form-action 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests; block-all-mixed-content; img-src 'self' data:"}
97
+ # For Excel online, you need to remove "frame-ancestors 'none' and append: "; font-src 'self' https://res-1.cdn.office.net; style-src 'self' 'unsafe-inline'"
98
+ # With XLWINGS_CDN_OFFICEJS=true, you need to append "; script-src 'self' https://appsforoffice.microsoft.com;"
99
+ # XLWINGS_CUSTOM_HEADERS={}
100
+
101
+ # This will be prepended to all custom functions, e.g., "XLWINGS.MYFUNC". Note that
102
+ # if the environment is not "prod", it will append the environment to the namespace,
103
+ # e.g., XLWINGS_DEV to prevent name clashes when you have the same add-in from multiple
104
+ # environments installed.
105
+ # XLWINGS_FUNCTIONS_NAMESPACE="XLWINGS"
106
+
107
+ # In case xlwings Server doesn't manage to get the URL correct under /manifest, you
108
+ # can set the proper hostname here, e.g., mydomain.com (without the https://)
109
+ # XLWINGS_HOSTNAME=
110
+
111
+ # Set the log level to "DEBUG" for more details, but this can log sensitive tokens!
112
+ # XLWINGS_LOG_LEVEL="INFO"
113
+
114
+ # A Redis URL for the caching of object handles. Required for production use.
115
+ # Example: redis://host:6379/0 or rediss://host:6379/0
116
+ # XLWINGS_OBJECT_CACHE_URL=
117
+
118
+ # This setting expects a cron expression that determines when objects that are cached
119
+ # via object handles should be purged from the cache. This requires
120
+ # XLWINGS_OBJECT_CACHE_URL to be configured. By default, the object cache is purged
121
+ # every Saturday at 12:00 PM (UTC).
122
+ # XLWINGS_OBJECT_CACHE_EXPIRE_AT="0 12 * * sat"
123
+
124
+ # If true, cached objects (via object handles) will be compressed when stored in Redis.
125
+ # This requires XLWINGS_OBJECT_CACHE_URL to be configured.
126
+ # XLWINGS_OBJECT_CACHE_ENABLE_COMPRESSION=true
127
+
128
+ # This will determine the name of the add-in. If the environment is not "prod", the name
129
+ # of the environment will be shown like this: Project Name [dev].
130
+ # XLWINGS_PROJECT_NAME=""
131
+
132
+ # Number of seconds after which requests to the backend will timeout
133
+ # XLWINGS_REQUEST_TIMEOUT=300
134
+
135
+ # If you run the Socket.IO server in an own process, you need to configure a message
136
+ # queue, such as Redis/Valkey. E.g.: redis://host:6379/0
137
+ # XLWINGS_SOCKETIO_MESSAGE_QUEUE_URL=
138
+
139
+ # If you run the Socket.IO server in an own process, you need to set this to true for
140
+ # ONLY (!) the app that represents the Socket.IO server.
141
+ # XLWINGS_SOCKETIO_SERVER_APP=false
142
+
143
+ # The absolute path to the static files. If you set XLWINGS_APP_PATH to "/myapp", you
144
+ # likely need to change this to "/myapp/static".
145
+ # XLWINGS_STATIC_URL_PATH="/static"
@@ -0,0 +1,12 @@
1
+ from pathlib import Path
2
+
3
+ try:
4
+ from ._version import __version__
5
+ except ImportError:
6
+ __version__ = "0.0.0"
7
+
8
+ # Store package directory for use throughout the application
9
+ # This is evaluated when the package is first imported, before any sys.path manipulation
10
+ PACKAGE_DIR = Path(__file__).parent.resolve()
11
+
12
+ from .config import settings # noqa: E402
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '1.1.0'
32
+ __version_tuple__ = version_tuple = (1, 1, 0)
33
+
34
+ __commit_id__ = commit_id = None
File without changes
@@ -0,0 +1,26 @@
1
+ import logging
2
+ import secrets
3
+
4
+ from aiocache import cached
5
+ from fastapi import status
6
+ from fastapi.exceptions import HTTPException
7
+
8
+ from xlwings_server.models import User
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ @cached(ttl=60 * 60)
14
+ async def validate_token(token_string: str):
15
+ """token_string has to be delivered via the Authorization header from Excel. The
16
+ Authorization header is set by implementing the globalThis.getAuth() function
17
+ under static/js/auth.js.
18
+ See https://server.xlwings.org/en/latest/auth_providers/#custom-authentication
19
+ """
20
+ if secrets.compare_digest(token_string, "test-token"): # TODO: implement
21
+ return User(id="customid", name="custom user")
22
+ else:
23
+ raise HTTPException(
24
+ status_code=status.HTTP_401_UNAUTHORIZED,
25
+ detail="Custom Auth Error: Couldn't validate token",
26
+ )
@@ -0,0 +1,131 @@
1
+ """
2
+ See https://learn.microsoft.com/en-us/entra/identity-platform/access-tokens
3
+ """
4
+
5
+ import logging
6
+ import re
7
+
8
+ import httpx
9
+ from aiocache import cached
10
+ from fastapi import Depends, Header, status
11
+ from fastapi.exceptions import HTTPException
12
+ from joserfc import jwt
13
+ from joserfc.jwk import KeySet
14
+ from joserfc.jwt import JWTClaimsRegistry
15
+
16
+ from ... import models
17
+ from ...config import settings
18
+
19
+ # Try to import jwks from project directory first (user override)
20
+ # Fall back to package location (default implementation)
21
+ try:
22
+ from auth.entraid import jwks
23
+ except ModuleNotFoundError:
24
+ from . import jwks
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ OPENID_CONNECT_DISCOVERY_DOCUMENT_URL = (
29
+ "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"
30
+ )
31
+
32
+
33
+ @cached(ttl=60 * 60 * 24)
34
+ async def get_jwks_json_default():
35
+ logger.info("Get default JWKS json for Entra ID")
36
+ async with httpx.AsyncClient() as client:
37
+ response = await client.get(OPENID_CONNECT_DISCOVERY_DOCUMENT_URL)
38
+ jwks_uri = response.json()["jwks_uri"]
39
+ async with httpx.AsyncClient() as client:
40
+ response = await client.get(jwks_uri)
41
+ return response.json()
42
+
43
+
44
+ async def get_key_set():
45
+ jwks_data = await jwks.get_jwks_json()
46
+ if jwks_data is None:
47
+ jwks_data = await get_jwks_json_default()
48
+ key_set = KeySet.import_key_set(jwks_data)
49
+ return key_set
50
+
51
+
52
+ @cached(ttl=60 * 60)
53
+ async def validate_token(token_string: str):
54
+ """Validates the Entra ID access/id token. Returns a user object."""
55
+ logger.debug(f"Validating token: {token_string}")
56
+ if token_string.lower().startswith("error"):
57
+ raise HTTPException(
58
+ status_code=status.HTTP_401_UNAUTHORIZED,
59
+ detail=f"Auth error with Entra ID token: {token_string} See https://learn.microsoft.com/en-us/office/dev/add-ins/develop/troubleshoot-sso-in-office-add-ins#causes-and-handling-of-errors-from-getaccesstoken",
60
+ )
61
+ if token_string.lower().startswith("bearer"):
62
+ parts = token_string.split()
63
+ if len(parts) != 2:
64
+ raise HTTPException(
65
+ status_code=status.HTTP_401_UNAUTHORIZED,
66
+ detail="Auth error: Invalid token, must be in format 'Bearer xxxx'",
67
+ )
68
+ token_string = parts[1]
69
+ else:
70
+ raise HTTPException(
71
+ status_code=status.HTTP_401_UNAUTHORIZED,
72
+ detail="Auth error: Invalid token, must be in format 'Bearer xxxx'",
73
+ )
74
+ key_set = await get_key_set()
75
+ try:
76
+ token = jwt.decode(token_string, key_set)
77
+ except Exception:
78
+ logger.exception("Auth error: Failed to decode token")
79
+ raise HTTPException(
80
+ status_code=status.HTTP_401_UNAUTHORIZED,
81
+ detail="Auth error: Failed to decode token",
82
+ )
83
+ token_version = token.claims.get("ver")
84
+ # https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens#token-formats
85
+ # Upgrade to 2.0:
86
+ # https://learn.microsoft.com/en-us/answers/questions/639834/how-to-get-access-token-version-20.html
87
+ if token_version == "1.0":
88
+ issuer = f"https://sts.windows.net/{settings.auth_entraid_tenant_id}/"
89
+ audience = f"api://{settings.auth_entraid_client_id}"
90
+ elif token_version == "2.0":
91
+ issuer = (
92
+ f"https://login.microsoftonline.com/{settings.auth_entraid_tenant_id}/v2.0"
93
+ )
94
+ audience = settings.auth_entraid_client_id
95
+ else:
96
+ raise HTTPException(
97
+ status_code=status.HTTP_401_UNAUTHORIZED,
98
+ detail=f"Auth error: Unsupported token version: {token_version}",
99
+ )
100
+ try:
101
+ if not settings.auth_entraid_multitenant:
102
+ claims_requests = JWTClaimsRegistry(
103
+ aud={"essential": True, "value": audience},
104
+ iss={"essential": True, "value": issuer},
105
+ )
106
+ claims_requests.validate(token.claims)
107
+ else:
108
+ claims_requests = JWTClaimsRegistry(
109
+ aud={"essential": True, "value": audience},
110
+ )
111
+ claims_requests.validate(token.claims)
112
+ # External users have their own tenant_id
113
+ issuer_regex = r"https://login\.microsoftonline\.com/(common|organizations|consumers|[0-9a-fA-F-]{36})/v2\.0"
114
+ if not re.match(issuer_regex, issuer):
115
+ logger.debug(f"Couldn't match issuer for token: {token_string}")
116
+ raise HTTPException(
117
+ status_code=status.HTTP_401_UNAUTHORIZED,
118
+ detail="Auth error: Couldn't validate token",
119
+ )
120
+ logger.debug(claims_requests)
121
+ except Exception as e:
122
+ logger.debug(f"Authentication error for token: {token_string}")
123
+ logger.info(repr(e))
124
+ raise HTTPException(
125
+ status_code=status.HTTP_401_UNAUTHORIZED,
126
+ detail="Auth error: Couldn't validate token",
127
+ )
128
+
129
+ current_user = models.User(claims=token.claims)
130
+ logger.info(f"User authenticated: {current_user.name}")
131
+ return current_user
@@ -0,0 +1,10 @@
1
+ from aiocache import cached
2
+
3
+
4
+ @cached(ttl=60 * 60 * 24)
5
+ async def get_jwks_json():
6
+ """If your application runs on an air-gapped server and can't download the
7
+ Azure JSON Web Key Set (JWKS), you can provide your own function here to access
8
+ the JSON file.
9
+ """
10
+ pass
@@ -0,0 +1,28 @@
1
+ # Virtual environments
2
+ .venv/
3
+ venv/
4
+
5
+ # Secrets & certificates
6
+ .env
7
+ certs/
8
+ *.pem
9
+ *.key
10
+
11
+ # Git
12
+ .git/
13
+ .gitignore
14
+
15
+ # Python artifacts
16
+ __pycache__/
17
+ *.pyc
18
+
19
+ # Local dev settings
20
+ local.settings.json
21
+
22
+ # IDE
23
+ .vscode/
24
+ .idea/
25
+
26
+ # Misc
27
+ *.md
28
+ .DS_Store
@@ -0,0 +1,28 @@
1
+ """
2
+ Azure Functions
3
+
4
+ Note: Azure Functions don't support streaming functions/socket.io.
5
+
6
+ The other files required for Azure Functions are:
7
+ - host.json
8
+ - local.settings.json
9
+ - .funcignore
10
+
11
+ The function is always called http_app_func, see:
12
+ https://github.com/Azure-Samples/fastapi-on-azure-functions/issues/31
13
+
14
+ For app logs, in Azure portal go to:
15
+ Function App > My Function App. Than, under `http_app_func`, click on `Invocations and more`.
16
+ """
17
+
18
+ import os
19
+ from pathlib import Path
20
+
21
+ import azure.functions as func
22
+
23
+ # Must come before importing xlwings_server
24
+ os.environ["XLWINGS_PROJECT_DIR"] = str(Path(__file__).parent)
25
+
26
+ from xlwings_server.main import main_app # noqa: E402
27
+
28
+ app = func.AsgiFunctionApp(app=main_app, http_auth_level=func.AuthLevel.ANONYMOUS)
@@ -0,0 +1,22 @@
1
+ {
2
+ "version": "2.0",
3
+ "logging": {
4
+ "applicationInsights": {
5
+ "samplingSettings": {
6
+ "isEnabled": true,
7
+ "excludedTypes": "Request"
8
+ }
9
+ }
10
+ },
11
+ "extensionBundle": {
12
+ "id": "Microsoft.Azure.Functions.ExtensionBundle",
13
+ "version": "[4.*, 5.0.0)"
14
+ },
15
+ "extensions":
16
+ {
17
+ "http":
18
+ {
19
+ "routePrefix": ""
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "IsEncrypted": false,
3
+ "Values": {
4
+ "AzureWebJobsStorage": "",
5
+ "FUNCTIONS_WORKER_RUNTIME": "python",
6
+ "AzureWebJobsFeatureFlags": "EnableWorkerIndexing"
7
+ }
8
+ }
@@ -0,0 +1,9 @@
1
+ """Build-time utilities for xlwings Server.
2
+
3
+ This package contains tools used during build and deployment processes,
4
+ such as static file hashing for cache-busting.
5
+ """
6
+
7
+ from .static_file_hasher import StaticFileHasher
8
+
9
+ __all__ = ["StaticFileHasher"]
@@ -0,0 +1,212 @@
1
+ """Static file hasher for cache-busting in production builds.
2
+
3
+ This module provides the StaticFileHasher class which adds content-based hashes
4
+ to static file names (e.g., main.js -> main.a1b2c3d4.js) and updates all references
5
+ in HTML, JS, and CSS files accordingly. This is primarily used during the Wasm
6
+ build process to enable effective cache-busting in production deployments.
7
+ """
8
+
9
+ import hashlib
10
+ import os
11
+ import re
12
+ from pathlib import Path
13
+
14
+
15
+ class StaticFileHasher:
16
+ """Hashes static files and updates references for cache-busting.
17
+
18
+ This utility is used during the Wasm build process to add content-based
19
+ hashes to static file names (e.g., main.js -> main.a1b2c3d4.js) and update
20
+ all references in HTML, JS, and CSS files accordingly.
21
+
22
+ Args:
23
+ static_dir: Directory containing static files to hash
24
+ templates_dir: Directory containing templates with references to update
25
+ """
26
+
27
+ def __init__(self, static_dir: Path, templates_dir: Path):
28
+ self.static_dir = static_dir
29
+ self.templates_dir = templates_dir
30
+ self.file_mapping: dict[
31
+ str, dict[str, str]
32
+ ] = {} # rel_path -> {old_name: new_name, path: Path}
33
+ self.processed_files: set[Path] = set()
34
+
35
+ def get_relative_path(self, path: Path, base_dir: Path) -> Path:
36
+ """Get the relative path from base_dir to the given path"""
37
+ try:
38
+ return path.relative_to(base_dir)
39
+ except ValueError:
40
+ return path
41
+
42
+ def should_process_file(self, path: Path) -> bool:
43
+ """Determine if a file should be processed based on various criteria"""
44
+ if not path.is_file() or path.name.startswith("."):
45
+ return False
46
+
47
+ excluded_patterns = {
48
+ ".map",
49
+ ".md",
50
+ ".py",
51
+ ".scss",
52
+ ".txt",
53
+ ".whl",
54
+ ".xml",
55
+ "custom-functions-code.js",
56
+ "custom-functions-meta.json",
57
+ "eula.html",
58
+ "fonts/",
59
+ "images/ribbon/",
60
+ "index.html",
61
+ "manifest.xml",
62
+ "privacy.html",
63
+ "support.html",
64
+ "taskpane.html",
65
+ "vendor/",
66
+ }
67
+
68
+ path_str = str(path)
69
+ return (
70
+ "." in path.name # requires extension
71
+ and not any(pattern in path_str for pattern in excluded_patterns)
72
+ )
73
+
74
+ def hash_file(self, path: Path) -> str:
75
+ """Generate a hash for the given file"""
76
+ contents = path.read_bytes()
77
+ return hashlib.sha256(contents).hexdigest()[:8]
78
+
79
+ def generate_new_name(self, filename: str, digest: str) -> str:
80
+ """Generate the new filename with hash included"""
81
+ name, ext = os.path.splitext(filename)
82
+ return f"{name}.{digest}{ext}"
83
+
84
+ def get_replacement_patterns(
85
+ self, file_path: Path, rel_path: str, old_name: str, new_name: str
86
+ ) -> list[tuple[str, str]]:
87
+ """Generate all possible path patterns for replacement"""
88
+ patterns = []
89
+ dir_path = os.path.dirname(rel_path)
90
+
91
+ # For absolute paths (mainly HTML references)
92
+ abs_patterns = [
93
+ (f"{rel_path}", f"{dir_path}/{new_name}"),
94
+ (f"/{rel_path}", f"/{dir_path}/{new_name}"),
95
+ ]
96
+
97
+ # For relative paths (mainly JS imports)
98
+ try:
99
+ current_dir = file_path.parent.relative_to(self.static_dir)
100
+ target_dir = Path(dir_path)
101
+ rel_import_path = os.path.relpath(target_dir, current_dir)
102
+
103
+ # Handle the case where files are in the same directory
104
+ if rel_import_path == ".":
105
+ rel_patterns = [
106
+ (f"./{old_name}", f"./{new_name}"),
107
+ (old_name, new_name),
108
+ ]
109
+ else:
110
+ rel_import_path = rel_import_path.replace("\\", "/")
111
+ if not rel_import_path.startswith("."):
112
+ rel_import_path = f"./{rel_import_path}"
113
+ rel_patterns = [
114
+ (f"{rel_import_path}/{old_name}", f"{rel_import_path}/{new_name}"),
115
+ ]
116
+ except ValueError:
117
+ rel_patterns = []
118
+
119
+ # Combine all patterns
120
+ all_paths = abs_patterns + rel_patterns
121
+
122
+ # Generate variations for each pattern
123
+ for old_path, new_path in all_paths:
124
+ # Normalize slashes
125
+ old_path = old_path.replace("\\", "/")
126
+ new_path = new_path.replace("\\", "/")
127
+
128
+ # Remove any double slashes
129
+ old_path = re.sub(r"/{2,}", "/", old_path)
130
+ new_path = re.sub(r"/{2,}", "/", new_path)
131
+
132
+ variations = [
133
+ (old_path, new_path),
134
+ (f"'{old_path}'", f"'{new_path}'"),
135
+ (f'"{old_path}"', f'"{new_path}"'),
136
+ (f'from "{old_path}"', f'from "{new_path}"'),
137
+ (f"from '{old_path}'", f"from '{new_path}'"),
138
+ (f'import "{old_path}"', f'import "{new_path}"'),
139
+ (f"import '{old_path}'", f"import '{new_path}'"),
140
+ (f"url({old_path})", f"url({new_path})"),
141
+ (f'url("{old_path}")', f'url("{new_path}")'),
142
+ (f"url('{old_path}')", f"url('{new_path}')"),
143
+ ]
144
+ patterns.extend(variations)
145
+
146
+ return patterns
147
+
148
+ def replace_in_file(
149
+ self, file_path: Path, excluded_dirs: list[str] | None = None
150
+ ) -> None:
151
+ """Replace all occurrences of original filenames with hashed versions"""
152
+ if excluded_dirs is None:
153
+ excluded_dirs = []
154
+
155
+ if any(excluded_dir in file_path.parts for excluded_dir in excluded_dirs):
156
+ return
157
+
158
+ content = file_path.read_text()
159
+ new_content = content
160
+
161
+ # Sort paths by length (longest first) to avoid partial replacements
162
+ sorted_paths = sorted(self.file_mapping.keys(), key=len, reverse=True)
163
+
164
+ for rel_path in sorted_paths:
165
+ file_info = self.file_mapping[rel_path]
166
+ old_name = file_info["old_name"]
167
+ new_name = file_info["new_name"]
168
+
169
+ patterns = self.get_replacement_patterns(
170
+ file_path, rel_path, old_name, new_name
171
+ )
172
+
173
+ for old_pattern, new_pattern in patterns:
174
+ new_content = new_content.replace(old_pattern, new_pattern)
175
+
176
+ if new_content != content:
177
+ file_path.write_text(new_content)
178
+
179
+ def process_files(self):
180
+ """Process all static files and update references"""
181
+ # First pass: hash all files and create mapping
182
+ for source_path in self.static_dir.rglob("*"):
183
+ if self.should_process_file(source_path):
184
+ rel_path = self.get_relative_path(source_path, self.static_dir)
185
+ rel_path_str = str(rel_path).replace("\\", "/")
186
+
187
+ digest = self.hash_file(source_path)
188
+ old_name = source_path.name
189
+ new_name = self.generate_new_name(old_name, digest)
190
+
191
+ self.file_mapping[rel_path_str] = {
192
+ "old_name": old_name,
193
+ "new_name": new_name,
194
+ "path": source_path,
195
+ }
196
+
197
+ # Second pass: rename files
198
+ for file_info in self.file_mapping.values():
199
+ old_path = file_info["path"]
200
+ if old_path.exists(): # Check if not already renamed
201
+ new_path = old_path.with_name(file_info["new_name"])
202
+ old_path.rename(new_path)
203
+
204
+ # Third pass: update references in files
205
+ for template_file in self.templates_dir.rglob("*.html"):
206
+ self.replace_in_file(template_file)
207
+
208
+ for js_file in self.static_dir.rglob("*.js"):
209
+ self.replace_in_file(js_file, excluded_dirs=["vendor"])
210
+
211
+ for css_file in self.static_dir.rglob("*.css"):
212
+ self.replace_in_file(css_file, excluded_dirs=["vendor"])