quantum-framework 0.11.0__tar.gz → 0.13.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (365) hide show
  1. {quantum_framework-0.11.0/quantum_framework.egg-info → quantum_framework-0.13.0}/PKG-INFO +5 -1
  2. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/pyproject.toml +16 -2
  3. quantum_framework-0.13.0/quantum/cli/admin.py +131 -0
  4. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/commands/migrate.py +31 -3
  5. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/runner.py +13 -0
  6. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/ast_nodes.py +1 -0
  7. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/expression_stdlib.py +28 -2
  8. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/expressions.py +81 -16
  9. quantum_framework-0.13.0/quantum/core/guards.py +53 -0
  10. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parser.py +175 -12
  11. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/control_flow/if_parser.py +25 -22
  12. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/data/invoke_parser.py +4 -1
  13. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/forms/action_parser.py +17 -7
  14. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/action_handler.py +27 -13
  15. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/component.py +49 -24
  16. quantum_framework-0.13.0/quantum/runtime/component_composer.py +133 -0
  17. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/database_service.py +32 -40
  18. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/error_handler.py +1 -1
  19. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/execution_context.py +0 -3
  20. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/__init__.py +2 -2
  21. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/base.py +3 -1
  22. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/control_flow/__init__.py +2 -1
  23. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/control_flow/if_executor.py +14 -10
  24. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/control_flow/loop_executor.py +6 -6
  25. quantum_framework-0.13.0/quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
  26. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/data/invoke_executor.py +60 -15
  27. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/renderer.py +21 -22
  28. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/web_server.py +155 -49
  29. quantum_framework-0.13.0/quantum/services.py +109 -0
  30. quantum_framework-0.13.0/quantum_admin/__init__.py +4 -0
  31. quantum_framework-0.13.0/quantum_admin/assets/quantum-admin-connectors.js +123 -0
  32. quantum_framework-0.13.0/quantum_admin/assets/quantum-admin.css +2058 -0
  33. quantum_framework-0.13.0/quantum_admin/assets/quantum-source-highlight.js +106 -0
  34. quantum_framework-0.13.0/quantum_admin/backend/__init__.py +4 -0
  35. quantum_framework-0.13.0/quantum_admin/backend/audit_service.py +346 -0
  36. quantum_framework-0.13.0/quantum_admin/backend/auth_service.py +394 -0
  37. quantum_framework-0.13.0/quantum_admin/backend/component_discovery.py +766 -0
  38. quantum_framework-0.13.0/quantum_admin/backend/config_generator.py +586 -0
  39. quantum_framework-0.13.0/quantum_admin/backend/connector_service.py +1016 -0
  40. quantum_framework-0.13.0/quantum_admin/backend/crud.py +557 -0
  41. quantum_framework-0.13.0/quantum_admin/backend/database.py +226 -0
  42. quantum_framework-0.13.0/quantum_admin/backend/db_setup_service.py +193 -0
  43. quantum_framework-0.13.0/quantum_admin/backend/deploy_service.py +698 -0
  44. quantum_framework-0.13.0/quantum_admin/backend/docker_service.py +422 -0
  45. quantum_framework-0.13.0/quantum_admin/backend/environment_service.py +418 -0
  46. quantum_framework-0.13.0/quantum_admin/backend/git_service.py +370 -0
  47. quantum_framework-0.13.0/quantum_admin/backend/health_service.py +390 -0
  48. quantum_framework-0.13.0/quantum_admin/backend/job_service.py +347 -0
  49. quantum_framework-0.13.0/quantum_admin/backend/log_service.py +331 -0
  50. quantum_framework-0.13.0/quantum_admin/backend/main.py +14426 -0
  51. quantum_framework-0.13.0/quantum_admin/backend/models.py +986 -0
  52. quantum_framework-0.13.0/quantum_admin/backend/pipeline_service.py +812 -0
  53. quantum_framework-0.13.0/quantum_admin/backend/resource_manager.py +1118 -0
  54. quantum_framework-0.13.0/quantum_admin/backend/schemas.py +419 -0
  55. quantum_framework-0.13.0/quantum_admin/backend/secret_manager.py +277 -0
  56. quantum_framework-0.13.0/quantum_admin/backend/settings_service.py +488 -0
  57. quantum_framework-0.13.0/quantum_admin/backend/template_service.py +710 -0
  58. quantum_framework-0.13.0/quantum_admin/backend/test_execution_service.py +364 -0
  59. quantum_framework-0.13.0/quantum_admin/backend/test_generator.py +782 -0
  60. quantum_framework-0.13.0/quantum_admin/backend/webhook_service.py +454 -0
  61. quantum_framework-0.13.0/quantum_admin/backend/websocket_manager.py +360 -0
  62. quantum_framework-0.13.0/quantum_admin/components/admin/_layout/AdminShell.q +121 -0
  63. quantum_framework-0.13.0/quantum_admin/components/admin/agents.q +107 -0
  64. quantum_framework-0.13.0/quantum_admin/components/admin/app/[name].q +558 -0
  65. quantum_framework-0.13.0/quantum_admin/components/admin/applications.q +160 -0
  66. quantum_framework-0.13.0/quantum_admin/components/admin/component/[...path].q +205 -0
  67. quantum_framework-0.13.0/quantum_admin/components/admin/components.q +59 -0
  68. quantum_framework-0.13.0/quantum_admin/components/admin/connectors.q +283 -0
  69. quantum_framework-0.13.0/quantum_admin/components/admin/dashboard.q +86 -0
  70. quantum_framework-0.13.0/quantum_admin/components/admin/database.q +95 -0
  71. quantum_framework-0.13.0/quantum_admin/components/admin/features.q +69 -0
  72. quantum_framework-0.13.0/quantum_admin/components/admin/index.q +12 -0
  73. quantum_framework-0.13.0/quantum_admin/components/admin/jobs.q +75 -0
  74. quantum_framework-0.13.0/quantum_admin/components/admin/login.q +65 -0
  75. quantum_framework-0.13.0/quantum_admin/components/admin/logout.q +11 -0
  76. quantum_framework-0.13.0/quantum_admin/components/admin/settings.q +174 -0
  77. quantum_framework-0.13.0/quantum_admin/components/admin/source.q +45 -0
  78. quantum_framework-0.13.0/quantum_admin/components/admin/tests.q +73 -0
  79. quantum_framework-0.13.0/quantum_admin/run.py +13 -0
  80. quantum_framework-0.13.0/quantum_admin/services/__init__.py +16 -0
  81. quantum_framework-0.13.0/quantum_admin/services/_base.py +84 -0
  82. quantum_framework-0.13.0/quantum_admin/services/_component_test_generator.py +205 -0
  83. quantum_framework-0.13.0/quantum_admin/services/_yaml_editor.py +82 -0
  84. quantum_framework-0.13.0/quantum_admin/services/apps.py +330 -0
  85. quantum_framework-0.13.0/quantum_admin/services/auth.py +61 -0
  86. quantum_framework-0.13.0/quantum_admin/services/components.py +210 -0
  87. quantum_framework-0.13.0/quantum_admin/services/connectors.py +164 -0
  88. quantum_framework-0.13.0/quantum_admin/services/projects.py +189 -0
  89. quantum_framework-0.13.0/quantum_admin/services/repository.py +223 -0
  90. quantum_framework-0.13.0/quantum_admin/services/settings.py +150 -0
  91. quantum_framework-0.13.0/quantum_admin/services/yaml_import.py +118 -0
  92. {quantum_framework-0.11.0 → quantum_framework-0.13.0/quantum_framework.egg-info}/PKG-INFO +5 -1
  93. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum_framework.egg-info/SOURCES.txt +66 -1
  94. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum_framework.egg-info/requires.txt +5 -0
  95. quantum_framework-0.13.0/quantum_framework.egg-info/top_level.txt +2 -0
  96. quantum_framework-0.11.0/quantum/runtime/component_composer.py +0 -320
  97. quantum_framework-0.11.0/quantum_framework.egg-info/top_level.txt +0 -1
  98. quantum_framework-0.11.0/tests/test_projects_blog_components_admin.py +0 -514
  99. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/LICENSE +0 -0
  100. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/README.md +0 -0
  101. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/__init__.py +0 -0
  102. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/__init__.py +0 -0
  103. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/commands/__init__.py +0 -0
  104. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/commands/build.py +0 -0
  105. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/commands/dev.py +0 -0
  106. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/commands/docs.py +0 -0
  107. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/commands/lint.py +0 -0
  108. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/commands/new.py +0 -0
  109. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/commands/serve.py +0 -0
  110. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/commands/test.py +0 -0
  111. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/deploy.py +0 -0
  112. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/hot_reload.py +0 -0
  113. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/jobs.py +0 -0
  114. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/mq.py +0 -0
  115. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/pkg.py +0 -0
  116. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/server_process.py +0 -0
  117. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/cli/utils.py +0 -0
  118. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/__init__.py +0 -0
  119. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/base_generator.py +0 -0
  120. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/cli.py +0 -0
  121. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/expression_transformer.py +0 -0
  122. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/javascript/__init__.py +0 -0
  123. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/javascript/generator.py +0 -0
  124. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/optimizer.py +0 -0
  125. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/python/__init__.py +0 -0
  126. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/python/generator.py +0 -0
  127. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/python/runtime.py +0 -0
  128. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/compiler/transpiler.py +0 -0
  129. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/__init__.py +0 -0
  130. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/config_env.py +0 -0
  131. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/expression_diagnostics.py +0 -0
  132. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/agents/src/__init__.py +0 -0
  133. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/agents/src/ast_node.py +0 -0
  134. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/conditionals/src/__init__.py +0 -0
  135. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/conditionals/src/ast_node.py +0 -0
  136. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/data_import/src/__init__.py +0 -0
  137. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/data_import/src/ast_node.py +0 -0
  138. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/data_import/src/runtime.py +0 -0
  139. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/dump/src/__init__.py +0 -0
  140. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/dump/src/ast_node.py +0 -0
  141. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/dump/src/parser.py +0 -0
  142. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/dump/src/runtime.py +0 -0
  143. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/functions/src/__init__.py +0 -0
  144. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/functions/src/ast_node.py +0 -0
  145. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/game_engine_2d/src/__init__.py +0 -0
  146. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/game_engine_2d/src/ast_nodes.py +0 -0
  147. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/game_engine_2d/src/parser.py +0 -0
  148. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/invocation/src/__init__.py +0 -0
  149. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/invocation/src/ast_node.py +0 -0
  150. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/invocation/src/runtime.py +0 -0
  151. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/knowledge_base/src/__init__.py +0 -0
  152. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/knowledge_base/src/ast_node.py +0 -0
  153. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/knowledge_base/src/parser.py +0 -0
  154. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/logging/src/__init__.py +0 -0
  155. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/logging/src/ast_node.py +0 -0
  156. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/logging/src/parser.py +0 -0
  157. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/logging/src/runtime.py +0 -0
  158. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/loops/src/__init__.py +0 -0
  159. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/loops/src/ast_node.py +0 -0
  160. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/query/src/__init__.py +0 -0
  161. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/query/src/database_service.py +0 -0
  162. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/query/src/query_validators.py +0 -0
  163. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/state_management/src/__init__.py +0 -0
  164. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/state_management/src/ast_node.py +0 -0
  165. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/terminal_engine/src/__init__.py +0 -0
  166. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/terminal_engine/src/ast_nodes.py +0 -0
  167. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/terminal_engine/src/parser.py +0 -0
  168. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/testing_engine/src/__init__.py +0 -0
  169. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/testing_engine/src/ast_nodes.py +0 -0
  170. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/testing_engine/src/parser.py +0 -0
  171. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/theming/src/__init__.py +0 -0
  172. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/theming/src/ast_node.py +0 -0
  173. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/theming/src/presets.py +0 -0
  174. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/ui_engine/src/__init__.py +0 -0
  175. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/ui_engine/src/ast_nodes.py +0 -0
  176. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/ui_engine/src/parser.py +0 -0
  177. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/websocket/src/__init__.py +0 -0
  178. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/features/websocket/src/ast_node.py +0 -0
  179. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/html_compat.py +0 -0
  180. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parser_registry.py +0 -0
  181. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/__init__.py +0 -0
  182. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/ai/__init__.py +0 -0
  183. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/ai/agent_parser.py +0 -0
  184. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/ai/knowledge_parser.py +0 -0
  185. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/ai/llm_parser.py +0 -0
  186. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/ai/team_parser.py +0 -0
  187. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/base.py +0 -0
  188. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/composition/__init__.py +0 -0
  189. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/composition/import_parser.py +0 -0
  190. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/composition/slot_parser.py +0 -0
  191. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/control_flow/__init__.py +0 -0
  192. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/control_flow/loop_parser.py +0 -0
  193. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/control_flow/set_parser.py +0 -0
  194. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/data/__init__.py +0 -0
  195. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/data/data_parser.py +0 -0
  196. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/data/query_parser.py +0 -0
  197. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/data/transaction_parser.py +0 -0
  198. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/events/__init__.py +0 -0
  199. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/events/dispatch_event_parser.py +0 -0
  200. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/forms/__init__.py +0 -0
  201. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/forms/flash_parser.py +0 -0
  202. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/forms/redirect_parser.py +0 -0
  203. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/functions/__init__.py +0 -0
  204. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/functions/function_parser.py +0 -0
  205. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/functions/param_parser.py +0 -0
  206. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/functions/return_parser.py +0 -0
  207. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/html/__init__.py +0 -0
  208. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/html/component_call_parser.py +0 -0
  209. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/html/html_parser.py +0 -0
  210. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/jobs/__init__.py +0 -0
  211. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/jobs/job_parser.py +0 -0
  212. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/jobs/schedule_parser.py +0 -0
  213. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/jobs/thread_parser.py +0 -0
  214. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/messaging/__init__.py +0 -0
  215. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/messaging/message_ack_parser.py +0 -0
  216. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/messaging/message_nack_parser.py +0 -0
  217. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/messaging/message_parser.py +0 -0
  218. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/messaging/queue_parser.py +0 -0
  219. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/messaging/websocket_parser.py +0 -0
  220. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/persistence/__init__.py +0 -0
  221. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/persistence/persist_parser.py +0 -0
  222. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/routing/__init__.py +0 -0
  223. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/routing/route_parser.py +0 -0
  224. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/scripting/__init__.py +0 -0
  225. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/scripting/pyclass_parser.py +0 -0
  226. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/scripting/pydecorator_parser.py +0 -0
  227. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/scripting/pyimport_parser.py +0 -0
  228. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/scripting/python_parser.py +0 -0
  229. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/services/__init__.py +0 -0
  230. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/services/dump_parser.py +0 -0
  231. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/services/file_parser.py +0 -0
  232. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/services/log_parser.py +0 -0
  233. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/parsers/services/mail_parser.py +0 -0
  234. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/core/tiers.py +0 -0
  235. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/packages/__init__.py +0 -0
  236. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/packages/manager.py +0 -0
  237. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/packages/manifest.py +0 -0
  238. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/packages/registry.py +0 -0
  239. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/packages/resolver.py +0 -0
  240. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/plugins/__init__.py +0 -0
  241. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/plugins/hooks.py +0 -0
  242. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/plugins/loader.py +0 -0
  243. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/plugins/manifest.py +0 -0
  244. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/plugins/registry.py +0 -0
  245. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/__init__.py +0 -0
  246. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/adapters/__init__.py +0 -0
  247. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/adapters/memory_adapter.py +0 -0
  248. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/adapters/rabbitmq_adapter.py +0 -0
  249. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/adapters/redis_adapter.py +0 -0
  250. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/adapters/sqlite_adapter.py +0 -0
  251. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/agent_service.py +0 -0
  252. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ast_cache.py +0 -0
  253. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/auth_service.py +0 -0
  254. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/component_resolver.py +0 -0
  255. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/email_service.py +0 -0
  256. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executor_registry.py +0 -0
  257. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/ai/__init__.py +0 -0
  258. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/ai/agent_executor.py +0 -0
  259. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/ai/knowledge_executor.py +0 -0
  260. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/ai/llm_executor.py +0 -0
  261. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/ai/team_executor.py +0 -0
  262. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/control_flow/set_executor.py +0 -0
  263. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/data/__init__.py +0 -0
  264. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/data/data_executor.py +0 -0
  265. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/data/query_executor.py +0 -0
  266. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/data/transaction_executor.py +0 -0
  267. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/jobs/__init__.py +0 -0
  268. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/jobs/job_executor.py +0 -0
  269. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/jobs/schedule_executor.py +0 -0
  270. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/jobs/thread_executor.py +0 -0
  271. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/messaging/__init__.py +0 -0
  272. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/messaging/message_ack_executor.py +0 -0
  273. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/messaging/message_executor.py +0 -0
  274. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/messaging/queue_executor.py +0 -0
  275. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/messaging/websocket_executor.py +0 -0
  276. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/scripting/__init__.py +0 -0
  277. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/scripting/pyclass_executor.py +0 -0
  278. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/scripting/pyimport_executor.py +0 -0
  279. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/scripting/python_executor.py +0 -0
  280. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/services/__init__.py +0 -0
  281. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/services/dump_executor.py +0 -0
  282. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/services/file_executor.py +0 -0
  283. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/services/log_executor.py +0 -0
  284. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/executors/services/mail_executor.py +0 -0
  285. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/expression_cache.py +0 -0
  286. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/file_upload_service.py +0 -0
  287. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/function_registry.py +0 -0
  288. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/game_builder.py +0 -0
  289. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/game_code_generator.py +0 -0
  290. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/game_templates.py +0 -0
  291. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/godot_code_generator.py +0 -0
  292. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/godot_templates.py +0 -0
  293. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/job_executor.py +0 -0
  294. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/knowledge_service.py +0 -0
  295. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/llm_cache.py +0 -0
  296. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/llm_providers.py +0 -0
  297. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/llm_service.py +0 -0
  298. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/logging_setup.py +0 -0
  299. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/message_broker.py +0 -0
  300. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/message_queue_service.py +0 -0
  301. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/param_validation.py +0 -0
  302. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/pypy_compat.py +0 -0
  303. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/python_bridge.py +0 -0
  304. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/query_validators.py +0 -0
  305. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/service_container.py +0 -0
  306. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/terminal_builder.py +0 -0
  307. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/terminal_code_generator.py +0 -0
  308. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/terminal_templates.py +0 -0
  309. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/testing_builder.py +0 -0
  310. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/testing_code_generator.py +0 -0
  311. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/testing_templates.py +0 -0
  312. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_builder.py +0 -0
  313. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_desktop_adapter.py +0 -0
  314. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_desktop_templates.py +0 -0
  315. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_html_adapter.py +0 -0
  316. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_html_templates.py +0 -0
  317. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_mobile_adapter.py +0 -0
  318. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_mobile_templates.py +0 -0
  319. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_textual_adapter.py +0 -0
  320. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_textual_templates.py +0 -0
  321. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_tokens.py +0 -0
  322. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/ui_validator.py +0 -0
  323. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/validators.py +0 -0
  324. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/websocket_adapter.py +0 -0
  325. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/websocket_service.py +0 -0
  326. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/websocket_transport.py +0 -0
  327. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/runtime/wsgi.py +0 -0
  328. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum/utils/__init__.py +0 -0
  329. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum_framework.egg-info/dependency_links.txt +0 -0
  330. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/quantum_framework.egg-info/entry_points.txt +0 -0
  331. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/setup.cfg +0 -0
  332. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_agent.py +0 -0
  333. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_animations.py +0 -0
  334. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_ast_cache.py +0 -0
  335. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_cli_commands.py +0 -0
  336. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_component_library.py +0 -0
  337. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_desktop_bridge.py +0 -0
  338. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_expression_cache.py +0 -0
  339. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_form_validation.py +0 -0
  340. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_game_codegen.py +0 -0
  341. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_game_e2e.py +0 -0
  342. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_game_engine.py +0 -0
  343. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_game_parser.py +0 -0
  344. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_godot_codegen.py +0 -0
  345. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_job_executor.py +0 -0
  346. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_llm_providers.py +0 -0
  347. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_message_broker.py +0 -0
  348. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_message_queue.py +0 -0
  349. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_mobile_target.py +0 -0
  350. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_multi_agent.py +0 -0
  351. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_packages.py +0 -0
  352. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_plugins.py +0 -0
  353. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_pypy_compat.py +0 -0
  354. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_python_scripting.py +0 -0
  355. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_state_persistence.py +0 -0
  356. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_terminal_codegen.py +0 -0
  357. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_terminal_parser.py +0 -0
  358. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_testing_codegen.py +0 -0
  359. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_testing_parser.py +0 -0
  360. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_theming.py +0 -0
  361. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_ui_html_adapter.py +0 -0
  362. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_ui_parity.py +0 -0
  363. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_ui_parser.py +0 -0
  364. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_ui_textual_adapter.py +0 -0
  365. {quantum_framework-0.11.0 → quantum_framework-0.13.0}/tests/test_websocket.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantum-framework
