metaobjects 0.19.3__tar.gz → 0.19.5__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.
- {metaobjects-0.19.3 → metaobjects-0.19.5}/PKG-INFO +7 -3
- {metaobjects-0.19.3 → metaobjects-0.19.5}/README.md +6 -2
- {metaobjects-0.19.3 → metaobjects-0.19.5}/pyproject.toml +1 -1
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/cli.py +26 -4
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/router_generator.py +133 -1
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_cli_verify_subverbs.py +110 -0
- metaobjects-0.19.5/tests/codegen/test_router_autoset.py +168 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/api_contract_assertions.py +42 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/api_contract_server.py +72 -17
- {metaobjects-0.19.3 → metaobjects-0.19.5}/.gitignore +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/LICENSE +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/hatch_build.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/agent_context/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/agent_context/scaffold.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/apidocs/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/apidocs/api_model.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/apidocs/builder.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/apidocs/naming.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/apidocs/paths.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/apidocs/renderer.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/attr_class_map.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/config.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/format.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generator_registry.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/entity_model.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/template_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/instance_artifacts.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/overwrite_policy.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/runner.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/runtime/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/type_map.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/core_types.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/datatype.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/documentation/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/documentation/doc_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/documentation/doc_provider.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/documentation/doc_schema.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/errors.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/merge.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/meta_data_loader.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/registered_validation.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/sources/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/sources/directory_source.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/sources/file_source.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/sources/uri_source.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/validate_discriminator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/validate_field_readonly.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/validate_source_escapes.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/loader/validation_passes.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/attr/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/field/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/field/field_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/field/meta_field.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/identity/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/index/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/index/index_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/index/meta_index.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/object/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/object/meta_object.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/object/object_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/object/object_extract.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/object/value_object.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/validator/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/meta_data.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/meta_root.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/persistence/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/presentation/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/provider_extends.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/template/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/template/meta_template.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/template/prompt_provider.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/meta/template/template_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/naming_refs.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/parser.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/parser_yaml.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/provider.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/py.typed +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/registry.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/registry_manifest.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/email_document.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/escapers.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/coerce.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/extract.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/extract_map.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/locate.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/normalize.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/strip.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/types.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/filesystem_provider.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/prompt/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/prompt/prompt_field.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/prompt/prompt_style.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/renderer.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/render/verify.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/runtime/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/runtime/llm_recorder.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/runtime/n2m_resolver.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/runtime/object_manager.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/runtime/tph.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/serializer_json.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/shared/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/shared/base_types.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/shared/separators.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/shared/structural.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/source/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/source/error_source.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/source/json_path.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/source/semantic_diff.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/source/yaml_positions.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/attr.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/db.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/documentation.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/field.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/identity.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/index.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/layout.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/object.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/origin.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/prompt.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/relationship.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/source.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/template.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/ui.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/validator.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/spec_metamodel/view.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/super_resolve.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/validation_types.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/yaml_desugar.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/extends/expected/Program.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/extends/meta.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/nested-array/meta.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/scalars/expected/Report.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/scalars/meta.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/golden/vanilla/meta.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_abstract_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_api_docs_builder.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_api_docs_paths.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_cli.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_cli_providers.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_cli_registry.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_cli_staleness_nudge.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_cli_verify_strict.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_constants_config.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_entity_model.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_enum_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_extractor_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_filter_allowlist_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_format.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_fr010_output_codegen.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_generator_extension_seams.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_golden.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_inheritance_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_instance_artifacts.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_m2m_codegen.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_origin_nullability.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_output_parser_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_overwrite_policy.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_payload_vo_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_projection_compile.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_render_helper_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_render_helper_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_reverse_finders.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_router_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_runner.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_template_data.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_template_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_template_output_pattern.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_template_scope_helpers.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_template_scope_walk.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_template_spec.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_tph_codegen.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_trace_helper_generator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_type_map.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_validation_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/codegen/test_write_through.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/capabilities.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/conformance-expected-failures.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/conformance_adapter.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/corpus.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/expected_failures.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/fixture_discovery.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/navigator.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_extract_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_extract_object_verdict.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_fr010_loader_attrs.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_fr011_attrs.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_generator_registry_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_object_model_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_provider_composition_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_registry_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_runner_hardfail.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_spec_metamodel_embed.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_strict_attr_load.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_template_codegen_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_template_generator_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/test_yaml_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/api_contract_jsonb_server.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/api_contract_m2m_server.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/generated_jsonb_app.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/generated_m2m_app.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/generated_router_app.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/generated_tph_app.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/meta_ai_trace.yaml +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/normalization.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/postgres_container.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/query_runner.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/scenarios.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_api_contract.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_api_contract_generated.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_api_contract_jsonb.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_api_contract_m2m.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_api_contract_m2m_generated.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_api_contract_tph_generated.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_generated_router_uuid_pk.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_llm_call_trace.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_normalization.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_query_scenarios.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/integration/test_runtime_return_types.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/open_closed_proof_test.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/extract/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/extract/test_coerce.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/extract/test_extract.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/extract/test_extract_map.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/extract/test_json_forgiving_reader.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/extract/test_locate.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/extract/test_model.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/extract/test_normalize.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/extract/test_strip.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/prompt/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/prompt/test_output_format_renderer.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/test_email_document.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/test_filesystem_provider.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/test_output_format_renderer_nested.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/test_output_prompt_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/test_render_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/test_render_max_chars.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/test_verify.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/render/test_verify_conformance.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/source/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/source/test_fr5c_merge_attribution.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/source/test_fr5d_reference_resolution.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/source/test_fr5e_database_source_shape.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/source/test_json_path.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/source/test_semantic_diff.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/source/test_source_on_node.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/source/test_yaml_positions.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/test_api_docs_accuracy.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/__init__.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_agent_context_staleness.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_capabilities.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_common_attrs.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_core_types.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_effective_package.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_errors.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_field_enum.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_field_map_validation.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_index_lookup.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_llm_recorder.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_loader.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_loader_bom.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_loader_class.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_merge.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_meta_attr.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_meta_data.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_meta_source.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_module_shortcuts.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_n2m_resolver.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_n2m_resolver_inherited.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_object_manager_write_through.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_one_primary_source.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_parser.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_parser_yaml.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_provider.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_provider_extension.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_registry.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_registry_completeness.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_registry_extend.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_registry_sealed.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_relationship_referential_actions.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_resolution_key.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_serializer.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_shared_constants.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_smoke.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_source_escapes.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_sources.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_strict_child_placement.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_super_resolve.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_template_toolcall.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_validation_attr_schema.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_validation_filter_values.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_validation_origin_195.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_validation_origin_paths.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_validation_sort_field.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_validation_warnings.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_write_coercion_arrays.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/tests/unit/test_yaml_desugar.py +0 -0
- {metaobjects-0.19.3 → metaobjects-0.19.5}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metaobjects
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.5
|
|
4
4
|
Summary: Cross-language metadata standard: declare typed entities once, generate idiomatic drift-checked code across languages — Python port.
|
|
5
5
|
Project-URL: Homepage, https://metaobjects.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/metaobjectsdev/metaobjects
|
|
@@ -45,11 +45,13 @@ other language port.
|
|
|
45
45
|
|
|
46
46
|
## Install
|
|
47
47
|
|
|
48
|
+
Requires **Python 3.11+**.
|
|
49
|
+
|
|
48
50
|
```bash
|
|
49
51
|
pip install metaobjects
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
The only runtime dependency is PyYAML.
|
|
53
55
|
|
|
54
56
|
## Quick start
|
|
55
57
|
|
|
@@ -77,7 +79,9 @@ The primary public API is the **loader** (`load_directory` / `load_uris` /
|
|
|
77
79
|
`load_string`, `MetaDataLoader`, `LoadResult`, `ErrorCode`, `MetaError`). The
|
|
78
80
|
distribution also ships the Python implementations of the other pillars used by the CLI
|
|
79
81
|
and tooling: `codegen` (Pydantic + FastAPI emit), `render` (Mustache + payload-VO +
|
|
80
|
-
verify), `runtime` (
|
|
82
|
+
verify), and `runtime` (a DB-API 2 `ObjectManager` — pg8000 / psycopg — no SQLAlchemy).
|
|
83
|
+
Schema migrations are owned by the TypeScript `meta` CLI (ADR-0015); there is
|
|
84
|
+
**no `migrate` module** in this distribution.
|
|
81
85
|
|
|
82
86
|
## Authoring formats
|
|
83
87
|
|
|
@@ -10,11 +10,13 @@ other language port.
|
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
|
+
Requires **Python 3.11+**.
|
|
14
|
+
|
|
13
15
|
```bash
|
|
14
16
|
pip install metaobjects
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
The only runtime dependency is PyYAML.
|
|
18
20
|
|
|
19
21
|
## Quick start
|
|
20
22
|
|
|
@@ -42,7 +44,9 @@ The primary public API is the **loader** (`load_directory` / `load_uris` /
|
|
|
42
44
|
`load_string`, `MetaDataLoader`, `LoadResult`, `ErrorCode`, `MetaError`). The
|
|
43
45
|
distribution also ships the Python implementations of the other pillars used by the CLI
|
|
44
46
|
and tooling: `codegen` (Pydantic + FastAPI emit), `render` (Mustache + payload-VO +
|
|
45
|
-
verify), `runtime` (
|
|
47
|
+
verify), and `runtime` (a DB-API 2 `ObjectManager` — pg8000 / psycopg — no SQLAlchemy).
|
|
48
|
+
Schema migrations are owned by the TypeScript `meta` CLI (ADR-0015); there is
|
|
49
|
+
**no `migrate` module** in this distribution.
|
|
46
50
|
|
|
47
51
|
## Authoring formats
|
|
48
52
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "metaobjects"
|
|
3
|
-
version = "0.19.
|
|
3
|
+
version = "0.19.5"
|
|
4
4
|
description = "Cross-language metadata standard: declare typed entities once, generate idiomatic drift-checked code across languages — Python port."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -573,6 +573,16 @@ _TEMPLATE_TEXT_REF_ATTRS = (
|
|
|
573
573
|
)
|
|
574
574
|
|
|
575
575
|
|
|
576
|
+
def _attr_str_list(raw: object) -> list[str] | None:
|
|
577
|
+
"""Coerce a string-array attr (a list, or a single string) to ``list[str]``; ``None``
|
|
578
|
+
when absent — the shape :func:`render.verify` expects for required_slots/required_tags."""
|
|
579
|
+
if isinstance(raw, str):
|
|
580
|
+
return [raw]
|
|
581
|
+
if isinstance(raw, (list, tuple)):
|
|
582
|
+
return [str(x) for x in raw]
|
|
583
|
+
return None
|
|
584
|
+
|
|
585
|
+
|
|
576
586
|
def _verify_templates(args: argparse.Namespace) -> int:
|
|
577
587
|
"""``verify --templates`` — the template/prompt ``{{field}}`` ↔ payload drift gate.
|
|
578
588
|
|
|
@@ -659,6 +669,16 @@ def _verify_templates(args: argparse.Namespace) -> int:
|
|
|
659
669
|
error_count += 1
|
|
660
670
|
continue
|
|
661
671
|
|
|
672
|
+
# #193/#230: @requiredSlots/@requiredTags are a template.prompt concept — enforced ONLY on
|
|
673
|
+
# the prompt path (the email/document output gate does not apply them per-part; the #193
|
|
674
|
+
# cross-port consensus). A prompt whose body omits a required output tag →
|
|
675
|
+
# ERR_OUTPUT_TAG_MISSING (drift, fails); an unused required slot → ERR_REQUIRED_SLOT_UNUSED
|
|
676
|
+
# (warning). This brings Python to parity with TS/Java/C# (previously it enforced neither).
|
|
677
|
+
is_prompt = tmpl.sub_type == tc.TEMPLATE_SUBTYPE_PROMPT
|
|
678
|
+
# ADR-0039: resolving read — an inherited @requiredSlots/@requiredTags is honored.
|
|
679
|
+
req_slots = _attr_str_list(tmpl.get_meta_attr(tc.TEMPLATE_ATTR_REQUIRED_SLOTS)) if is_prompt else None
|
|
680
|
+
req_tags = _attr_str_list(tmpl.get_meta_attr(tc.TEMPLATE_ATTR_REQUIRED_TAGS)) if is_prompt else None
|
|
681
|
+
|
|
662
682
|
for ref in refs:
|
|
663
683
|
text = provider.resolve(ref)
|
|
664
684
|
if text is None:
|
|
@@ -670,12 +690,14 @@ def _verify_templates(args: argparse.Namespace) -> int:
|
|
|
670
690
|
error_count += 1
|
|
671
691
|
continue
|
|
672
692
|
checked += 1
|
|
673
|
-
for e in render_verify(
|
|
693
|
+
for e in render_verify(
|
|
694
|
+
text, fields, provider=provider,
|
|
695
|
+
required_slots=req_slots, required_tags=req_tags,
|
|
696
|
+
):
|
|
674
697
|
if e.code == ERR_REQUIRED_SLOT_UNUSED:
|
|
675
|
-
continue # warning, not drift
|
|
698
|
+
continue # an unused required slot is a warning, not drift
|
|
676
699
|
print(
|
|
677
|
-
f"error: [{tmpl.name}] ref '{ref}' — {e.code}: "
|
|
678
|
-
f"{{{{{e.path}}}}} not on payload VO '{payload_ref}'.",
|
|
700
|
+
f"error: [{tmpl.name}] ref '{ref}' — {e.code}: {e.path}",
|
|
679
701
|
file=sys.stderr,
|
|
680
702
|
)
|
|
681
703
|
error_count += 1
|
{metaobjects-0.19.3 → metaobjects-0.19.5}/src/metaobjects/codegen/generators/router_generator.py
RENAMED
|
@@ -52,6 +52,7 @@ from metaobjects.codegen.instance_artifacts import emits_instance_artifacts
|
|
|
52
52
|
from metaobjects.codegen.type_map import PyType, py_type_for
|
|
53
53
|
from metaobjects.meta.core.field import field_constants as fc
|
|
54
54
|
from metaobjects.meta.core.field.meta_field import MetaField
|
|
55
|
+
from metaobjects.meta.persistence.db import db_constants as dbc
|
|
55
56
|
from metaobjects.meta.core.identity.identity_constants import (
|
|
56
57
|
IDENTITY_ATTR_FIELDS,
|
|
57
58
|
IDENTITY_REFERENCE_ATTR_REFERENCES,
|
|
@@ -95,7 +96,73 @@ def _required_field_names(entity: MetaObject) -> list[str]:
|
|
|
95
96
|
_scalar_fields, which drops object fields for the sort allowlist). FR-035
|
|
96
97
|
PATCH-2 guards present-null on any of these, and a @required jsonb column can
|
|
97
98
|
be nulled just like a scalar one."""
|
|
98
|
-
|
|
99
|
+
# #203/ADR-0045: an @autoSet field is server-owned — never a caller-facing PATCH field, so
|
|
100
|
+
# it is not present-null-checked (a caller-sent null is ignored/stamped, not a 400). Mirrors
|
|
101
|
+
# the Kotlin controller (which excludes @autoSet from its patch-settable set).
|
|
102
|
+
return [f.name for f in entity.fields() if is_required(f) and not _is_auto_set(f)]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# --- #203 / ADR-0045: @autoSet stamping in the generated router (above the repo seam) ----------
|
|
106
|
+
|
|
107
|
+
_AUTO_SET_TEMPORAL_SUBTYPES = (
|
|
108
|
+
fc.FIELD_SUBTYPE_DATE,
|
|
109
|
+
fc.FIELD_SUBTYPE_TIME,
|
|
110
|
+
fc.FIELD_SUBTYPE_TIMESTAMP,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _is_auto_set(field: MetaField) -> bool:
|
|
115
|
+
"""True iff *field* is a server-owned ``@autoSet`` column (ADR-0039 resolving read)."""
|
|
116
|
+
return field.get_meta_attr(fc.FIELD_ATTR_AUTO_SET) in (
|
|
117
|
+
fc.AUTO_SET_ON_CREATE,
|
|
118
|
+
fc.AUTO_SET_ON_UPDATE,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _auto_set_split(entity: MetaObject) -> tuple[list[MetaField], list[MetaField]]:
|
|
123
|
+
"""The entity's ``@autoSet`` temporal fields split into ``(onCreate, onUpdate)``.
|
|
124
|
+
|
|
125
|
+
Reads the RESOLVING ``@autoSet`` (ADR-0039) so an inherited
|
|
126
|
+
``BaseEntity.createdAt/updatedAt`` is honored; a non-temporal field is skipped.
|
|
127
|
+
Both lists are empty for an entity that declares none, keeping its router
|
|
128
|
+
byte-identical to the pre-#203 output."""
|
|
129
|
+
on_create: list[MetaField] = []
|
|
130
|
+
on_update: list[MetaField] = []
|
|
131
|
+
for f in entity.fields():
|
|
132
|
+
if f.sub_type not in _AUTO_SET_TEMPORAL_SUBTYPES:
|
|
133
|
+
continue
|
|
134
|
+
mode = f.get_meta_attr(fc.FIELD_ATTR_AUTO_SET) # ADR-0039 resolving: may be inherited via extends
|
|
135
|
+
if mode == fc.AUTO_SET_ON_CREATE:
|
|
136
|
+
on_create.append(f)
|
|
137
|
+
elif mode == fc.AUTO_SET_ON_UPDATE:
|
|
138
|
+
on_update.append(f)
|
|
139
|
+
return on_create, on_update
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _auto_set_stamp_expr(field: MetaField, base_var: str) -> str:
|
|
143
|
+
"""The inline now()-expression for an ``@autoSet`` *field*, derived from one shared
|
|
144
|
+
tz-aware *base_var* datetime so every column stamped in one op is equal (a fresh
|
|
145
|
+
row's created == updated). Keyed off the COLUMN's temporal type: ``field.date`` →
|
|
146
|
+
``.date()``; ``field.time`` → ``.time()``; ``field.timestamp`` → the tz-aware base
|
|
147
|
+
(``@localTime`` → a naive wall-clock value). Mirrors ObjectManager._auto_set_stamp."""
|
|
148
|
+
sub = field.sub_type
|
|
149
|
+
if sub == fc.FIELD_SUBTYPE_DATE:
|
|
150
|
+
return f"{base_var}.date()"
|
|
151
|
+
if sub == fc.FIELD_SUBTYPE_TIME:
|
|
152
|
+
return f"{base_var}.time()"
|
|
153
|
+
is_tz = field.get_meta_attr(dbc.FIELD_ATTR_LOCAL_TIME) is not True # ADR-0039 resolving
|
|
154
|
+
return base_var if is_tz else f"{base_var}.replace(tzinfo=None)"
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _auto_set_stamp_lines(fields: list[MetaField], comment: str) -> list[str]:
|
|
158
|
+
"""Router-handler body lines stamping *fields* into ``dto`` from ONE captured base
|
|
159
|
+
instant (so all same-op columns are equal), led by *comment*. Empty for no fields
|
|
160
|
+
(so the call site spreads to nothing — a non-@autoSet router stays byte-identical)."""
|
|
161
|
+
if not fields:
|
|
162
|
+
return []
|
|
163
|
+
lines = [f" # {comment}", " _asnow = _dt.datetime.now(_dt.timezone.utc)"]
|
|
164
|
+
lines += [f' dto["{f.name}"] = {_auto_set_stamp_expr(f, "_asnow")}' for f in fields]
|
|
165
|
+
return lines
|
|
99
166
|
|
|
100
167
|
|
|
101
168
|
def _py_set_literal(names: list[str], *, frozen: bool = False) -> str:
|
|
@@ -259,6 +326,8 @@ class RouterGenerator:
|
|
|
259
326
|
ops_const: str,
|
|
260
327
|
model_name: str,
|
|
261
328
|
patch_model: str,
|
|
329
|
+
create_autoset: list[str] = (),
|
|
330
|
+
update_autoset: list[str] = (),
|
|
262
331
|
) -> list[str]:
|
|
263
332
|
"""One CRUD route handler block, dispatched by *name*
|
|
264
333
|
(``list`` / ``get`` / ``create`` / ``update`` / ``delete``). Override to
|
|
@@ -318,6 +387,10 @@ class RouterGenerator:
|
|
|
318
387
|
f" {model_name}(**dto)",
|
|
319
388
|
" except ValidationError:",
|
|
320
389
|
' return JSONResponse(status_code=400, content={"error": "validation"})',
|
|
390
|
+
# #203/ADR-0045: the generated router (the API surface) stamps @autoSet columns
|
|
391
|
+
# ABOVE the consumer repo seam — onCreate + onUpdate both stamped from one instant
|
|
392
|
+
# (a fresh row's createdAt == updatedAt), ignoring any caller-supplied value.
|
|
393
|
+
*create_autoset,
|
|
321
394
|
" return repo.create(dto)",
|
|
322
395
|
]
|
|
323
396
|
if name == "update":
|
|
@@ -341,6 +414,10 @@ class RouterGenerator:
|
|
|
341
414
|
f" {patch_model}(**dto)",
|
|
342
415
|
" except ValidationError:",
|
|
343
416
|
' return JSONResponse(status_code=400, content={"error": "validation"})',
|
|
417
|
+
# #203/ADR-0045: bump onUpdate @autoSet columns (server-owned) on EVERY patch,
|
|
418
|
+
# above the repo seam — a server-inserted present key (compatible with the PATCH
|
|
419
|
+
# present-key tristate); onCreate columns are never touched here.
|
|
420
|
+
*update_autoset,
|
|
344
421
|
f" saved = repo.update({pk_param}, dto)",
|
|
345
422
|
" if saved is None:",
|
|
346
423
|
' return JSONResponse(status_code=404, content={"error": "not_found"})',
|
|
@@ -417,6 +494,24 @@ class RouterGenerator:
|
|
|
417
494
|
ops_const = f"{upper}_FILTER_OPS_BY_FIELD"
|
|
418
495
|
allowlist_module = f"{snake}_filter_allowlist"
|
|
419
496
|
|
|
497
|
+
# #203/ADR-0045: @autoSet columns live on the BASE entity and are shared by every
|
|
498
|
+
# subtype (RESOLVING _auto_set_split(entity) picks up an inherited BaseEntity
|
|
499
|
+
# column too) — computed once here and threaded into each per-subtype handler
|
|
500
|
+
# below (empty for a non-@autoSet hierarchy → byte-identical output).
|
|
501
|
+
on_create_auto, on_update_auto = _auto_set_split(entity)
|
|
502
|
+
has_autoset = bool(on_create_auto or on_update_auto)
|
|
503
|
+
create_autoset = _auto_set_stamp_lines(
|
|
504
|
+
on_create_auto + on_update_auto,
|
|
505
|
+
"#203/ADR-0045: stamp @autoSet columns (server-owned; caller ignored).",
|
|
506
|
+
)
|
|
507
|
+
update_autoset = [
|
|
508
|
+
f' dto.pop("{f.name}", None) # onCreate @autoSet is write-once (server-owned)'
|
|
509
|
+
for f in on_create_auto
|
|
510
|
+
] + _auto_set_stamp_lines(
|
|
511
|
+
on_update_auto,
|
|
512
|
+
"#203/ADR-0045: bump onUpdate @autoSet column(s) (server-owned).",
|
|
513
|
+
)
|
|
514
|
+
|
|
420
515
|
# Sort allowlist = base scalar fields ∪ every subtype's own scalar fields, so a
|
|
421
516
|
# per-subtype list can sort on a subtype-only column too. Stable order.
|
|
422
517
|
sort_fields: list[str] = [f.name for f in _scalar_fields(entity)]
|
|
@@ -448,6 +543,10 @@ class RouterGenerator:
|
|
|
448
543
|
)
|
|
449
544
|
parts.append("from __future__ import annotations")
|
|
450
545
|
parts.append("")
|
|
546
|
+
if has_autoset:
|
|
547
|
+
# #203/ADR-0045: the router stamps @autoSet columns inline (no runtime dependency).
|
|
548
|
+
parts.append("import datetime as _dt")
|
|
549
|
+
parts.append("")
|
|
451
550
|
for import_line in sorted(pk.imports):
|
|
452
551
|
parts.append(import_line)
|
|
453
552
|
if pk.imports:
|
|
@@ -555,6 +654,9 @@ class RouterGenerator:
|
|
|
555
654
|
parts.append(f" {sub}Create(**dto)")
|
|
556
655
|
parts.append(" except ValidationError:")
|
|
557
656
|
parts.append(' return JSONResponse(status_code=400, content={"error": "validation"})')
|
|
657
|
+
# #203/ADR-0045: stamp @autoSet columns ABOVE the repo seam (empty for a
|
|
658
|
+
# non-@autoSet hierarchy — byte-identical output).
|
|
659
|
+
parts.extend(create_autoset)
|
|
558
660
|
parts.append(f' return repo.create("{val}", dto)')
|
|
559
661
|
parts.append("")
|
|
560
662
|
parts.append("")
|
|
@@ -585,6 +687,9 @@ class RouterGenerator:
|
|
|
585
687
|
parts.append(f" {sub}Patch(**dto)")
|
|
586
688
|
parts.append(" except ValidationError:")
|
|
587
689
|
parts.append(' return JSONResponse(status_code=400, content={"error": "validation"})')
|
|
690
|
+
# #203/ADR-0045: pop the write-once onCreate column(s), bump onUpdate — ABOVE
|
|
691
|
+
# the repo seam (empty for a non-@autoSet hierarchy — byte-identical output).
|
|
692
|
+
parts.extend(update_autoset)
|
|
588
693
|
parts.append(f' saved = repo.update("{val}", {pk_param}, dto)')
|
|
589
694
|
parts.append(" if saved is None:")
|
|
590
695
|
parts.append(' return JSONResponse(status_code=404, content={"error": "not_found"})')
|
|
@@ -684,6 +789,27 @@ class RouterGenerator:
|
|
|
684
789
|
# is a 400 — the update handler guards these before the repo call.
|
|
685
790
|
required_set_body = _py_set_literal(_required_field_names(entity), frozen=True)
|
|
686
791
|
|
|
792
|
+
# #203/ADR-0045: @autoSet stamp lines for the create/update handlers (empty for a
|
|
793
|
+
# non-@autoSet entity → byte-identical output). insert stamps onCreate+onUpdate from one
|
|
794
|
+
# instant; a patch bumps onUpdate only (createdAt stays immutable).
|
|
795
|
+
_on_create_auto, _on_update_auto = _auto_set_split(entity)
|
|
796
|
+
create_autoset = _auto_set_stamp_lines(
|
|
797
|
+
_on_create_auto + _on_update_auto,
|
|
798
|
+
"#203/ADR-0045: stamp @autoSet columns (server-owned; caller ignored).",
|
|
799
|
+
)
|
|
800
|
+
# #203/ADR-0045 (integrity): onCreate @autoSet columns are write-once — strip any
|
|
801
|
+
# caller-supplied value on PATCH before the repo call (a caller cannot mutate createdAt via
|
|
802
|
+
# the deployed API). Mirrors ObjectManager.update (`data.pop(...)`) + the Kotlin controller
|
|
803
|
+
# (which excludes @autoSet from its patch-settable set).
|
|
804
|
+
update_autoset = [
|
|
805
|
+
f' dto.pop("{f.name}", None) # onCreate @autoSet is write-once (server-owned)'
|
|
806
|
+
for f in _on_create_auto
|
|
807
|
+
] + _auto_set_stamp_lines(
|
|
808
|
+
_on_update_auto,
|
|
809
|
+
"#203/ADR-0045: bump onUpdate @autoSet column(s) (server-owned).",
|
|
810
|
+
)
|
|
811
|
+
has_autoset = bool(_on_create_auto or _on_update_auto)
|
|
812
|
+
|
|
687
813
|
parts: list[str] = []
|
|
688
814
|
parts.append(
|
|
689
815
|
generated_header(short_name, _effective_fqn(entity)).rstrip() + "\n"
|
|
@@ -691,6 +817,10 @@ class RouterGenerator:
|
|
|
691
817
|
)
|
|
692
818
|
parts.append("from __future__ import annotations")
|
|
693
819
|
parts.append("")
|
|
820
|
+
if has_autoset:
|
|
821
|
+
# #203/ADR-0045: the router stamps @autoSet columns inline (no runtime dependency).
|
|
822
|
+
parts.append("import datetime as _dt")
|
|
823
|
+
parts.append("")
|
|
694
824
|
for import_line in sorted(pk.imports):
|
|
695
825
|
parts.append(import_line)
|
|
696
826
|
if pk.imports:
|
|
@@ -762,6 +892,8 @@ class RouterGenerator:
|
|
|
762
892
|
ops_const=ops_const,
|
|
763
893
|
model_name=f"{short_name}Create",
|
|
764
894
|
patch_model=f"{short_name}Patch",
|
|
895
|
+
create_autoset=create_autoset,
|
|
896
|
+
update_autoset=update_autoset,
|
|
765
897
|
)
|
|
766
898
|
for i, hname in enumerate(("list", "get", "create", "update", "delete")):
|
|
767
899
|
if i > 0:
|
|
@@ -79,6 +79,46 @@ def _templates_dir(tmp_path: Path, body: str) -> str:
|
|
|
79
79
|
return str(root)
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
# An email `template.output` (@kind=email) carries subject + html body part-refs
|
|
83
|
+
# (NO @textRef). Each part's mustache must be drift-checked against the payload VO,
|
|
84
|
+
# parity with a document output and template.prompt (#193).
|
|
85
|
+
_META_EMAIL = """\
|
|
86
|
+
{
|
|
87
|
+
"metadata.root": {
|
|
88
|
+
"package": "acme::ai",
|
|
89
|
+
"children": [
|
|
90
|
+
{
|
|
91
|
+
"object.value": {
|
|
92
|
+
"name": "Welcome",
|
|
93
|
+
"children": [
|
|
94
|
+
{ "field.string": { "name": "name", "@required": true } }
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"template.output": {
|
|
100
|
+
"name": "WelcomeEmail",
|
|
101
|
+
"@kind": "email",
|
|
102
|
+
"@payloadRef": "Welcome",
|
|
103
|
+
"@subjectRef": "emails/subject",
|
|
104
|
+
"@htmlBodyRef": "emails/html"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _email_templates_dir(tmp_path: Path, subject: str, html: str) -> str:
|
|
114
|
+
"""Write a templates root with the email subject + html body parts."""
|
|
115
|
+
root = tmp_path / "email_templates"
|
|
116
|
+
(root / "emails").mkdir(parents=True)
|
|
117
|
+
(root / "emails" / "subject.mustache").write_text(subject)
|
|
118
|
+
(root / "emails" / "html.mustache").write_text(html)
|
|
119
|
+
return str(root)
|
|
120
|
+
|
|
121
|
+
|
|
82
122
|
def _meta_dir_with(tmp_path: Path, meta_json: str) -> str:
|
|
83
123
|
d = tmp_path / "tmeta"
|
|
84
124
|
d.mkdir()
|
|
@@ -135,6 +175,76 @@ def test_templates_unresolvable_ref_is_drift(tmp_path: Path) -> None:
|
|
|
135
175
|
assert rc != 0
|
|
136
176
|
|
|
137
177
|
|
|
178
|
+
def test_templates_email_clean_returns_zero(tmp_path: Path) -> None:
|
|
179
|
+
# An @kind=email output whose subject + html parts reference only payload
|
|
180
|
+
# fields is clean (#193 — email parts drift-checked like a document body).
|
|
181
|
+
meta_dir = _meta_dir_with(tmp_path, _META_EMAIL)
|
|
182
|
+
troot = _email_templates_dir(tmp_path, "Hello {{name}}", "<p>Hi {{name}}</p>")
|
|
183
|
+
rc = main(["verify", "--templates", meta_dir, "--templates-root", troot])
|
|
184
|
+
assert rc == 0
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def test_templates_email_body_drift_is_caught(tmp_path: Path, capsys) -> None:
|
|
188
|
+
# A {{missing}} in the email HTML body part must be reported (#193): the
|
|
189
|
+
# per-port bug was skipping @kind=email templates entirely.
|
|
190
|
+
meta_dir = _meta_dir_with(tmp_path, _META_EMAIL)
|
|
191
|
+
troot = _email_templates_dir(tmp_path, "Hello {{name}}", "<p>Hi {{missing}}</p>")
|
|
192
|
+
rc = main(["verify", "--templates", meta_dir, "--templates-root", troot])
|
|
193
|
+
assert rc != 0
|
|
194
|
+
err = capsys.readouterr().err
|
|
195
|
+
assert "missing" in err
|
|
196
|
+
assert "WelcomeEmail" in err
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# --- #230: template.prompt @requiredTags / @requiredSlots enforcement (parity w/ TS/Java/C#) ---
|
|
200
|
+
|
|
201
|
+
_META_PROMPT_TAGS = """\
|
|
202
|
+
{
|
|
203
|
+
"metadata.root": {
|
|
204
|
+
"package": "acme::ai",
|
|
205
|
+
"children": [
|
|
206
|
+
{
|
|
207
|
+
"object.value": {
|
|
208
|
+
"name": "Welcome",
|
|
209
|
+
"children": [
|
|
210
|
+
{ "field.string": { "name": "name", "@required": true } }
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"template.prompt": {
|
|
216
|
+
"name": "TaggedPrompt",
|
|
217
|
+
"@payloadRef": "Welcome",
|
|
218
|
+
"@textRef": "pages/welcome",
|
|
219
|
+
"@requiredTags": ["answer"]
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def test_templates_prompt_required_tag_missing_is_drift(tmp_path: Path, capsys) -> None:
|
|
229
|
+
# #230: a template.prompt @requiredTags whose rendered body omits the <answer> tag must FAIL
|
|
230
|
+
# verify. Python previously passed required_slots/required_tags to render_verify NOWHERE, so it
|
|
231
|
+
# enforced neither — a divergence from TS/Java/C#.
|
|
232
|
+
meta_dir = _meta_dir_with(tmp_path, _META_PROMPT_TAGS)
|
|
233
|
+
troot = _templates_dir(tmp_path, "Hello {{name}}") # no <answer>…</answer> tag
|
|
234
|
+
rc = main(["verify", "--templates", meta_dir, "--templates-root", troot])
|
|
235
|
+
assert rc != 0
|
|
236
|
+
err = capsys.readouterr().err
|
|
237
|
+
assert "ERR_OUTPUT_TAG_MISSING" in err
|
|
238
|
+
assert "answer" in err
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def test_templates_prompt_required_tag_present_passes(tmp_path: Path) -> None:
|
|
242
|
+
meta_dir = _meta_dir_with(tmp_path, _META_PROMPT_TAGS)
|
|
243
|
+
troot = _templates_dir(tmp_path, "Hello {{name}} <answer>{{name}}</answer>")
|
|
244
|
+
rc = main(["verify", "--templates", meta_dir, "--templates-root", troot])
|
|
245
|
+
assert rc == 0
|
|
246
|
+
|
|
247
|
+
|
|
138
248
|
# --- 3. bare verify = codegen (back-compat) + the subverb note --------------
|
|
139
249
|
|
|
140
250
|
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""#203 / ADR-0045 — the generated FastAPI router (the deployed API surface) stamps
|
|
2
|
+
``@autoSet`` timestamps ABOVE the consumer repository seam, so an adopter who wires
|
|
3
|
+
their own persistence into the generated router still gets the shipped stamping
|
|
4
|
+
semantics. (The ObjectManager keeps stamping for non-HTTP writes — defense-in-depth,
|
|
5
|
+
not the wire-tier guarantee.) A non-``@autoSet`` entity's router stays byte-identical."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import shutil
|
|
10
|
+
import tempfile
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from metaobjects import MetaDataLoader
|
|
14
|
+
from metaobjects.codegen.generators.m2m_codegen import build_object_index
|
|
15
|
+
from metaobjects.codegen.generators.router_generator import render_router
|
|
16
|
+
from metaobjects.meta.core.object.meta_object import MetaObject
|
|
17
|
+
from metaobjects.shared.base_types import TYPE_OBJECT
|
|
18
|
+
|
|
19
|
+
# Event declares both @autoSet policies on field.timestamp columns; Note declares none
|
|
20
|
+
# (the byte-identical baseline).
|
|
21
|
+
_AUTOSET_FIXTURE = """{
|
|
22
|
+
"metadata.root": { "package": "acme::events", "children": [
|
|
23
|
+
{ "object.entity": { "name": "Event", "children": [
|
|
24
|
+
{ "source.rdb": { "@table": "events" } },
|
|
25
|
+
{ "field.long": { "name": "id" } },
|
|
26
|
+
{ "field.string": { "name": "name", "@required": true, "@maxLength": 120 } },
|
|
27
|
+
{ "field.timestamp": { "name": "createdAt", "@autoSet": "onCreate" } },
|
|
28
|
+
{ "field.timestamp": { "name": "updatedAt", "@autoSet": "onUpdate" } },
|
|
29
|
+
{ "identity.primary": { "@fields": "id", "@generation": "increment" } }
|
|
30
|
+
] } },
|
|
31
|
+
{ "object.entity": { "name": "Note", "children": [
|
|
32
|
+
{ "source.rdb": { "@table": "notes" } },
|
|
33
|
+
{ "field.long": { "name": "id" } },
|
|
34
|
+
{ "field.string": { "name": "title", "@required": true, "@maxLength": 200 } },
|
|
35
|
+
{ "identity.primary": { "@fields": "id", "@generation": "increment" } }
|
|
36
|
+
] } }
|
|
37
|
+
] }
|
|
38
|
+
}"""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _load(fixture: str) -> dict[str, MetaObject]:
|
|
42
|
+
tmp = Path(tempfile.mkdtemp(prefix="router-autoset-"))
|
|
43
|
+
try:
|
|
44
|
+
(tmp / "meta.events.json").write_text(fixture)
|
|
45
|
+
result = MetaDataLoader.from_directory(str(tmp))
|
|
46
|
+
assert not result.errors, [f"{e.code}: {e.message}" for e in result.errors]
|
|
47
|
+
return {
|
|
48
|
+
c.name: c
|
|
49
|
+
for c in result.root.children()
|
|
50
|
+
if c.type == TYPE_OBJECT and isinstance(c, MetaObject)
|
|
51
|
+
}
|
|
52
|
+
finally:
|
|
53
|
+
shutil.rmtree(tmp, ignore_errors=True)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
_ENTITIES = _load(_AUTOSET_FIXTURE)
|
|
57
|
+
_INDEX = build_object_index(list(_ENTITIES.values()))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_router_create_stamps_both_autoset_columns_from_one_instant() -> None:
|
|
61
|
+
src = render_router(_ENTITIES["Event"], _INDEX)
|
|
62
|
+
assert src is not None
|
|
63
|
+
assert "import datetime as _dt" in src
|
|
64
|
+
# ONE captured base instant, assigned to BOTH columns → a fresh row's createdAt == updatedAt.
|
|
65
|
+
assert "_asnow = _dt.datetime.now(_dt.timezone.utc)" in src
|
|
66
|
+
assert 'dto["createdAt"] = _asnow' in src
|
|
67
|
+
assert 'dto["updatedAt"] = _asnow' in src
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_router_update_bumps_only_onupdate() -> None:
|
|
71
|
+
src = render_router(_ENTITIES["Event"], _INDEX)
|
|
72
|
+
assert src is not None
|
|
73
|
+
update_body = src.split("def update_")[1].split("def delete_")[0]
|
|
74
|
+
# the update handler bumps updatedAt; createdAt is never stamped on update (immutable).
|
|
75
|
+
assert 'dto["updatedAt"] = _asnow' in update_body
|
|
76
|
+
assert 'dto["createdAt"] = ' not in update_body
|
|
77
|
+
# #203/ADR-0045 integrity: a caller-supplied onCreate @autoSet value is STRIPPED on PATCH
|
|
78
|
+
# (write-once — cannot be mutated via the deployed API), so it never reaches repo.update.
|
|
79
|
+
assert 'dto.pop("createdAt", None)' in update_body
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_non_autoset_router_is_byte_identical() -> None:
|
|
83
|
+
src = render_router(_ENTITIES["Note"], _INDEX)
|
|
84
|
+
assert src is not None
|
|
85
|
+
assert "import datetime as _dt" not in src
|
|
86
|
+
assert "_asnow" not in src
|
|
87
|
+
assert "_dt.datetime.now" not in src
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# ---- FR-017 TPH: the per-subtype router also stamps @autoSet (#203/#229) ----------------
|
|
91
|
+
#
|
|
92
|
+
# The base `Auth` declares both @autoSet policies on field.timestamp columns (shared by
|
|
93
|
+
# every subtype via `extends`); `PlainAuth` declares none (the byte-identical baseline).
|
|
94
|
+
|
|
95
|
+
TPH_AUTOSET = """
|
|
96
|
+
{ "metadata.root": { "package": "acme::auth", "children": [
|
|
97
|
+
{ "object.entity": { "name": "Auth", "@discriminator": "type", "children": [
|
|
98
|
+
{ "source.rdb": { "@table": "auths" } },
|
|
99
|
+
{ "field.long": { "name": "id" } },
|
|
100
|
+
{ "field.enum": { "name": "type", "@values": ["Bridge"] } },
|
|
101
|
+
{ "field.string": { "name": "reference", "@required": true, "@maxLength": 80 } },
|
|
102
|
+
{ "field.timestamp": { "name": "autoCreatedAt", "@autoSet": "onCreate" } },
|
|
103
|
+
{ "field.timestamp": { "name": "autoUpdatedAt", "@autoSet": "onUpdate" } },
|
|
104
|
+
{ "identity.primary": { "name": "id", "@fields": "id", "@generation": "increment" } }
|
|
105
|
+
]}},
|
|
106
|
+
{ "object.entity": { "name": "BridgeAuth", "extends": "Auth", "@discriminatorValue": "Bridge", "children": [
|
|
107
|
+
{ "field.int": { "name": "quantity", "@required": true } }
|
|
108
|
+
]}}
|
|
109
|
+
]}}
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
TPH_PLAIN = """
|
|
113
|
+
{ "metadata.root": { "package": "acme::auth", "children": [
|
|
114
|
+
{ "object.entity": { "name": "Auth", "@discriminator": "type", "children": [
|
|
115
|
+
{ "source.rdb": { "@table": "auths" } },
|
|
116
|
+
{ "field.long": { "name": "id" } },
|
|
117
|
+
{ "field.enum": { "name": "type", "@values": ["Bridge"] } },
|
|
118
|
+
{ "field.string": { "name": "reference", "@required": true, "@maxLength": 80 } },
|
|
119
|
+
{ "identity.primary": { "name": "id", "@fields": "id", "@generation": "increment" } }
|
|
120
|
+
]}},
|
|
121
|
+
{ "object.entity": { "name": "BridgeAuth", "extends": "Auth", "@discriminatorValue": "Bridge", "children": [
|
|
122
|
+
{ "field.int": { "name": "quantity", "@required": true } }
|
|
123
|
+
]}}
|
|
124
|
+
]}}
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _render_tph(fixture: str, name: str) -> str:
|
|
129
|
+
"""Load *fixture*, build the object index, and render *name*'s (the TPH base's)
|
|
130
|
+
router — dispatched to ``_render_tph_router`` inside ``render_router`` once a
|
|
131
|
+
discriminator plan resolves for it."""
|
|
132
|
+
entities = _load(fixture)
|
|
133
|
+
index = build_object_index(list(entities.values()))
|
|
134
|
+
src = render_router(entities[name], index)
|
|
135
|
+
assert src is not None
|
|
136
|
+
return src
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _handler_body(src: str, def_line: str) -> str:
|
|
140
|
+
"""Slice out one handler's source, from *def_line* up to (not including) the next
|
|
141
|
+
top-level ``def `` (the next handler in the module)."""
|
|
142
|
+
after = src.split(def_line, 1)[1]
|
|
143
|
+
return after.split("\ndef ", 1)[0]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_tph_router_imports_datetime_when_autoset_present() -> None:
|
|
147
|
+
src = _render_tph(TPH_AUTOSET, "Auth")
|
|
148
|
+
assert "import datetime as _dt" in src
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_tph_per_subtype_create_stamps_both_autoset_columns() -> None:
|
|
152
|
+
body = _handler_body(_render_tph(TPH_AUTOSET, "Auth"), "def create_auths_bridge")
|
|
153
|
+
assert "_asnow = _dt.datetime.now(_dt.timezone.utc)" in body
|
|
154
|
+
assert 'dto["autoCreatedAt"] = _asnow' in body
|
|
155
|
+
assert 'dto["autoUpdatedAt"] = _asnow' in body
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_tph_per_subtype_update_pops_oncreate_and_bumps_onupdate() -> None:
|
|
159
|
+
body = _handler_body(_render_tph(TPH_AUTOSET, "Auth"), "def update_auths_bridge")
|
|
160
|
+
assert 'dto.pop("autoCreatedAt", None)' in body
|
|
161
|
+
assert 'dto["autoUpdatedAt"] = _asnow' in body
|
|
162
|
+
assert 'dto["autoCreatedAt"] = ' not in body
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_tph_router_without_autoset_is_byte_identical() -> None:
|
|
166
|
+
src = _render_tph(TPH_PLAIN, "Auth")
|
|
167
|
+
assert "import datetime as _dt" not in src
|
|
168
|
+
assert "_asnow" not in src
|