quantum-framework 0.11.0__tar.gz → 0.12.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.
- {quantum_framework-0.11.0/quantum_framework.egg-info → quantum_framework-0.12.0}/PKG-INFO +5 -1
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/pyproject.toml +16 -2
- quantum_framework-0.12.0/quantum/cli/admin.py +131 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/runner.py +9 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/ast_nodes.py +1 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/expressions.py +12 -12
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parser.py +108 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/invoke_parser.py +4 -1
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/action_handler.py +19 -11
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/component.py +3 -4
- quantum_framework-0.12.0/quantum/runtime/component_composer.py +133 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/data/invoke_executor.py +60 -15
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/renderer.py +20 -21
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/web_server.py +33 -8
- quantum_framework-0.12.0/quantum/services.py +109 -0
- quantum_framework-0.12.0/quantum_admin/__init__.py +4 -0
- quantum_framework-0.12.0/quantum_admin/assets/quantum-admin-connectors.js +123 -0
- quantum_framework-0.12.0/quantum_admin/assets/quantum-admin.css +2058 -0
- quantum_framework-0.12.0/quantum_admin/assets/quantum-source-highlight.js +106 -0
- quantum_framework-0.12.0/quantum_admin/backend/__init__.py +4 -0
- quantum_framework-0.12.0/quantum_admin/backend/audit_service.py +346 -0
- quantum_framework-0.12.0/quantum_admin/backend/auth_service.py +394 -0
- quantum_framework-0.12.0/quantum_admin/backend/component_discovery.py +766 -0
- quantum_framework-0.12.0/quantum_admin/backend/config_generator.py +586 -0
- quantum_framework-0.12.0/quantum_admin/backend/connector_service.py +1016 -0
- quantum_framework-0.12.0/quantum_admin/backend/crud.py +557 -0
- quantum_framework-0.12.0/quantum_admin/backend/database.py +226 -0
- quantum_framework-0.12.0/quantum_admin/backend/db_setup_service.py +193 -0
- quantum_framework-0.12.0/quantum_admin/backend/deploy_service.py +698 -0
- quantum_framework-0.12.0/quantum_admin/backend/docker_service.py +422 -0
- quantum_framework-0.12.0/quantum_admin/backend/environment_service.py +418 -0
- quantum_framework-0.12.0/quantum_admin/backend/git_service.py +370 -0
- quantum_framework-0.12.0/quantum_admin/backend/health_service.py +390 -0
- quantum_framework-0.12.0/quantum_admin/backend/job_service.py +347 -0
- quantum_framework-0.12.0/quantum_admin/backend/log_service.py +331 -0
- quantum_framework-0.12.0/quantum_admin/backend/main.py +14426 -0
- quantum_framework-0.12.0/quantum_admin/backend/models.py +986 -0
- quantum_framework-0.12.0/quantum_admin/backend/pipeline_service.py +812 -0
- quantum_framework-0.12.0/quantum_admin/backend/resource_manager.py +1118 -0
- quantum_framework-0.12.0/quantum_admin/backend/schemas.py +419 -0
- quantum_framework-0.12.0/quantum_admin/backend/secret_manager.py +277 -0
- quantum_framework-0.12.0/quantum_admin/backend/settings_service.py +488 -0
- quantum_framework-0.12.0/quantum_admin/backend/template_service.py +710 -0
- quantum_framework-0.12.0/quantum_admin/backend/test_execution_service.py +364 -0
- quantum_framework-0.12.0/quantum_admin/backend/test_generator.py +782 -0
- quantum_framework-0.12.0/quantum_admin/backend/webhook_service.py +454 -0
- quantum_framework-0.12.0/quantum_admin/backend/websocket_manager.py +360 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/_layout/AdminShell.q +121 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/agents.q +107 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/app/[name].q +558 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/applications.q +160 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/component/[...path].q +205 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/components.q +59 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/connectors.q +283 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/dashboard.q +86 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/database.q +95 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/features.q +69 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/index.q +12 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/jobs.q +75 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/login.q +65 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/logout.q +11 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/settings.q +174 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/source.q +45 -0
- quantum_framework-0.12.0/quantum_admin/components/admin/tests.q +73 -0
- quantum_framework-0.12.0/quantum_admin/run.py +13 -0
- quantum_framework-0.12.0/quantum_admin/services/__init__.py +16 -0
- quantum_framework-0.12.0/quantum_admin/services/_base.py +84 -0
- quantum_framework-0.12.0/quantum_admin/services/_component_test_generator.py +205 -0
- quantum_framework-0.12.0/quantum_admin/services/_yaml_editor.py +82 -0
- quantum_framework-0.12.0/quantum_admin/services/apps.py +330 -0
- quantum_framework-0.12.0/quantum_admin/services/auth.py +61 -0
- quantum_framework-0.12.0/quantum_admin/services/components.py +210 -0
- quantum_framework-0.12.0/quantum_admin/services/connectors.py +164 -0
- quantum_framework-0.12.0/quantum_admin/services/projects.py +189 -0
- quantum_framework-0.12.0/quantum_admin/services/repository.py +223 -0
- quantum_framework-0.12.0/quantum_admin/services/settings.py +150 -0
- quantum_framework-0.12.0/quantum_admin/services/yaml_import.py +118 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0/quantum_framework.egg-info}/PKG-INFO +5 -1
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum_framework.egg-info/SOURCES.txt +64 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum_framework.egg-info/requires.txt +5 -0
- quantum_framework-0.12.0/quantum_framework.egg-info/top_level.txt +2 -0
- quantum_framework-0.11.0/quantum/runtime/component_composer.py +0 -320
- quantum_framework-0.11.0/quantum_framework.egg-info/top_level.txt +0 -1
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/LICENSE +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/README.md +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/build.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/dev.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/docs.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/lint.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/migrate.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/new.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/serve.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/commands/test.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/deploy.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/hot_reload.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/jobs.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/mq.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/pkg.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/server_process.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/cli/utils.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/base_generator.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/cli.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/expression_transformer.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/javascript/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/javascript/generator.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/optimizer.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/python/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/python/generator.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/python/runtime.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/compiler/transpiler.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/config_env.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/expression_diagnostics.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/expression_stdlib.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/agents/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/agents/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/conditionals/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/conditionals/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/data_import/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/data_import/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/data_import/src/runtime.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/dump/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/dump/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/dump/src/parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/dump/src/runtime.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/functions/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/functions/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/game_engine_2d/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/game_engine_2d/src/ast_nodes.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/game_engine_2d/src/parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/invocation/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/invocation/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/invocation/src/runtime.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/knowledge_base/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/knowledge_base/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/knowledge_base/src/parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/logging/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/logging/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/logging/src/parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/logging/src/runtime.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/loops/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/loops/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/query/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/query/src/database_service.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/query/src/query_validators.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/state_management/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/state_management/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/terminal_engine/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/terminal_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/terminal_engine/src/parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/testing_engine/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/testing_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/testing_engine/src/parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/theming/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/theming/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/theming/src/presets.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/ui_engine/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/ui_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/ui_engine/src/parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/websocket/src/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/features/websocket/src/ast_node.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/html_compat.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parser_registry.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/ai/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/ai/agent_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/ai/knowledge_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/ai/llm_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/ai/team_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/base.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/composition/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/composition/import_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/composition/slot_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/control_flow/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/control_flow/if_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/control_flow/loop_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/control_flow/set_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/data_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/query_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/transaction_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/events/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/events/dispatch_event_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/forms/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/forms/action_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/forms/flash_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/forms/redirect_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/functions/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/functions/function_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/functions/param_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/functions/return_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/html/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/html/component_call_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/html/html_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/jobs/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/jobs/job_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/jobs/schedule_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/jobs/thread_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/message_ack_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/message_nack_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/message_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/queue_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/messaging/websocket_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/persistence/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/persistence/persist_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/routing/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/routing/route_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/scripting/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/scripting/pyclass_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/scripting/pydecorator_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/scripting/pyimport_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/scripting/python_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/services/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/services/dump_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/services/file_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/services/log_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/services/mail_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/tiers.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/packages/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/packages/manager.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/packages/manifest.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/packages/registry.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/packages/resolver.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/plugins/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/plugins/hooks.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/plugins/loader.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/plugins/manifest.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/plugins/registry.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/adapters/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/adapters/memory_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/adapters/rabbitmq_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/adapters/redis_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/adapters/sqlite_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/agent_service.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ast_cache.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/auth_service.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/component_resolver.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/database_service.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/email_service.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/error_handler.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/execution_context.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executor_registry.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/ai/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/ai/agent_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/ai/knowledge_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/ai/llm_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/ai/team_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/base.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/control_flow/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/control_flow/if_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/control_flow/loop_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/control_flow/set_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/data/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/data/data_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/data/query_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/data/transaction_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/jobs/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/jobs/job_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/jobs/schedule_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/jobs/thread_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/messaging/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/messaging/message_ack_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/messaging/message_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/messaging/queue_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/messaging/websocket_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/scripting/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/scripting/pyclass_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/scripting/pyimport_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/scripting/python_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/services/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/services/dump_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/services/file_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/services/log_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/executors/services/mail_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/expression_cache.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/file_upload_service.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/function_registry.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/game_builder.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/game_code_generator.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/game_templates.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/godot_code_generator.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/godot_templates.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/job_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/knowledge_service.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/llm_cache.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/llm_providers.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/llm_service.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/logging_setup.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/message_broker.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/message_queue_service.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/param_validation.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/pypy_compat.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/python_bridge.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/query_validators.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/service_container.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/terminal_builder.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/terminal_code_generator.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/terminal_templates.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/testing_builder.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/testing_code_generator.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/testing_templates.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_builder.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_desktop_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_desktop_templates.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_html_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_html_templates.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_mobile_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_mobile_templates.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_textual_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_textual_templates.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_tokens.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/ui_validator.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/validators.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/websocket_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/websocket_service.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/websocket_transport.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/runtime/wsgi.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/utils/__init__.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum_framework.egg-info/dependency_links.txt +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum_framework.egg-info/entry_points.txt +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/setup.cfg +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_agent.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_animations.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_ast_cache.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_cli_commands.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_component_library.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_desktop_bridge.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_expression_cache.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_form_validation.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_game_codegen.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_game_e2e.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_game_engine.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_game_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_godot_codegen.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_job_executor.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_llm_providers.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_message_broker.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_message_queue.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_mobile_target.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_multi_agent.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_packages.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_plugins.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_projects_blog_components_admin.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_pypy_compat.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_python_scripting.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_state_persistence.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_terminal_codegen.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_terminal_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_testing_codegen.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_testing_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_theming.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_ui_html_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_ui_parity.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_ui_parser.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.0}/tests/test_ui_textual_adapter.py +0 -0
- {quantum_framework-0.11.0 → quantum_framework-0.12.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.
|
|
3
|
+
Version: 0.12.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.
|
|
7
|
+
version = "0.12.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
|
|
@@ -280,6 +280,7 @@ def main():
|
|
|
280
280
|
Examples:
|
|
281
281
|
quantum start # Start web server (magic!)
|
|
282
282
|
quantum run hello.q # Execute component
|
|
283
|
+
quantum admin # Start the Quantum Admin
|
|
283
284
|
quantum run game.q --engine godot # Build Godot 4 project
|
|
284
285
|
quantum run backup-job.q # Execute job
|
|
285
286
|
quantum deploy ./my-app # Deploy application
|
|
@@ -330,6 +331,10 @@ Examples:
|
|
|
330
331
|
# Stop command
|
|
331
332
|
subparsers.add_parser('stop', help='Stop running web server')
|
|
332
333
|
|
|
334
|
+
# Admin command (the screens need the [admin] extra; checked when run)
|
|
335
|
+
from quantum.cli.admin import create_admin_parser
|
|
336
|
+
create_admin_parser(subparsers)
|
|
337
|
+
|
|
333
338
|
# Deploy commands (if available)
|
|
334
339
|
deploy_available, create_deploy_parser, create_apps_parser, handle_deploy, handle_apps = _get_deploy_module()
|
|
335
340
|
|
|
@@ -392,6 +397,10 @@ Examples:
|
|
|
392
397
|
traceback.print_exc()
|
|
393
398
|
sys.exit(1)
|
|
394
399
|
|
|
400
|
+
elif args.command == 'admin':
|
|
401
|
+
from quantum.cli.admin import handle_admin
|
|
402
|
+
sys.exit(handle_admin(args))
|
|
403
|
+
|
|
395
404
|
# Handle 'stop' command
|
|
396
405
|
elif args.command == 'stop':
|
|
397
406
|
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)
|
|
@@ -423,18 +423,18 @@ class ExpressionEvaluator:
|
|
|
423
423
|
return op(operand)
|
|
424
424
|
|
|
425
425
|
if isinstance(node, ast.BoolOp):
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
if
|
|
436
|
-
return
|
|
437
|
-
return
|
|
426
|
+
# EXPR-6: short-circuit, as in Python. Every operand used to be
|
|
427
|
+
# evaluated first, so `user and user.name` failed with "variable
|
|
428
|
+
# 'user' is not defined"-style errors exactly when the left side
|
|
429
|
+
# was there to guard the right.
|
|
430
|
+
valor = False
|
|
431
|
+
for operando in node.values:
|
|
432
|
+
valor = self._eval(operando, ctx)
|
|
433
|
+
if isinstance(node.op, ast.And) and not valor:
|
|
434
|
+
return valor
|
|
435
|
+
if isinstance(node.op, ast.Or) and valor:
|
|
436
|
+
return valor
|
|
437
|
+
return valor
|
|
438
438
|
|
|
439
439
|
if isinstance(node, ast.Compare):
|
|
440
440
|
left = self._eval(node.left, ctx)
|
|
@@ -584,6 +584,13 @@ class QuantumParser:
|
|
|
584
584
|
require_auth_attr = root.get('require_auth', 'false').lower()
|
|
585
585
|
component.require_auth = require_auth_attr in ['true', '1', 'yes']
|
|
586
586
|
component.require_role = root.get('require_role')
|
|
587
|
+
# AUTH-4: where this component sends a visitor without a session.
|
|
588
|
+
component.login_url = root.get('login_url')
|
|
589
|
+
if component.login_url is not None and (not component.login_url.startswith('/')
|
|
590
|
+
or component.login_url.startswith('//')):
|
|
591
|
+
raise QuantumParseError(
|
|
592
|
+
f'<q:component name="{component.name}"> login_url must be a path on this server, '
|
|
593
|
+
f'like /login; got {component.login_url!r}')
|
|
587
594
|
component.require_permission = root.get('require_permission')
|
|
588
595
|
|
|
589
596
|
# HTML rendering & interactivity (Phase 1 & future Phase 3)
|
|
@@ -618,7 +625,108 @@ class QuantumParser:
|
|
|
618
625
|
# Parse control flow statements (if, loop, set, dispatchEvent, etc)
|
|
619
626
|
self._parse_control_flow_statements(root, component)
|
|
620
627
|
|
|
628
|
+
self._check_statements_in_markup(component)
|
|
629
|
+
|
|
621
630
|
return component
|
|
631
|
+
|
|
632
|
+
# Nodes the HTML renderer draws. Anything else inside markup is a statement,
|
|
633
|
+
# and statements run in the execution pass, before the render pass sees
|
|
634
|
+
# the markup around them.
|
|
635
|
+
_RENDERED = (HTMLNode, TextNode, CommentNode, DocTypeNode, LoopNode, IfNode,
|
|
636
|
+
ComponentCallNode, SlotNode, ImportNode)
|
|
637
|
+
|
|
638
|
+
def _check_statements_in_markup(self, component: ComponentNode) -> None:
|
|
639
|
+
"""PARSE-2: a statement where it would never run, or run with the wrong values.
|
|
640
|
+
|
|
641
|
+
Execution walks the component's statements and skips markup; the
|
|
642
|
+
renderer walks the markup and skips statements. So a q:set inside a
|
|
643
|
+
<ul> never ran — `<li>{y}</li>` printed the literal `{y}` — and a q:set
|
|
644
|
+
in a q:loop that also renders rows ran once per row in the execution
|
|
645
|
+
pass, leaving every row to render with the LAST value. Both were
|
|
646
|
+
silent.
|
|
647
|
+
"""
|
|
648
|
+
def filhos(node):
|
|
649
|
+
if isinstance(node, IfNode):
|
|
650
|
+
corpo = list(node.if_body)
|
|
651
|
+
for bloco in node.elseif_blocks:
|
|
652
|
+
corpo += list(bloco.get('body', []))
|
|
653
|
+
return corpo + list(node.else_body)
|
|
654
|
+
if isinstance(node, LoopNode):
|
|
655
|
+
return list(node.body)
|
|
656
|
+
if isinstance(node, (HTMLNode, ComponentCallNode)):
|
|
657
|
+
return list(node.children or [])
|
|
658
|
+
return []
|
|
659
|
+
|
|
660
|
+
def renderiza(nodes):
|
|
661
|
+
for node in nodes:
|
|
662
|
+
if isinstance(node, TextNode):
|
|
663
|
+
if node.content.strip():
|
|
664
|
+
return True
|
|
665
|
+
elif isinstance(node, (HTMLNode, ComponentCallNode)):
|
|
666
|
+
return True
|
|
667
|
+
elif isinstance(node, (IfNode, LoopNode)) and renderiza(filhos(node)):
|
|
668
|
+
return True
|
|
669
|
+
return False
|
|
670
|
+
|
|
671
|
+
def tag(node):
|
|
672
|
+
nome = type(node).__name__
|
|
673
|
+
nome = nome[:-4] if nome.endswith('Node') else nome
|
|
674
|
+
rotulo = f"q:{nome[:1].lower()}{nome[1:]}"
|
|
675
|
+
atributo = getattr(node, 'name', None)
|
|
676
|
+
return f'<{rotulo} name="{atributo}">' if isinstance(atributo, str) else f'<{rotulo}>'
|
|
677
|
+
|
|
678
|
+
def expressoes(nodes):
|
|
679
|
+
"""The text the renderer evaluates under `nodes`: contents, attributes, conditions."""
|
|
680
|
+
partes = []
|
|
681
|
+
for node in nodes:
|
|
682
|
+
if isinstance(node, TextNode):
|
|
683
|
+
partes.append(node.content)
|
|
684
|
+
elif isinstance(node, HTMLNode):
|
|
685
|
+
partes.extend(str(v) for v in (node.attributes or {}).values())
|
|
686
|
+
elif isinstance(node, ComponentCallNode):
|
|
687
|
+
partes.extend(str(v) for v in (node.props or {}).values())
|
|
688
|
+
elif isinstance(node, IfNode):
|
|
689
|
+
partes.append('{%s}' % node.condition)
|
|
690
|
+
partes.extend('{%s}' % b.get('condition', '') for b in node.elseif_blocks)
|
|
691
|
+
elif isinstance(node, LoopNode):
|
|
692
|
+
partes.append('{%s}' % (node.items or ''))
|
|
693
|
+
partes.append(expressoes(filhos(node)))
|
|
694
|
+
return '\n'.join(partes)
|
|
695
|
+
|
|
696
|
+
def usado(nome, texto):
|
|
697
|
+
padrao = re.compile(r'(?<![\w.])' + re.escape(nome) + r'(?![\w])')
|
|
698
|
+
return any(padrao.search(m) for m in re.findall(r'\{([^{}]*)\}', texto))
|
|
699
|
+
|
|
700
|
+
def andar(nodes, onde):
|
|
701
|
+
for node in nodes:
|
|
702
|
+
if not isinstance(node, self._RENDERED):
|
|
703
|
+
if onde and onde[0] == 'markup':
|
|
704
|
+
raise QuantumParseError(
|
|
705
|
+
f'{tag(node)} inside {onde[1]} in <q:component name="{component.name}"> '
|
|
706
|
+
f'never runs: statements run before the page is rendered, and markup '
|
|
707
|
+
f'is only rendered. Move it above the markup (or into a q:action).')
|
|
708
|
+
nome = getattr(node, 'name', None)
|
|
709
|
+
# A running total read AFTER the loop is fine; a value the
|
|
710
|
+
# loop's own rows read is not.
|
|
711
|
+
if onde and isinstance(nome, str) and usado(nome, onde[1]):
|
|
712
|
+
raise QuantumParseError(
|
|
713
|
+
f'{tag(node)} inside a q:loop whose rows use {{{nome}}} in '
|
|
714
|
+
f'<q:component name="{component.name}">: the loop runs for every item '
|
|
715
|
+
f'before any row is rendered, so every row would show the last value. '
|
|
716
|
+
f'Compute it in the expression instead, e.g. {{item.price * item.qty}}.')
|
|
717
|
+
continue
|
|
718
|
+
if isinstance(node, HTMLNode):
|
|
719
|
+
andar(filhos(node), onde if onde and onde[0] == 'markup' else ('markup', f'<{node.tag}>'))
|
|
720
|
+
elif isinstance(node, ComponentCallNode):
|
|
721
|
+
andar(filhos(node), onde if onde and onde[0] == 'markup'
|
|
722
|
+
else ('markup', f'the content of <{node.component_name}>'))
|
|
723
|
+
elif isinstance(node, LoopNode):
|
|
724
|
+
corpo = filhos(node)
|
|
725
|
+
andar(corpo, onde or (('loop', expressoes(corpo)) if renderiza(corpo) else None))
|
|
726
|
+
elif isinstance(node, IfNode):
|
|
727
|
+
andar(filhos(node), onde)
|
|
728
|
+
|
|
729
|
+
andar(component.statements, None)
|
|
622
730
|
|
|
623
731
|
def _parse_control_flow_statements(self, parent: ET.Element, component: ComponentNode):
|
|
624
732
|
"""Parse control flow statements - delegates to modular parser registry"""
|
{quantum_framework-0.11.0 → quantum_framework-0.12.0}/quantum/core/parsers/data/invoke_parser.py
RENAMED
|
@@ -46,7 +46,10 @@ class InvokeParser(BaseTagParser):
|
|
|
46
46
|
invoke_node.function = self.get_attr(element, 'function')
|
|
47
47
|
invoke_node.component = self.get_attr(element, 'component')
|
|
48
48
|
invoke_node.url = self.get_attr(element, 'url')
|
|
49
|
-
|
|
49
|
+
if self.get_attr(element, 'endpoint') is not None:
|
|
50
|
+
raise ParserError(
|
|
51
|
+
f'<q:invoke name="{name}"> endpoint= is not supported: it was accepted and never '
|
|
52
|
+
f'did anything, and was removed in Quantum 0.12. Use url= or service=.')
|
|
50
53
|
invoke_node.service = self.get_attr(element, 'service')
|
|
51
54
|
|
|
52
55
|
# HTTP attributes
|
|
@@ -46,7 +46,8 @@ class ActionHandler:
|
|
|
46
46
|
def handle_action(
|
|
47
47
|
self,
|
|
48
48
|
action: ActionNode,
|
|
49
|
-
context: Optional[ExecutionContext] = None
|
|
49
|
+
context: Optional[ExecutionContext] = None,
|
|
50
|
+
route_params: Optional[Dict[str, str]] = None
|
|
50
51
|
) -> Tuple[Optional[str], int]:
|
|
51
52
|
"""
|
|
52
53
|
Handle action execution.
|
|
@@ -97,6 +98,14 @@ class ActionHandler:
|
|
|
97
98
|
if key != 'files' and not param_validation.is_uploaded_file(value)
|
|
98
99
|
})
|
|
99
100
|
|
|
101
|
+
# ROUTE-2: the page's route segments ([nome], [...caminho]) are
|
|
102
|
+
# variables of the action too, set last so a form field with the
|
|
103
|
+
# same name cannot replace them. A POST to /admin/app/loja used to
|
|
104
|
+
# run without `name`, so every form had to repeat it in a hidden
|
|
105
|
+
# field — one the browser can change.
|
|
106
|
+
for key, value in (route_params or {}).items():
|
|
107
|
+
context.set_variable(key, value)
|
|
108
|
+
|
|
100
109
|
# 4. Execute action body
|
|
101
110
|
redirect_info = self._execute_action_body(action, context)
|
|
102
111
|
|
|
@@ -419,18 +428,17 @@ class ActionHandler:
|
|
|
419
428
|
context.set_variable(key, value, scope="component")
|
|
420
429
|
|
|
421
430
|
def _resolve_databinding(self, text: str, context: ExecutionContext) -> str:
|
|
422
|
-
"""Resolve {
|
|
431
|
+
"""Resolve {expressions} in a redirect URL or flash message (ACT-3).
|
|
432
|
+
|
|
433
|
+
This looked each {name} up with get_variable, so anything but a plain
|
|
434
|
+
name failed — `flash="{result.error}"` or `{len(itens)} saved` ended
|
|
435
|
+
the action with "Variable 'result.error' not found in any scope" and a
|
|
436
|
+
500. It now goes through the same evaluator as every q: attribute.
|
|
437
|
+
"""
|
|
423
438
|
if not text or '{' not in text:
|
|
424
439
|
return text
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
def replace_var(match):
|
|
429
|
-
var_name = match.group(1)
|
|
430
|
-
value = context.get_variable(var_name)
|
|
431
|
-
return str(value) if value is not None else ''
|
|
432
|
-
|
|
433
|
-
return re.sub(r'\{([^}]+)\}', replace_var, text)
|
|
440
|
+
valor = self.component_runtime._apply_databinding(text, context.get_all_variables())
|
|
441
|
+
return '' if valor is None else str(valor)
|
|
434
442
|
|
|
435
443
|
def _set_flash_message(self, message: str, flash_type: str = 'info'):
|
|
436
444
|
"""Set flash message in session"""
|
|
@@ -455,10 +455,9 @@ class ComponentRuntime:
|
|
|
455
455
|
# assembles code out of data. Same change as HTMLRenderer.
|
|
456
456
|
stripped = self._databinding_pattern.sub(lambda m: m.group(1), condition).strip()
|
|
457
457
|
|
|
458
|
-
#
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
458
|
+
# IF-2: `1` and `{1}` are the number 1, true. There used to be a
|
|
459
|
+
# regex-quantifier check here that made every such condition false;
|
|
460
|
+
# a condition is never a regex, and HTMLRenderer never had the check.
|
|
462
461
|
try:
|
|
463
462
|
return bool(self._expressions.evaluate(stripped, context or {}))
|
|
464
463
|
except UndefinedError:
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quantum Component Composer — <Component prop="…">slot content</Component>.
|
|
3
|
+
|
|
4
|
+
COMP-1..4 (SPEC.md). What this used to get wrong, found by building the admin
|
|
5
|
+
layout on it:
|
|
6
|
+
|
|
7
|
+
- the component was searched in ./components relative to the process's
|
|
8
|
+
working directory — paths.components from quantum.config.yaml and the
|
|
9
|
+
`from=` of q:import were ignored;
|
|
10
|
+
- a component that was not found, or failed, became an HTML comment and the
|
|
11
|
+
page answered 200 with the section missing;
|
|
12
|
+
- the child ran as ComponentRuntime() with no configuration, so a q:query
|
|
13
|
+
or q:invoke service= inside a child component had no datasources/services;
|
|
14
|
+
- slot content was grafted into the child's AST and evaluated in the
|
|
15
|
+
CHILD's scope: a q:loop over the page's rows inside a layout saw nothing.
|
|
16
|
+
Worse, the graft mutated the child AST held in the resolver cache, so the
|
|
17
|
+
first request's slot content replaced the q:slot for every later request;
|
|
18
|
+
- props used get_variable only (no expressions) and fell back to the
|
|
19
|
+
placeholder text on failure.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import re
|
|
23
|
+
from typing import Any, Dict, List
|
|
24
|
+
|
|
25
|
+
from quantum.core.ast_nodes import ComponentCallNode, ComponentNode, HTMLNode, QuantumNode, SlotNode
|
|
26
|
+
from quantum.core.expressions import ExpressionError
|
|
27
|
+
from quantum.runtime.component import ComponentRuntime
|
|
28
|
+
from quantum.runtime.component_resolver import ComponentNotFoundError, ComponentResolver
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ComponentCompositionError(Exception):
|
|
32
|
+
"""A component call could not be composed; the message names the component."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class RenderedHTML(QuantumNode):
|
|
36
|
+
"""Markup the parent already rendered (slot content), inserted as-is."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, html: str):
|
|
39
|
+
self.html = html
|
|
40
|
+
|
|
41
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
42
|
+
return {"type": "rendered_html", "length": len(self.html)}
|
|
43
|
+
|
|
44
|
+
def validate(self) -> List[str]:
|
|
45
|
+
return []
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ComponentComposer:
|
|
49
|
+
def __init__(self, resolver: ComponentResolver):
|
|
50
|
+
self.resolver = resolver
|
|
51
|
+
|
|
52
|
+
def compose(self, call: ComponentCallNode, parent) -> str:
|
|
53
|
+
"""Render `call` as a child of the HTMLRenderer `parent`."""
|
|
54
|
+
from quantum.runtime.renderer import HTMLRenderer
|
|
55
|
+
|
|
56
|
+
nome, origem = parent.imports.get(call.component_name, (call.component_name, None))
|
|
57
|
+
try:
|
|
58
|
+
child = self.resolver.resolve(nome, origem).ast
|
|
59
|
+
except ComponentNotFoundError as exc:
|
|
60
|
+
raise ComponentCompositionError(f"<{call.component_name}>: {exc}") from exc
|
|
61
|
+
|
|
62
|
+
props = self._props(child, call, parent)
|
|
63
|
+
# COMP-4: the same configuration and the same session, application and
|
|
64
|
+
# request scopes as the page — a layout showing {session.userName}
|
|
65
|
+
# used to see an empty session.
|
|
66
|
+
props.update(_session_scope=parent.context.session_vars,
|
|
67
|
+
_application_scope=parent.context.application_vars,
|
|
68
|
+
_request_scope=parent.context.request_vars)
|
|
69
|
+
runtime = ComponentRuntime(config=parent.config)
|
|
70
|
+
runtime.execute_component(child, props)
|
|
71
|
+
|
|
72
|
+
# COMP-3: slot content belongs to the page — rendered here, with the
|
|
73
|
+
# page's variables, before the child sees it.
|
|
74
|
+
slot = RenderedHTML(parent.render_all(call.children)) if call.children else None
|
|
75
|
+
preenchido = ComponentNode(name=child.name, component_type=child.component_type)
|
|
76
|
+
preenchido.statements = self._com_slot(child.statements, slot)
|
|
77
|
+
preenchido.params, preenchido.returns, preenchido.functions = child.params, child.returns, child.functions
|
|
78
|
+
|
|
79
|
+
renderer = HTMLRenderer(runtime.execution_context, components_dir=parent.components_dir,
|
|
80
|
+
function_resolver=runtime._resolve_expression_function, config=parent.config)
|
|
81
|
+
return renderer.render(preenchido)
|
|
82
|
+
|
|
83
|
+
def _props(self, child: ComponentNode, call: ComponentCallNode, parent) -> Dict[str, Any]:
|
|
84
|
+
props = {}
|
|
85
|
+
for definicao in child.params:
|
|
86
|
+
if definicao.name in call.props:
|
|
87
|
+
props[definicao.name] = self._avaliar(call.props[definicao.name], definicao.name, call, parent)
|
|
88
|
+
elif definicao.default is not None:
|
|
89
|
+
props[definicao.name] = definicao.default
|
|
90
|
+
elif definicao.required:
|
|
91
|
+
raise ComponentCompositionError(
|
|
92
|
+
f"<{call.component_name}> requires the prop {definicao.name!r}")
|
|
93
|
+
return props
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def _avaliar(valor, prop, call, parent):
|
|
97
|
+
"""COMP-2: a prop is an attribute of a tag — an expression that fails is an error."""
|
|
98
|
+
if not isinstance(valor, str) or "{" not in valor:
|
|
99
|
+
return valor
|
|
100
|
+
variaveis = parent.context.get_all_variables()
|
|
101
|
+
try:
|
|
102
|
+
inteira = re.fullmatch(r"\s*\{([^{}]*)\}\s*", valor)
|
|
103
|
+
if inteira:
|
|
104
|
+
return parent._expressions.evaluate(inteira.group(1), variaveis)
|
|
105
|
+
return re.sub(r"\{([^{}]*)\}",
|
|
106
|
+
lambda m: str(parent._expressions.evaluate(m.group(1), variaveis)), valor)
|
|
107
|
+
except ExpressionError as exc:
|
|
108
|
+
raise ComponentCompositionError(
|
|
109
|
+
f"<{call.component_name} {prop}=\"{valor}\">: {exc}") from exc
|
|
110
|
+
|
|
111
|
+
def _com_slot(self, nodes: List[QuantumNode], slot) -> List[QuantumNode]:
|
|
112
|
+
"""Copy of `nodes` with each q:slot replaced — the cached child AST is never touched."""
|
|
113
|
+
resultado = []
|
|
114
|
+
for node in nodes:
|
|
115
|
+
if isinstance(node, SlotNode):
|
|
116
|
+
# Only the default slot receives the call's children; named
|
|
117
|
+
# slots keep their default content (named slots are not
|
|
118
|
+
# implemented, as before).
|
|
119
|
+
if slot is not None and (node.name or "default") == "default":
|
|
120
|
+
resultado.append(slot)
|
|
121
|
+
else:
|
|
122
|
+
resultado.extend(node.default_content or [])
|
|
123
|
+
elif isinstance(node, HTMLNode) and self._tem_slot(node.children):
|
|
124
|
+
copia = HTMLNode(tag=node.tag, attributes=dict(node.attributes),
|
|
125
|
+
children=self._com_slot(node.children, slot), self_closing=node.self_closing)
|
|
126
|
+
resultado.append(copia)
|
|
127
|
+
else:
|
|
128
|
+
resultado.append(node)
|
|
129
|
+
return resultado
|
|
130
|
+
|
|
131
|
+
def _tem_slot(self, nodes) -> bool:
|
|
132
|
+
return any(isinstance(n, SlotNode) or (isinstance(n, HTMLNode) and self._tem_slot(n.children))
|
|
133
|
+
for n in nodes)
|