3
- Version: 0.11.0
3
+ Version: 0.13.0
4
4
  Summary: Full-stack declarative framework for building web, desktop, and mobile applications
5
5
  Author-email: Daniel Gregorio <danielgregorio2611@gmail.com>
6
6
  License: MIT
@@ -37,6 +37,10 @@ Provides-Extra: rag
37
37
  Requires-Dist: chromadb>=0.5.0; extra == "rag"
38
38
  Provides-Extra: jobs
39
39
  Requires-Dist: apscheduler>=3.10.0; extra == "jobs"
40
+ Provides-Extra: admin
41
+ Requires-Dist: sqlalchemy>=2.0; extra == "admin"
42
+ Requires-Dist: PyJWT>=2.8; extra == "admin"
43
+ Requires-Dist: cryptography>=42.0; extra == "admin"
40
44
  Provides-Extra: websocket
41
45
  Requires-Dist: websockets>=12.0; extra == "websocket"
42
46
  Dynamic: license-file
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quantum-framework"
7
- version = "0.11.0"
7
+ version = "0.13.0"
8
8
  description = "Full-stack declarative framework for building web, desktop, and mobile applications"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -50,6 +50,13 @@ jobs = [
50
50
  # q:schedule failed on any clean install.
51
51
  "apscheduler>=3.10.0",
52
52
  ]
