rational 0.1.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. rational-0.1.0a1/.gitignore +183 -0
  2. rational-0.1.0a1/CHANGELOG.md +51 -0
  3. rational-0.1.0a1/CONTRIBUTING.md +38 -0
  4. rational-0.1.0a1/LICENSE +24 -0
  5. rational-0.1.0a1/PKG-INFO +308 -0
  6. rational-0.1.0a1/README.md +249 -0
  7. rational-0.1.0a1/SECURITY.md +24 -0
  8. rational-0.1.0a1/benchmarks/run.py +231 -0
  9. rational-0.1.0a1/docs/api.rst +103 -0
  10. rational-0.1.0a1/docs/benchmarks.rst +96 -0
  11. rational-0.1.0a1/docs/components.rst +120 -0
  12. rational-0.1.0a1/docs/conf.py +32 -0
  13. rational-0.1.0a1/docs/crash-course-beginner.rst +218 -0
  14. rational-0.1.0a1/docs/crash-course-fullstack.rst +149 -0
  15. rational-0.1.0a1/docs/design.rst +100 -0
  16. rational-0.1.0a1/docs/examples/blog.rst +66 -0
  17. rational-0.1.0a1/docs/examples/counter.rst +47 -0
  18. rational-0.1.0a1/docs/examples/cv-portfolio.rst +64 -0
  19. rational-0.1.0a1/docs/examples/dashboard.rst +53 -0
  20. rational-0.1.0a1/docs/examples/hello.rst +20 -0
  21. rational-0.1.0a1/docs/examples/index.rst +90 -0
  22. rational-0.1.0a1/docs/examples/marketplace.rst +59 -0
  23. rational-0.1.0a1/docs/examples/media.rst +83 -0
  24. rational-0.1.0a1/docs/examples/social-network.rst +67 -0
  25. rational-0.1.0a1/docs/examples/todos.rst +62 -0
  26. rational-0.1.0a1/docs/examples/user-platform.rst +64 -0
  27. rational-0.1.0a1/docs/index.rst +97 -0
  28. rational-0.1.0a1/docs/projects.rst +113 -0
  29. rational-0.1.0a1/docs/quickstart.rst +75 -0
  30. rational-0.1.0a1/docs/running.rst +119 -0
  31. rational-0.1.0a1/docs/security.rst +40 -0
  32. rational-0.1.0a1/docs/services.rst +102 -0
  33. rational-0.1.0a1/docs/styling.rst +92 -0
  34. rational-0.1.0a1/docs/testing.rst +28 -0
  35. rational-0.1.0a1/docs/tutorial/01-first-page.rst +37 -0
  36. rational-0.1.0a1/docs/tutorial/02-components.rst +34 -0
  37. rational-0.1.0a1/docs/tutorial/03-state-and-events.rst +43 -0
  38. rational-0.1.0a1/docs/tutorial/04-styling.rst +40 -0
  39. rational-0.1.0a1/docs/tutorial/05-data-and-api.rst +58 -0
  40. rational-0.1.0a1/docs/tutorial/06-project-structure.rst +74 -0
  41. rational-0.1.0a1/docs/tutorial/07-testing.rst +35 -0
  42. rational-0.1.0a1/docs/tutorial/08-deploying.rst +41 -0
  43. rational-0.1.0a1/docs/tutorial/guestbook/app.py +19 -0
  44. rational-0.1.0a1/docs/tutorial/guestbook/models/__init__.py +6 -0
  45. rational-0.1.0a1/docs/tutorial/guestbook/models/database.py +19 -0
  46. rational-0.1.0a1/docs/tutorial/guestbook/models/entry.py +31 -0
  47. rational-0.1.0a1/docs/tutorial/guestbook/services/__init__.py +5 -0
  48. rational-0.1.0a1/docs/tutorial/guestbook/services/guestbook.py +30 -0
  49. rational-0.1.0a1/docs/tutorial/guestbook/settings.py +8 -0
  50. rational-0.1.0a1/docs/tutorial/guestbook/tests/conftest.py +12 -0
  51. rational-0.1.0a1/docs/tutorial/guestbook/tests/test_guestbook.py +36 -0
  52. rational-0.1.0a1/docs/tutorial/guestbook/views/__init__.py +1 -0
  53. rational-0.1.0a1/docs/tutorial/guestbook/views/components/__init__.py +3 -0
  54. rational-0.1.0a1/docs/tutorial/guestbook/views/components/entry_card.py +27 -0
  55. rational-0.1.0a1/docs/tutorial/guestbook/views/pages/__init__.py +8 -0
  56. rational-0.1.0a1/docs/tutorial/guestbook/views/pages/home.py +73 -0
  57. rational-0.1.0a1/docs/tutorial/guestbook/views/styles/__init__.py +5 -0
  58. rational-0.1.0a1/docs/tutorial/guestbook/views/styles/layout.py +6 -0
  59. rational-0.1.0a1/docs/tutorial/index.rst +30 -0
  60. rational-0.1.0a1/docs/tutorial/steps/step1.py +21 -0
  61. rational-0.1.0a1/docs/tutorial/steps/step2.py +45 -0
  62. rational-0.1.0a1/docs/tutorial/steps/step3.py +95 -0
  63. rational-0.1.0a1/docs/tutorial/steps/step4.py +119 -0
  64. rational-0.1.0a1/docs/tutorial/steps/step5.py +171 -0
  65. rational-0.1.0a1/examples/README.md +54 -0
  66. rational-0.1.0a1/examples/blog/README.md +55 -0
  67. rational-0.1.0a1/examples/blog/app.py +26 -0
  68. rational-0.1.0a1/examples/blog/models/__init__.py +19 -0
  69. rational-0.1.0a1/examples/blog/models/account.py +7 -0
  70. rational-0.1.0a1/examples/blog/models/comment.py +35 -0
  71. rational-0.1.0a1/examples/blog/models/database.py +19 -0
  72. rational-0.1.0a1/examples/blog/models/post.py +53 -0
  73. rational-0.1.0a1/examples/blog/services/__init__.py +6 -0
  74. rational-0.1.0a1/examples/blog/services/blog.py +106 -0
  75. rational-0.1.0a1/examples/blog/services/seed.py +53 -0
  76. rational-0.1.0a1/examples/blog/settings.py +10 -0
  77. rational-0.1.0a1/examples/blog/views/__init__.py +1 -0
  78. rational-0.1.0a1/examples/blog/views/components/__init__.py +7 -0
  79. rational-0.1.0a1/examples/blog/views/components/comments.py +45 -0
  80. rational-0.1.0a1/examples/blog/views/components/editor.py +61 -0
  81. rational-0.1.0a1/examples/blog/views/components/markup.py +25 -0
  82. rational-0.1.0a1/examples/blog/views/components/post_list.py +34 -0
  83. rational-0.1.0a1/examples/blog/views/components/shell.py +30 -0
  84. rational-0.1.0a1/examples/blog/views/pages/__init__.py +14 -0
  85. rational-0.1.0a1/examples/blog/views/pages/admin.py +141 -0
  86. rational-0.1.0a1/examples/blog/views/pages/feed.py +28 -0
  87. rational-0.1.0a1/examples/blog/views/pages/public.py +60 -0
  88. rational-0.1.0a1/examples/blog/views/styles/__init__.py +26 -0
  89. rational-0.1.0a1/examples/blog/views/styles/article.py +10 -0
  90. rational-0.1.0a1/examples/blog/views/styles/editor.py +4 -0
  91. rational-0.1.0a1/examples/blog/views/styles/layout.py +9 -0
  92. rational-0.1.0a1/examples/blog/views/styles/theme.py +1 -0
  93. rational-0.1.0a1/examples/counter/README.md +34 -0
  94. rational-0.1.0a1/examples/counter/app.py +16 -0
  95. rational-0.1.0a1/examples/counter/views/__init__.py +1 -0
  96. rational-0.1.0a1/examples/counter/views/components/__init__.py +4 -0
  97. rational-0.1.0a1/examples/counter/views/components/counter.py +27 -0
  98. rational-0.1.0a1/examples/counter/views/components/greeter.py +16 -0
  99. rational-0.1.0a1/examples/counter/views/pages/__init__.py +8 -0
  100. rational-0.1.0a1/examples/counter/views/pages/home.py +30 -0
  101. rational-0.1.0a1/examples/counter/views/styles/__init__.py +8 -0
  102. rational-0.1.0a1/examples/cv-portfolio/README.md +51 -0
  103. rational-0.1.0a1/examples/cv-portfolio/app.py +19 -0
  104. rational-0.1.0a1/examples/cv-portfolio/content.py +64 -0
  105. rational-0.1.0a1/examples/cv-portfolio/models/__init__.py +6 -0
  106. rational-0.1.0a1/examples/cv-portfolio/models/contact.py +38 -0
  107. rational-0.1.0a1/examples/cv-portfolio/models/profile.py +43 -0
  108. rational-0.1.0a1/examples/cv-portfolio/services/__init__.py +5 -0
  109. rational-0.1.0a1/examples/cv-portfolio/services/contact.py +26 -0
  110. rational-0.1.0a1/examples/cv-portfolio/views/__init__.py +1 -0
  111. rational-0.1.0a1/examples/cv-portfolio/views/components/__init__.py +8 -0
  112. rational-0.1.0a1/examples/cv-portfolio/views/components/contact.py +53 -0
  113. rational-0.1.0a1/examples/cv-portfolio/views/components/experience.py +34 -0
  114. rational-0.1.0a1/examples/cv-portfolio/views/components/header.py +37 -0
  115. rational-0.1.0a1/examples/cv-portfolio/views/components/hero.py +32 -0
  116. rational-0.1.0a1/examples/cv-portfolio/views/components/projects.py +52 -0
  117. rational-0.1.0a1/examples/cv-portfolio/views/components/section.py +9 -0
  118. rational-0.1.0a1/examples/cv-portfolio/views/components/skills.py +20 -0
  119. rational-0.1.0a1/examples/cv-portfolio/views/pages/__init__.py +8 -0
  120. rational-0.1.0a1/examples/cv-portfolio/views/pages/home.py +23 -0
  121. rational-0.1.0a1/examples/cv-portfolio/views/styles/__init__.py +20 -0
  122. rational-0.1.0a1/examples/cv-portfolio/views/styles/layout.py +13 -0
  123. rational-0.1.0a1/examples/cv-portfolio/views/styles/theme.py +1 -0
  124. rational-0.1.0a1/examples/cv-portfolio/views/styles/timeline.py +22 -0
  125. rational-0.1.0a1/examples/dashboard/README.md +45 -0
  126. rational-0.1.0a1/examples/dashboard/app.py +18 -0
  127. rational-0.1.0a1/examples/dashboard/models/__init__.py +5 -0
  128. rational-0.1.0a1/examples/dashboard/models/metrics.py +23 -0
  129. rational-0.1.0a1/examples/dashboard/services/__init__.py +5 -0
  130. rational-0.1.0a1/examples/dashboard/services/metrics.py +33 -0
  131. rational-0.1.0a1/examples/dashboard/views/__init__.py +1 -0
  132. rational-0.1.0a1/examples/dashboard/views/components/__init__.py +6 -0
  133. rational-0.1.0a1/examples/dashboard/views/components/recent_orders.py +25 -0
  134. rational-0.1.0a1/examples/dashboard/views/components/region_chart.py +24 -0
  135. rational-0.1.0a1/examples/dashboard/views/components/sidebar.py +29 -0
  136. rational-0.1.0a1/examples/dashboard/views/components/stat_card.py +22 -0
  137. rational-0.1.0a1/examples/dashboard/views/pages/__init__.py +8 -0
  138. rational-0.1.0a1/examples/dashboard/views/pages/overview.py +53 -0
  139. rational-0.1.0a1/examples/dashboard/views/styles/__init__.py +24 -0
  140. rational-0.1.0a1/examples/dashboard/views/styles/data.py +18 -0
  141. rational-0.1.0a1/examples/dashboard/views/styles/layout.py +30 -0
  142. rational-0.1.0a1/examples/dashboard/views/styles/theme.py +1 -0
  143. rational-0.1.0a1/examples/hello/README.md +17 -0
  144. rational-0.1.0a1/examples/hello/app.py +27 -0
  145. rational-0.1.0a1/examples/marketplace/README.md +58 -0
  146. rational-0.1.0a1/examples/marketplace/app.py +29 -0
  147. rational-0.1.0a1/examples/marketplace/models/__init__.py +24 -0
  148. rational-0.1.0a1/examples/marketplace/models/account.py +7 -0
  149. rational-0.1.0a1/examples/marketplace/models/database.py +19 -0
  150. rational-0.1.0a1/examples/marketplace/models/money.py +3 -0
  151. rational-0.1.0a1/examples/marketplace/models/order.py +54 -0
  152. rational-0.1.0a1/examples/marketplace/models/product.py +40 -0
  153. rational-0.1.0a1/examples/marketplace/services/__init__.py +6 -0
  154. rational-0.1.0a1/examples/marketplace/services/seed.py +38 -0
  155. rational-0.1.0a1/examples/marketplace/services/shop.py +98 -0
  156. rational-0.1.0a1/examples/marketplace/settings.py +9 -0
  157. rational-0.1.0a1/examples/marketplace/views/__init__.py +1 -0
  158. rational-0.1.0a1/examples/marketplace/views/components/__init__.py +6 -0
  159. rational-0.1.0a1/examples/marketplace/views/components/cart.py +41 -0
  160. rational-0.1.0a1/examples/marketplace/views/components/header.py +39 -0
  161. rational-0.1.0a1/examples/marketplace/views/components/product_card.py +45 -0
  162. rational-0.1.0a1/examples/marketplace/views/components/shell.py +9 -0
  163. rational-0.1.0a1/examples/marketplace/views/pages/__init__.py +10 -0
  164. rational-0.1.0a1/examples/marketplace/views/pages/account.py +93 -0
  165. rational-0.1.0a1/examples/marketplace/views/pages/catalog.py +146 -0
  166. rational-0.1.0a1/examples/marketplace/views/pages/checkout.py +129 -0
  167. rational-0.1.0a1/examples/marketplace/views/styles/__init__.py +25 -0
  168. rational-0.1.0a1/examples/marketplace/views/styles/cart.py +7 -0
  169. rational-0.1.0a1/examples/marketplace/views/styles/catalog.py +15 -0
  170. rational-0.1.0a1/examples/marketplace/views/styles/layout.py +8 -0
  171. rational-0.1.0a1/examples/marketplace/views/styles/theme.py +24 -0
  172. rational-0.1.0a1/examples/media/README.md +11 -0
  173. rational-0.1.0a1/examples/media/app.py +19 -0
  174. rational-0.1.0a1/examples/media/models/__init__.py +3 -0
  175. rational-0.1.0a1/examples/media/services/__init__.py +3 -0
  176. rational-0.1.0a1/examples/media/settings.py +20 -0
  177. rational-0.1.0a1/examples/media/static/logo.svg +269 -0
  178. rational-0.1.0a1/examples/media/views/__init__.py +1 -0
  179. rational-0.1.0a1/examples/media/views/pages/__init__.py +8 -0
  180. rational-0.1.0a1/examples/media/views/pages/media.py +139 -0
  181. rational-0.1.0a1/examples/media/views/styles/__init__.py +5 -0
  182. rational-0.1.0a1/examples/media/views/styles/layout.py +7 -0
  183. rational-0.1.0a1/examples/social-network/README.md +51 -0
  184. rational-0.1.0a1/examples/social-network/app.py +21 -0
  185. rational-0.1.0a1/examples/social-network/models/__init__.py +20 -0
  186. rational-0.1.0a1/examples/social-network/models/database.py +19 -0
  187. rational-0.1.0a1/examples/social-network/models/member.py +65 -0
  188. rational-0.1.0a1/examples/social-network/models/post.py +57 -0
  189. rational-0.1.0a1/examples/social-network/services/__init__.py +6 -0
  190. rational-0.1.0a1/examples/social-network/services/seed.py +54 -0
  191. rational-0.1.0a1/examples/social-network/services/social.py +180 -0
  192. rational-0.1.0a1/examples/social-network/settings.py +10 -0
  193. rational-0.1.0a1/examples/social-network/views/__init__.py +1 -0
  194. rational-0.1.0a1/examples/social-network/views/components/__init__.py +6 -0
  195. rational-0.1.0a1/examples/social-network/views/components/composer.py +58 -0
  196. rational-0.1.0a1/examples/social-network/views/components/post_card.py +85 -0
  197. rational-0.1.0a1/examples/social-network/views/components/shell.py +39 -0
  198. rational-0.1.0a1/examples/social-network/views/components/who_to_follow.py +50 -0
  199. rational-0.1.0a1/examples/social-network/views/pages/__init__.py +9 -0
  200. rational-0.1.0a1/examples/social-network/views/pages/account.py +68 -0
  201. rational-0.1.0a1/examples/social-network/views/pages/explore.py +28 -0
  202. rational-0.1.0a1/examples/social-network/views/pages/home.py +42 -0
  203. rational-0.1.0a1/examples/social-network/views/pages/profile.py +64 -0
  204. rational-0.1.0a1/examples/social-network/views/styles/__init__.py +5 -0
  205. rational-0.1.0a1/examples/social-network/views/styles/layout.py +9 -0
  206. rational-0.1.0a1/examples/todos/README.md +41 -0
  207. rational-0.1.0a1/examples/todos/app.py +21 -0
  208. rational-0.1.0a1/examples/todos/models/__init__.py +7 -0
  209. rational-0.1.0a1/examples/todos/models/account.py +7 -0
  210. rational-0.1.0a1/examples/todos/models/database.py +19 -0
  211. rational-0.1.0a1/examples/todos/models/todo.py +35 -0
  212. rational-0.1.0a1/examples/todos/services/__init__.py +5 -0
  213. rational-0.1.0a1/examples/todos/services/todos.py +62 -0
  214. rational-0.1.0a1/examples/todos/settings.py +8 -0
  215. rational-0.1.0a1/examples/todos/views/__init__.py +1 -0
  216. rational-0.1.0a1/examples/todos/views/components/__init__.py +3 -0
  217. rational-0.1.0a1/examples/todos/views/components/todo_item.py +41 -0
  218. rational-0.1.0a1/examples/todos/views/pages/__init__.py +9 -0
  219. rational-0.1.0a1/examples/todos/views/pages/login.py +31 -0
  220. rational-0.1.0a1/examples/todos/views/pages/todos.py +112 -0
  221. rational-0.1.0a1/examples/todos/views/styles/__init__.py +7 -0
  222. rational-0.1.0a1/examples/todos/views/styles/layout.py +5 -0
  223. rational-0.1.0a1/examples/todos/views/styles/list.py +7 -0
  224. rational-0.1.0a1/examples/todos/views/styles/theme.py +1 -0
  225. rational-0.1.0a1/examples/user-platform/README.md +52 -0
  226. rational-0.1.0a1/examples/user-platform/app.py +29 -0
  227. rational-0.1.0a1/examples/user-platform/models/__init__.py +18 -0
  228. rational-0.1.0a1/examples/user-platform/models/database.py +19 -0
  229. rational-0.1.0a1/examples/user-platform/models/forms.py +44 -0
  230. rational-0.1.0a1/examples/user-platform/models/user.py +40 -0
  231. rational-0.1.0a1/examples/user-platform/services/__init__.py +8 -0
  232. rational-0.1.0a1/examples/user-platform/services/accounts.py +60 -0
  233. rational-0.1.0a1/examples/user-platform/services/admin.py +50 -0
  234. rational-0.1.0a1/examples/user-platform/services/auth.py +17 -0
  235. rational-0.1.0a1/examples/user-platform/services/security.py +28 -0
  236. rational-0.1.0a1/examples/user-platform/settings.py +8 -0
  237. rational-0.1.0a1/examples/user-platform/views/__init__.py +1 -0
  238. rational-0.1.0a1/examples/user-platform/views/components/__init__.py +4 -0
  239. rational-0.1.0a1/examples/user-platform/views/components/account_forms.py +60 -0
  240. rational-0.1.0a1/examples/user-platform/views/components/shell.py +35 -0
  241. rational-0.1.0a1/examples/user-platform/views/pages/__init__.py +9 -0
  242. rational-0.1.0a1/examples/user-platform/views/pages/account.py +145 -0
  243. rational-0.1.0a1/examples/user-platform/views/pages/admin.py +67 -0
  244. rational-0.1.0a1/examples/user-platform/views/styles/__init__.py +7 -0
  245. rational-0.1.0a1/examples/user-platform/views/styles/layout.py +5 -0
  246. rational-0.1.0a1/examples/user-platform/views/styles/table.py +5 -0
  247. rational-0.1.0a1/examples/user-platform/views/styles/theme.py +1 -0
  248. rational-0.1.0a1/pyproject.toml +112 -0
  249. rational-0.1.0a1/src/rational/__init__.py +99 -0
  250. rational-0.1.0a1/src/rational/__main__.py +3 -0
  251. rational-0.1.0a1/src/rational/_tracking.py +50 -0
  252. rational-0.1.0a1/src/rational/apidocs.py +40 -0
  253. rational-0.1.0a1/src/rational/app.py +377 -0
  254. rational-0.1.0a1/src/rational/backend/__init__.py +14 -0
  255. rational-0.1.0a1/src/rational/backend/database.py +46 -0
  256. rational-0.1.0a1/src/rational/backend/services.py +174 -0
  257. rational-0.1.0a1/src/rational/binding.py +94 -0
  258. rational-0.1.0a1/src/rational/cli.py +299 -0
  259. rational-0.1.0a1/src/rational/component.py +167 -0
  260. rational-0.1.0a1/src/rational/components/__init__.py +52 -0
  261. rational-0.1.0a1/src/rational/components/basic.py +247 -0
  262. rational-0.1.0a1/src/rational/components/forms.py +253 -0
  263. rational-0.1.0a1/src/rational/components/layout.py +138 -0
  264. rational-0.1.0a1/src/rational/context.py +69 -0
  265. rational-0.1.0a1/src/rational/dev.py +208 -0
  266. rational-0.1.0a1/src/rational/document.py +54 -0
  267. rational-0.1.0a1/src/rational/errors.py +99 -0
  268. rational-0.1.0a1/src/rational/events.py +73 -0
  269. rational-0.1.0a1/src/rational/http.py +72 -0
  270. rational-0.1.0a1/src/rational/logging.py +185 -0
  271. rational-0.1.0a1/src/rational/pages.py +181 -0
  272. rational-0.1.0a1/src/rational/protocol.py +142 -0
  273. rational-0.1.0a1/src/rational/py.typed +0 -0
  274. rational-0.1.0a1/src/rational/rendering/__init__.py +5 -0
  275. rational-0.1.0a1/src/rational/rendering/builder.py +86 -0
  276. rational-0.1.0a1/src/rational/rendering/html.py +60 -0
  277. rational-0.1.0a1/src/rational/rendering/nodes.py +121 -0
  278. rational-0.1.0a1/src/rational/router.py +77 -0
  279. rational-0.1.0a1/src/rational/runtime.py +323 -0
  280. rational-0.1.0a1/src/rational/session.py +152 -0
  281. rational-0.1.0a1/src/rational/state.py +113 -0
  282. rational-0.1.0a1/src/rational/static/dev.js +53 -0
  283. rational-0.1.0a1/src/rational/static/runtime.js +311 -0
  284. rational-0.1.0a1/src/rational/styling/__init__.py +6 -0
  285. rational-0.1.0a1/src/rational/styling/compiler.py +301 -0
  286. rational-0.1.0a1/src/rational/styling/responsive.py +53 -0
  287. rational-0.1.0a1/src/rational/styling/style.py +228 -0
  288. rational-0.1.0a1/src/rational/styling/theme.py +338 -0
  289. rational-0.1.0a1/src/rational/testing.py +232 -0
  290. rational-0.1.0a1/tests/__init__.py +0 -0
  291. rational-0.1.0a1/tests/browser/__init__.py +0 -0
  292. rational-0.1.0a1/tests/browser/test_runtime.py +137 -0
  293. rational-0.1.0a1/tests/examples/__init__.py +0 -0
  294. rational-0.1.0a1/tests/examples/conftest.py +52 -0
  295. rational-0.1.0a1/tests/examples/test_blog.py +79 -0
  296. rational-0.1.0a1/tests/examples/test_cv_portfolio.py +52 -0
  297. rational-0.1.0a1/tests/examples/test_marketplace.py +97 -0
  298. rational-0.1.0a1/tests/examples/test_media.py +40 -0
  299. rational-0.1.0a1/tests/examples/test_small_examples.py +32 -0
  300. rational-0.1.0a1/tests/examples/test_social_network.py +108 -0
  301. rational-0.1.0a1/tests/examples/test_todos.py +65 -0
  302. rational-0.1.0a1/tests/examples/test_user_platform.py +94 -0
  303. rational-0.1.0a1/tests/test_compatibility.py +14 -0
  304. rational-0.1.0a1/tests/test_components.py +364 -0
  305. rational-0.1.0a1/tests/test_dev_and_cli.py +242 -0
  306. rational-0.1.0a1/tests/test_logging.py +92 -0
  307. rational-0.1.0a1/tests/test_protocol.py +432 -0
  308. rational-0.1.0a1/tests/test_rendering.py +81 -0
  309. rational-0.1.0a1/tests/test_styling.py +193 -0
  310. rational-0.1.0a1/tests/test_tutorial.py +72 -0
