quantum-framework 0.9.1__tar.gz → 0.11.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 (304) hide show
  1. {quantum_framework-0.9.1/quantum_framework.egg-info → quantum_framework-0.11.0}/PKG-INFO +2 -2
  2. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/README.md +1 -1
  3. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/pyproject.toml +1 -1
  4. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/commands/migrate.py +62 -81
  5. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/runner.py +9 -35
  6. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/ast_nodes.py +1 -0
  7. quantum_framework-0.11.0/quantum/core/config_env.py +54 -0
  8. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/expression_stdlib.py +34 -0
  9. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/expressions.py +52 -31
  10. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/data_import/src/ast_node.py +1 -0
  11. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/data_import/src/runtime.py +13 -18
  12. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/invocation/src/ast_node.py +1 -0
  13. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/invocation/src/runtime.py +8 -3
  14. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parser.py +78 -1
  15. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/data/data_parser.py +14 -2
  16. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/data/invoke_parser.py +9 -0
  17. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/data/query_parser.py +11 -6
  18. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/data/transaction_parser.py +9 -0
  19. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/functions/function_parser.py +22 -68
  20. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/tiers.py +63 -0
  21. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/__init__.py +0 -3
  22. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/action_handler.py +9 -0
  23. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/component.py +134 -84
  24. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/component_composer.py +2 -1
  25. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/control_flow/set_executor.py +91 -39
  26. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/data/data_executor.py +12 -1
  27. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/data/invoke_executor.py +11 -4
  28. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/knowledge_service.py +78 -32
  29. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/param_validation.py +8 -6
  30. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/renderer.py +13 -5
  31. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/service_container.py +34 -8
  32. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/web_server.py +35 -12
  33. {quantum_framework-0.9.1 → quantum_framework-0.11.0/quantum_framework.egg-info}/PKG-INFO +2 -2
  34. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum_framework.egg-info/SOURCES.txt +1 -7
  35. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_game_engine.py +2 -2
  36. quantum_framework-0.9.1/quantum/core/features/data_fetching/src/__init__.py +0 -21
  37. quantum_framework-0.9.1/quantum/core/features/data_fetching/src/ast_node.py +0 -312
  38. quantum_framework-0.9.1/quantum/core/features/data_fetching/src/desktop_adapter.py +0 -351
  39. quantum_framework-0.9.1/quantum/core/features/data_fetching/src/html_adapter.py +0 -474
  40. quantum_framework-0.9.1/quantum/core/features/data_fetching/src/parser.py +0 -225
  41. quantum_framework-0.9.1/quantum/runtime/api_server.py +0 -86
  42. quantum_framework-0.9.1/tests/test_data_fetching.py +0 -593
  43. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/LICENSE +0 -0
  44. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/__init__.py +0 -0
  45. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/__init__.py +0 -0
  46. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/commands/__init__.py +0 -0
  47. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/commands/build.py +0 -0
  48. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/commands/dev.py +0 -0
  49. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/commands/docs.py +0 -0
  50. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/commands/lint.py +0 -0
  51. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/commands/new.py +0 -0
  52. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/commands/serve.py +0 -0
  53. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/commands/test.py +0 -0
  54. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/deploy.py +0 -0
  55. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/hot_reload.py +0 -0
  56. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/jobs.py +0 -0
  57. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/mq.py +0 -0
  58. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/pkg.py +0 -0
  59. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/server_process.py +0 -0
  60. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/cli/utils.py +0 -0
  61. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/__init__.py +0 -0
  62. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/base_generator.py +0 -0
  63. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/cli.py +0 -0
  64. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/expression_transformer.py +0 -0
  65. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/javascript/__init__.py +0 -0
  66. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/javascript/generator.py +0 -0
  67. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/optimizer.py +0 -0
  68. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/python/__init__.py +0 -0
  69. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/python/generator.py +0 -0
  70. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/python/runtime.py +0 -0
  71. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/compiler/transpiler.py +0 -0
  72. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/__init__.py +0 -0
  73. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/expression_diagnostics.py +0 -0
  74. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/agents/src/__init__.py +0 -0
  75. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/agents/src/ast_node.py +0 -0
  76. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/conditionals/src/__init__.py +0 -0
  77. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/conditionals/src/ast_node.py +0 -0
  78. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/data_import/src/__init__.py +0 -0
  79. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/dump/src/__init__.py +0 -0
  80. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/dump/src/ast_node.py +0 -0
  81. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/dump/src/parser.py +0 -0
  82. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/dump/src/runtime.py +0 -0
  83. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/functions/src/__init__.py +0 -0
  84. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/functions/src/ast_node.py +0 -0
  85. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/game_engine_2d/src/__init__.py +0 -0
  86. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/game_engine_2d/src/ast_nodes.py +0 -0
  87. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/game_engine_2d/src/parser.py +0 -0
  88. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/invocation/src/__init__.py +0 -0
  89. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/knowledge_base/src/__init__.py +0 -0
  90. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/knowledge_base/src/ast_node.py +0 -0
  91. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/knowledge_base/src/parser.py +0 -0
  92. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/logging/src/__init__.py +0 -0
  93. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/logging/src/ast_node.py +0 -0
  94. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/logging/src/parser.py +0 -0
  95. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/logging/src/runtime.py +0 -0
  96. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/loops/src/__init__.py +0 -0
  97. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/loops/src/ast_node.py +0 -0
  98. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/query/src/__init__.py +0 -0
  99. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/query/src/database_service.py +0 -0
  100. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/query/src/query_validators.py +0 -0
  101. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/state_management/src/__init__.py +0 -0
  102. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/state_management/src/ast_node.py +0 -0
  103. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/terminal_engine/src/__init__.py +0 -0
  104. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/terminal_engine/src/ast_nodes.py +0 -0
  105. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/terminal_engine/src/parser.py +0 -0
  106. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/testing_engine/src/__init__.py +0 -0
  107. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/testing_engine/src/ast_nodes.py +0 -0
  108. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/testing_engine/src/parser.py +0 -0
  109. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/theming/src/__init__.py +0 -0
  110. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/theming/src/ast_node.py +0 -0
  111. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/theming/src/presets.py +0 -0
  112. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/ui_engine/src/__init__.py +0 -0
  113. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/ui_engine/src/ast_nodes.py +0 -0
  114. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/ui_engine/src/parser.py +0 -0
  115. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/websocket/src/__init__.py +0 -0
  116. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/features/websocket/src/ast_node.py +0 -0
  117. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/html_compat.py +0 -0
  118. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parser_registry.py +0 -0
  119. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/__init__.py +0 -0
  120. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/ai/__init__.py +0 -0
  121. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/ai/agent_parser.py +0 -0
  122. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/ai/knowledge_parser.py +0 -0
  123. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/ai/llm_parser.py +0 -0
  124. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/ai/team_parser.py +0 -0
  125. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/base.py +0 -0
  126. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/composition/__init__.py +0 -0
  127. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/composition/import_parser.py +0 -0
  128. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/composition/slot_parser.py +0 -0
  129. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/control_flow/__init__.py +0 -0
  130. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/control_flow/if_parser.py +0 -0
  131. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/control_flow/loop_parser.py +0 -0
  132. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/control_flow/set_parser.py +0 -0
  133. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/data/__init__.py +0 -0
  134. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/events/__init__.py +0 -0
  135. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/events/dispatch_event_parser.py +0 -0
  136. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/forms/__init__.py +0 -0
  137. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/forms/action_parser.py +0 -0
  138. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/forms/flash_parser.py +0 -0
  139. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/forms/redirect_parser.py +0 -0
  140. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/functions/__init__.py +0 -0
  141. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/functions/param_parser.py +0 -0
  142. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/functions/return_parser.py +0 -0
  143. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/html/__init__.py +0 -0
  144. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/html/component_call_parser.py +0 -0
  145. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/html/html_parser.py +0 -0
  146. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/jobs/__init__.py +0 -0
  147. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/jobs/job_parser.py +0 -0
  148. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/jobs/schedule_parser.py +0 -0
  149. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/jobs/thread_parser.py +0 -0
  150. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/messaging/__init__.py +0 -0
  151. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/messaging/message_ack_parser.py +0 -0
  152. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/messaging/message_nack_parser.py +0 -0
  153. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/messaging/message_parser.py +0 -0
  154. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/messaging/queue_parser.py +0 -0
  155. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/messaging/websocket_parser.py +0 -0
  156. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/persistence/__init__.py +0 -0
  157. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/persistence/persist_parser.py +0 -0
  158. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/routing/__init__.py +0 -0
  159. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/routing/route_parser.py +0 -0
  160. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/scripting/__init__.py +0 -0
  161. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/scripting/pyclass_parser.py +0 -0
  162. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/scripting/pydecorator_parser.py +0 -0
  163. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/scripting/pyimport_parser.py +0 -0
  164. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/scripting/python_parser.py +0 -0
  165. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/services/__init__.py +0 -0
  166. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/services/dump_parser.py +0 -0
  167. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/services/file_parser.py +0 -0
  168. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/services/log_parser.py +0 -0
  169. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/core/parsers/services/mail_parser.py +0 -0
  170. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/packages/__init__.py +0 -0
  171. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/packages/manager.py +0 -0
  172. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/packages/manifest.py +0 -0
  173. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/packages/registry.py +0 -0
  174. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/packages/resolver.py +0 -0
  175. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/plugins/__init__.py +0 -0
  176. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/plugins/hooks.py +0 -0
  177. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/plugins/loader.py +0 -0
  178. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/plugins/manifest.py +0 -0
  179. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/plugins/registry.py +0 -0
  180. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/adapters/__init__.py +0 -0
  181. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/adapters/memory_adapter.py +0 -0
  182. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/adapters/rabbitmq_adapter.py +0 -0
  183. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/adapters/redis_adapter.py +0 -0
  184. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/adapters/sqlite_adapter.py +0 -0
  185. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/agent_service.py +0 -0
  186. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ast_cache.py +0 -0
  187. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/auth_service.py +0 -0
  188. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/component_resolver.py +0 -0
  189. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/database_service.py +0 -0
  190. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/email_service.py +0 -0
  191. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/error_handler.py +0 -0
  192. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/execution_context.py +0 -0
  193. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executor_registry.py +0 -0
  194. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/__init__.py +0 -0
  195. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/ai/__init__.py +0 -0
  196. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/ai/agent_executor.py +0 -0
  197. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/ai/knowledge_executor.py +0 -0
  198. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/ai/llm_executor.py +0 -0
  199. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/ai/team_executor.py +0 -0
  200. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/base.py +0 -0
  201. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/control_flow/__init__.py +0 -0
  202. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/control_flow/if_executor.py +0 -0
  203. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/control_flow/loop_executor.py +0 -0
  204. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/data/__init__.py +0 -0
  205. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/data/query_executor.py +0 -0
  206. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/data/transaction_executor.py +0 -0
  207. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/jobs/__init__.py +0 -0
  208. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/jobs/job_executor.py +0 -0
  209. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/jobs/schedule_executor.py +0 -0
  210. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/jobs/thread_executor.py +0 -0
  211. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/messaging/__init__.py +0 -0
  212. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/messaging/message_ack_executor.py +0 -0
  213. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/messaging/message_executor.py +0 -0
  214. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/messaging/queue_executor.py +0 -0
  215. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/messaging/websocket_executor.py +0 -0
  216. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/scripting/__init__.py +0 -0
  217. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/scripting/pyclass_executor.py +0 -0
  218. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/scripting/pyimport_executor.py +0 -0
  219. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/scripting/python_executor.py +0 -0
  220. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/services/__init__.py +0 -0
  221. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/services/dump_executor.py +0 -0
  222. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/services/file_executor.py +0 -0
  223. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/services/log_executor.py +0 -0
  224. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/executors/services/mail_executor.py +0 -0
  225. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/expression_cache.py +0 -0
  226. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/file_upload_service.py +0 -0
  227. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/function_registry.py +0 -0
  228. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/game_builder.py +0 -0
  229. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/game_code_generator.py +0 -0
  230. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/game_templates.py +0 -0
  231. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/godot_code_generator.py +0 -0
  232. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/godot_templates.py +0 -0
  233. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/job_executor.py +0 -0
  234. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/llm_cache.py +0 -0
  235. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/llm_providers.py +0 -0
  236. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/llm_service.py +0 -0
  237. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/logging_setup.py +0 -0
  238. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/message_broker.py +0 -0
  239. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/message_queue_service.py +0 -0
  240. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/pypy_compat.py +0 -0
  241. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/python_bridge.py +0 -0
  242. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/query_validators.py +0 -0
  243. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/terminal_builder.py +0 -0
  244. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/terminal_code_generator.py +0 -0
  245. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/terminal_templates.py +0 -0
  246. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/testing_builder.py +0 -0
  247. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/testing_code_generator.py +0 -0
  248. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/testing_templates.py +0 -0
  249. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_builder.py +0 -0
  250. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_desktop_adapter.py +0 -0
  251. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_desktop_templates.py +0 -0
  252. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_html_adapter.py +0 -0
  253. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_html_templates.py +0 -0
  254. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_mobile_adapter.py +0 -0
  255. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_mobile_templates.py +0 -0
  256. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_textual_adapter.py +0 -0
  257. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_textual_templates.py +0 -0
  258. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_tokens.py +0 -0
  259. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/ui_validator.py +0 -0
  260. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/validators.py +0 -0
  261. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/websocket_adapter.py +0 -0
  262. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/websocket_service.py +0 -0
  263. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/websocket_transport.py +0 -0
  264. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/runtime/wsgi.py +0 -0
  265. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum/utils/__init__.py +0 -0
  266. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum_framework.egg-info/dependency_links.txt +0 -0
  267. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum_framework.egg-info/entry_points.txt +0 -0
  268. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum_framework.egg-info/requires.txt +0 -0
  269. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/quantum_framework.egg-info/top_level.txt +0 -0
  270. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/setup.cfg +0 -0
  271. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_agent.py +0 -0
  272. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_animations.py +0 -0
  273. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_ast_cache.py +0 -0
  274. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_cli_commands.py +0 -0
  275. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_component_library.py +0 -0
  276. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_desktop_bridge.py +0 -0
  277. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_expression_cache.py +0 -0
  278. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_form_validation.py +0 -0
  279. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_game_codegen.py +0 -0
  280. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_game_e2e.py +0 -0
  281. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_game_parser.py +0 -0
  282. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_godot_codegen.py +0 -0
  283. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_job_executor.py +0 -0
  284. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_llm_providers.py +0 -0
  285. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_message_broker.py +0 -0
  286. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_message_queue.py +0 -0
  287. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_mobile_target.py +0 -0
  288. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_multi_agent.py +0 -0
  289. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_packages.py +0 -0
  290. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_plugins.py +0 -0
  291. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_projects_blog_components_admin.py +0 -0
  292. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_pypy_compat.py +0 -0
  293. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_python_scripting.py +0 -0
  294. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_state_persistence.py +0 -0
  295. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_terminal_codegen.py +0 -0
  296. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_terminal_parser.py +0 -0
  297. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_testing_codegen.py +0 -0
  298. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_testing_parser.py +0 -0
  299. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_theming.py +0 -0
  300. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_ui_html_adapter.py +0 -0
  301. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_ui_parity.py +0 -0
  302. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_ui_parser.py +0 -0
  303. {quantum_framework-0.9.1 → quantum_framework-0.11.0}/tests/test_ui_textual_adapter.py +0 -0
  304. {quantum_framework-0.9.1 → quantum_framework-0.11.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.9.1
3
+ Version: 0.11.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
@@ -216,7 +216,7 @@ Full docs at **[danielgregorio.github.io/quantum](https://danielgregorio.github.
216
216
 
217
217
  - [Getting Started](https://danielgregorio.github.io/quantum/guide/getting-started) · [Installation](https://danielgregorio.github.io/quantum/guide/installation) · [Quick Start](https://danielgregorio.github.io/quantum/guide/quick-start)
218
218
  - [Components](https://danielgregorio.github.io/quantum/guide/components) · [State](https://danielgregorio.github.io/quantum/guide/state-management) · [Loops](https://danielgregorio.github.io/quantum/guide/loops) · [Conditionals](https://danielgregorio.github.io/quantum/guide/conditionals)
219
- - [Queries](https://danielgregorio.github.io/quantum/guide/query) · [Functions](https://danielgregorio.github.io/quantum/guide/functions) · [Data fetching](https://danielgregorio.github.io/quantum/guide/data-fetching)
219
+ - [Queries](https://danielgregorio.github.io/quantum/guide/query) · [Functions](https://danielgregorio.github.io/quantum/guide/functions)
220
220
 
221
221
  Releases and their notes are on the [GitHub Releases](https://github.com/danielgregorio/quantum/releases) page.
222
222
 
@@ -173,7 +173,7 @@ Full docs at **[danielgregorio.github.io/quantum](https://danielgregorio.github.
173
173
 
174
174
  - [Getting Started](https://danielgregorio.github.io/quantum/guide/getting-started) · [Installation](https://danielgregorio.github.io/quantum/guide/installation) · [Quick Start](https://danielgregorio.github.io/quantum/guide/quick-start)
175
175
  - [Components](https://danielgregorio.github.io/quantum/guide/components) · [State](https://danielgregorio.github.io/quantum/guide/state-management) · [Loops](https://danielgregorio.github.io/quantum/guide/loops) · [Conditionals](https://danielgregorio.github.io/quantum/guide/conditionals)
176
- - [Queries](https://danielgregorio.github.io/quantum/guide/query) · [Functions](https://danielgregorio.github.io/quantum/guide/functions) · [Data fetching](https://danielgregorio.github.io/quantum/guide/data-fetching)
176
+ - [Queries](https://danielgregorio.github.io/quantum/guide/query) · [Functions](https://danielgregorio.github.io/quantum/guide/functions)
177
177
 
178
178
  Releases and their notes are on the [GitHub Releases](https://github.com/danielgregorio/quantum/releases) page.
179
179
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quantum-framework"
7
- version = "0.9.1"
7
+ version = "0.11.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"}
@@ -1,6 +1,6 @@
1
1
  """
2
2
  Quantum CLI - Database Migration Commands
3
- Handles database migrations with automatic PostgreSQL/SQLite failover
3
+ Applies migrations to a datasource declared in quantum.config.yaml (DB-6)
4
4
  """
5
5
  import os
6
6
  import sys
@@ -44,95 +44,76 @@ class Migration:
44
44
 
45
45
 
46
46
  class DatabaseConnection:
47
- """Database connection with automatic failover"""
47
+ """Connection to the datasource the migrations are for (DB-6).
48
48
 
49
- def __init__(self, project_path: Path = None):
49
+ It used to read a separate `database:` section that nothing else uses and,
50
+ without one, try a PostgreSQL at localhost/quantum with user postgres before
51
+ falling back to ./data/quantum.db. A project whose pages query
52
+ datasources.db (./data/app.db) had its migrations applied to a different
53
+ file — or to whatever local PostgreSQL answered.
54
+ """
55
+
56
+ def __init__(self, project_path: Path = None, datasource: str = None):
50
57
  self.project_path = project_path or Path.cwd()
58
+ self.datasource = datasource
51
59
  self._conn = None
52
60
  self._db_type = None
53
61
 
54
62
  def connect(self) -> Any:
55
- """
56
- Connect to database with automatic failover.
57
- Tries PostgreSQL first, falls back to SQLite.
58
- """
59
- # Try to load database config from quantum.config.yaml
60
- config = self._load_config()
61
-
62
- # Try PostgreSQL
63
- if self._try_postgres(config):
63
+ nome, config = self._datasource_config()
64
+ driver = str(config.get('driver', '')).lower()
65
+ if driver == 'sqlite':
66
+ import sqlite3
67
+ caminho = Path(config.get('database') or '')
68
+ if not config.get('database'):
69
+ raise MigrationError(f"datasource '{nome}' (sqlite) has no database: path")
70
+ if not caminho.is_absolute():
71
+ caminho = self.project_path / caminho
72
+ caminho.parent.mkdir(parents=True, exist_ok=True)
73
+ self._conn = sqlite3.connect(str(caminho))
74
+ self._db_type = 'sqlite'
75
+ logger.info("Migrating datasource %s (SQLite at %s)", nome, caminho)
64
76
  return self._conn
65
-
66
- # Fallback to SQLite
67
- logger.warning("PostgreSQL unavailable, falling back to SQLite")
68
- if self._try_sqlite(config):
77
+ if driver in ('postgres', 'postgresql'):
78
+ try:
79
+ import psycopg2
80
+ except ImportError as exc:
81
+ raise MigrationError("datasource '%s' is PostgreSQL: pip install psycopg2-binary" % nome) from exc
82
+ self._conn = psycopg2.connect(
83
+ host=config.get('host', 'localhost'), port=int(config.get('port', 5432)),
84
+ dbname=config.get('database'), user=config.get('username') or config.get('user'),
85
+ password=config.get('password', ''), connect_timeout=5)
86
+ self._db_type = 'postgres'
87
+ logger.info("Migrating datasource %s (PostgreSQL %s)", nome, config.get('database'))
69
88
  return self._conn
89
+ raise MigrationError(f"datasource '{nome}': driver {driver!r} is not supported by quantum migrate "
90
+ f"(use sqlite or postgres)")
70
91
 
71
- raise MigrationError("Could not connect to any database")
72
-
73
- def _load_config(self) -> Dict:
74
- """Load database configuration from quantum.config.yaml"""
92
+ def _datasource_config(self):
75
93
  config_path = self.project_path / "quantum.config.yaml"
76
- if not config_path.exists():
77
- return {}
78
-
79
- try:
94
+ datasources = {}
95
+ if config_path.exists():
80
96
  import yaml
81
- with open(config_path) as f:
82
- config = yaml.safe_load(f) or {}
83
- return config.get('database', {})
84
- except Exception as e:
85
- logger.warning(f"Could not load config: {e}")
86
- return {}
87
-
88
- def _try_postgres(self, config: Dict) -> bool:
89
- """Try to connect to PostgreSQL"""
90
- try:
91
- import psycopg2
92
-
93
- # Get connection params from config or environment
94
- host = config.get('host') or os.environ.get('QUANTUM_DB_HOST', 'localhost')
95
- port = config.get('port') or os.environ.get('QUANTUM_DB_PORT', '5432')
96
- database = config.get('database') or os.environ.get('QUANTUM_DB_NAME', 'quantum')
97
- user = config.get('user') or os.environ.get('QUANTUM_DB_USER', 'postgres')
98
- password = config.get('password') or os.environ.get('QUANTUM_DB_PASSWORD', '')
99
-
100
- self._conn = psycopg2.connect(
101
- host=host,
102
- port=int(port),
103
- database=database,
104
- user=user,
105
- password=password,
106
- connect_timeout=5
107
- )
108
- self._db_type = 'postgres'
109
- logger.info(f"Connected to PostgreSQL at {host}:{port}/{database}")
110
- return True
111
-
112
- except ImportError:
113
- logger.debug("psycopg2 not installed")
114
- return False
115
- except Exception as e:
116
- logger.debug(f"PostgreSQL connection failed: {e}")
117
- return False
118
-
119
- def _try_sqlite(self, config: Dict) -> bool:
120
- """Try to connect to SQLite"""
121
- try:
122
- import sqlite3
123
-
124
- db_path = config.get('sqlite_path') or self.project_path / "data" / "quantum.db"
125
- db_path = Path(db_path)
126
- db_path.parent.mkdir(parents=True, exist_ok=True)
127
-
128
- self._conn = sqlite3.connect(str(db_path))
129
- self._db_type = 'sqlite'
130
- logger.info(f"Connected to SQLite at {db_path}")
131
- return True
132
-
133
- except Exception as e:
134
- logger.error(f"SQLite connection failed: {e}")
135
- return False
97
+ from quantum.core.config_env import expand_env
98
+ with open(config_path, encoding='utf-8') as f:
99
+ datasources = (expand_env(yaml.safe_load(f) or {}).get('datasources') or {})
100
+ if not datasources:
101
+ raise MigrationError(
102
+ "quantum.config.yaml declares no datasources. Add one, for example:\n"
103
+ " datasources:\n db:\n driver: sqlite\n database: ./data/app.db")
104
+ if self.datasource:
105
+ if self.datasource not in datasources:
106
+ raise MigrationError(f"no datasource '{self.datasource}'; declared: {', '.join(datasources)}")
107
+ return self.datasource, datasources[self.datasource]
108
+ if len(datasources) > 1:
109
+ raise MigrationError(f"several datasources are declared ({', '.join(datasources)}); "
110
+ f"choose one with --datasource")
111
+ nome = next(iter(datasources))
112
+ return nome, datasources[nome]
113
+
114
+ def _ensure_connected(self):
115
+ if self._conn is None:
116
+ self.connect()
136
117
 
137
118
  @property
138
119
  def db_type(self) -> str:
@@ -163,10 +144,10 @@ class MigrationRunner:
163
144
 
164
145
  MIGRATIONS_TABLE = "_migrations"
165
146
 
166
- def __init__(self, project_path: Path = None):
147
+ def __init__(self, project_path: Path = None, datasource: str = None):
167
148
  self.project_path = project_path or Path.cwd()
168
149
  self.migrations_dir = self.project_path / "migrations"
169
- self.db = DatabaseConnection(self.project_path)
150
+ self.db = DatabaseConnection(self.project_path, datasource)
170
151
 
171
152
  def _ensure_migrations_table(self):
172
153
  """Create migrations tracking table if it doesn't exist"""
@@ -25,7 +25,6 @@ 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
27
  from quantum.runtime.web_server import QuantumWebServer
28
- from quantum.runtime.api_server import QuantumAPIServer
29
28
 
30
29
  # Import deploy commands (lazy load to avoid import errors if requests not installed)
31
30
  def _get_deploy_module():
@@ -77,12 +76,15 @@ def load_config(config_path: str = 'quantum.config.yaml') -> dict:
77
76
  so `datasources:` declared in quantum.config.yaml were never seen and
78
77
  every q:query fell through to the optional Quantum Admin API.
79
78
  """
79
+ from quantum.core.config_env import ConfigEnvError, expand_env
80
80
  try:
81
81
  import yaml
82
82
  path = Path(config_path)
83
83
  if path.exists():
84
84
  with open(path, 'r', encoding='utf-8') as fh:
85
- return yaml.safe_load(fh) or {}
85
+ return expand_env(yaml.safe_load(fh) or {})
86
+ except ConfigEnvError:
87
+ raise # CFG-1: never run with a half-read config
86
88
  except Exception as exc:
87
89
  print(f"[WARN] Could not read {config_path}: {exc}")
88
90
  return {}
@@ -175,6 +177,9 @@ class QuantumRunner:
175
177
  for route in app.routes:
176
178
  print(f" {route.method} {route.path}")
177
179
 
180
+ from quantum.core.tiers import warn_app_type
181
+ warn_app_type(app.app_type)
182
+
178
183
  if app.app_type == 'game':
179
184
  return self._build_game(app, debug)
180
185
  elif app.app_type == 'terminal':
@@ -184,10 +189,6 @@ class QuantumRunner:
184
189
  elif app.app_type == 'ui':
185
190
  target = getattr(self, '_ui_target', 'html')
186
191
  return self._build_ui(app, target, debug)
187
- elif app.app_type == 'html':
188
- return self._start_web_server(app)
189
- elif app.app_type in ('microservices', 'api'):
190
- return self._start_api_server(app)
191
192
  else:
192
193
  print(f"[ERROR] Application type '{app.app_type}' not supported")
193
194
  return 1
@@ -268,32 +269,6 @@ class QuantumRunner:
268
269
  # TODO: Implement real job execution
269
270
  print("[SUCCESS] Job executed successfully")
270
271
  return 0
271
-
272
- def _start_web_server(self, app: ApplicationNode) -> int:
273
- """Start web server"""
274
- try:
275
- server = QuantumWebServer(port=8080)
276
- server.configure_from_ast(app)
277
- # Propagate the server's exit code: it returns non-zero when the
278
- # port is taken or the bind fails, and swallowing that made
279
- # `quantum run` report success with nothing listening.
280
- return server.start() or 0
281
- except Exception as e:
282
- print(f"[ERROR] Web server error: {e}")
283
- return 1
284
-
285
- def _start_api_server(self, app: ApplicationNode) -> int:
286
- """Start API server"""
287
- try:
288
- server = QuantumAPIServer(port=8080)
289
- server.configure_from_ast(app)
290
- # Propagate the server's exit code: it returns non-zero when the
291
- # port is taken or the bind fails, and swallowing that made
292
- # `quantum run` report success with nothing listening.
293
- return server.start() or 0
294
- except Exception as e:
295
- print(f"[ERROR] API server error: {e}")
296
- return 1
297
272
 
298
273
 
299
274
  def main():
@@ -305,8 +280,6 @@ def main():
305
280
  Examples:
306
281
  quantum start # Start web server (magic!)
307
282
  quantum run hello.q # Execute component
308
- quantum run webapp.q # Start web server from .q file
309
- quantum run api.q # Start API server
310
283
  quantum run game.q --engine godot # Build Godot 4 project
311
284
  quantum run backup-job.q # Execute job
312
285
  quantum deploy ./my-app # Deploy application
@@ -384,6 +357,7 @@ Examples:
384
357
 
385
358
  # Migration commands
386
359
  migrate_parser = subparsers.add_parser('migrate', help='Database migration commands')
360
+ migrate_parser.add_argument('--datasource', help='Datasource from quantum.config.yaml (default: the only one declared)')
387
361
  migrate_subparsers = migrate_parser.add_subparsers(dest='migrate_command')
388
362
 
389
363
  migrate_subparsers.add_parser('status', help='Show migration status')
@@ -488,7 +462,7 @@ Examples:
488
462
  MigrationRunner = result
489
463
 
490
464
  try:
491
- runner = MigrationRunner()
465
+ runner = MigrationRunner(datasource=getattr(args, 'datasource', None))
492
466
 
493
467
  if args.migrate_command == 'status':
494
468
  import json
@@ -1919,6 +1919,7 @@ class DataNode(QuantumNode):
1919
1919
 
1920
1920
  # Result metadata
1921
1921
  self.result: Optional[str] = None
1922
+ self.on_error: str = 'fail' # DATA-4: 'fail' or 'continue'
1922
1923
 
1923
1924
  # Child elements
1924
1925
  self.columns: List[ColumnNode] = []
@@ -0,0 +1,54 @@
1
+ """
2
+ `${VAR}` in quantum.config.yaml (CFG-1).
3
+
4
+ datasources:
5
+ db:
6
+ password: ${DB_PASSWORD}
7
+ host: ${DB_HOST:-localhost}
8
+ port: ${DB_PORT:5432} # ':' works as well as ':-'
9
+
10
+ A config file is the place people put secrets by reference, and every loader
11
+ used to read `${DB_PASSWORD}` as the literal password. There were four loaders
12
+ (the CLI, the web server, the service container, migrations); they all go
13
+ through expand_env now.
14
+ """
15
+
16
+ import os
17
+ import re
18
+ from typing import Any
19
+
20
+ _REFERENCE = re.compile(r'\$\$|\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-?([^}]*))?\}')
21
+
22
+
23
+ class ConfigEnvError(ValueError):
24
+ """A `${VAR}` with no value and no default."""
25
+
26
+
27
+ def expand_env(value: Any, path: str = '') -> Any:
28
+ """Replace `${NAME}` and `${NAME:-default}` in every string of a loaded config.
29
+
30
+ `$$` is a literal `$`. A variable that is not set and has no default is an
31
+ error naming the variable and where it was used — an empty password or
32
+ database path would otherwise fail much later, somewhere else.
33
+ """
34
+ if isinstance(value, dict):
35
+ return {k: expand_env(v, f'{path}.{k}' if path else str(k)) for k, v in value.items()}
36
+ if isinstance(value, list):
37
+ return [expand_env(v, f'{path}[{i}]') for i, v in enumerate(value)]
38
+ if not isinstance(value, str) or '$' not in value:
39
+ return value
40
+
41
+ def substitute(match):
42
+ if match.group(0) == '$$':
43
+ return '$'
44
+ name, default = match.group(1), match.group(2)
45
+ if name in os.environ:
46
+ return os.environ[name]
47
+ if default is not None:
48
+ return default
49
+ raise ConfigEnvError(
50
+ f"quantum.config.yaml: {path or 'value'} uses ${{{name}}}, but the "
51
+ f"environment variable {name} is not set. Set it, or give a default: "
52
+ f"${{{name}:-value}}")
53
+
54
+ return _REFERENCE.sub(substitute, value)
@@ -118,7 +118,41 @@ def _round(v: Any, digits: int = 0) -> Any:
118
118
  return int(result) if digits == 0 else result
119
119
 
120
120
 
121
+ # -- passwords ---------------------------------------------------------------
122
+ #
123
+ # Authentication is Core (decision D4), and a login is "look the user up, then
124
+ # check the password". Without these, a .q could not check a password against
125
+ # a stored hash without q:python — so every login example just set
126
+ # session.authenticated=true for whoever submitted the form (gap AUTH-1).
127
+
128
+ def hash_password(password: Any) -> str:
129
+ """bcrypt hash of a password, for storing. Never store the password itself."""
130
+ import bcrypt
131
+ if password is None or password == '':
132
+ raise ValueError("hashPassword: the password is empty")
133
+ return bcrypt.hashpw(str(password).encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
134
+
135
+
136
+ def verify_password(password: Any, hashed: Any) -> bool:
137
+ """True when `password` matches a hash made by hashPassword.
138
+
139
+ Fail-closed: an empty password, a missing hash or a malformed hash is
140
+ False, never an error — so `q:if condition="verifyPassword(senha, h)"`
141
+ cannot be tricked into the true branch by bad data.
142
+ """
143
+ import bcrypt
144
+ if not password or not hashed or not isinstance(hashed, str):
145
+ return False
146
+ try:
147
+ return bcrypt.checkpw(str(password).encode('utf-8'), hashed.encode('utf-8'))
148
+ except (ValueError, TypeError):
149
+ return False
150
+
151
+
121
152
  STDLIB = {
153
+ # passwords
154
+ 'hashPassword': hash_password,
155
+ 'verifyPassword': verify_password,
122
156
  # dates
123
157
  'now': now,
124
158
  'dateAdd': date_add,
@@ -35,6 +35,15 @@ class ExpressionError(Exception):
35
35
  """Raised when an expression cannot be evaluated."""
36
36
 
37
37
 
38
+ class UndefinedError(ExpressionError):
39
+ """A name, key or attribute that does not exist.
40
+
41
+ Kept apart from the other failures because a condition is a presence test
42
+ (EXPR-5): `<q:if condition="flash">` is false before there is a flash, but
43
+ `a === b` or a misspelled function is still an error.
44
+ """
45
+
46
+
38
47
  # `{n}`, `{n,}` and `{n,m}` are regex quantifiers, not Quantum expressions.
39
48
  # They occur for real: examples/form_validation.q has pattern="\d{10,11}" and
40
49
  # examples/python-scripting.q has r'[a-zA-Z]{2,}'. The old evaluator left them
@@ -51,9 +60,9 @@ _REGEX_QUANTIFIER = re.compile(r'^\d+\s*(,\s*\d*)?$')
51
60
  def is_regex_quantifier(content: str) -> bool:
52
61
  """True when a `{...}` group should be left as literal text.
53
62
 
54
- Note this also catches a bare `{5}` meant as the number 5 — ambiguous by
55
- construction, and resolved in favour of not corrupting regexes. It matches
56
- what the engine already did, so nothing changes for existing templates.
63
+ A q: attribute that is ONLY `{5}` is the number 5 (EXPR-7) — the runtime
64
+ checks that case before calling this. Inside other text, `[0-9]{3}` stays a
65
+ quantifier.
57
66
  """
58
67
  return bool(_REGEX_QUANTIFIER.match(content.strip()))
59
68
 
@@ -148,14 +157,12 @@ def _suggest(wanted: str, target: Any, limit: int = 3) -> str:
148
157
  #
149
158
  # {9**9**9} -> does not return
150
159
  # {10**100000000} -> does not return
151
- # {'a' * 999999999} -> allocates ~1 GB and returns
160
+ # ({'a' * 999999999} allocated ~1 GB; since EXPR-7 `*` needs numbers.)
152
161
  #
153
162
  # No dunder, no escape, no attacker — a typo in a template is enough, and there
154
163
  # is no timeout anywhere above this to catch it. The caps are far above any
155
- # real expression: 4096 bits is a 1233-digit number, and 10 MB of repeated
156
- # string is already absurd for a page.
164
+ # real expression: 4096 bits is a 1233-digit number.
157
165
  _MAX_POW_BITS = 4096
158
- _MAX_REPEAT = 10_000_000
159
166
 
160
167
 
161
168
  def _guard_pow(left: Any, right: Any) -> None:
@@ -193,31 +200,24 @@ def _guard_pow(left: Any, right: Any) -> None:
193
200
  )
194
201
 
195
202
 
196
- def _guard_repeat(left: Any, right: Any) -> None:
197
- for seq, count in ((left, right), (right, left)):
198
- if isinstance(seq, (str, bytes, list, tuple)) and isinstance(count, int) \
199
- and not isinstance(count, bool):
200
- if len(seq) * max(count, 0) > _MAX_REPEAT:
201
- raise ExpressionError(
202
- f"repeating a {len(seq)}-item sequence {count} times "
203
- f"exceeds {_MAX_REPEAT}; this would exhaust memory"
204
- )
205
-
206
-
207
203
  def _both_numeric(left: Any, right: Any) -> bool:
208
204
  return isinstance(left, (int, float)) and isinstance(right, (int, float)) \
209
205
  and not isinstance(left, bool) and not isinstance(right, bool)
210
206
 
211
207
 
212
208
 
213
- def _normalise_not(source: str) -> str:
214
- """Rewrite JavaScript-style `!x` negation into Python's `not x`.
209
+ def _normalise_js_operators(source: str) -> str:
210
+ """Rewrite JavaScript-style `!x`, `a && b` and `a || b` into Python's
211
+ `not`, `and` and `or`.
215
212
 
216
- Real .q files use it ({!result.success} appears across the examples), so it
217
- is part of the language surface whether or not it was ever specified. `!=`
218
- is left alone, and `!` inside a string literal is not touched.
213
+ Real .q files use them ({!result.success} appears across the examples, and
214
+ docs/guide/conditionals.md teaches `age >= 18 && hasLicense`), so they are
215
+ part of the language surface. `&&` and `||` were never translated: the
216
+ condition failed to parse and, read as false, took the else branch for
217
+ every input. `!=` is left alone, and nothing inside a string literal is
218
+ touched.
219
219
  """
220
- if '!' not in source:
220
+ if '!' not in source and '&&' not in source and '||' not in source:
221
221
  return source
222
222
 
223
223
  out = []
@@ -240,6 +240,10 @@ def _normalise_not(source: str) -> str:
240
240
  out.append('not ')
241
241
  i += 1
242
242
  continue
243
+ if source[i:i + 2] in ('&&', '||'):
244
+ out.append(' and ' if ch == '&' else ' or ')
245
+ i += 2
246
+ continue
243
247
  out.append(ch)
244
248
  i += 1
245
249
  return ''.join(out)
@@ -281,7 +285,7 @@ class ExpressionEvaluator:
281
285
  if len(source) > self.max_length:
282
286
  raise ExpressionError(f"expression exceeds {self.max_length} characters")
283
287
 
284
- source = _normalise_not(source)
288
+ source = _normalise_js_operators(source)
285
289
 
286
290
  try:
287
291
  tree = ast.parse(source, mode='eval')
@@ -374,12 +378,27 @@ class ExpressionEvaluator:
374
378
  cl, cr = coerce_number(left), coerce_number(right)
375
379
  if _both_numeric(cl, cr):
376
380
  left, right = cl, cr
381
+ elif isinstance(node.op, ast.Add) and not (
382
+ (isinstance(left, str) and isinstance(right, str))
383
+ or (isinstance(left, list) and isinstance(right, list))):
384
+ # EXPR-7: '+' adds numbers or joins two texts / two lists. The
385
+ # mixed case used to surface Python's "can only concatenate
386
+ # str (not "int") to str".
387
+ raise ExpressionError(
388
+ f"'+' needs two numbers, two texts or two lists, got {left!r} and {right!r}")
389
+ elif not isinstance(node.op, ast.Add):
390
+ # EXPR-7: arithmetic is on numbers. Python would repeat text
391
+ # ('ab' * 3), format it ('%s' % x) or fail with its own
392
+ # message; `n * factorial(n - 1)` whose base case returned the
393
+ # text '{1}' produced '{1}{1}{1}{1}{1}' and no error.
394
+ simbolo = {ast.Sub: '-', ast.Mult: '*', ast.Div: '/', ast.FloorDiv: '//',
395
+ ast.Mod: '%', ast.Pow: '**'}[type(node.op)]
396
+ raise ExpressionError(
397
+ f"'{simbolo}' needs two numbers, got {left!r} and {right!r}")
377
398
 
378
399
  # Refuse what would not finish, before attempting it.
379
400
  if isinstance(node.op, ast.Pow):
380
401
  _guard_pow(left, right)
381
- elif isinstance(node.op, ast.Mult):
382
- _guard_repeat(left, right)
383
402
 
384
403
  try:
385
404
  return op(left, right)
@@ -467,7 +486,9 @@ class ExpressionEvaluator:
467
486
  if isinstance(target, dict):
468
487
  return target.get(key)
469
488
  return target[key]
470
- except (IndexError, KeyError, TypeError) as exc:
489
+ except (IndexError, KeyError) as exc:
490
+ raise UndefinedError(f"cannot index: {exc}") from exc
491
+ except TypeError as exc:
471
492
  raise ExpressionError(f"cannot index: {exc}") from exc
472
493
 
473
494
  if isinstance(node, ast.Dict):
@@ -513,7 +534,7 @@ class ExpressionEvaluator:
513
534
  return False
514
535
  if name == 'null':
515
536
  return None
516
- raise ExpressionError(
537
+ raise UndefinedError(
517
538
  f"variable {name!r} is not defined{_suggest(name, ctx)}"
518
539
  )
519
540
 
@@ -521,7 +542,7 @@ class ExpressionEvaluator:
521
542
  if isinstance(target, dict):
522
543
  if attr in target:
523
544
  return target[attr]
524
- raise ExpressionError(
545
+ raise UndefinedError(
525
546
  f"key {attr!r} not found{_suggest(attr, target)}"
526
547
  )
527
548
  if isinstance(target, (list, tuple, str)) and attr == 'length':
@@ -560,7 +581,7 @@ class ExpressionEvaluator:
560
581
  f"{attr!r}; use .length for the count, or the query's "
561
582
  f"<name>_result for its metadata"
562
583
  )
563
- raise ExpressionError(
584
+ raise UndefinedError(
564
585
  f"attribute {attr!r} not found on {type(target).__name__}"
565
586
  f"{_suggest(attr, target)}"
566
587
  )
@@ -38,6 +38,7 @@ class DataNode(QuantumNode):
38
38
 
39
39
  # Result metadata
40
40
  self.result = None # Variable name for metadata
41
+ self.on_error = 'fail' # DATA-4: 'fail' or 'continue'
41
42
 
42
43
  def add_column(self, column: 'ColumnNode'):
43
44
  """Add CSV column definition"""