metaobjects 0.19.1__tar.gz → 0.19.3__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.1 → metaobjects-0.19.3}/PKG-INFO +1 -1
- {metaobjects-0.19.1 → metaobjects-0.19.3}/pyproject.toml +1 -1
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/entity_model.py +29 -12
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/payload_vo_generator.py +196 -34
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/errors.py +3 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/field.json +1 -1
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/validator.json +2 -2
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli_staleness_nudge.py +4 -4
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_payload_vo_generator.py +89 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_render_helper_conformance.py +43 -1
- {metaobjects-0.19.1 → metaobjects-0.19.3}/.gitignore +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/LICENSE +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/README.md +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/hatch_build.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/agent_context/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/agent_context/scaffold.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/api_model.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/builder.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/naming.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/paths.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/renderer.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/attr_class_map.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/cli.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/config.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/format.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generator_registry.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/router_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/template_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/instance_artifacts.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/overwrite_policy.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/runner.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/runtime/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/type_map.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/core_types.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/datatype.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/documentation/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/documentation/doc_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/documentation/doc_provider.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/documentation/doc_schema.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/merge.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/meta_data_loader.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/registered_validation.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/sources/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/sources/directory_source.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/sources/file_source.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/sources/uri_source.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validate_discriminator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validate_field_readonly.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validate_source_escapes.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validation_passes.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/attr/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/field/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/field/field_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/field/meta_field.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/identity/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/index/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/index/index_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/index/meta_index.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/meta_object.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/object_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/object_extract.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/value_object.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/validator/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/meta_data.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/meta_root.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/provider_extends.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/template/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/template/meta_template.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/template/prompt_provider.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/template/template_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/naming_refs.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/parser.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/parser_yaml.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/provider.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/py.typed +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/registry.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/registry_manifest.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/email_document.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/escapers.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/coerce.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/extract.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/extract_map.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/locate.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/normalize.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/strip.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/types.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/filesystem_provider.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/prompt_field.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/prompt_style.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/renderer.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/verify.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/runtime/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/runtime/llm_recorder.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/runtime/n2m_resolver.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/runtime/object_manager.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/runtime/tph.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/serializer_json.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/shared/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/shared/base_types.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/shared/separators.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/shared/structural.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/source/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/source/error_source.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/source/json_path.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/source/semantic_diff.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/source/yaml_positions.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/attr.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/db.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/documentation.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/identity.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/index.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/layout.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/object.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/origin.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/prompt.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/relationship.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/source.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/template.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/ui.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/view.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/super_resolve.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/validation_types.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/yaml_desugar.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/extends/expected/Program.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/extends/meta.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/nested-array/meta.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/scalars/expected/Report.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/scalars/meta.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/vanilla/meta.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_abstract_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_api_docs_builder.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_api_docs_paths.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli_providers.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli_registry.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli_verify_strict.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli_verify_subverbs.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_constants_config.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_entity_model.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_enum_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_extractor_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_filter_allowlist_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_format.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_fr010_output_codegen.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_generator_extension_seams.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_golden.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_inheritance_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_instance_artifacts.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_m2m_codegen.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_origin_nullability.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_output_parser_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_overwrite_policy.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_projection_compile.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_render_helper_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_reverse_finders.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_router_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_runner.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_data.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_output_pattern.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_scope_helpers.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_scope_walk.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_spec.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_tph_codegen.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_trace_helper_generator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_type_map.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_validation_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_write_through.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/capabilities.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/conformance-expected-failures.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/conformance_adapter.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/corpus.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/expected_failures.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/fixture_discovery.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/navigator.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_extract_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_extract_object_verdict.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_fr010_loader_attrs.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_fr011_attrs.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_generator_registry_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_object_model_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_provider_composition_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_registry_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_runner_hardfail.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_spec_metamodel_embed.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_strict_attr_load.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_template_codegen_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_template_generator_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_yaml_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/api_contract_assertions.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/api_contract_jsonb_server.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/api_contract_m2m_server.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/api_contract_server.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/generated_jsonb_app.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/generated_m2m_app.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/generated_router_app.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/generated_tph_app.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/meta_ai_trace.yaml +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/normalization.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/postgres_container.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/query_runner.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/scenarios.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract_generated.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract_jsonb.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract_m2m.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract_m2m_generated.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract_tph_generated.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_generated_router_uuid_pk.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_llm_call_trace.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_normalization.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_query_scenarios.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_runtime_return_types.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/open_closed_proof_test.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_coerce.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_extract.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_extract_map.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_json_forgiving_reader.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_locate.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_model.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_normalize.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_strip.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/prompt/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/prompt/test_output_format_renderer.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_email_document.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_filesystem_provider.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_output_format_renderer_nested.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_output_prompt_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_render_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_render_max_chars.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_verify.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_verify_conformance.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_fr5c_merge_attribution.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_fr5d_reference_resolution.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_fr5e_database_source_shape.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_json_path.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_semantic_diff.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_source_on_node.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_yaml_positions.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/test_api_docs_accuracy.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/__init__.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_agent_context_staleness.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_capabilities.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_common_attrs.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_core_types.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_effective_package.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_errors.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_field_enum.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_field_map_validation.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_index_lookup.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_llm_recorder.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_loader.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_loader_bom.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_loader_class.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_merge.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_meta_attr.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_meta_data.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_meta_source.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_module_shortcuts.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_n2m_resolver.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_n2m_resolver_inherited.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_object_manager_write_through.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_one_primary_source.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_parser.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_parser_yaml.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_provider.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_provider_extension.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_registry.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_registry_completeness.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_registry_extend.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_registry_sealed.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_relationship_referential_actions.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_resolution_key.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_serializer.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_shared_constants.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_smoke.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_source_escapes.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_sources.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_strict_child_placement.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_super_resolve.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_template_toolcall.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_attr_schema.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_filter_values.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_origin_195.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_origin_paths.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_sort_field.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_warnings.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_write_coercion_arrays.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_yaml_desugar.py +0 -0
- {metaobjects-0.19.1 → metaobjects-0.19.3}/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.3
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "metaobjects"
|
|
3
|
-
version = "0.19.
|
|
3
|
+
version = "0.19.3"
|
|
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"
|
{metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/entity_model.py
RENAMED
|
@@ -99,7 +99,7 @@ def _first_attr(field: MetaField, sub_type: str, attr_name: str) -> object | Non
|
|
|
99
99
|
return None
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
def _validator_constraints(field: MetaField, wire: bool
|
|
102
|
+
def _validator_constraints(field: MetaField, *, wire: bool) -> dict[str, object]:
|
|
103
103
|
"""Map the field's ``validator.*`` children + field attrs to Pydantic ``Field``
|
|
104
104
|
kwargs. Cross-port-canonical semantics (TS is the reference):
|
|
105
105
|
|
|
@@ -114,8 +114,13 @@ def _validator_constraints(field: MetaField, wire: bool = False) -> dict[str, ob
|
|
|
114
114
|
every OTHER constraint above unconditionally but must not self-validate Pin 1
|
|
115
115
|
at construction (that broke a downstream adopter's in-process prompt-payload
|
|
116
116
|
``object.value`` models on legitimately-empty slots). Callers building a read
|
|
117
|
-
model (``_field_line``) pass
|
|
118
|
-
|
|
117
|
+
model (``_field_line``) pass ``wire=False``; callers building a wire model
|
|
118
|
+
(``_create_field_line``/``_patch_field_line``) pass ``wire=True``.
|
|
119
|
+
|
|
120
|
+
*wire* is keyword-only and REQUIRED (no default, item 5 of the #224
|
|
121
|
+
cleanup): a forgotten flag at a future wire call site must fail loudly
|
|
122
|
+
(``TypeError``) at generation time rather than silently falling back to a
|
|
123
|
+
permissive read-model tier.
|
|
119
124
|
"""
|
|
120
125
|
kwargs: dict[str, object] = {}
|
|
121
126
|
|
|
@@ -171,17 +176,26 @@ def _validator_constraints(field: MetaField, wire: bool = False) -> dict[str, ob
|
|
|
171
176
|
kwargs["pattern"] = _HOSTNAME_REGEX
|
|
172
177
|
|
|
173
178
|
# FR-036 Pin 1 (#224 — WIRE TIER ONLY) — a @required non-array string is
|
|
174
|
-
# non-empty: reject null / "" but ACCEPT whitespace-only. Emit an
|
|
175
|
-
# min_length of 1
|
|
176
|
-
#
|
|
179
|
+
# non-empty by default: reject null / "" but ACCEPT whitespace-only. Emit an
|
|
180
|
+
# implicit min_length of 1 ONLY when no validator.length @min was authored.
|
|
181
|
+
# Never applied to the in-process read model (wire=False).
|
|
182
|
+
#
|
|
183
|
+
# An explicitly authored @min is always AUTHORITATIVE over the implicit floor
|
|
184
|
+
# (ADR-0044 ruling on #224) — @min: 0 restores "must be provided, may be
|
|
185
|
+
# empty" by opting out of the non-empty floor entirely. Per the cross-port
|
|
186
|
+
# emission pin, an authored @min: 0 emits NO minimum constraint at all
|
|
187
|
+
# (cleanest, deterministic output across ports), so it is popped rather than
|
|
188
|
+
# kept as min_length=0.
|
|
177
189
|
if (
|
|
178
190
|
wire
|
|
179
191
|
and field.sub_type == fc.FIELD_SUBTYPE_STRING
|
|
180
192
|
and not field_is_array(field)
|
|
181
193
|
and field.attrs().get(fc.FIELD_ATTR_REQUIRED) is True
|
|
182
194
|
):
|
|
183
|
-
|
|
184
|
-
|
|
195
|
+
if min_len is None:
|
|
196
|
+
kwargs["min_length"] = 1
|
|
197
|
+
elif min_len == 0:
|
|
198
|
+
kwargs.pop("min_length", None)
|
|
185
199
|
|
|
186
200
|
return kwargs
|
|
187
201
|
|
|
@@ -196,7 +210,7 @@ def _constraint_parts(constraints: dict[str, object]) -> list[str]:
|
|
|
196
210
|
|
|
197
211
|
|
|
198
212
|
def _type_expr_for_field(
|
|
199
|
-
field: MetaField, imports: set[str], config: GenConfig, wire: bool
|
|
213
|
+
field: MetaField, imports: set[str], config: GenConfig, *, wire: bool
|
|
200
214
|
) -> tuple[str, str | None]:
|
|
201
215
|
"""The Python annotation text for *field* (arrays wrapped in ``list[...]``) plus the
|
|
202
216
|
shared-enum class name when the field resolves to one (used to render an enum
|
|
@@ -211,7 +225,10 @@ def _type_expr_for_field(
|
|
|
211
225
|
``@objectRef`` annotates with the referenced VO's own WIRE model (``<Ref>Create``)
|
|
212
226
|
rather than its read model, so a required-string Pin 1 violation nested inside the
|
|
213
227
|
VO still validates on the wire (api-contract jsonb r10). A VO's own Create lines
|
|
214
|
-
are emitted through this same wire path, so nested-VO-in-VO recurses naturally.
|
|
228
|
+
are emitted through this same wire path, so nested-VO-in-VO recurses naturally.
|
|
229
|
+
|
|
230
|
+
*wire* is keyword-only and REQUIRED (no default, item 5 of the #224 cleanup)
|
|
231
|
+
— see ``_validator_constraints`` for the rationale."""
|
|
215
232
|
shared = (
|
|
216
233
|
fr019.shared_enum_for_field(field)
|
|
217
234
|
if field.sub_type == fc.FIELD_SUBTYPE_ENUM
|
|
@@ -275,7 +292,7 @@ def origin_guaranteed_non_null(field: MetaField) -> bool:
|
|
|
275
292
|
|
|
276
293
|
def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple[str, bool]:
|
|
277
294
|
"""Return (source line, uses_field). Collects required imports into *imports*."""
|
|
278
|
-
type_expr, enum_type_name = _type_expr_for_field(field, imports, config)
|
|
295
|
+
type_expr, enum_type_name = _type_expr_for_field(field, imports, config, wire=False)
|
|
279
296
|
required = field.attrs().get(fc.FIELD_ATTR_REQUIRED) is True
|
|
280
297
|
default_raw = field.attrs().get(fc.FIELD_ATTR_DEFAULT)
|
|
281
298
|
# A server-side expression default (now(), gen_random_uuid(), CURRENT_TIMESTAMP)
|
|
@@ -283,7 +300,7 @@ def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple
|
|
|
283
300
|
# its required/optional shape). Only literal defaults become Pydantic defaults.
|
|
284
301
|
has_default = default_raw is not None and not _is_sql_expr_default(default_raw)
|
|
285
302
|
|
|
286
|
-
parts = _constraint_parts(_validator_constraints(field))
|
|
303
|
+
parts = _constraint_parts(_validator_constraints(field, wire=False))
|
|
287
304
|
uses_field = bool(parts)
|
|
288
305
|
|
|
289
306
|
# @default — render an enum member (Type.MEMBER, UPPER_CASE) or a Python literal.
|
{metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/payload_vo_generator.py
RENAMED
|
@@ -69,6 +69,13 @@ from metaobjects.shared.separators import PACKAGE_SEP
|
|
|
69
69
|
|
|
70
70
|
_GENERATOR_NAME = "payload-vo-generator"
|
|
71
71
|
|
|
72
|
+
# ADR-0044 backstop error code — a codegen-time (not loader) error, peer of the
|
|
73
|
+
# render tier's ERR_VAR_NOT_ON_PAYLOAD. Declared LOCALLY here for the same reason
|
|
74
|
+
# the TS reference (codegen-ts/src/payload-codegen.ts) declares it locally rather
|
|
75
|
+
# than in the shared cross-port ledger: promoting it to the ledger is coordinated
|
|
76
|
+
# with the Java/Kotlin follow-up so no port reddens on a code it doesn't yet emit.
|
|
77
|
+
ERR_PAYLOAD_NAME_COLLISION = "ERR_PAYLOAD_NAME_COLLISION"
|
|
78
|
+
|
|
72
79
|
|
|
73
80
|
# ---------------------------------------------------------------------------
|
|
74
81
|
# Naming helpers (snake_case mirrors the router/output-parser local helpers).
|
|
@@ -193,13 +200,17 @@ def _resolve_object_field_type(
|
|
|
193
200
|
field: MetaField,
|
|
194
201
|
root: MetaData,
|
|
195
202
|
nested_emit_queue: list[tuple[MetaObject, str]],
|
|
196
|
-
|
|
203
|
+
emitted_nested_keys: set[str],
|
|
204
|
+
name_map: dict[str, str],
|
|
197
205
|
) -> tuple[str, set[str]]:
|
|
198
206
|
"""A plain ``field.object`` (``@objectRef``, no origin child) — resolve to the
|
|
199
|
-
nested
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
nested payload class (single) or ``list[...]`` (array). The class name comes
|
|
208
|
+
from the ADR-0044 *name_map* (bare ``<Short>Payload`` when unique in the module
|
|
209
|
+
closure, package-qualified on a cross-package short-name collision). The target
|
|
210
|
+
VO is scheduled for in-file emission, deduped by ``resolution_key()`` (NOT
|
|
211
|
+
``fqn()``, which returns the bare name and would collapse two same-short-name
|
|
212
|
+
VOs from different packages into one). Falls back to the bare type-map form when
|
|
213
|
+
the ``@objectRef`` can't be resolved (defensive — loader validation gates it)."""
|
|
203
214
|
ref = field.attrs().get(fc.FIELD_ATTR_OBJECT_REF)
|
|
204
215
|
if not isinstance(ref, str) or not ref:
|
|
205
216
|
return _fallback_type(field)
|
|
@@ -211,12 +222,14 @@ def _resolve_object_field_type(
|
|
|
211
222
|
# both loaded trees (file_default_package) and programmatic trees (root package).
|
|
212
223
|
referrer_pkg = _pkg_of(field.parent) if field.parent is not None else ""
|
|
213
224
|
target = resolve_object_ref(root, ref, referrer_pkg)
|
|
214
|
-
if target
|
|
225
|
+
if not isinstance(target, MetaObject): # narrows MetaData|None -> MetaObject
|
|
215
226
|
return _fallback_type(field)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
227
|
+
target_key = target.resolution_key()
|
|
228
|
+
# name_map was populated by the ADR-0044 closure walk (pass 1/2), which visits
|
|
229
|
+
# the identical targets; the fallback guards a defensive gap only.
|
|
230
|
+
nested_class = name_map.get(target_key) or payload_class_name(target.name)
|
|
231
|
+
if target_key not in emitted_nested_keys:
|
|
232
|
+
emitted_nested_keys.add(target_key)
|
|
220
233
|
nested_emit_queue.append((target, nested_class))
|
|
221
234
|
if type_map.field_is_array(field):
|
|
222
235
|
return f"list[{nested_class}]", set()
|
|
@@ -345,16 +358,20 @@ def _resolve_collection_type(
|
|
|
345
358
|
root: MetaData,
|
|
346
359
|
fallback: MetaField,
|
|
347
360
|
nested_emit_queue: list[tuple[MetaObject, str]],
|
|
348
|
-
|
|
361
|
+
emitted_nested_keys: set[str],
|
|
362
|
+
name_map: dict[str, str],
|
|
349
363
|
) -> tuple[str, set[str]]:
|
|
350
364
|
"""``origin.collection @via "Parent.rel"`` — walk Parent's relationship to
|
|
351
|
-
its ``@objectRef`` target, schedule a nested
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
365
|
+
its ``@objectRef`` target, schedule a nested payload class for in-file
|
|
366
|
+
emission, return ``list[<NestedClass>]``. The class name comes from the
|
|
367
|
+
ADR-0044 *name_map* (bare when unique in the module closure, package-qualified
|
|
368
|
+
on a cross-package short-name collision).
|
|
369
|
+
|
|
370
|
+
Dedupe is per-file via *emitted_nested_keys*, keyed by ``resolution_key()`` —
|
|
371
|
+
if the same target is referenced by two fields in the same payload module,
|
|
372
|
+
only one nested class is emitted; two same-short-name targets in DIFFERENT
|
|
373
|
+
packages are NOT collapsed (each keeps its own key + qualified name).
|
|
374
|
+
Cross-file dedupe would leave forward-references dangling (module docstring)."""
|
|
358
375
|
via = origin.attr(ORIGIN_ATTR_VIA) # ADR-0039 sanctioned own: origin.* never inherits (ADR-0029)
|
|
359
376
|
if not isinstance(via, str) or not via:
|
|
360
377
|
return _fallback_type(fallback)
|
|
@@ -374,11 +391,10 @@ def _resolve_collection_type(
|
|
|
374
391
|
target = _resolve_object_by_short_or_fqn(root, target_ref)
|
|
375
392
|
if target is None:
|
|
376
393
|
return _fallback_type(fallback)
|
|
377
|
-
|
|
378
|
-
nested_class = payload_class_name(target.name)
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
emitted_nested_fqns.add(target_fqn)
|
|
394
|
+
target_key = target.resolution_key()
|
|
395
|
+
nested_class = name_map.get(target_key) or payload_class_name(target.name)
|
|
396
|
+
if target_key not in emitted_nested_keys:
|
|
397
|
+
emitted_nested_keys.add(target_key)
|
|
382
398
|
nested_emit_queue.append((target, nested_class))
|
|
383
399
|
return f"list[{nested_class}]", set()
|
|
384
400
|
|
|
@@ -393,8 +409,9 @@ def _resolve_field_type(
|
|
|
393
409
|
field: MetaField,
|
|
394
410
|
root: MetaData,
|
|
395
411
|
nested_emit_queue: list[tuple[MetaObject, str]],
|
|
396
|
-
|
|
412
|
+
emitted_nested_keys: set[str],
|
|
397
413
|
enum_aliases: dict[str, str],
|
|
414
|
+
name_map: dict[str, str],
|
|
398
415
|
) -> tuple[str, set[str]]:
|
|
399
416
|
"""Resolve the Python annotation for one payload-VO field, honoring any
|
|
400
417
|
``origin.*`` child. Falls back to ``type_map.py_type_for`` when none.
|
|
@@ -415,7 +432,7 @@ def _resolve_field_type(
|
|
|
415
432
|
# reference an undefined bare entity name (Pydantic "not fully defined").
|
|
416
433
|
if field.sub_type == fc.FIELD_SUBTYPE_OBJECT:
|
|
417
434
|
return _resolve_object_field_type(
|
|
418
|
-
field, root, nested_emit_queue,
|
|
435
|
+
field, root, nested_emit_queue, emitted_nested_keys, name_map
|
|
419
436
|
)
|
|
420
437
|
# A ``field.enum`` → Literal[...] (inline) or a named module alias (shared).
|
|
421
438
|
enum_type = _enum_field_type(field, enum_aliases)
|
|
@@ -429,11 +446,142 @@ def _resolve_field_type(
|
|
|
429
446
|
return _resolve_aggregate_type(origin, root, field)
|
|
430
447
|
if origin.sub_type == "collection":
|
|
431
448
|
return _resolve_collection_type(
|
|
432
|
-
origin, root, field, nested_emit_queue,
|
|
449
|
+
origin, root, field, nested_emit_queue, emitted_nested_keys, name_map
|
|
433
450
|
)
|
|
434
451
|
return _fallback_type(field)
|
|
435
452
|
|
|
436
453
|
|
|
454
|
+
# ---------------------------------------------------------------------------
|
|
455
|
+
# ADR-0044 — collision-scoped nested-payload naming (three-pass pipeline).
|
|
456
|
+
#
|
|
457
|
+
# A payload module is a SINGLE self-contained file, so its `@objectRef` /
|
|
458
|
+
# `origin.collection` closure is the collision domain (ADR-0044's "closure of the
|
|
459
|
+
# payload root(s) emitted into one artifact"). Two value-objects sharing a bare
|
|
460
|
+
# short name across packages (`acme::alpha::Note` + `acme::beta::Note`, both
|
|
461
|
+
# reachable from one payload) must emit as DISTINCT classes — the pre-ADR-0044
|
|
462
|
+
# code deduped by `fqn()` (which returns the BARE name here), collapsing them to
|
|
463
|
+
# one class, last-wins, silently dropping the second shape. Pass 1 collects the
|
|
464
|
+
# closure keyed by `resolution_key()` (the true package-qualified FQN); pass 2
|
|
465
|
+
# assigns names as a pure function of the closure (bare when unique, package-
|
|
466
|
+
# qualified on collision, hard fail on a still-colliding derived name); pass 3
|
|
467
|
+
# (the existing emit path) uses the name map for both declaration and reference.
|
|
468
|
+
# ---------------------------------------------------------------------------
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def _package_qualified_name(pkg: str, short_name: str) -> str:
|
|
472
|
+
"""ADR-0044 — PascalCase each ``::``-segment of *pkg*, concatenate, append the
|
|
473
|
+
bare *short_name* (``acme::alpha`` + ``Note`` → ``AcmeAlphaNote``). A root-level
|
|
474
|
+
(empty-package) node keeps its bare short name — the loader's own-package
|
|
475
|
+
uniqueness already precludes two root-level VOs sharing a name, so this can't
|
|
476
|
+
silently under-qualify."""
|
|
477
|
+
if pkg == "":
|
|
478
|
+
return short_name
|
|
479
|
+
return "".join(_pascal(seg) for seg in pkg.split(PACKAGE_SEP)) + short_name
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def _nested_target_of(field: MetaField, root: MetaData) -> MetaObject | None:
|
|
483
|
+
"""The nested-payload target VO a *field* contributes to the module closure,
|
|
484
|
+
or ``None`` when it contributes no nested class. Mirrors the resolution in
|
|
485
|
+
:func:`_resolve_object_field_type` (a plain ``field.object`` ``@objectRef``)
|
|
486
|
+
and :func:`_resolve_collection_type` (``origin.collection`` ``@via``) EXACTLY,
|
|
487
|
+
so the ADR-0044 closure walk and the emission walk agree on the target set.
|
|
488
|
+
``origin.passthrough`` / ``origin.aggregate`` yield scalar types (no nested
|
|
489
|
+
class) and so contribute nothing."""
|
|
490
|
+
origin = _find_origin_child(field)
|
|
491
|
+
if origin is None:
|
|
492
|
+
if field.sub_type != fc.FIELD_SUBTYPE_OBJECT:
|
|
493
|
+
return None
|
|
494
|
+
ref = field.attrs().get(fc.FIELD_ATTR_OBJECT_REF)
|
|
495
|
+
if not isinstance(ref, str) or not ref:
|
|
496
|
+
return None
|
|
497
|
+
referrer_pkg = _pkg_of(field.parent) if field.parent is not None else ""
|
|
498
|
+
target = resolve_object_ref(root, ref, referrer_pkg)
|
|
499
|
+
return target if isinstance(target, MetaObject) else None
|
|
500
|
+
if origin.sub_type == "collection":
|
|
501
|
+
via = origin.attr(ORIGIN_ATTR_VIA) # ADR-0039 sanctioned own: origin.* never inherits
|
|
502
|
+
if not isinstance(via, str) or not via:
|
|
503
|
+
return None
|
|
504
|
+
parts = _split_dotted_ref(via)
|
|
505
|
+
if parts is None:
|
|
506
|
+
return None
|
|
507
|
+
parent_name, rel_name = parts
|
|
508
|
+
parent = _resolve_object_by_short_or_fqn(root, parent_name)
|
|
509
|
+
if parent is None:
|
|
510
|
+
return None
|
|
511
|
+
rel = _find_relationship_on(parent, rel_name)
|
|
512
|
+
if rel is None:
|
|
513
|
+
return None
|
|
514
|
+
target_ref = rel.object_ref()
|
|
515
|
+
if not target_ref:
|
|
516
|
+
return None
|
|
517
|
+
return _resolve_object_by_short_or_fqn(root, target_ref)
|
|
518
|
+
return None
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def _collect_nested_closure(
|
|
522
|
+
root: MetaData,
|
|
523
|
+
payload_vo: MetaObject,
|
|
524
|
+
closure: dict[str, MetaObject],
|
|
525
|
+
seen: set[str],
|
|
526
|
+
) -> None:
|
|
527
|
+
"""ADR-0044 pass 1 — walk *payload_vo*'s transitive nested-payload closure,
|
|
528
|
+
collecting each target VO keyed by ``resolution_key()`` (never ``fqn()`` —
|
|
529
|
+
which returns the bare name and would collapse a cross-package short-name
|
|
530
|
+
collision). *seen* is seeded with the primary payload VO's key so the primary
|
|
531
|
+
(which is named after the TEMPLATE, outside the VO-short-name collision
|
|
532
|
+
domain) is never treated as a nested class, and doubles as the cycle guard."""
|
|
533
|
+
for field in payload_vo.fields():
|
|
534
|
+
if not isinstance(field, MetaField):
|
|
535
|
+
continue
|
|
536
|
+
target = _nested_target_of(field, root)
|
|
537
|
+
if target is None:
|
|
538
|
+
continue
|
|
539
|
+
key = target.resolution_key()
|
|
540
|
+
if key in seen:
|
|
541
|
+
continue
|
|
542
|
+
seen.add(key)
|
|
543
|
+
closure[key] = target
|
|
544
|
+
_collect_nested_closure(root, target, closure, seen)
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def _assign_nested_names(closure: dict[str, MetaObject]) -> dict[str, str]:
|
|
548
|
+
"""ADR-0044 pass 2 — ``resolution_key()`` → emitted class name. A PURE function
|
|
549
|
+
of the closure's ``(key, short-name, package)`` triples, never of traversal
|
|
550
|
+
order: a bare short name unique in the closure emits ``<Short>Payload``
|
|
551
|
+
(byte-identical to pre-ADR-0044 output); a short-name collision emits EVERY
|
|
552
|
+
member under its package-qualified derived name
|
|
553
|
+
(``<PkgSegmentsPascal><Short>Payload``). If two distinct keys still derive the
|
|
554
|
+
same name, fail loud with ``ERR_PAYLOAD_NAME_COLLISION`` — never silently
|
|
555
|
+
collide a second time."""
|
|
556
|
+
by_short: dict[str, list[str]] = {}
|
|
557
|
+
for key, node in closure.items():
|
|
558
|
+
by_short.setdefault(node.name, []).append(key)
|
|
559
|
+
|
|
560
|
+
name_map: dict[str, str] = {}
|
|
561
|
+
for short, keys in by_short.items():
|
|
562
|
+
if len(keys) == 1:
|
|
563
|
+
name_map[keys[0]] = payload_class_name(short)
|
|
564
|
+
continue
|
|
565
|
+
for key in keys:
|
|
566
|
+
node = closure[key]
|
|
567
|
+
name_map[key] = payload_class_name(_package_qualified_name(_pkg_of(node), short))
|
|
568
|
+
|
|
569
|
+
# Backstop — sorted by key so both the emptiness of the colliding set and the
|
|
570
|
+
# pair named in the message are a pure function of the closure, not dict order.
|
|
571
|
+
owner: dict[str, str] = {}
|
|
572
|
+
for key in sorted(name_map):
|
|
573
|
+
emitted = name_map[key]
|
|
574
|
+
existing = owner.get(emitted)
|
|
575
|
+
if existing is not None and existing != key:
|
|
576
|
+
raise ValueError(
|
|
577
|
+
f"{ERR_PAYLOAD_NAME_COLLISION}: payload record name collision: "
|
|
578
|
+
f'"{emitted}" derives from both "{existing}" and "{key}" — rename one '
|
|
579
|
+
"value-object or move it to a package that derives a distinct name"
|
|
580
|
+
)
|
|
581
|
+
owner[emitted] = key
|
|
582
|
+
return name_map
|
|
583
|
+
|
|
584
|
+
|
|
437
585
|
# ---------------------------------------------------------------------------
|
|
438
586
|
# Class-block emission.
|
|
439
587
|
# ---------------------------------------------------------------------------
|
|
@@ -444,10 +592,11 @@ def _emit_payload_class(
|
|
|
444
592
|
payload_vo: MetaObject,
|
|
445
593
|
root: MetaData,
|
|
446
594
|
nested_emit_queue: list[tuple[MetaObject, str]],
|
|
447
|
-
|
|
595
|
+
emitted_nested_keys: set[str],
|
|
448
596
|
extra_imports: set[str],
|
|
449
597
|
enum_aliases: dict[str, str],
|
|
450
598
|
docstring: str,
|
|
599
|
+
name_map: dict[str, str],
|
|
451
600
|
) -> list[str]:
|
|
452
601
|
"""Build the source lines for one Pydantic ``BaseModel`` subclass."""
|
|
453
602
|
lines: list[str] = [f"class {class_name}(BaseModel):", f' """{docstring}"""']
|
|
@@ -456,7 +605,7 @@ def _emit_payload_class(
|
|
|
456
605
|
if not isinstance(field, MetaField):
|
|
457
606
|
continue
|
|
458
607
|
annotation, imports = _resolve_field_type(
|
|
459
|
-
field, root, nested_emit_queue,
|
|
608
|
+
field, root, nested_emit_queue, emitted_nested_keys, enum_aliases, name_map
|
|
460
609
|
)
|
|
461
610
|
extra_imports.update(imports)
|
|
462
611
|
# Optionality mirrors the cross-port (TS) payload-codegen: a ``@required``
|
|
@@ -515,9 +664,18 @@ def render_payload_vo(
|
|
|
515
664
|
if payload is None:
|
|
516
665
|
return None
|
|
517
666
|
|
|
667
|
+
# ADR-0044 pass 1/2 — the collision domain is this module's nested-payload
|
|
668
|
+
# closure. Seed `seen` with the primary VO's key so the primary (named after
|
|
669
|
+
# the TEMPLATE, not the VO) stays out of the VO-short-name collision domain.
|
|
670
|
+
closure: dict[str, MetaObject] = {}
|
|
671
|
+
_collect_nested_closure(root, payload, closure, {payload.resolution_key()})
|
|
672
|
+
name_map = _assign_nested_names(closure)
|
|
673
|
+
|
|
518
674
|
# Per-file dedupe set: scoped to this single render call so each emitted
|
|
519
|
-
# module is self-contained (no cross-template forward references).
|
|
520
|
-
|
|
675
|
+
# module is self-contained (no cross-template forward references). Keyed by
|
|
676
|
+
# ``resolution_key()`` (see _resolve_object_field_type) so two same-short-name
|
|
677
|
+
# VOs from different packages both emit.
|
|
678
|
+
emitted_nested_keys: set[str] = set()
|
|
521
679
|
class_name = payload_class_name(template.name)
|
|
522
680
|
extra_imports: set[str] = set()
|
|
523
681
|
nested_emit_queue: list[tuple[MetaObject, str]] = []
|
|
@@ -541,13 +699,14 @@ def render_payload_vo(
|
|
|
541
699
|
payload_vo=payload,
|
|
542
700
|
root=root,
|
|
543
701
|
nested_emit_queue=nested_emit_queue,
|
|
544
|
-
|
|
702
|
+
emitted_nested_keys=emitted_nested_keys,
|
|
545
703
|
extra_imports=extra_imports,
|
|
546
704
|
enum_aliases=enum_aliases,
|
|
547
705
|
docstring=(
|
|
548
706
|
f"GENERATED payload for template ``{template.name}``.\n\n"
|
|
549
707
|
f" Field shape derived from the ``{payload.name}`` object.value."
|
|
550
708
|
),
|
|
709
|
+
name_map=name_map,
|
|
551
710
|
)
|
|
552
711
|
if strict_extras:
|
|
553
712
|
primary_block = _with_strict_extras(primary_block)
|
|
@@ -564,12 +723,13 @@ def render_payload_vo(
|
|
|
564
723
|
payload_vo=target,
|
|
565
724
|
root=root,
|
|
566
725
|
nested_emit_queue=nested_emit_queue,
|
|
567
|
-
|
|
726
|
+
emitted_nested_keys=emitted_nested_keys,
|
|
568
727
|
extra_imports=extra_imports,
|
|
569
728
|
enum_aliases=enum_aliases,
|
|
570
729
|
docstring=(
|
|
571
730
|
f"GENERATED nested payload for collection target ``{target.name}``."
|
|
572
731
|
),
|
|
732
|
+
name_map=name_map,
|
|
573
733
|
)
|
|
574
734
|
if strict_extras:
|
|
575
735
|
block = _with_strict_extras(block)
|
|
@@ -650,10 +810,11 @@ class PayloadVoGenerator:
|
|
|
650
810
|
payload_vo: MetaObject,
|
|
651
811
|
root: MetaData,
|
|
652
812
|
nested_emit_queue: list[tuple[MetaObject, str]],
|
|
653
|
-
|
|
813
|
+
emitted_nested_keys: set[str],
|
|
654
814
|
extra_imports: set[str],
|
|
655
815
|
enum_aliases: dict[str, str],
|
|
656
816
|
docstring: str,
|
|
817
|
+
name_map: dict[str, str],
|
|
657
818
|
) -> list[str]:
|
|
658
819
|
"""EXTENSION SEAM — the source lines for one Pydantic ``BaseModel`` subclass
|
|
659
820
|
(primary OR a nested collection target). Defaults to the module-level
|
|
@@ -664,10 +825,11 @@ class PayloadVoGenerator:
|
|
|
664
825
|
payload_vo,
|
|
665
826
|
root,
|
|
666
827
|
nested_emit_queue,
|
|
667
|
-
|
|
828
|
+
emitted_nested_keys,
|
|
668
829
|
extra_imports,
|
|
669
830
|
enum_aliases,
|
|
670
831
|
docstring,
|
|
832
|
+
name_map,
|
|
671
833
|
)
|
|
672
834
|
|
|
673
835
|
def _render_module(self, template: MetaTemplate, root: MetaData) -> str | None:
|
|
@@ -133,6 +133,9 @@ class ErrorCode(str, Enum):
|
|
|
133
133
|
ERR_PARTIAL_UNRESOLVED = "ERR_PARTIAL_UNRESOLVED"
|
|
134
134
|
ERR_REQUIRED_SLOT_UNUSED = "ERR_REQUIRED_SLOT_UNUSED"
|
|
135
135
|
ERR_VAR_NOT_ON_PAYLOAD = "ERR_VAR_NOT_ON_PAYLOAD"
|
|
136
|
+
# Codegen (ADR-0044): two value-object FQNs in one payload artifact derive the same
|
|
137
|
+
# emitted record name even after package-qualification. Peer of ERR_VAR_NOT_ON_PAYLOAD.
|
|
138
|
+
ERR_PAYLOAD_NAME_COLLISION = "ERR_PAYLOAD_NAME_COLLISION"
|
|
136
139
|
ERR_OUTPUT_TAG_MISSING = "ERR_OUTPUT_TAG_MISSING"
|
|
137
140
|
# SP-H Unit9 — @filterable: true on a field subtype with no filter-operator
|
|
138
141
|
# band (e.g. field.object). Would silently generate an empty-ops filter.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{ "type": "validator", "subType": "*", "name": "*", "min": 0, "max": null },
|
|
11
11
|
{ "type": "view", "subType": "*", "name": "*", "min": 0, "max": null },
|
|
12
12
|
{ "type": "origin", "subType": "*", "name": "*", "min": 0, "max": null },
|
|
13
|
-
{ "type": "attr", "subType": "boolean", "name": "required", "min": 0, "max": 1, "description": "When true, the field is NOT NULL. Equivalent to attaching a validator.required child." },
|
|
13
|
+
{ "type": "attr", "subType": "boolean", "name": "required", "min": 0, "max": 1, "description": "When true, the field is NOT NULL. Equivalent to attaching a validator.required child. On a non-array string, generated wire-tier input validation (create/patch) additionally rejects the empty string by default — whitespace is accepted — unless an explicit validator.length @min: 0 opts back to presence-only. In-process read models never enforce this at construction." },
|
|
14
14
|
{ "type": "attr", "subType": "boolean", "name": "readOnly", "min": 0, "max": 1, "description": "FR-013: when true, the field is read-only — codegen emits no setter / writable property, the persistence layer skips the column on INSERT/UPDATE, and Zod/Pydantic/class-validator schemas mark it read-only on input variants. The value is populated by the database (computed column, default expression, trigger), by replication, or by another external owner." },
|
|
15
15
|
{ "type": "attr", "name": "default", "min": 0, "max": 1, "description": "Default value applied to the column when no value is supplied. Its type follows the field's own subtype (string / boolean / number / ...). Converted at consumption time via MetaField.defaultValue()." },
|
|
16
16
|
{ "type": "attr", "subType": "boolean", "name": "unique", "min": 0, "max": 1, "description": "When true, the field gets a column-level UNIQUE constraint." }
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
{
|
|
14
14
|
"type": "validator",
|
|
15
15
|
"subType": "required",
|
|
16
|
-
"description": "Fails when the value is null
|
|
17
|
-
"whenToUse": "A field must be present (NOT NULL). Declare it instead of hand-writing a
|
|
16
|
+
"description": "Fails when the value is null (NOT NULL). Equivalent to @required on the owning field: on a non-array string, generated wire-tier input validation also rejects the empty string by default unless an explicit validator.length @min: 0 opts back to presence-only.",
|
|
17
|
+
"whenToUse": "A field must be present (NOT NULL). Declare it instead of hand-writing a presence check."
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
"type": "validator",
|
|
@@ -47,7 +47,7 @@ def test_gen_nudges_on_stale_manifest(tmp_path, capsys, monkeypatch) -> None:
|
|
|
47
47
|
assert rc == 0 # advisory: never changes the exit code
|
|
48
48
|
err = capsys.readouterr().err
|
|
49
49
|
assert "0.0.1-old" in err
|
|
50
|
-
assert "
|
|
50
|
+
assert "npx meta agent-docs" in err
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
def test_verify_nudges_on_stale_manifest(tmp_path, capsys, monkeypatch) -> None:
|
|
@@ -65,7 +65,7 @@ def test_verify_nudges_on_stale_manifest(tmp_path, capsys, monkeypatch) -> None:
|
|
|
65
65
|
assert rc == 0
|
|
66
66
|
err = capsys.readouterr().err
|
|
67
67
|
assert "0.0.1-old" in err
|
|
68
|
-
assert "
|
|
68
|
+
assert "npx meta agent-docs" in err
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
def test_gen_silent_when_no_manifest(tmp_path, capsys, monkeypatch) -> None:
|
|
@@ -76,7 +76,7 @@ def test_gen_silent_when_no_manifest(tmp_path, capsys, monkeypatch) -> None:
|
|
|
76
76
|
rc = main(["gen", meta_dir, "--out", str(out)])
|
|
77
77
|
assert rc == 0
|
|
78
78
|
err = capsys.readouterr().err
|
|
79
|
-
assert "
|
|
79
|
+
assert "npx meta agent-docs" not in err
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
def test_gen_silent_on_corrupt_manifest(tmp_path, capsys, monkeypatch) -> None:
|
|
@@ -90,4 +90,4 @@ def test_gen_silent_on_corrupt_manifest(tmp_path, capsys, monkeypatch) -> None:
|
|
|
90
90
|
rc = main(["gen", meta_dir, "--out", str(out)])
|
|
91
91
|
assert rc == 0
|
|
92
92
|
err = capsys.readouterr().err
|
|
93
|
-
assert "
|
|
93
|
+
assert "npx meta agent-docs" not in err
|
|
@@ -8,10 +8,13 @@ same target).
|
|
|
8
8
|
"""
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
11
13
|
import metaobjects.core_types # noqa: F401 — side-effect: registers attr classes
|
|
12
14
|
from metaobjects.codegen.config import GenConfig
|
|
13
15
|
from metaobjects.codegen.generator import GenContext
|
|
14
16
|
from metaobjects.codegen.generators.payload_vo_generator import (
|
|
17
|
+
ERR_PAYLOAD_NAME_COLLISION,
|
|
15
18
|
PayloadVoGenerator,
|
|
16
19
|
payload_class_name,
|
|
17
20
|
payload_module_name,
|
|
@@ -428,6 +431,92 @@ def test_plain_object_field_strips_fqn_to_bare_nested_payload() -> None:
|
|
|
428
431
|
assert "::" not in code
|
|
429
432
|
|
|
430
433
|
|
|
434
|
+
def test_cross_package_short_name_collision_qualifies_both_nested_classes() -> None:
|
|
435
|
+
"""ADR-0044 — two ``object.value`` ``Note``s in different packages, both reachable
|
|
436
|
+
from one payload by FQN ``@objectRef``, emit as TWO distinct package-qualified
|
|
437
|
+
classes (``AcmeAlphaNotePayload`` / ``AcmeBetaNotePayload``) — never one shadowed
|
|
438
|
+
``NotePayload`` that drops the second shape. The un-colliding ``Digest`` primary
|
|
439
|
+
stays bare (template-named)."""
|
|
440
|
+
alpha = _value_object("Note", [_field("alphaText", fc.FIELD_SUBTYPE_STRING)], package="acme::alpha")
|
|
441
|
+
beta = _value_object("Note", [_field("betaText", fc.FIELD_SUBTYPE_STRING)], package="acme::beta")
|
|
442
|
+
digest = _value_object(
|
|
443
|
+
"Digest",
|
|
444
|
+
[
|
|
445
|
+
_object_field("fromAlpha", "acme::alpha::Note"),
|
|
446
|
+
_object_field("fromBeta", "acme::beta::Note"),
|
|
447
|
+
],
|
|
448
|
+
package="acme::app",
|
|
449
|
+
)
|
|
450
|
+
tmpl = _template("DigestOut", "Digest")
|
|
451
|
+
root = _root([alpha, beta, digest, tmpl])
|
|
452
|
+
out = render_payload_vo(tmpl, root)
|
|
453
|
+
assert out is not None
|
|
454
|
+
assert "class AcmeAlphaNotePayload(BaseModel):" in out
|
|
455
|
+
assert "class AcmeBetaNotePayload(BaseModel):" in out
|
|
456
|
+
assert "class NotePayload(BaseModel):" not in out
|
|
457
|
+
assert "fromAlpha: AcmeAlphaNotePayload | None = None" in out
|
|
458
|
+
assert "fromBeta: AcmeBetaNotePayload | None = None" in out
|
|
459
|
+
assert '__all__ = ["DigestOutPayload", "AcmeAlphaNotePayload", "AcmeBetaNotePayload"]' in out
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def test_nested_of_nested_collision_qualifies_across_depths() -> None:
|
|
463
|
+
"""ADR-0044 — the collision domain is the WHOLE transitive closure, not one
|
|
464
|
+
depth. A ``Note`` reached at depth 2 (via ``Outer.inner``) collides with a
|
|
465
|
+
``Note`` reached at depth 1; both qualify (``AcmeBetaNotePayload`` /
|
|
466
|
+
``AcmeGammaNotePayload``) while the un-colliding intermediate ``Outer`` stays
|
|
467
|
+
bare. Guards the recursive `_collect_nested_closure` walk."""
|
|
468
|
+
beta_note = _value_object("Note", [_field("betaText", fc.FIELD_SUBTYPE_STRING)], package="acme::beta")
|
|
469
|
+
gamma_note = _value_object("Note", [_field("gammaText", fc.FIELD_SUBTYPE_STRING)], package="acme::gamma")
|
|
470
|
+
outer = _value_object(
|
|
471
|
+
"Outer", [_object_field("inner", "acme::beta::Note")], package="acme::alpha"
|
|
472
|
+
)
|
|
473
|
+
digest = _value_object(
|
|
474
|
+
"Digest",
|
|
475
|
+
[
|
|
476
|
+
_object_field("fromOuter", "acme::alpha::Outer"),
|
|
477
|
+
_object_field("fromGamma", "acme::gamma::Note"),
|
|
478
|
+
],
|
|
479
|
+
package="acme::app",
|
|
480
|
+
)
|
|
481
|
+
tmpl = _template("DigestOut", "Digest")
|
|
482
|
+
root = _root([beta_note, gamma_note, outer, digest, tmpl])
|
|
483
|
+
out = render_payload_vo(tmpl, root)
|
|
484
|
+
assert out is not None
|
|
485
|
+
assert "class OuterPayload(BaseModel):" in out # unique → bare
|
|
486
|
+
assert "class AcmeBetaNotePayload(BaseModel):" in out # depth-2 collision member
|
|
487
|
+
assert "class AcmeGammaNotePayload(BaseModel):" in out # depth-1 collision member
|
|
488
|
+
assert "class NotePayload(BaseModel):" not in out
|
|
489
|
+
assert "inner: AcmeBetaNotePayload | None = None" in out
|
|
490
|
+
assert "fromGamma: AcmeGammaNotePayload | None = None" in out
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def test_derived_name_still_colliding_fails_loud() -> None:
|
|
494
|
+
"""ADR-0044 §4 backstop — two DISTINCT package FQNs whose package-qualified derived
|
|
495
|
+
names still collide (``acme::alpha`` and ``acmeAlpha`` both PascalCase-fold to
|
|
496
|
+
``AcmeAlpha``) must fail loud with ``ERR_PAYLOAD_NAME_COLLISION``, never silently
|
|
497
|
+
emit one class twice."""
|
|
498
|
+
a = _value_object("Note", [_field("a", fc.FIELD_SUBTYPE_STRING)], package="acme::alpha")
|
|
499
|
+
b = _value_object("Note", [_field("b", fc.FIELD_SUBTYPE_STRING)], package="acmeAlpha")
|
|
500
|
+
digest = _value_object(
|
|
501
|
+
"Digest",
|
|
502
|
+
[
|
|
503
|
+
_object_field("fromA", "acme::alpha::Note"),
|
|
504
|
+
_object_field("fromB", "acmeAlpha::Note"),
|
|
505
|
+
],
|
|
506
|
+
package="acme::app",
|
|
507
|
+
)
|
|
508
|
+
tmpl = _template("DigestOut", "Digest")
|
|
509
|
+
root = _root([a, b, digest, tmpl])
|
|
510
|
+
with pytest.raises(ValueError) as ei:
|
|
511
|
+
render_payload_vo(tmpl, root)
|
|
512
|
+
msg = str(ei.value)
|
|
513
|
+
assert ERR_PAYLOAD_NAME_COLLISION in msg
|
|
514
|
+
assert "AcmeAlphaNotePayload" in msg
|
|
515
|
+
# Both source FQNs are named so the author can find and rename one.
|
|
516
|
+
assert "acme::alpha::Note" in msg
|
|
517
|
+
assert "acmeAlpha::Note" in msg
|
|
518
|
+
|
|
519
|
+
|
|
431
520
|
# ---------------------------------------------------------------------------
|
|
432
521
|
# Resolution edge cases.
|
|
433
522
|
# ---------------------------------------------------------------------------
|