@@ -0,0 +1,183 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # Ruff stuff:
171
+ .ruff_cache/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
175
+
176
+ # Cursor
177
+ # Cursor is an AI-powered code editor.`.cursorignore` specifies files/directories to
178
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
179
+ # refer to https://docs.cursor.com/context/ignore-files
180
+ .cursorignore
181
+ .cursorindexingignore
182
+ # Local databases from examples
183
+ *.db
@@ -0,0 +1,51 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
+ [Semantic Versioning](https://semver.org/) (pre-1.0: minor versions may break the API).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+ - A `media` example and walkthrough covering local and remote images, multiple favicon sources,
11
+ internal and external links, video sources, and embedded PDF documents through `h()`.
12
+ - Structured request logging and telemetry through `app.logging`: request IDs, request and event
13
+ timings, slow-render warnings, and optional OpenTelemetry spans.
14
+ - A broken app no longer kills the dev server: import failures and failing startup hooks serve the
15
+ readable error page instead (with live reload, so the browser recovers once you save a fix).
16
+ `rational.cli.TargetError` replaces the `SystemExit` raised for unusable targets.
17
+
18
+ ### Changed
19
+ - Examples and the tutorial project use a new layout: `app.py`, `settings.py`, `models/`,
20
+ `services/` and `views/{styles,components,pages}`. Imports are relative inside a layer and
21
+ absolute across layers (`from views.styles import X`).
22
+
23
+ ## [0.1.0a1] - 2026-10-22
24
+
25
+ First public alpha.
26
+
27
+ ### Added
28
+ - Component model: `Component`, `Page`, props from annotations, `State` with component, page
29
+ and session scopes, dependency tracking, keyed identity, async `mount()`/`render()`.
30
+ - Safe node IR and deterministic HTML serializer (escaping, tag/attribute/URL validation).
31
+ - Typed `Style` objects compiled to hashed, deduplicated CSS classes; pseudo-states,
32
+ viewport breakpoints, container queries; four theme presets with light/dark/system modes.
33
+ - Primitives: text, headings, buttons, links, images, layout, cards, accessible form controls.
34
+ - HTTP event protocol v1 with DOM morphing, CSRF, session binding, payload validation,
35
+ rate and size limits, stale-handler resynchronization.
36
+ - `Service` and `@remote`: a typed JSON API under `/api` with OpenAPI docs.
37
+ - Async SQLAlchemy integration (`Database`, `Model`).
38
+ - `python app.py --dev` (supervised hot reload, live browser reload, readable error pages
39
+ and overlays) and `--prod` (single origin, proxy-aware); `python -m rational`.
40
+ - `rational.testing.TestClient`, Playwright browser tests, benchmarks, Sphinx documentation.
41
+ - `Router` for declaring pages, services and startup hooks in their own modules (`App.include`).
42
+ - Swagger UI at `/api/docs` that sends the session's CSRF token, so "Try it out" works when signed in;
43
+ `App(description=..., version=...)`, API tags from service docstrings.
44
+ - `FieldError` for business-rule errors shown under form fields; validator messages without the
45
+ "Value error, " prefix.
46
+ - `Badge` and `Avatar` primitives; `js.confirm()` to ask before a server event.
47
+ - `rational.testing` finds controls by accessible name (`aria-label`, then text).
48
+ - Examples, each laid out as a project: hello, counter, dashboard, cv-portfolio, todos, blog,
49
+ user-platform, marketplace, social-network.
50
+ - Documentation: a step-by-step tutorial (tested), example walkthroughs, a project-structure guide,
51
+ GitHub Pages and Read the Docs publishing.
@@ -0,0 +1,38 @@
1
+ # Contributing
2
+
3
+ Thanks for helping make web development less irrational!
4
+
5
+ ## Setup
6
+
7
+ ```console
8
+ $ python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
9
+ $ pip install -e ".[dev,docs,browser]"
10
+ $ playwright install chromium # optional: real-browser tests
11
+ ```
12
+
13
+ ## Checks
14
+
15
+ Every change should pass:
16
+
17
+ ```console
18
+ $ pytest # unit, integration, examples (+ browser tests if Chromium is present)
19
+ $ ruff check . && ruff format --check .
20
+ $ mypy # strict
21
+ $ python -m sphinx -W -b html docs docs/_build/html
22
+ ```
23
+
24
+ ## Guidelines
25
+
26
+ - Keep the core small. A feature that can live in user code or an optional module should.
27
+ - Every behaviour change needs a test; security-relevant code needs a test for the *attack*,
28
+ not only the happy path.
29
+ - Public API changes need docs (`docs/`) and a `CHANGELOG.md` entry under *Unreleased*.
30
+ - Performance claims need a benchmark (`benchmarks/run.py`).
31
+ - Write code that reads like the code around it.
32
+
33
+ ## Releasing (maintainers)
34
+
35
+ 1. Update `__version__` in `src/rational/__init__.py` and move *Unreleased* entries in
36
+ `CHANGELOG.md` under the new version.
37
+ 2. Tag `vX.Y.Z` and push the tag. The *Release* workflow builds, checks, and publishes to PyPI
38
+ through trusted publishing.
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <https://unlicense.org>
@@ -0,0 +1,308 @@
1
+ Metadata-Version: 2.5
2
+ Name: rational
3
+ Version: 0.1.0a1
4
+ Summary: A Python-first, object-oriented web framework: server-rendered components, typed styles, explicit remote calls.
5
+ Project-URL: Homepage, https://github.com/DutraIRS/Rational-Web
6
+ Project-URL: Repository, https://github.com/DutraIRS/Rational-Web
7
+ Project-URL: Issues, https://github.com/DutraIRS/Rational-Web/issues
8
+ Project-URL: Changelog, https://github.com/DutraIRS/Rational-Web/blob/main/CHANGELOG.md
9
+ Project-URL: Funding, https://ko-fi.com/dutrairs
10
+ Author: Iago Riveiro Santos Dutra
11
+ License-Expression: Unlicense
12
+ License-File: LICENSE
13
+ Keywords: components,css-in-python,fastapi,framework,reactive,ssr,web
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Web Environment
16
+ Classifier: Framework :: FastAPI
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
26
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.11
29
+ Requires-Dist: fastapi>=0.110
30
+ Requires-Dist: pydantic>=2.6
31
+ Requires-Dist: uvicorn>=0.27
32
+ Provides-Extra: browser
33
+ Requires-Dist: playwright>=1.44; extra == 'browser'
34
+ Provides-Extra: db
35
+ Requires-Dist: aiosqlite>=0.19; extra == 'db'
36
+ Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'db'
37
+ Provides-Extra: dev
38
+ Requires-Dist: aiosqlite>=0.19; extra == 'dev'
39
+ Requires-Dist: build>=1.2; extra == 'dev'
40
+ Requires-Dist: httpx2>=2; extra == 'dev'
41
+ Requires-Dist: httpx<0.28,>=0.27; extra == 'dev'
42
+ Requires-Dist: hypothesis>=6; extra == 'dev'
43
+ Requires-Dist: mypy>=1.10; extra == 'dev'
44
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
45
+ Requires-Dist: pytest>=8; extra == 'dev'
46
+ Requires-Dist: ruff>=0.5; extra == 'dev'
47
+ Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'dev'
48
+ Requires-Dist: twine>=5; extra == 'dev'
49
+ Provides-Extra: docs
50
+ Requires-Dist: furo>=2024.1; extra == 'docs'
51
+ Requires-Dist: sphinx-autobuild>=2024.2; extra == 'docs'
52
+ Requires-Dist: sphinx>=7; extra == 'docs'
53
+ Provides-Extra: telemetry
54
+ Requires-Dist: opentelemetry-api>=1.25; extra == 'telemetry'
55
+ Provides-Extra: testing
56
+ Requires-Dist: httpx2>=2; extra == 'testing'
57
+ Requires-Dist: httpx<0.28,>=0.27; extra == 'testing'
58
+ Description-Content-Type: text/markdown
59
+
60
+ # Rational Web
61
+
62
+ *Trying to make web development less irrational.*
63
+
64
+ [![PyPI](https://img.shields.io/pypi/v/rational.svg)](https://pypi.org/project/rational/)
65
+ [![Python](https://img.shields.io/pypi/pyversions/rational.svg)](https://pypi.org/project/rational/)
66
+ [![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](LICENSE)
67
+
68
+ Rational Web is a Python-first, object-oriented web framework. Pages, components, services
69
+ and models are Python objects. HTML, CSS and JavaScript still exist, but as implementation
70
+ details behind one consistent, typed, inspectable API. It is built on FastAPI, Pydantic and
71
+ (optionally) SQLAlchemy.
72
+
73
+ ```python
74
+ from rational import App, Button, Column, Component, Page, State, Text
75
+
76
+ app = App()
77
+
78
+
79
+ class Counter(Component):
80
+ count = State(0)
81
+
82
+ def increment(self):
83
+ self.count += 1
84
+
85
+ def render(self):
86
+ return Column(Text(f"Count: {self.count}"), Button("Increment", on_click=self.increment))
87
+
88
+
89
+ @app.page("/")
90
+ class Home(Page):
91
+ def render(self):
92
+ return Counter()
93
+
94
+
95
+ if __name__ == "__main__":
96
+ app.run()
97
+ ```
98
+
99
+ ```console
100
+ $ pip install rational
101
+ $ python app.py --dev # hot reload, live browser reload, readable errors
102
+ $ python app.py --prod # production: pages at /, JSON API at /api, one origin
103
+ ```
104
+
105
+ > **Status: alpha (0.1).** The architecture is implemented and tested end to end, including in
106
+ > a real browser, but the public API may still change before 1.0.
107
+
108
+ ## Why
109
+
110
+ Full-stack Python today usually means two codebases: a Python API and a JavaScript front end,
111
+ glued together by hand-written fetch calls, duplicated validation and a build pipeline.
112
+ Rational Web keeps the whole application in Python **without pretending the browser isn't
113
+ there**:
114
+
115
+ - **Server-rendered first.** Every page is real HTML before any script runs. Static pages ship
116
+ zero JavaScript. Interactive pages load a 4 KiB (gzipped) runtime that wires events and
117
+ morphs updated HTML into place, preserving focus and typing.
118
+ - **Explicit boundaries.** Event handlers always run on the server. Only methods marked
119
+ `@remote` become an HTTP API. Nothing is callable from the browser by accident.
120
+ - **Typed boundaries.** Path parameters, event payloads and API bodies are validated from your
121
+ annotations by Pydantic. Invalid form input never reaches your handler and shows up next to
122
+ the right field. Component prop annotations support static typing; runtime checks cover required
123
+ and unexpected prop names.
124
+ - **Styles as values.** Immutable `Style` objects compile to deduplicated, hashed CSS classes,
125
+ with theme tokens, pseudo-states, `@media` breakpoints and `@container` queries. You get no
126
+ specificity wars, no class-name soup and no CSS injection.
127
+ - **Secure by default.** Escaped output, session-bound events, CSRF tokens, session rotation,
128
+ rate and size limits, and an optional nonce-based CSP.
129
+
130
+ ## Features
131
+
132
+ | | |
133
+ |---|---|
134
+ | Components | Props from annotations, `State` (component/page/session scope), keyed identity, async `mount()`/`render()` |
135
+ | Primitives | `Text`, `Heading`, `Button`, `Link`, `Image`, `Badge`, `Avatar`, `Row`/`Column`/`Stack`/`Grid`, `Card`, `Container`, `Form`, `Input`, `Textarea`, `Select`, `Checkbox`, semantic regions, `h()` for anything else |
136
+ | Events | `on_click`, `on_input` (debounced), `on_change`, `on_submit`, `on_enter`, `on_keydown`, `on_blur`; sync or async; lambdas capture values server-side |
137
+ | Forms | Pydantic validation from handler annotations; `FieldError` for business rules; messages shown under the right field |
138
+ | Local interactions | `js.confirm`, `js.toggle_class`, `js.set_attr`, `js.focus`, `js.set_theme`... run with no round trip |
139
+ | Styling | `Style` merging with `\|`, four theme presets, light/dark/system, responsive breakpoints and container queries |
140
+ | Backend | `Service` + `@remote` → typed JSON API under `/api`, interactive **Swagger UI** at `/api/docs`; async SQLAlchemy |
141
+ | Projects | `Router` (pages and services in their own modules, with prefixes) and a composition root in `app.py` |
142
+ | Developer experience | `--dev` hot reload on every OS, live browser reload, error pages that point at *your* line with hints |
143
+ | Deployment | `--prod`, `python -m rational`, any ASGI server, mountable under a sub-path, proxy-aware |
144
+ | Testing | `rational.testing.TestClient` drives pages by accessible name: `page.click("Save")` |
145
+
146
+ ## Examples
147
+
148
+ Ten complete applications, each laid out like a real project (see [`examples/`](examples/)):
149
+
150
+ | Example | What you'll learn |
151
+ |---|---|
152
+ | [hello](examples/hello/) | The smallest app: pages, primitives, zero-JS output |
153
+ | [counter](examples/counter/) | State, keys, events, debounced input |
154
+ | [media](examples/media/) | Favicons, local and remote images, links, video and document embeds |
155
+ | [dashboard](examples/dashboard/) | Breakpoints, container queries, fluid grids, no custom CSS |
156
+ | [cv-portfolio](examples/cv-portfolio/) | Content as data, function components, a honeypot contact form |
157
+ | [todos](examples/todos/) | Databases, services, per-user isolation, the JSON API |
158
+ | [blog](examples/blog/) | Path parameters, safe rich text, a live-preview editor, RSS |
159
+ | [user-platform](examples/user-platform/) | Password hashing, authenticators, `FieldError`, roles and an admin area |
160
+ | [marketplace](examples/marketplace/) | A session cart shared across tabs, transactional checkout, money |
161
+ | [social-network](examples/social-network/) | Follows, likes and timelines with a fixed number of queries |
162
+
163
+ ```console
164
+ $ pip install "rational[db]"
165
+ $ cd examples/marketplace
166
+ $ python app.py --dev
167
+ ```
168
+
169
+ ## Documentation
170
+
171
+ The documentation is a Sphinx site in [`docs/`](docs/):
172
+
173
+ - **Tutorial:** build a guestbook step by step, from one file to a tested, deployed project. Every
174
+ step is a runnable file, executed by the test suite.
175
+ - **Examples:** a file-by-file walkthrough of all ten examples.
176
+ - **Guide:** components, styling, services and the API, project structure, running and deploying,
177
+ security, testing.
178
+ - **Background and reference:** the design record, benchmarks and the full API reference.
179
+
180
+ Preview it locally, rebuilding as you edit:
181
+
182
+ ```console
183
+ $ pip install -e ".[docs]"
184
+ $ sphinx-autobuild docs docs/_build/html # then open http://127.0.0.1:8000
185
+ ```
186
+
187
+ Publishing is ready on two hosts. The `Docs` GitHub Actions workflow builds the site with warnings
188
+ as errors and deploys it to GitHub Pages (enable *Settings → Pages → Source: GitHub Actions* once),
189
+ and `.readthedocs.yaml` configures Read the Docs.
190
+
191
+ ## Benchmarks
192
+
193
+ Is it blazingly fast? It's Python, so not in the Rust or Go sense. It is built so that the
194
+ framework is never the bottleneck of a typical page, and so that the browser does very little
195
+ work. The numbers below are measured, not claimed: run `python benchmarks/run.py` to
196
+ reproduce them.
197
+
198
+ These are full requests through the ASGI stack in-process (routing, rendering, CSS compilation,
199
+ sessions, JSON) on **one CPU core**, with no network. The baseline is plain FastAPI returning
200
+ an equivalent *pre-built* HTML string. Measured on Python 3.14.7, Windows 11, Intel Core
201
+ (Tiger Lake).
202
+
203
+ | Scenario | Requests/s | p50 | p95 |
204
+ |---|---:|---:|---:|
205
+ | FastAPI baseline: prebuilt HTML string | 2,651 | 0.35 ms | 0.56 ms |
206
+ | **Rational: static page** | 1,942 | 0.48 ms | 0.76 ms |
207
+ | **Rational: interactive page** (counter) | 1,390 | 0.66 ms | 0.88 ms |
208
+ | **Rational: event round trip** (click → patch) | 1,390 | 0.69 ms | 0.92 ms |
209
+ | Rational: 100-row styled table page | 390 | 2.39 ms | 2.91 ms |
210
+
211
+ | On the wire | gzip |
212
+ |---|---:|
213
+ | `runtime.js` (interactive pages only, cached) | 4.0 KiB |
214
+ | Theme CSS (immutable, cached) | 1.2 KiB |
215
+ | A click's response | 0.2 KiB |
216
+
217
+ - A static page keeps ~73% of raw FastAPI throughput while rendering components and
218
+ compiling CSS.
219
+ - An interaction costs well under 1 ms of server time, so network latency dominates.
220
+ - Rendering costs about 18 µs per element, so pages with thousands of rows should paginate.
221
+
222
+ See [docs/benchmarks.rst](docs/benchmarks.rst) for the method and the micro-benchmarks.
223
+
224
+ ## Manifesto
225
+
226
+ 1. **Python is the interface; the web platform is the implementation.** We don't hide HTML,
227
+ CSS, JavaScript or HTTP. We give them one coherent, typed front door.
228
+ 2. **The network is a boundary, not a detail.** Where code runs must always be obvious. Nothing
229
+ crosses the wire unless you said so.
230
+ 3. **HTML first.** A page should be useful before a single script runs. JavaScript is an
231
+ enhancement, measured in kilobytes.
232
+ 4. **Secure is the default, not a setting.** Escaping, CSRF, validation and isolation are on
233
+ before you know you need them.
234
+ 5. **Values over magic.** Styles are immutable values, render output is plain data, and state
235
+ changes are explicit. What happens can be inspected.
236
+ 6. **Composition over inheritance.** Small pieces combine: `Style | Style`, components in
237
+ components, services in services.
238
+ 7. **Escape hatches, always.** Raw CSS, raw HTML, FastAPI routes, SQLAlchemy queries and
239
+ custom JavaScript are always available. The framework should never trap you.
240
+ 8. **Measure, don't claim.** Performance, accessibility and security are tested, not
241
+ asserted.
242
+
243
+ ## Roadmap
244
+
245
+ **0.1: foundation** *(this release)*
246
+ - [x] Component model, `State` scopes, dependency tracking, keyed identity
247
+ - [x] Safe HTML IR and serializer, typed `Style` compiler, four themes, responsive variants
248
+ - [x] HTTP event protocol with morph patches, CSRF, session binding, validation, rate limits
249
+ - [x] Services with `@remote` → typed JSON API, async SQLAlchemy integration
250
+ - [x] `--dev` (hot reload, live reload, readable errors) and `--prod` modes
251
+ - [x] `Router` for multi-file projects; Swagger UI that works with sessions and CSRF
252
+ - [x] Test client, Playwright browser tests, benchmarks
253
+ - [x] Sphinx documentation with a step-by-step tutorial and ten example projects
254
+
255
+ **0.2: interactivity**
256
+ - [ ] WebSocket transport (optional) and server push for collaborative pages
257
+ - [ ] Client-side navigation between pages, with no full reload
258
+ - [ ] Finer-grained server diffs (text and attribute patches)
259
+ - [ ] More components: `Modal`, `Tabs`, `Table`, `Toast`, `Menu`, with full keyboard support
260
+ - [ ] Pluggable session store (for example Redis); page runtimes remain worker-local, so
261
+ multi-worker deployments still need sticky routing
262
+
263
+ **0.3: productivity**
264
+ - [ ] `rational new` project scaffolding and an Alembic migration workflow
265
+ - [x] Structured logging and request telemetry (`app.logging`): request IDs, event timing,
266
+ slow-render tracing, standard-library logs + optional OpenTelemetry spans
267
+ - [ ] Streaming SSR and loading states
268
+ - [ ] Form helpers bound to Pydantic models
269
+ - [ ] Internationalization
270
+ - [ ] Optional Bootstrap and Tailwind adapters
271
+
272
+ **1.0: stability**
273
+ - [ ] Frozen public API with a deprecation policy
274
+ - [ ] Accessibility audit and an automated a11y suite
275
+ - [ ] Browser matrix in CI (Chromium, Firefox, WebKit) and load tests
276
+ - [ ] Independent security review
277
+
278
+ Have an idea? Open an issue. The roadmap is a conversation.
279
+
280
+ ## Contributing
281
+
282
+ ```console
283
+ git clone https://github.com/DutraIRS/Rational-Web.git
284
+ cd Rational-Web
285
+ python -m venv .venv
286
+ . .venv/bin/activate # Windows: .venv\Scripts\activate
287
+ pip install -e ".[dev,docs,browser]"
288
+ pytest # browser tests run if Chromium is available
289
+ ruff check . && ruff format --check . && mypy
290
+ ```
291
+
292
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Report security issues privately as described in
293
+ [SECURITY.md](SECURITY.md).
294
+
295
+ ## Sponsor
296
+
297
+ Rational Web is free, public-domain software built in the open. If it saves you time, or you
298
+ want to see the roadmap happen sooner, please consider sponsoring:
299
+
300
+ - **Ko-fi:** https://ko-fi.com/dutrairs
301
+ - **Companies:** sponsorship can fund a specific roadmap item. Get in touch through GitHub.
302
+
303
+ Sponsors are thanked in the release notes. Thank you for helping keep web development
304
+ rational. ❤️
305
+
306
+ ## License
307
+
308
+ Public domain, under the [Unlicense](LICENSE).