53
+ admin = [
54
+ # `quantum admin`: the admin database (SQLAlchemy), its login tokens
55
+ # (PyJWT) and encrypted connector secrets (cryptography).
56
+ "sqlalchemy>=2.0",
57
+ "PyJWT>=2.8",
58
+ "cryptography>=42.0",
59
+ ]
53
60
  websocket = [
54
61
  # The transport behind q:websocket. Without it the tag registers a
55
62
  # connection and opens nothing — and says so, rather than pretending.
@@ -71,7 +78,14 @@ Repository = "https://github.com/danielgregorio/quantum"
71
78
  # do core), e as pastas `quantum-as4`/`quantum-lsp` (projetos irmaos,
72
79
  # hifenados e nem importaveis) — 91 arquivos de peso morto no wheel. O ponto
73
80
  # em `quantum.*` casa subpacotes; sem ele, `quantum_admin` nao entra.
74
- include = ["quantum", "quantum.*"]
81
+ include = ["quantum", "quantum.*",
82
+ # The admin (`quantum admin`, extra [admin]): its screens, its
83
+ # services and the backend library they use. The backend's
84
+ # FastAPI modules ship with it, unused by the screens.
85
+ "quantum_admin", "quantum_admin.services", "quantum_admin.backend"]
86
+
87
+ [tool.setuptools.package-data]
88
+ quantum_admin = ["components/**/*.q", "assets/*"]
75
89
 
76
90
 
77
91
  [tool.black]
@@ -0,0 +1,131 @@
1
+ """`quantum admin` — serve the Quantum Admin (quantum_admin/components/admin).
2
+
3
+ The admin is a Quantum app: .q screens over declared services. This command
4
+ writes its configuration into a data folder and starts the same server as
5
+ `quantum start`, so nothing has to be set up by hand.
6
+
7
+ quantum admin # data in ./.quantum-admin, projects under ./
8
+ quantum admin --data quantum_admin # the repository's own admin data
9
+
10
+ The data folder holds the admin database, its settings (connectors, global
11
+ settings, process PIDs) and the generated config. Paths of projects are
12
+ relative to the folder the command runs in (--root to change it).
13
+ """
14
+
15
+ import os
16
+ import secrets
17
+ from pathlib import Path
18
+
19
+ MODULES = [
20
+ "quantum_admin.services.auth",
21
+ "quantum_admin.services.projects",
22
+ "quantum_admin.services.yaml_import",
23
+ "quantum_admin.services.connectors",
24
+ "quantum_admin.services.settings",
25
+ "quantum_admin.services.components",
26
+ "quantum_admin.services.apps",
27
+ "quantum_admin.services.repository",
28
+ ]
29
+
30
+ INSTALL_HINT = 'pip install "quantum-framework[admin]"'
31
+
32
+
33
+ def create_admin_parser(subparsers):
34
+ parser = subparsers.add_parser('admin', help='Start the Quantum Admin')
35
+ parser.add_argument('--port', type=int, default=8090, help='Port (default 8090)')
36
+ parser.add_argument('--data', default='.quantum-admin',
37
+ help='Folder for the admin database and settings (default ./.quantum-admin)')
38
+ parser.add_argument('--root', default='.',
39
+ help='Folder the project paths are relative to (default: current folder)')
40
+ return parser
41
+
42
+
43
+ def missing_dependencies():
44
+ """Names of the [admin] extra's packages that do not import."""
45
+ faltando = []
46
+ for modulo, pacote in (("sqlalchemy", "SQLAlchemy"), ("jwt", "PyJWT"),
47
+ ("cryptography", "cryptography")):
48
+ try:
49
+ __import__(modulo)
50
+ except ImportError:
51
+ faltando.append(pacote)
52
+ return faltando
53
+
54
+
55
+ def _package() -> Path:
56
+ import quantum_admin
57
+ return Path(quantum_admin.__file__).resolve().parent
58
+
59
+
60
+ def screens_folder() -> Path:
61
+ return _package() / "components"
62
+
63
+
64
+ def assets_folder() -> Path:
65
+ """The screens' CSS and JS, served under /static/."""
66
+ return _package() / "assets"
67
+
68
+
69
+ def _secret(arquivo: Path) -> str:
70
+ """A key kept in the data folder, so sessions survive a restart."""
71
+ if arquivo.exists():
72
+ return arquivo.read_text(encoding="utf-8").strip()
73
+ chave = secrets.token_hex(32)
74
+ arquivo.write_text(chave, encoding="utf-8")
75
+ try:
76
+ arquivo.chmod(0o600)
77
+ except OSError:
78
+ pass
79
+ return chave
80
+
81
+
82
+ def prepare(data: str, root: str, port: int) -> Path:
83
+ """Create the data folder, point the admin at it, write the config. Returns its path."""
84
+ import yaml
85
+
86
+ pasta = Path(data).resolve()
87
+ (pasta / "settings").mkdir(parents=True, exist_ok=True)
88
+
89
+ # Read by quantum_admin.backend at import time, so set before any import.
90
+ os.environ["QUANTUM_ADMIN_DATABASE_URL"] = f"sqlite:///{(pasta / 'quantum_admin.db').as_posix()}"
91
+ os.environ["QUANTUM_ADMIN_SETTINGS_DIR"] = str(pasta / "settings")
92
+ os.environ["QUANTUM_ADMIN_ROOT"] = str(Path(root).resolve())
93
+ os.environ.setdefault("JWT_SECRET_KEY", _secret(pasta / "jwt_secret"))
94
+
95
+ config = pasta / "quantum.config.yaml"
96
+ config.write_text(
97
+ "# Generated by `quantum admin` on every start; edits are overwritten.\n" +
98
+ yaml.safe_dump({
99
+ "server": {"host": "127.0.0.1", "port": port, "reload": False, "debug": False},
100
+ "paths": {"components": screens_folder().as_posix(),
101
+ "static": assets_folder().as_posix(),
102
+ "uploads": (pasta / "uploads").as_posix(),
103
+ "logs": (pasta / "logs").as_posix()},
104
+ "security": {"login_url": "/admin/login", "secret_key": _secret(pasta / "session_secret")},
105
+ "logging": {"level": "WARNING", "console": True, "file": False},
106
+ "services": MODULES,
107
+ }, sort_keys=False),
108
+ encoding="utf-8")
109
+ return config
110
+
111
+
112
+ def handle_admin(args) -> int:
113
+ faltando = missing_dependencies()
114
+ if faltando:
115
+ print(f"[ERROR] The admin needs {', '.join(faltando)}. Install with:\n {INSTALL_HINT}")
116
+ return 1
117
+
118
+ config = prepare(args.data, args.root, args.port)
119
+
120
+ from quantum.runtime.web_server import QuantumWebServer
121
+ from quantum_admin.backend.auth_service import get_auth_service
122
+
123
+ # Created now, not at the first login, so a generated password is printed
124
+ # before the URL instead of in the middle of the request log.
125
+ get_auth_service()
126
+ print(f"\n Quantum Admin: http://127.0.0.1:{args.port}/admin"
127
+ f"\n data: {config.parent}\n")
128
+
129
+ server = QuantumWebServer(str(config))
130
+ server.PID_FILE = str(config.parent / "admin.pid")
131
+ return server.start() or 0
@@ -70,7 +70,9 @@ class DatabaseConnection:
70
70
  if not caminho.is_absolute():
71
71
  caminho = self.project_path / caminho
72
72
  caminho.parent.mkdir(parents=True, exist_ok=True)
73
- self._conn = sqlite3.connect(str(caminho))
73
+ # isolation_level=None: execute_script opens the transaction
74
+ # itself, so a CREATE TABLE is inside it too (DB-8).
75
+ self._conn = sqlite3.connect(str(caminho), isolation_level=None)
74
76
  self._db_type = 'sqlite'
75
77
  logger.info("Migrating datasource %s (SQLite at %s)", nome, caminho)
76
78
  return self._conn
@@ -128,6 +130,32 @@ class DatabaseConnection:
128
130
  cursor.execute(sql)
129
131
  return cursor
130
132
 
133
+ def execute_script(self, sql: str):
134
+ """Run a whole migration file, in one transaction (DB-8).
135
+
136
+ sqlite3's execute() takes ONE statement, so every migration with more
137
+ than one — a table and its index — failed with "You can only execute
138
+ one statement at a time". The statements are split here and run
139
+ inside an explicit BEGIN, which commit()/rollback() close: a failure
140
+ halfway leaves no table behind.
141
+ """
142
+ if self._db_type != 'sqlite':
143
+ return self.execute(sql)
144
+ import sqlite3
145
+ cursor = self._conn.cursor()
146
+ if not self._conn.in_transaction:
147
+ cursor.execute('BEGIN')
148
+ pedaco = ''
149
+ for linha in sql.splitlines(keepends=True):
150
+ pedaco += linha
151
+ if sqlite3.complete_statement(pedaco):
152
+ if pedaco.strip():
153
+ cursor.execute(pedaco)
154
+ pedaco = ''
155
+ if pedaco.strip() and not all(l.strip().startswith('--') or not l.strip() for l in pedaco.splitlines()):
156
+ raise MigrationError(f"incomplete SQL statement at the end: {pedaco.strip()[:80]!r}")
157
+ return cursor
158
+
131
159
  def commit(self):
132
160
  self._conn.commit()
133
161
 
@@ -275,7 +303,7 @@ class MigrationRunner:
275
303
  click.echo(f"Applying {migration.version}: {migration.name}...")
276
304
 
277
305
  # Execute migration SQL
278
- self.db.execute(migration.content)
306
+ self.db.execute_script(migration.content)
279
307
 
280
308
  # Record migration
281
309
  self._record_migration(migration)
@@ -338,7 +366,7 @@ class MigrationRunner:
338
366
  click.echo(f"Rolling back {version}: {migration_info['name']}...")
339
367
 
340
368
  # Execute rollback SQL
341
- self.db.execute(down_file.read_text())
369
+ self.db.execute_script(down_file.read_text())
342
370
 
343
371
  # Remove migration record
344
372
  self._remove_migration_record(version)
@@ -24,6 +24,7 @@ if os.name == 'nt': # Windows
24
24
  from quantum.core.parser import QuantumParser, QuantumParseError
25
25
  from quantum.core.ast_nodes import ComponentNode, ApplicationNode, JobNode
26
26
  from quantum.runtime.component import ComponentRuntime, ComponentExecutionError
27
+ from quantum.runtime.executors.control_flow.redirect_executor import PageRedirect
27
28
  from quantum.runtime.web_server import QuantumWebServer
28
29
 
29
30
  # Import deploy commands (lazy load to avoid import errors if requests not installed)
@@ -166,6 +167,9 @@ class QuantumRunner:
166
167
  except ComponentExecutionError as e:
167
168
  print(f"[ERROR] Execution error: {e}")
168
169
  return 1
170
+ except PageRedirect as r:
171
+ print(f"[REDIRECT] {r.status} {r.url}")
172
+ return 0
169
173
 
170
174
  def _execute_application(self, app: ApplicationNode, debug: bool = False) -> int:
171
175
  """Execute q:application"""
@@ -280,6 +284,7 @@ def main():
280
284
  Examples:
281
285
  quantum start # Start web server (magic!)
282
286
  quantum run hello.q # Execute component
287
+ quantum admin # Start the Quantum Admin
283
288
  quantum run game.q --engine godot # Build Godot 4 project
284
289
  quantum run backup-job.q # Execute job
285
290
  quantum deploy ./my-app # Deploy application
@@ -330,6 +335,10 @@ Examples:
330
335
  # Stop command
331
336
  subparsers.add_parser('stop', help='Stop running web server')
332
337
 
338
+ # Admin command (the screens need the [admin] extra; checked when run)
339
+ from quantum.cli.admin import create_admin_parser
340
+ create_admin_parser(subparsers)
341
+
333
342
  # Deploy commands (if available)
334
343
  deploy_available, create_deploy_parser, create_apps_parser, handle_deploy, handle_apps = _get_deploy_module()
335
344
 
@@ -392,6 +401,10 @@ Examples:
392
401
  traceback.print_exc()
393
402
  sys.exit(1)
394
403
 
404
+ elif args.command == 'admin':
405
+ from quantum.cli.admin import handle_admin
406
+ sys.exit(handle_admin(args))
407
+
395
408
  # Handle 'stop' command
396
409
  elif args.command == 'stop':
397
410
  from quantum.cli.server_process import stop_server
@@ -112,6 +112,7 @@ class ComponentNode(QuantumNode):
112
112
  # Phase G: Authentication & Security
113
113
  self.require_auth = False # Require authentication to access component
114
114
  self.require_role = None # Required role(s) to access (comma-separated: "admin,editor")
115
+ self.login_url = None # AUTH-4: overrides security.login_url for this component
115
116
  self.require_permission = None # Required permission(s)
116
117
 
117
118
  # HTML rendering & interactivity (Phase 1 & future Phase 3)
@@ -15,6 +15,9 @@ Functions are looked up by name from expressions, so anything added here
15
15
  becomes language surface. That is the reason to keep it short.
16
16
  """
17
17
 
18
+ import math
19
+ import re
20
+ from decimal import Decimal, ROUND_HALF_UP
18
21
  from datetime import datetime, timedelta
19
22
  from typing import Any, Iterable, Optional
20
23
 
@@ -84,6 +87,13 @@ def _split(v: Any, sep: str = ",") -> list: return str(v).split(sep)
84
87
  def _contains(v: Any, needle: Any) -> bool: return str(needle) in str(v)
85
88
 
86
89
 
90
+ def _slugify(v: Any) -> str:
91
+ """'Olá, Mundo!' -> 'ola-mundo': for URLs made from a title (EXPR-9)."""
92
+ import unicodedata
93
+ texto = unicodedata.normalize('NFKD', str(v)).encode('ascii', 'ignore').decode('ascii')
94
+ return re.sub(r'[^a-z0-9]+', '-', texto.lower()).strip('-')
95
+
96
+
87
97
  # -- collections -----------------------------------------------------------
88
98
 
89
99
  def _len(v: Any) -> int:
@@ -114,8 +124,21 @@ def _sort(v: Iterable, reverse: bool = False) -> list:
114
124
  # -- numbers ---------------------------------------------------------------
115
125
 
116
126
  def _round(v: Any, digits: int = 0) -> Any:
117
- result = round(float(v), int(digits))
118
- return int(result) if digits == 0 else result
127
+ # EXPR-9: half away from zero, as in ColdFusion and spreadsheets. Python's
128
+ # round() is banker's rounding over binary floats: round(2.5) was 2 and
129
+ # round(0.125, 2) was 0.12. Decimal over the number's text avoids both.
130
+ casas = int(digits)
131
+ passo = Decimal(1).scaleb(-casas)
132
+ result = Decimal(str(v)).quantize(passo, rounding=ROUND_HALF_UP)
133
+ return int(result) if casas <= 0 else float(result)
134
+
135
+
136
+ def _ceil(v: Any) -> int:
137
+ return math.ceil(float(v))
138
+
139
+
140
+ def _floor(v: Any) -> int:
141
+ return math.floor(float(v))
119
142
 
120
143
 
121
144
  # -- passwords ---------------------------------------------------------------
@@ -165,6 +188,7 @@ STDLIB = {
165
188
  'replace': _replace,
166
189
  'split': _split,
167
190
  'contains': _contains,
191
+ 'slugify': _slugify,
168
192
  # collections
169
193
  'len': _len,
170
194
  'first': _first,
@@ -173,6 +197,8 @@ STDLIB = {
173
197
  'sort': _sort,
174
198
  # numbers
175
199
  'round': _round,
200
+ 'ceil': _ceil,
201
+ 'floor': _floor,
176
202
  'abs': abs,
177
203
  'min': min,
178
204
  'max': max,
@@ -23,6 +23,7 @@ access, and the whole arithmetic/comparison/call surface is about 15%.
23
23
  """
24
24
 
25
25
  import ast
26
+ import contextvars
26
27
  import math
27
28
  import operator
28
29
  import re
@@ -30,6 +31,11 @@ from typing import Any, Callable, Dict, Optional
30
31
 
31
32
  from quantum.core.expression_stdlib import STDLIB
32
33
 
34
+ # EXPR-8: in a condition, a key a scope does not have (`session.userId`
35
+ # before login) is absent, not a failure — see ExpressionEvaluator.evaluate.
36
+ _SCOPES = ('session', 'application', 'request', 'cookie', 'form', 'query')
37
+ _ABSENT_SCOPE_KEY_IS_NONE = contextvars.ContextVar('absent_scope_key_is_none', default=False)
38
+
33
39
 
34
40
  class ExpressionError(Exception):
35
41
  """Raised when an expression cannot be evaluated."""
@@ -249,6 +255,43 @@ def _normalise_js_operators(source: str) -> str:
249
255
  return ''.join(out)
250
256
 
251
257
 
258
+ # EXPR-10: what a JavaScript habit is called here. `Math.ceil(x)` and
259
+ # `text.split(' ')` are the first things people write; the bare "only named
260
+ # function calls are allowed" did not say what to write instead.
261
+ _JS_EQUIVALENTS = {
262
+ 'Math.ceil': 'ceil(x)', 'Math.floor': 'floor(x)', 'Math.round': 'round(x)',
263
+ 'Math.max': 'max(a, b)', 'Math.min': 'min(a, b)', 'Math.abs': 'abs(x)',
264
+ 'Date.now': 'now()', 'parseInt': 'int(x)', 'parseFloat': 'float(x)',
265
+ 'split': "split(text, ' ')", 'join': "join(items, ', ')", 'toUpperCase': 'upper(text)',
266
+ 'toLowerCase': 'lower(text)', 'trim': 'trim(text)', 'replace': 'replace(text, old, new)',
267
+ 'includes': 'contains(text, part)', 'indexOf': 'contains(text, part)',
268
+ }
269
+
270
+
271
+ def _method_hint(func: ast.AST) -> str:
272
+ if not isinstance(func, ast.Attribute):
273
+ return ""
274
+ base = func.value.id if isinstance(func.value, ast.Name) else None
275
+ chave = f"{base}.{func.attr}" if base in ('Math', 'Date') else func.attr
276
+ if chave in _JS_EQUIVALENTS:
277
+ escrito = chave if '.' in chave else f"x.{func.attr}"
278
+ return f"; write {_JS_EQUIVALENTS[chave]} instead of {escrito}(...)"
279
+ return f"; call functions by name, e.g. {func.attr}(x), not x.{func.attr}()"
280
+
281
+
282
+ def names_read(expression: str) -> set:
283
+ """The root names an expression reads — `user` in `user.name > 1` — not the
284
+ functions it calls. An expression that does not parse reads nothing here;
285
+ evaluating it reports the error."""
286
+ try:
287
+ tree = ast.parse(_normalise_js_operators(expression.strip()), mode='eval')
288
+ except SyntaxError:
289
+ return set()
290
+ chamadas = {id(n.func) for n in ast.walk(tree) if isinstance(n, ast.Call)}
291
+ return {n.id for n in ast.walk(tree)
292
+ if isinstance(n, ast.Name) and id(n) not in chamadas}
293
+
294
+
252
295
  class ExpressionEvaluator:
253
296
  """Evaluates Quantum `{...}` expressions against a variable context."""
254
297
 
@@ -274,8 +317,24 @@ class ExpressionEvaluator:
274
317
 
275
318
  # -- public API --------------------------------------------------------
276
319
 
277
- def evaluate(self, expression: str, context: Dict[str, Any]) -> Any:
278
- """Evaluate an expression, raising ExpressionError on any failure."""
320
+ def evaluate(self, expression: str, context: Dict[str, Any],
321
+ absent_scope_key_is_none: bool = False) -> Any:
322
+ """Evaluate an expression, raising ExpressionError on any failure.
323
+
324
+ absent_scope_key_is_none (conditions, EXPR-8): `session.x` where the
325
+ scope has no `x` reads as None instead of raising. A condition is a
326
+ presence test, and a scope key that is not there yet is the ordinary
327
+ case (a page runs before login) — raising made the WHOLE condition
328
+ false, so `not session.authenticated` was false for a visitor with no
329
+ session and a guard written that way never fired.
330
+ """
331
+ marca = _ABSENT_SCOPE_KEY_IS_NONE.set(absent_scope_key_is_none)
332
+ try:
333
+ return self._evaluate(expression, context)
334
+ finally:
335
+ _ABSENT_SCOPE_KEY_IS_NONE.reset(marca)
336
+
337
+ def _evaluate(self, expression: str, context: Dict[str, Any]) -> Any:
279
338
  if expression is None:
280
339
  raise ExpressionError("expression is None")
281
340
 
@@ -423,18 +482,18 @@ class ExpressionEvaluator:
423
482
  return op(operand)
424
483
 
425
484
  if isinstance(node, ast.BoolOp):
426
- values = [self._eval(v, ctx) for v in node.values]
427
- if isinstance(node.op, ast.And):
428
- result = True
429
- for v in values:
430
- if not v:
431
- return v
432
- result = v
433
- return result
434
- for v in values:
435
- if v:
436
- return v
437
- return values[-1] if values else False
485
+ # EXPR-6: short-circuit, as in Python. Every operand used to be
486
+ # evaluated first, so `user and user.name` failed with "variable
487
+ # 'user' is not defined"-style errors exactly when the left side
488
+ # was there to guard the right.
489
+ valor = False
490
+ for operando in node.values:
491
+ valor = self._eval(operando, ctx)
492
+ if isinstance(node.op, ast.And) and not valor:
493
+ return valor
494
+ if isinstance(node.op, ast.Or) and valor:
495
+ return valor
496
+ return valor
438
497
 
439
498
  if isinstance(node, ast.Compare):
440
499
  left = self._eval(node.left, ctx)
@@ -473,6 +532,11 @@ class ExpressionEvaluator:
473
532
  plano = self._dotted_name(node)
474
533
  if plano is not None and plano in ctx:
475
534
  return ctx[plano]
535
+ if (_ABSENT_SCOPE_KEY_IS_NONE.get() and isinstance(node.value, ast.Name)
536
+ and node.value.id in _SCOPES):
537
+ escopo = ctx.get(node.value.id)
538
+ if escopo is None or (isinstance(escopo, dict) and node.attr not in escopo):
539
+ return None
476
540
  return self._attribute(self._eval(node.value, ctx), node.attr)
477
541
 
478
542
  if isinstance(node, ast.Subscript):
@@ -588,7 +652,7 @@ class ExpressionEvaluator:
588
652
 
589
653
  def _call(self, node: ast.Call, ctx: Dict[str, Any]) -> Any:
590
654
  if not isinstance(node.func, ast.Name):
591
- raise ExpressionError("only named function calls are allowed")
655
+ raise ExpressionError("only named function calls are allowed" + _method_hint(node.func))
592
656
  fn = self.functions.get(node.func.id)
593
657
  if fn is None and self.function_resolver is not None:
594
658
  # A q:function declared by the component being executed.
@@ -600,7 +664,8 @@ class ExpressionEvaluator:
600
664
  if fn is None:
601
665
  raise ExpressionError(
602
666
  f"function {node.func.id!r} is not defined"
603
- f"{_suggest(node.func.id, self.functions)}"
667
+ + (f"; write {_JS_EQUIVALENTS[node.func.id]}" if node.func.id in _JS_EQUIVALENTS
668
+ else _suggest(node.func.id, self.functions))
604
669
  )
605
670
  args = [self._eval(a, ctx) for a in node.args]
606
671
  kwargs = {kw.arg: self._eval(kw.value, ctx) for kw in node.keywords}
@@ -0,0 +1,53 @@
1
+ """Page guards (AUTH-6): a q:if at the top of a page whose branch has a q:redirect.
2
+
3
+ <q:if condition="not session.authenticated">
4
+ <q:redirect url="/login" />
5
+ </q:if>
6
+
7
+ A q:action does not run the page's statements, so a guard written like this
8
+ used to protect only the GET — the POST ran the action without a session.
9
+ Guards now run before each action of the page as well as before the render.
10
+ """
11
+
12
+ from typing import List
13
+
14
+ from quantum.core.ast_nodes import RedirectNode
15
+ from quantum.core.features.conditionals.src.ast_node import IfNode
16
+
17
+
18
+ def _branches(node: IfNode) -> List:
19
+ corpo = list(node.if_body)
20
+ for bloco in node.elseif_blocks:
21
+ corpo += list(bloco.get('body', []))
22
+ return corpo + list(node.else_body)
23
+
24
+
25
+ def _has_redirect(nodes) -> bool:
26
+ return any(isinstance(n, RedirectNode) or (isinstance(n, IfNode) and _has_redirect(_branches(n)))
27
+ for n in nodes)
28
+
29
+
30
+ def page_guards(component) -> List[IfNode]:
31
+ """The top-level q:if statements that can redirect, in document order."""
32
+ return [s for s in getattr(component, 'statements', [])
33
+ if isinstance(s, IfNode) and _has_redirect(_branches(s))]
34
+
35
+
36
+ def guard_conditions(node: IfNode) -> List[str]:
37
+ """Every condition a guard evaluates, nested q:if included."""
38
+ condicoes = [node.condition] + [b.get('condition', '') for b in node.elseif_blocks]
39
+ for filho in _branches(node):
40
+ if isinstance(filho, IfNode):
41
+ condicoes += guard_conditions(filho)
42
+ return [c for c in condicoes if c]
43
+
44
+
45
+ def mark_guards(component) -> None:
46
+ """Flag each guard, nested q:if included, so its condition fails closed (AUTH-6)."""
47
+ def marcar(node):
48
+ node.is_guard = True
49
+ for filho in _branches(node):
50
+ if isinstance(filho, IfNode):
51
+ marcar(filho)
52
+ for guarda in page_guards(component):
53
+ marcar(guarda)