metaobjects 0.24.0__tar.gz → 0.24.1__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.24.0 → metaobjects-0.24.1}/PKG-INFO +1 -1
- {metaobjects-0.24.0 → metaobjects-0.24.1}/pyproject.toml +1 -1
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/core_types.py +10 -2
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/errors.py +17 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validation_passes.py +308 -33
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/registry_manifest.py +1 -1
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/identity.json +1 -1
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/index.json +1 -1
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/origin.json +5 -5
- {metaobjects-0.24.0 → metaobjects-0.24.1}/uv.lock +1 -1
- {metaobjects-0.24.0 → metaobjects-0.24.1}/.gitignore +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/LICENSE +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/README.md +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/hatch_build.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/scripts/generate_embedded_library.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/agent_context/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/agent_context/scaffold.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/api_model.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/builder.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/naming.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/paths.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/renderer.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/attr_class_map.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/cli.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/collision_names.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/config.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/format.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generator_registry.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/entity_model.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/find_inbound.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/router_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/template_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/instance_artifacts.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/metaobjects-config.schema.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/overwrite_policy.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/project_config.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/runner.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/runtime/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/type_map.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/config/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/config/neutral_config.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/config/source_resolver.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/datatype.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/documentation/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/documentation/doc_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/documentation/doc_provider.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/documentation/doc_schema.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/library/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/library/embedded_library.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/library/library_sources.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/merge.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/meta_data_loader.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/registered_validation.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/sources/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/sources/directory_source.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/sources/file_source.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/sources/uri_source.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_discriminator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_enum_normalize_ambiguity.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_field_mutability.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_source_escapes.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/attr/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/field/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/field/field_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/field/meta_field.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/identity/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/index/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/index/index_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/index/meta_index.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/meta_object.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/object_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/object_extract.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/value_object.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/requirement/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/requirement/meta_requirement.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/requirement/requirement_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/validator/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/meta_data.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/meta_root.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/provider_extends.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/template/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/template/meta_template.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/template/prompt_provider.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/template/template_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/naming_refs.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/parser.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/parser_yaml.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/provider.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/py.typed +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/registry.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/email_document.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/escapers.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/coerce.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/extract.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/extract_map.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/locate.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/normalize.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/strip.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/types.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/filesystem_provider.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/payload_accessors.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/prompt_field.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/prompt_style.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/renderer.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/verify.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/runtime/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/runtime/llm_recorder.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/runtime/n2m_resolver.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/runtime/object_manager.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/runtime/tph.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/serializer_json.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/shared/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/shared/base_types.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/shared/separators.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/shared/structural.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/source/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/source/error_source.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/source/json_path.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/source/semantic_diff.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/source/yaml_positions.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/attr.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/db.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/documentation.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/field.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/layout.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/object.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/prompt.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/relationship.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/requirement.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/source.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/template.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/ui.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/validator.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/view.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/super_resolve.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/validation_types.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/yaml_desugar.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/extends/expected/Program.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/extends/meta.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/nested-array/meta.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/scalars/expected/Report.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/scalars/meta.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/vanilla/meta.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_abstract_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_api_docs_builder.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_api_docs_paths.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_config_gen.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_config_verify.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_providers.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_registry.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_staleness_nudge.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_verify_strict.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_verify_subverbs.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_constants_config.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_entity_model.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_enum_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_extract_tier_collision.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_extractor_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_filter_allowlist_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_format.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_fr010_output_codegen.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_generator_extension_seams.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_golden.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_inheritance_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_instance_artifacts.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_m2m_codegen.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_origin_nullability.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_output_parser_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_overwrite_policy.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_overwrite_policy_hash.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_payload_vo_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_project_config.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_projection_compile.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_render_helper_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_render_helper_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_reverse_finders.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_router_autoset.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_router_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_runner.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_sourceless_projection.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_data.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_output_pattern.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_scope_helpers.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_scope_walk.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_spec.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_tph_codegen.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_trace_helper_generator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_type_map.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_validation_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_write_through.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/config/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/config/test_neutral_config.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/config/test_source_resolver.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/capabilities.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/conformance-expected-failures.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/conformance_adapter.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/corpus.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/expected_failures.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/fixture_discovery.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/navigator.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_extract_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_extract_object_verdict.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_fr010_loader_attrs.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_fr011_attrs.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_generator_registry_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_object_model_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_provider_composition_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_registry_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_runner_hardfail.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_source_resolution_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_spec_metamodel_embed.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_strict_attr_load.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_template_codegen_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_template_generator_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_yaml_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/api_contract_assertions.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/api_contract_jsonb_server.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/api_contract_m2m_server.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/api_contract_server.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/generated_jsonb_app.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/generated_m2m_app.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/generated_router_app.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/generated_tph_app.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/meta_ai_trace.yaml +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/normalization.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/postgres_container.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/query_runner.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/scenarios.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract_generated.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract_jsonb.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract_m2m.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract_m2m_generated.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract_tph_generated.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_generated_router_uuid_pk.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_llm_call_trace.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_normalization.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_query_scenarios.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_runtime_return_types.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/loader/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/loader/test_enum_extends_values_conflict.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/loader/test_field_mutability_order.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/loader/test_shipped_library_ai.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/loader/test_template_payload_target_210.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/open_closed_proof_test.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_coerce.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_extract.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_extract_map.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_json_forgiving_reader.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_locate.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_model.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_normalize.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_strip.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/prompt/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/prompt/test_output_format_renderer.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_email_document.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_filesystem_provider.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_output_format_renderer_nested.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_output_prompt_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_render_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_render_max_chars.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_verify.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_verify_conformance.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/runtime/test_object_manager_enum_intvaluemap.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_fr5c_merge_attribution.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_fr5d_reference_resolution.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_fr5e_database_source_shape.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_json_path.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_semantic_diff.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_source_on_node.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_yaml_positions.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/test_api_docs_accuracy.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/__init__.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_agent_context_staleness.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_capabilities.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_common_attrs.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_core_types.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_effective_package.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_errors.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_field_enum.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_field_enum_intvaluemap.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_field_map_validation.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_index_lookup.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_llm_recorder.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_loader.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_loader_bom.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_loader_class.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_merge.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_meta_attr.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_meta_data.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_meta_source.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_module_shortcuts.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_n2m_resolver.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_n2m_resolver_inherited.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_object_manager_write_through.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_one_primary_source.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_parser.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_parser_yaml.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_provider.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_provider_extension.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_registry.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_registry_completeness.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_registry_extend.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_registry_sealed.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_relationship_referential_actions.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_resolution_key.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_serializer.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_shared_constants.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_smoke.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_source_escapes.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_sources.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_strict_child_placement.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_super_resolve.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_template_toolcall.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_attr_schema.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_filter_values.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_origin_195.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_origin_paths.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_sort_field.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_warnings.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_write_coercion_arrays.py +0 -0
- {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_yaml_desugar.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: metaobjects
|
|
3
|
-
Version: 0.24.
|
|
3
|
+
Version: 0.24.1
|
|
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.24.
|
|
3
|
+
version = "0.24.1"
|
|
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"
|
|
@@ -541,7 +541,13 @@ core_provider.add(
|
|
|
541
541
|
sub_type=IDENTITY_SUBTYPE_SECONDARY,
|
|
542
542
|
factory=MetaIdentity,
|
|
543
543
|
attrs=[
|
|
544
|
-
|
|
544
|
+
# #342: OPTIONAL, unlike identity.primary/reference. The real rule is
|
|
545
|
+
# @fields XOR @expr — a unique index may key off a raw expression
|
|
546
|
+
# instead of columns — and an exclusive-or is not expressible as a
|
|
547
|
+
# per-attr required flag, so it is enforced by the index-key
|
|
548
|
+
# validation pass (ERR_INVALID_INDEX). Required=True here fired first
|
|
549
|
+
# and made an expression index undeclarable.
|
|
550
|
+
AttrSchema(name=IDENTITY_ATTR_FIELDS, value_type=ATTR_SUBTYPE_STRING, required=False, is_array=True),
|
|
545
551
|
],
|
|
546
552
|
child_rules=[ChildRule(TYPE_ATTR, "*")],
|
|
547
553
|
)
|
|
@@ -576,7 +582,9 @@ for _idx_sub in INDEX_SUBTYPES:
|
|
|
576
582
|
sub_type=_idx_sub,
|
|
577
583
|
factory=MetaIndex,
|
|
578
584
|
attrs=[
|
|
579
|
-
|
|
585
|
+
# #342: OPTIONAL — the key is @fields XOR @expr, enforced by the
|
|
586
|
+
# index-key validation pass (ERR_INVALID_INDEX), not by this flag.
|
|
587
|
+
AttrSchema(name=INDEX_ATTR_FIELDS, value_type=ATTR_SUBTYPE_STRING, required=False, is_array=True),
|
|
580
588
|
],
|
|
581
589
|
child_rules=[ChildRule(TYPE_ATTR, "*")],
|
|
582
590
|
)
|
|
@@ -156,6 +156,23 @@ class ErrorCode(str, Enum):
|
|
|
156
156
|
# SP-H Unit9 — @filterable: true on a field subtype with no filter-operator
|
|
157
157
|
# band (e.g. field.object). Would silently generate an empty-ops filter.
|
|
158
158
|
ERR_FILTERABLE_UNSUPPORTED_SUBTYPE = "ERR_FILTERABLE_UNSUPPORTED_SUBTYPE"
|
|
159
|
+
# #335 Half B — @sortable: true on an array field or a subtype with no
|
|
160
|
+
# filter-operator band (e.g. field.object). Would silently emit a sort
|
|
161
|
+
# entry over a column no dialect can ORDER BY.
|
|
162
|
+
ERR_SORTABLE_UNSUPPORTED_SUBTYPE = "ERR_SORTABLE_UNSUPPORTED_SUBTYPE"
|
|
163
|
+
# #335 Half A — a whole-object @agg:collect (no @of; the carrying field.object
|
|
164
|
+
# rolls related rows up as its declared @objectRef value object) is malformed:
|
|
165
|
+
# carrier is not a field.object with @objectRef, @via absent, @distinct declared
|
|
166
|
+
# (refused — a no-op whenever the value object carries the primary key), an
|
|
167
|
+
# @orderBy key not on the @via TERMINAL entity, or a member's declared type
|
|
168
|
+
# disagreeing with the matched terminal field's. Distinct from ERR_INVALID_ORIGIN
|
|
169
|
+
# so a fixture can tell this arm from a loader that still requires @of.
|
|
170
|
+
ERR_COLLECT_WHOLE_OBJECT = "ERR_COLLECT_WHOLE_OBJECT"
|
|
171
|
+
# #335 Half A — a whole-object @agg:collect's value-object member has no
|
|
172
|
+
# matching field (by name) on the @via terminal entity. The lowering
|
|
173
|
+
# projects exactly the declared members; failing open here is how #270
|
|
174
|
+
# turned a curated value object into the full entity.
|
|
175
|
+
ERR_COLLECT_MEMBER_UNRESOLVED = "ERR_COLLECT_MEMBER_UNRESOLVED"
|
|
159
176
|
# ADR-0023 — a registration was attempted against a registry sealed after its
|
|
160
177
|
# agreed metamodel-provider bootstrap. Codegen cannot invent metamodel attrs.
|
|
161
178
|
ERR_REGISTRY_SEALED = "ERR_REGISTRY_SEALED"
|
|
@@ -26,6 +26,7 @@ from ..meta.core.field.field_constants import (
|
|
|
26
26
|
FIELD_ATTR_INT_VALUE_MAP,
|
|
27
27
|
FIELD_ATTR_OBJECT_REF,
|
|
28
28
|
FIELD_ATTR_REQUIRED,
|
|
29
|
+
FIELD_ATTR_SORTABLE,
|
|
29
30
|
FIELD_ATTR_STORAGE,
|
|
30
31
|
FIELD_ATTR_VALUE_TYPE,
|
|
31
32
|
FIELD_ATTR_VALUES,
|
|
@@ -124,8 +125,14 @@ from ..meta.core.object.object_constants import (
|
|
|
124
125
|
OBJECT_SUBTYPE_PROJECTION,
|
|
125
126
|
OBJECT_SUBTYPE_VALUE,
|
|
126
127
|
)
|
|
127
|
-
from ..meta.core.identity.identity_constants import
|
|
128
|
+
from ..meta.core.identity.identity_constants import (
|
|
129
|
+
IDENTITY_ATTR_FIELDS,
|
|
130
|
+
IDENTITY_SUBTYPE_SECONDARY,
|
|
131
|
+
)
|
|
128
132
|
from ..meta.core.index.index_constants import INDEX_ATTR_FIELDS, INDEX_SUBTYPE_LOOKUP
|
|
133
|
+
from ..meta.persistence.db.db_constants import (
|
|
134
|
+
IDENTITY_SECONDARY_ATTR_EXPR as IDENTITY_ATTR_EXPR,
|
|
135
|
+
)
|
|
129
136
|
from ..source import resolved_source
|
|
130
137
|
from ..naming_refs import did_you_mean_hint, resolve_object_ref
|
|
131
138
|
|
|
@@ -224,8 +231,10 @@ def run_validations(
|
|
|
224
231
|
_validate_identity_passthrough(root, errors)
|
|
225
232
|
_validate_max_occurs(root, registry, errors)
|
|
226
233
|
_validate_filterable_has_index(root, warnings)
|
|
227
|
-
# SP-H Unit9 — @filterable on a subtype with no operator band → error.
|
|
234
|
+
# SP-H Unit9 — @filterable on a subtype with no operator band, or an array → error.
|
|
228
235
|
_validate_filterable_has_supported_ops(root, errors)
|
|
236
|
+
# #335 Half B — @sortable on an array field, or a subtype with no operator band → error.
|
|
237
|
+
_validate_sortable_has_supported_subtype(root, errors)
|
|
229
238
|
_validate_index_lookup_fields(root, errors)
|
|
230
239
|
|
|
231
240
|
|
|
@@ -1575,6 +1584,16 @@ def _validate_entity_field_ref(
|
|
|
1575
1584
|
return (entity, field_node)
|
|
1576
1585
|
|
|
1577
1586
|
|
|
1587
|
+
class WalkedViaPath(NamedTuple):
|
|
1588
|
+
"""A fully-walked ``@via`` path: the relationship hop nodes in path order,
|
|
1589
|
+
plus the entity they terminate at. Returned as a pair (rather than re-walking
|
|
1590
|
+
for the terminal) because a second walk means a second copy of the ADR-0042
|
|
1591
|
+
package-resolution rule — mirrors the TS ``WalkedViaPath``."""
|
|
1592
|
+
|
|
1593
|
+
hops: list[MetaData]
|
|
1594
|
+
terminal: MetaData
|
|
1595
|
+
|
|
1596
|
+
|
|
1578
1597
|
def _validate_via_path(
|
|
1579
1598
|
via: str,
|
|
1580
1599
|
context: str,
|
|
@@ -1583,12 +1602,15 @@ def _validate_via_path(
|
|
|
1583
1602
|
errors: list[MetaError],
|
|
1584
1603
|
origin_node: MetaData,
|
|
1585
1604
|
referrer: str,
|
|
1586
|
-
) ->
|
|
1605
|
+
) -> WalkedViaPath | None:
|
|
1587
1606
|
"""Validate a dotted relationship path 'Entity.rel1[.rel2...]'.
|
|
1588
1607
|
|
|
1589
|
-
Returns the walked relationship hop nodes (in path order)
|
|
1590
|
-
(FR-024 B5 runs the cardinality checks
|
|
1591
|
-
|
|
1608
|
+
Returns the walked relationship hop nodes (in path order) together with the
|
|
1609
|
+
TERMINAL entity node on full success (FR-024 B5 runs the cardinality checks
|
|
1610
|
+
over the hops; #335's whole-object @agg:collect has no @of entity, so its
|
|
1611
|
+
@orderBy keys and value-object members resolve against the terminal);
|
|
1612
|
+
appends ERR_INVALID_ORIGIN and returns None if not. ``terminal`` is defined
|
|
1613
|
+
exactly when ``hops`` is — every early exit returns None.
|
|
1592
1614
|
|
|
1593
1615
|
*origin_node* carries the parse-time envelope (files/json_path); *referrer*
|
|
1594
1616
|
is the canonical referrer FQN (``<projection-FQN>::<fieldName>``) attached
|
|
@@ -1686,7 +1708,8 @@ def _validate_via_path(
|
|
|
1686
1708
|
hops.append(rel_node)
|
|
1687
1709
|
current_entity = next_entity
|
|
1688
1710
|
|
|
1689
|
-
|
|
1711
|
+
# current_entity is the terminal: every earlier exit returned None.
|
|
1712
|
+
return WalkedViaPath(hops, current_entity)
|
|
1690
1713
|
|
|
1691
1714
|
|
|
1692
1715
|
# ---------------------------------------------------------------------------
|
|
@@ -1995,6 +2018,61 @@ def _check_passthrough_type(
|
|
|
1995
2018
|
)
|
|
1996
2019
|
|
|
1997
2020
|
|
|
2021
|
+
def _type_label(field: MetaData) -> str:
|
|
2022
|
+
"""A field's declared type on BOTH axes — ``field.<subType>`` plus ``[]`` when it is
|
|
2023
|
+
an array. ADR-0039: resolved_is_array(), so array-ness inherited via extends counts.
|
|
2024
|
+
Mirrors the TS ``_typeLabel``."""
|
|
2025
|
+
return f"field.{field.sub_type}{'[]' if field.resolved_is_array() else ''}"
|
|
2026
|
+
|
|
2027
|
+
|
|
2028
|
+
def _check_collect_members(
|
|
2029
|
+
ref_target: MetaData,
|
|
2030
|
+
terminal: MetaData,
|
|
2031
|
+
obj: MetaData,
|
|
2032
|
+
field: MetaData,
|
|
2033
|
+
src: object,
|
|
2034
|
+
errors: list[MetaError],
|
|
2035
|
+
) -> None:
|
|
2036
|
+
"""#335 — a whole-object @agg:collect projects EXACTLY the declared value object's
|
|
2037
|
+
members, matched BY NAME against the @via terminal entity's fields:
|
|
2038
|
+
|
|
2039
|
+
- an unmatched member is an error, never a silent drop. Failing open here is how
|
|
2040
|
+
#270 turned a curated value object into the full entity, invisible in a diff
|
|
2041
|
+
because the metadata still read as curated.
|
|
2042
|
+
- a matched member must agree on BOTH type axes (#185 type-preserving doctrine),
|
|
2043
|
+
so a scalar member cannot bind an array field or vice versa.
|
|
2044
|
+
|
|
2045
|
+
Both refusals carry a whole-object-specific code — ERR_COLLECT_MEMBER_UNRESOLVED for
|
|
2046
|
+
the unmatched member, ERR_COLLECT_WHOLE_OBJECT for the type disagreement. The latter
|
|
2047
|
+
is deliberately NOT the scalar arm's ERR_INVALID_ORIGIN: a loader that still requires
|
|
2048
|
+
@of rejects this metadata with ERR_INVALID_ORIGIN too, so sharing the code would make
|
|
2049
|
+
a corpus fixture pass on a port that implements nothing.
|
|
2050
|
+
|
|
2051
|
+
Mirrors the TS ``_checkCollectMembers``."""
|
|
2052
|
+
# ADR-0039: resolving — a value object may inherit members via extends, and the
|
|
2053
|
+
# terminal entity may inherit fields; own-only would silently skip inherited
|
|
2054
|
+
# members, which is exactly the #270 bug class this guards.
|
|
2055
|
+
terminal_fields = [c for c in terminal.children() if c.type == TYPE_FIELD]
|
|
2056
|
+
for member in (c for c in ref_target.children() if c.type == TYPE_FIELD):
|
|
2057
|
+
match = next((f for f in terminal_fields if f.name == member.name), None)
|
|
2058
|
+
if match is None:
|
|
2059
|
+
errors.append(MetaError(
|
|
2060
|
+
f"origin.aggregate @agg:collect on {obj.name}.{field.name}: value-object "
|
|
2061
|
+
f"member '{member.name}' has no matching field on '{terminal.name}' — a "
|
|
2062
|
+
f"whole-object rollup projects exactly the declared members.",
|
|
2063
|
+
ErrorCode.ERR_COLLECT_MEMBER_UNRESOLVED, envelope=src))
|
|
2064
|
+
continue
|
|
2065
|
+
member_label = _type_label(member)
|
|
2066
|
+
match_label = _type_label(match)
|
|
2067
|
+
if member_label != match_label:
|
|
2068
|
+
errors.append(MetaError(
|
|
2069
|
+
f"origin.aggregate @agg:collect on {obj.name}.{field.name}: value-object "
|
|
2070
|
+
f"member '{member.name}' is {member_label} but "
|
|
2071
|
+
f"'{terminal.name}.{match.name}' is {match_label} — a whole-object rollup "
|
|
2072
|
+
f"preserves each member's type.",
|
|
2073
|
+
ErrorCode.ERR_COLLECT_WHOLE_OBJECT, envelope=src))
|
|
2074
|
+
|
|
2075
|
+
|
|
1998
2076
|
def _validate_order_by_keys(
|
|
1999
2077
|
order_by: object,
|
|
2000
2078
|
related_entity: MetaData | None,
|
|
@@ -2003,12 +2081,17 @@ def _validate_order_by_keys(
|
|
|
2003
2081
|
label: str,
|
|
2004
2082
|
origin_source: object,
|
|
2005
2083
|
errors: list[MetaError],
|
|
2084
|
+
code: ErrorCode = ErrorCode.ERR_INVALID_ORIGIN,
|
|
2006
2085
|
) -> None:
|
|
2007
2086
|
"""#195 — validate that ``@orderBy`` keys ('field[:asc|desc]') resolve against the
|
|
2008
2087
|
RELATED entity's effective fields (the entity reached via @via/@of), and that any
|
|
2009
2088
|
direction suffix is asc/desc. Shared by @agg:collect (element order) and origin.first
|
|
2010
2089
|
(row selection). A missing related entity means a prior error already fired — skip.
|
|
2011
|
-
Mirrors the TS _validateOrderByKeys.
|
|
2090
|
+
Mirrors the TS _validateOrderByKeys.
|
|
2091
|
+
|
|
2092
|
+
*code* lets #335's whole-object @agg:collect arm report ERR_COLLECT_WHOLE_OBJECT
|
|
2093
|
+
instead; it defaults to ERR_INVALID_ORIGIN so the scalar @of and origin.first call
|
|
2094
|
+
sites keep their existing envelope byte-for-byte."""
|
|
2012
2095
|
if not isinstance(order_by, (list, tuple)) or related_entity is None:
|
|
2013
2096
|
return
|
|
2014
2097
|
for raw in order_by:
|
|
@@ -2027,7 +2110,7 @@ def _validate_order_by_keys(
|
|
|
2027
2110
|
MetaError(
|
|
2028
2111
|
f'{label} on {obj.name}.{field_name}: @orderBy key "{raw}" — no such '
|
|
2029
2112
|
f'field "{key}" on {related_entity.name}.',
|
|
2030
|
-
|
|
2113
|
+
code,
|
|
2031
2114
|
envelope=origin_source,
|
|
2032
2115
|
)
|
|
2033
2116
|
)
|
|
@@ -2036,7 +2119,7 @@ def _validate_order_by_keys(
|
|
|
2036
2119
|
MetaError(
|
|
2037
2120
|
f'{label} on {obj.name}.{field_name}: @orderBy key "{raw}" — direction '
|
|
2038
2121
|
f"must be one of {'|'.join(SORT_ORDER_VALUES)}.",
|
|
2039
|
-
|
|
2122
|
+
code,
|
|
2040
2123
|
envelope=origin_source,
|
|
2041
2124
|
)
|
|
2042
2125
|
)
|
|
@@ -2130,7 +2213,8 @@ def _validate_origin_paths(
|
|
|
2130
2213
|
)
|
|
2131
2214
|
via = origin.attr(ORIGIN_ATTR_VIA)
|
|
2132
2215
|
if isinstance(via, str) and via:
|
|
2133
|
-
|
|
2216
|
+
walked = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
|
|
2217
|
+
hops = walked.hops if walked is not None else None
|
|
2134
2218
|
if hops is not None:
|
|
2135
2219
|
_check_passthrough_cardinality(hops, node.name, origin.source, errors)
|
|
2136
2220
|
elif from_target is not None and not is_value_host:
|
|
@@ -2226,20 +2310,94 @@ def _validate_origin_paths(
|
|
|
2226
2310
|
f"explicit @via (a quantifier has no @of to infer the path from).",
|
|
2227
2311
|
ErrorCode.ERR_INVALID_ORIGIN, envelope=src))
|
|
2228
2312
|
else:
|
|
2229
|
-
|
|
2313
|
+
walked = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
|
|
2314
|
+
hops = walked.hops if walked is not None else None
|
|
2230
2315
|
if hops is not None:
|
|
2231
2316
|
_check_aggregate_cardinality(hops, node.name, src, errors)
|
|
2232
2317
|
continue
|
|
2233
2318
|
|
|
2234
|
-
# --- count/sum/avg/min/max
|
|
2319
|
+
# --- @of: REQUIRED for count/sum/avg/min/max; OPTIONAL for collect ---
|
|
2320
|
+
# #335 — an @of-absent collect is a WHOLE-OBJECT rollup: collect the
|
|
2321
|
+
# related rows as an array of the field's declared @objectRef value
|
|
2322
|
+
# object rather than an array of one scalar column.
|
|
2235
2323
|
if not of_present:
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2324
|
+
if not is_collect:
|
|
2325
|
+
errors.append(
|
|
2326
|
+
MetaError(
|
|
2327
|
+
f"{ctx} is missing required attribute '@{ORIGIN_ATTR_OF}'",
|
|
2328
|
+
ErrorCode.ERR_INVALID_ORIGIN,
|
|
2329
|
+
envelope=src,
|
|
2330
|
+
)
|
|
2241
2331
|
)
|
|
2242
|
-
|
|
2332
|
+
continue
|
|
2333
|
+
# Whole-object rollup. The carrying field must be a field.object
|
|
2334
|
+
# naming a value object, and @via must be explicit (there is no @of
|
|
2335
|
+
# entity to infer the single-hop relation from).
|
|
2336
|
+
# ADR-0039: resolving — @objectRef may be inherited via extends.
|
|
2337
|
+
object_ref = node.get_meta_attr(FIELD_ATTR_OBJECT_REF)
|
|
2338
|
+
if node.sub_type != FIELD_SUBTYPE_OBJECT or not isinstance(object_ref, str) \
|
|
2339
|
+
or not object_ref:
|
|
2340
|
+
errors.append(MetaError(
|
|
2341
|
+
f"origin.aggregate @agg:collect on {obj.name}.{node.name}: @of is "
|
|
2342
|
+
f"omitted, so this is a whole-object rollup — the carrying field "
|
|
2343
|
+
f"must be a field.object declaring @objectRef (add @of to collect a "
|
|
2344
|
+
f"single column instead).",
|
|
2345
|
+
ErrorCode.ERR_COLLECT_WHOLE_OBJECT, envelope=src))
|
|
2346
|
+
continue
|
|
2347
|
+
# #210's value-only rule is PAYLOAD-scoped and never reaches a
|
|
2348
|
+
# projection-hosted field, so this branch enforces it itself.
|
|
2349
|
+
# Without it an @objectRef to an entity silently rolls up the FULL
|
|
2350
|
+
# entity — the #270 shape, this time baked into DDL.
|
|
2351
|
+
# ADR-0042 — a bare @objectRef resolves in the DECLARING owner's
|
|
2352
|
+
# package (an inherited field resolves in the package that declared it).
|
|
2353
|
+
ref_owner = node.parent or obj
|
|
2354
|
+
ref_pkg = ref_owner.package or ref_owner.file_default_package or ""
|
|
2355
|
+
ref_target = resolve_object_ref(root, object_ref, ref_pkg)
|
|
2356
|
+
if ref_target is not None and ref_target.sub_type != OBJECT_SUBTYPE_VALUE:
|
|
2357
|
+
errors.append(MetaError(
|
|
2358
|
+
f"origin.aggregate @agg:collect on {obj.name}.{node.name}: "
|
|
2359
|
+
f"@objectRef '{object_ref}' resolves to "
|
|
2360
|
+
f"{TYPE_OBJECT}.{ref_target.sub_type} — a whole-object rollup must "
|
|
2361
|
+
f"target an object.value (#210, ADR-0028).",
|
|
2362
|
+
ErrorCode.ERR_SUBTYPE_RULE_VIOLATION, envelope=src))
|
|
2363
|
+
continue
|
|
2364
|
+
# ADR-0039: own — origin.* never inherits (ADR-0029).
|
|
2365
|
+
via_attr = origin.attr(ORIGIN_ATTR_VIA)
|
|
2366
|
+
if not isinstance(via_attr, str) or not via_attr:
|
|
2367
|
+
errors.append(MetaError(
|
|
2368
|
+
f"origin.aggregate @agg:collect on {obj.name}.{node.name}: @via is "
|
|
2369
|
+
f"required on a whole-object rollup — there is no @of entity to "
|
|
2370
|
+
f"infer the relationship from.",
|
|
2371
|
+
ErrorCode.ERR_COLLECT_WHOLE_OBJECT, envelope=src))
|
|
2372
|
+
continue
|
|
2373
|
+
# @distinct is refused on the object form. It is NOT an engine limit
|
|
2374
|
+
# (both engines dedupe JSON objects); it is a guaranteed no-op
|
|
2375
|
+
# whenever the value object carries the entity's primary key, which
|
|
2376
|
+
# is the common case, and a silent no-op is worse than a refusal.
|
|
2377
|
+
if has_distinct:
|
|
2378
|
+
errors.append(MetaError(
|
|
2379
|
+
f"origin.aggregate @agg:collect on {obj.name}.{node.name}: @distinct "
|
|
2380
|
+
f"is not supported on a whole-object rollup (it is a no-op whenever "
|
|
2381
|
+
f"the value object carries the primary key).",
|
|
2382
|
+
ErrorCode.ERR_COLLECT_WHOLE_OBJECT, envelope=src))
|
|
2383
|
+
continue
|
|
2384
|
+
# One walk yields both the hops (cardinality) and the terminal entity
|
|
2385
|
+
# (@orderBy keys, member resolution). An invalid @via (e.g. a single-
|
|
2386
|
+
# segment "A") returns None having already pushed its own error, so
|
|
2387
|
+
# everything downstream is skipped and no second, misleadingly-scoped
|
|
2388
|
+
# error is emitted.
|
|
2389
|
+
walked = _validate_via_path(
|
|
2390
|
+
via_attr, ctx, root, host_pkg, errors, origin, referrer)
|
|
2391
|
+
if walked is not None:
|
|
2392
|
+
_check_aggregate_cardinality(walked.hops, node.name, src, errors)
|
|
2393
|
+
# @orderBy keys resolve against the @via TERMINAL entity, not @of.
|
|
2394
|
+
_validate_order_by_keys(
|
|
2395
|
+
order_by, walked.terminal, obj, node.name,
|
|
2396
|
+
"origin.aggregate @agg:collect", src, errors,
|
|
2397
|
+
ErrorCode.ERR_COLLECT_WHOLE_OBJECT)
|
|
2398
|
+
if ref_target is not None:
|
|
2399
|
+
_check_collect_members(
|
|
2400
|
+
ref_target, walked.terminal, obj, node, src, errors)
|
|
2243
2401
|
continue
|
|
2244
2402
|
# NOTE (FR-024 B6): NO extends/origin agreement on aggregates —
|
|
2245
2403
|
# an aggregate computes something new (spec §4 is passthrough-only).
|
|
@@ -2261,7 +2419,8 @@ def _validate_origin_paths(
|
|
|
2261
2419
|
obj, node.name, "origin.aggregate @agg:collect", src, errors)
|
|
2262
2420
|
via = origin.attr(ORIGIN_ATTR_VIA)
|
|
2263
2421
|
if isinstance(via, str) and via:
|
|
2264
|
-
|
|
2422
|
+
walked = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
|
|
2423
|
+
hops = walked.hops if walked is not None else None
|
|
2265
2424
|
if hops is not None:
|
|
2266
2425
|
_check_aggregate_cardinality(hops, node.name, src, errors)
|
|
2267
2426
|
continue
|
|
@@ -2372,7 +2531,8 @@ def _validate_origin_paths(
|
|
|
2372
2531
|
# @via — explicit (validated + cardinality) or single-hop-unique inferred.
|
|
2373
2532
|
via = origin.attr(ORIGIN_ATTR_VIA)
|
|
2374
2533
|
if isinstance(via, str) and via:
|
|
2375
|
-
|
|
2534
|
+
walked = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
|
|
2535
|
+
hops = walked.hops if walked is not None else None
|
|
2376
2536
|
if hops is not None:
|
|
2377
2537
|
_check_aggregate_cardinality(hops, node.name, src, errors)
|
|
2378
2538
|
elif of_target is not None:
|
|
@@ -3123,6 +3283,24 @@ def _validate_filterable_has_supported_ops(
|
|
|
3123
3283
|
for field in node.fields():
|
|
3124
3284
|
if field.attrs().get("filterable") is not True:
|
|
3125
3285
|
continue
|
|
3286
|
+
|
|
3287
|
+
# #335 Half B — an ARRAY field has no operator band either. Every
|
|
3288
|
+
# FR-009 operator (eq/ne/gt/gte/lt/lte/in/like/isNull) is a scalar
|
|
3289
|
+
# comparison; none applies to a collection column. Same reason as
|
|
3290
|
+
# the subtype check below, so same code.
|
|
3291
|
+
# ADR-0039: resolved_is_array(), never the own `is_array` flag.
|
|
3292
|
+
if field.resolved_is_array():
|
|
3293
|
+
errors.append(
|
|
3294
|
+
MetaError(
|
|
3295
|
+
f'Field "{node.name}.{field.name}" has @filterable: true but is an array '
|
|
3296
|
+
f"(isArray: true). No filter operator applies to a collection column. "
|
|
3297
|
+
f"Remove @filterable from this field.",
|
|
3298
|
+
ErrorCode.ERR_FILTERABLE_UNSUPPORTED_SUBTYPE,
|
|
3299
|
+
envelope=field.source,
|
|
3300
|
+
)
|
|
3301
|
+
)
|
|
3302
|
+
continue
|
|
3303
|
+
|
|
3126
3304
|
if ops_for_subtype(field.sub_type):
|
|
3127
3305
|
continue
|
|
3128
3306
|
errors.append(
|
|
@@ -3137,6 +3315,46 @@ def _validate_filterable_has_supported_ops(
|
|
|
3137
3315
|
)
|
|
3138
3316
|
|
|
3139
3317
|
|
|
3318
|
+
# ---------------------------------------------------------------------------
|
|
3319
|
+
# Pass: @sortable on an array field or unsupported subtype (#335 Half B)
|
|
3320
|
+
# ---------------------------------------------------------------------------
|
|
3321
|
+
# @sortable defaults FROM @filterable, so it is checked independently only
|
|
3322
|
+
# when explicit — nothing validated it before, while @filterable has had a
|
|
3323
|
+
# hard error since SP-H Unit9. A @sortable JSON or array column emits a sort
|
|
3324
|
+
# entry over a column no dialect can ORDER BY meaningfully.
|
|
3325
|
+
# → ERR_SORTABLE_UNSUPPORTED_SUBTYPE.
|
|
3326
|
+
|
|
3327
|
+
|
|
3328
|
+
def _validate_sortable_has_supported_subtype(
|
|
3329
|
+
root: MetaData,
|
|
3330
|
+
errors: list[MetaError],
|
|
3331
|
+
) -> None:
|
|
3332
|
+
for node in _walk(root):
|
|
3333
|
+
if node.type != TYPE_OBJECT or not isinstance(node, MetaObject):
|
|
3334
|
+
continue
|
|
3335
|
+
for field in node.fields():
|
|
3336
|
+
if field.attrs().get(FIELD_ATTR_SORTABLE) is not True:
|
|
3337
|
+
continue
|
|
3338
|
+
# ADR-0039: resolved_is_array(), never the own `is_array` flag.
|
|
3339
|
+
is_array = field.resolved_is_array()
|
|
3340
|
+
if not is_array and ops_for_subtype(field.sub_type):
|
|
3341
|
+
continue
|
|
3342
|
+
|
|
3343
|
+
reason = (
|
|
3344
|
+
"is an array (isArray: true) — a collection column has no ordering."
|
|
3345
|
+
if is_array
|
|
3346
|
+
else f'its subtype "{field.sub_type}" cannot be ordered.'
|
|
3347
|
+
)
|
|
3348
|
+
errors.append(
|
|
3349
|
+
MetaError(
|
|
3350
|
+
f'Field "{node.name}.{field.name}" has @sortable: true but {reason} '
|
|
3351
|
+
f"Remove @sortable from this field.",
|
|
3352
|
+
ErrorCode.ERR_SORTABLE_UNSUPPORTED_SUBTYPE,
|
|
3353
|
+
envelope=field.source,
|
|
3354
|
+
)
|
|
3355
|
+
)
|
|
3356
|
+
|
|
3357
|
+
|
|
3140
3358
|
# ---------------------------------------------------------------------------
|
|
3141
3359
|
# Pass: field.object @storage validation
|
|
3142
3360
|
# ---------------------------------------------------------------------------
|
|
@@ -3509,12 +3727,22 @@ def _parse_string_list(raw: object) -> tuple[str, ...]:
|
|
|
3509
3727
|
|
|
3510
3728
|
|
|
3511
3729
|
# ---------------------------------------------------------------------------
|
|
3512
|
-
# Pass: index.lookup
|
|
3730
|
+
# Pass: index-key resolution for index.lookup AND identity.secondary (#342)
|
|
3731
|
+
#
|
|
3732
|
+
# The key is @fields XOR @expr. @expr has always been registered as "used
|
|
3733
|
+
# INSTEAD of @fields" and migrate-ts has always keyed off it
|
|
3734
|
+
# (`columns: expr ? [] : cols`), but the loader required @fields
|
|
3735
|
+
# unconditionally — so an expression index could not be declared, and the one
|
|
3736
|
+
# spelling that DID load (@fields AND @expr) had its @fields silently discarded
|
|
3737
|
+
# downstream. Declaring both is now refused rather than half-honored, which is
|
|
3738
|
+
# the silent-wrong-output the sealed strict registry exists to prevent.
|
|
3739
|
+
#
|
|
3740
|
+
# identity.secondary is included because per ADR-0040 uniqueness lives in the
|
|
3741
|
+
# TYPE — identity.secondary IS a unique index and keys itself identically,
|
|
3742
|
+
# carrying @expr from the same db provider.
|
|
3513
3743
|
#
|
|
3514
|
-
#
|
|
3515
|
-
#
|
|
3516
|
-
# ADR-0039: use children() / MetaIndex.fields() — never own* — so that a
|
|
3517
|
-
# field inherited via extends still resolves correctly.
|
|
3744
|
+
# ADR-0039: children() / MetaIndex.fields() — never own* — so a field inherited
|
|
3745
|
+
# via extends still resolves correctly.
|
|
3518
3746
|
# ---------------------------------------------------------------------------
|
|
3519
3747
|
|
|
3520
3748
|
def _validate_index_lookup_fields(root: MetaData, errors: list[MetaError]) -> None:
|
|
@@ -3525,30 +3753,77 @@ def _validate_index_lookup_fields(root: MetaData, errors: list[MetaError]) -> No
|
|
|
3525
3753
|
f.name for f in obj.children() if f.type == TYPE_FIELD
|
|
3526
3754
|
}
|
|
3527
3755
|
for node in obj.children():
|
|
3528
|
-
|
|
3756
|
+
is_lookup = node.type == TYPE_INDEX and node.sub_type == INDEX_SUBTYPE_LOOKUP
|
|
3757
|
+
is_secondary = (
|
|
3758
|
+
node.type == TYPE_IDENTITY
|
|
3759
|
+
and node.sub_type == IDENTITY_SUBTYPE_SECONDARY
|
|
3760
|
+
)
|
|
3761
|
+
if not (is_lookup or is_secondary):
|
|
3529
3762
|
continue
|
|
3530
|
-
|
|
3763
|
+
label = f"{node.type}.{node.sub_type}"
|
|
3764
|
+
# PRESENCE vs CONTENT are two different questions, and conflating them is
|
|
3765
|
+
# a bug in both directions. The CONTRADICTION check needs PRESENCE — an
|
|
3766
|
+
# explicit `@fields: []` beside @expr is still a declaration of both, and
|
|
3767
|
+
# keying it on non-emptiness let that spelling load clean while
|
|
3768
|
+
# `@fields: ["x"]` + @expr was refused. The KEY-RESOLUTION check needs
|
|
3769
|
+
# normalized CONTENT, which folds absent/scalar/empty together — the
|
|
3770
|
+
# normalization is the fix for one and the obstacle for the other, so the
|
|
3771
|
+
# two questions are asked separately. attrs() is the RESOLVING accessor in
|
|
3772
|
+
# Python (attr() is own-only — ADR-0039).
|
|
3773
|
+
attrs = node.attrs()
|
|
3774
|
+
has_fields_attr = IDENTITY_ATTR_FIELDS in attrs
|
|
3775
|
+
if isinstance(node, MetaIndex):
|
|
3776
|
+
fields = node.fields()
|
|
3777
|
+
else:
|
|
3778
|
+
raw = attrs.get(IDENTITY_ATTR_FIELDS)
|
|
3779
|
+
fields = list(raw) if isinstance(raw, (list, tuple)) else []
|
|
3780
|
+
expr = attrs.get(IDENTITY_ATTR_EXPR)
|
|
3781
|
+
has_expr = isinstance(expr, str) and expr.strip() != ""
|
|
3782
|
+
|
|
3783
|
+
# Rule 1a: exactly one of @fields / @expr may be DECLARED.
|
|
3784
|
+
if has_fields_attr and has_expr:
|
|
3785
|
+
errors.append(MetaError(
|
|
3786
|
+
code=ErrorCode.ERR_INVALID_INDEX,
|
|
3787
|
+
message=(
|
|
3788
|
+
f'{label} "{node.name}" on "{obj.name}" declares BOTH '
|
|
3789
|
+
f"@{INDEX_ATTR_FIELDS} and @{IDENTITY_ATTR_EXPR}; they are the "
|
|
3790
|
+
f"two mutually exclusive ways to key an index. "
|
|
3791
|
+
f"@{IDENTITY_ATTR_EXPR} is used INSTEAD of @{INDEX_ATTR_FIELDS} "
|
|
3792
|
+
f"— drop one. (Declaring both previously loaded but silently "
|
|
3793
|
+
f"discarded @{INDEX_ATTR_FIELDS}.)"
|
|
3794
|
+
),
|
|
3795
|
+
envelope=node.source,
|
|
3796
|
+
))
|
|
3531
3797
|
continue
|
|
3532
|
-
fields = node.fields()
|
|
3533
3798
|
|
|
3534
|
-
|
|
3799
|
+
# Rule 1b: whichever is declared must actually supply a key.
|
|
3800
|
+
# envelope=node.source — FR5a/ADR-0009 provenance. TS, Java and C# have
|
|
3801
|
+
# always attached it here; Python did not, so these errors arrived with an
|
|
3802
|
+
# empty envelope. The cross-port corpus asserts envelope SHAPE, which is
|
|
3803
|
+
# what surfaced it.
|
|
3804
|
+
if len(fields) == 0 and not has_expr:
|
|
3535
3805
|
errors.append(MetaError(
|
|
3536
3806
|
code=ErrorCode.ERR_INVALID_INDEX,
|
|
3537
3807
|
message=(
|
|
3538
|
-
f'
|
|
3539
|
-
f"@{INDEX_ATTR_FIELDS}
|
|
3808
|
+
f'{label} "{node.name}" on "{obj.name}" declares no key: '
|
|
3809
|
+
f"it must have @{INDEX_ATTR_FIELDS} (one or more columns) "
|
|
3810
|
+
f"or @{IDENTITY_ATTR_EXPR} (a key expression)"
|
|
3540
3811
|
),
|
|
3812
|
+
envelope=node.source,
|
|
3541
3813
|
))
|
|
3542
3814
|
continue
|
|
3543
3815
|
|
|
3816
|
+
# Rule 2: every named field must resolve. An @expr index has no
|
|
3817
|
+
# @fields to resolve — @expr is raw SQL, deliberately not parsed here.
|
|
3544
3818
|
for field_name in fields:
|
|
3545
3819
|
if field_name not in effective_field_names:
|
|
3546
3820
|
errors.append(MetaError(
|
|
3547
3821
|
code=ErrorCode.ERR_INVALID_INDEX,
|
|
3548
3822
|
message=(
|
|
3549
|
-
f'
|
|
3823
|
+
f'{label} "{node.name}" on "{obj.name}" references '
|
|
3550
3824
|
f'field "{field_name}" which does not exist on "{obj.name}". '
|
|
3551
3825
|
f"Available fields: "
|
|
3552
3826
|
f"{', '.join(sorted(effective_field_names)) or '(none)'}"
|
|
3553
3827
|
),
|
|
3828
|
+
envelope=node.source,
|
|
3554
3829
|
))
|
|
@@ -255,7 +255,7 @@ def _sorted_children(definition: TypeDefinition) -> list[dict[str, object]]:
|
|
|
255
255
|
return children
|
|
256
256
|
|
|
257
257
|
|
|
258
|
-
METAMODEL_VERSION = "0.
|
|
258
|
+
METAMODEL_VERSION = "0.12"
|
|
259
259
|
"""Rolled-up spec-version for the cross-port registry manifest.
|
|
260
260
|
|
|
261
261
|
``"0"`` = pre-1.0 / unstable (semver major-0). Flips to ``"1.0"`` at the
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"description": "A secondary unique index on one or more fields. Always enforces uniqueness — use index.lookup for non-unique query-performance indexes.",
|
|
20
20
|
"whenToUse": "A column or set of columns must be unique (e.g. email, slug, composite business key). For a plain non-unique index, use index.lookup instead.",
|
|
21
21
|
"children": [
|
|
22
|
-
{ "type": "attr", "subType": "string", "name": "fields", "isArray": true, "min":
|
|
22
|
+
{ "type": "attr", "subType": "string", "name": "fields", "isArray": true, "min": 0, "max": 1, "description": "The field name(s) composing this identity. Single-element for a simple unique index, multiple for a composite unique constraint. Required UNLESS @expr is present — a unique index keys off plain columns (@fields) or a key expression (@expr), never both; declaring both is ERR_INVALID_INDEX." }
|
|
23
23
|
]
|
|
24
24
|
},
|
|
25
25
|
{
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"description": "A non-unique lookup index on one or more fields. Use for query-performance indexes that do NOT enforce uniqueness — declare identity.secondary for unique constraints instead.",
|
|
8
8
|
"whenToUse": "You need a DB index for query performance (fast lookups/sorts) but NOT uniqueness enforcement. @fields names the indexed columns; the db provider adds @orders / @expr / @where / @using for physical tuning.",
|
|
9
9
|
"children": [
|
|
10
|
-
{ "type": "attr", "subType": "string", "name": "fields", "isArray": true, "min":
|
|
10
|
+
{ "type": "attr", "subType": "string", "name": "fields", "isArray": true, "min": 0, "max": 1, "description": "The field name(s) this index covers. Required UNLESS @expr is present — an index keys off plain columns (@fields) or a key expression (@expr), never both; declaring both is ERR_INVALID_INDEX." }
|
|
11
11
|
]
|
|
12
12
|
}
|
|
13
13
|
]
|
|
@@ -20,15 +20,15 @@
|
|
|
20
20
|
{
|
|
21
21
|
"type": "origin",
|
|
22
22
|
"subType": "aggregate",
|
|
23
|
-
"description": "A value reduced from the related row-set reached along a relationship path (@via) from the base entity: count/sum/avg/min/max over a column (@of); any/all predicate quantifiers over a @filter; or collect (an array rollup of @of).",
|
|
23
|
+
"description": "A value reduced from the related row-set reached along a relationship path (@via) from the base entity: count/sum/avg/min/max over a column (@of); any/all predicate quantifiers over a @filter; or collect (an array rollup — of the @of column, or of the carrying field.object's declared @objectRef value object when @of is omitted).",
|
|
24
24
|
"whenToUse": "A projection needs a value derived by reducing related rows — a count/sum/avg/min/max, a 'did any/every related row match' flag, or an array of collected values. Declare it instead of hand-writing the aggregate query — it stays consistent and regenerates.",
|
|
25
|
-
"rules": "@via may be omitted only when exactly one single-hop relationship leads from the base entity to the @of entity (single-hop-unique inference; FR-024, ADR-0029). Multi-hop paths must always be stated explicitly. @of is required for count/sum/avg/min/max
|
|
25
|
+
"rules": "@via may be omitted only when exactly one single-hop relationship leads from the base entity to the @of entity (single-hop-unique inference; FR-024, ADR-0029). Multi-hop paths must always be stated explicitly. @of is required for count/sum/avg/min/max and forbidden for any/all (which quantify over rows via @filter, not a column). @filter is required for any/all. The field must be isArray:true for collect and isArray:false for every other @agg. @distinct and @orderBy are collect-only. On @agg:collect @of is OPTIONAL: omitting it declares a WHOLE-OBJECT rollup, which collects each related row as the carrying field's declared @objectRef value object instead of one scalar column. A whole-object rollup requires a field.object carrying @objectRef, requires that @objectRef to name an object.value, requires an explicit @via (there is no @of entity to infer the path from), and refuses @distinct. Its @orderBy keys resolve against the @via TERMINAL entity, not the head or a middle hop. Its value-object members bind to the terminal entity's fields BY NAME — member name == terminal field name, deliberately NOT extends, so one value object stays collectable from two different entities — and every member must match a terminal field agreeing on BOTH field.<subType> and array-ness.",
|
|
26
26
|
"children": [
|
|
27
|
-
{ "type": "attr", "subType": "string", "name": "agg", "min": 1, "max": 1, "allowedValues": ["count", "sum", "avg", "min", "max", "any", "all", "collect"], "description": "The reducing function applied over the related row-set: count/sum/avg/min/max (numeric/ordinal reduces over @of); any/all (predicate quantifiers over @filter — @of forbidden; empty set → any=false, all=true); collect (array rollup of @of
|
|
28
|
-
{ "type": "attr", "subType": "string", "name": "of", "min": 0, "max": 1, "description": "Dotted Entity.field reference identifying the column being aggregated (e.g. 'Week.durationMinutes'). Required for count/sum/avg/min/max
|
|
27
|
+
{ "type": "attr", "subType": "string", "name": "agg", "min": 1, "max": 1, "allowedValues": ["count", "sum", "avg", "min", "max", "any", "all", "collect"], "description": "The reducing function applied over the related row-set: count/sum/avg/min/max (numeric/ordinal reduces over @of); any/all (predicate quantifiers over @filter — @of forbidden; empty set → any=false, all=true); collect (array rollup — of the @of column, or of the carrying field.object's declared @objectRef value object when @of is omitted; the field must be isArray)." },
|
|
28
|
+
{ "type": "attr", "subType": "string", "name": "of", "min": 0, "max": 1, "description": "Dotted Entity.field reference identifying the column being aggregated (e.g. 'Week.durationMinutes'). Required for count/sum/avg/min/max; OPTIONAL for collect, where absent means a whole-object rollup of the field's declared @objectRef value object; forbidden for any/all (which quantify over rows via @filter, not a column)." },
|
|
29
29
|
{ "type": "attr", "subType": "string", "name": "via", "min": 0, "max": 1, "description": "Dotted relationship path from the base entity to the aggregated rows (e.g. 'Program.weeks' or 'Program.weeks.workouts'). May be omitted only when exactly one single-hop relationship leads from the base entity to the @of entity (FR-024, ADR-0029)." },
|
|
30
30
|
{ "type": "attr", "subType": "filter", "name": "filter", "min": 0, "max": 1, "description": "Optional structured predicate scoping which related rows the aggregate spans (required for any/all, where it is the quantified predicate). A portable attr.filter object (eq/ne/in/isNull with and/or), desugared to canonical { field: { op: value } } at parse time; codegen renders it per target (e.g. SQL FILTER (WHERE ...) or SQLite CASE WHEN for a relational view)." },
|
|
31
|
-
{ "type": "attr", "subType": "boolean", "name": "distinct", "min": 0, "max": 1, "description": "Set (collect-only) to dedupe collected values (set semantics)." },
|
|
31
|
+
{ "type": "attr", "subType": "boolean", "name": "distinct", "min": 0, "max": 1, "description": "Set (collect-only) to dedupe collected values (set semantics). Not supported on a whole-object collect (@of omitted): it is a guaranteed no-op whenever the value object carries the primary key, and a silent no-op is worse than a refusal." },
|
|
32
32
|
{ "type": "attr", "subType": "string", "name": "orderBy", "isArray": true, "min": 0, "max": 1, "description": "Ordering keys as 'field[:asc|desc]' (default asc) over the related entity's fields; nulls sort last. On @agg:collect sets element order (non-distinct only); on origin.first (required) selects the row. Semantic — carries no SQL syntax." }
|
|
33
33
|
]
|
|
34
34
|
},
|
|
File without changes
|
|
File without changes
|