metaobjects 1.0.12__tar.gz → 1.0.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (485) hide show
  1. {metaobjects-1.0.12 → metaobjects-1.0.13}/PKG-INFO +1 -1
  2. {metaobjects-1.0.12 → metaobjects-1.0.13}/pyproject.toml +1 -1
  3. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/apidocs/naming.py +56 -7
  4. metaobjects-1.0.13/src/metaobjects/codegen/collection_name_collision.py +58 -0
  5. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/runner.py +5 -0
  6. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/errors.py +6 -0
  7. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/persistence/source/meta_source.py +13 -2
  8. metaobjects-1.0.13/tests/codegen/test_collection_name_collision.py +109 -0
  9. metaobjects-1.0.13/tests/codegen/test_route_path_naming.py +149 -0
  10. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_runner.py +21 -0
  11. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_fr016_source_name_and_kind_aliases.py +33 -0
  12. {metaobjects-1.0.12 → metaobjects-1.0.13}/uv.lock +1 -1
  13. metaobjects-1.0.12/tests/codegen/test_route_path_naming.py +0 -67
  14. {metaobjects-1.0.12 → metaobjects-1.0.13}/.gitignore +0 -0
  15. {metaobjects-1.0.12 → metaobjects-1.0.13}/LICENSE +0 -0
  16. {metaobjects-1.0.12 → metaobjects-1.0.13}/README.md +0 -0
  17. {metaobjects-1.0.12 → metaobjects-1.0.13}/hatch_build.py +0 -0
  18. {metaobjects-1.0.12 → metaobjects-1.0.13}/scripts/generate_embedded_library.py +0 -0
  19. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/__init__.py +0 -0
  20. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/agent_context/__init__.py +0 -0
  21. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/agent_context/scaffold.py +0 -0
  22. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/apidocs/__init__.py +0 -0
  23. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/apidocs/api_model.py +0 -0
  24. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/apidocs/builder.py +0 -0
  25. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/apidocs/paths.py +0 -0
  26. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/apidocs/renderer.py +0 -0
  27. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/attr_class_map.py +0 -0
  28. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/cli.py +0 -0
  29. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
  30. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/__init__.py +0 -0
  31. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/collision_names.py +0 -0
  32. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/config.py +0 -0
  33. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/constants.py +0 -0
  34. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/eject.py +0 -0
  35. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  36. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  37. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/format.py +0 -0
  38. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  39. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generator.py +0 -0
  40. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generator_registry.py +0 -0
  41. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/__init__.py +0 -0
  42. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/entity_model.py +0 -0
  43. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  44. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
  45. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/find_inbound.py +0 -0
  46. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  47. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
  48. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/names_generator.py +0 -0
  49. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  50. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  51. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  52. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/router_generator.py +0 -0
  53. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  54. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  55. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  56. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  57. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/metaobjects-config.schema.json +0 -0
  58. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/model_walk.py +0 -0
  59. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  60. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  61. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/project_config.py +0 -0
  62. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  63. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/runtime/constraint_errors.py +0 -0
  64. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  65. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  66. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  67. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  68. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  69. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/type_map.py +0 -0
  70. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/codegen/value_objects.py +0 -0
  71. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/config/__init__.py +0 -0
  72. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/config/dependencies.py +0 -0
  73. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/config/neutral_config.py +0 -0
  74. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/config/source_resolver.py +0 -0
  75. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/core_types.py +0 -0
  76. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/datatype.py +0 -0
  77. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/documentation/__init__.py +0 -0
  78. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/documentation/doc_constants.py +0 -0
  79. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/documentation/doc_provider.py +0 -0
  80. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/documentation/doc_schema.py +0 -0
  81. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/fmt.py +0 -0
  82. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/library/__init__.py +0 -0
  83. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/library/embedded_library.py +0 -0
  84. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/library/library_sources.py +0 -0
  85. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/__init__.py +0 -0
  86. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/merge.py +0 -0
  87. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/meta_data_loader.py +0 -0
  88. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/registered_validation.py +0 -0
  89. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/sources/__init__.py +0 -0
  90. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/sources/directory_source.py +0 -0
  91. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/sources/file_source.py +0 -0
  92. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  93. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/sources/uri_source.py +0 -0
  94. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/validate_discriminator.py +0 -0
  95. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/validate_enum_normalize_ambiguity.py +0 -0
  96. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/validate_field_mutability.py +0 -0
  97. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/validate_source_escapes.py +0 -0
  98. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  99. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  100. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/loader/validation_passes.py +0 -0
  101. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/__init__.py +0 -0
  102. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/__init__.py +0 -0
  103. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  104. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  105. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  106. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/field/__init__.py +0 -0
  107. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  108. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  109. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  110. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  111. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  112. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/index/__init__.py +0 -0
  113. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  114. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  115. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/object/__init__.py +0 -0
  116. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  117. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  118. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  119. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  120. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  121. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/object/value_object.py +0 -0
  122. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  123. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  124. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  125. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  126. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/relationship/relationship_references.py +0 -0
  127. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/requirement/__init__.py +0 -0
  128. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/requirement/meta_requirement.py +0 -0
  129. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/requirement/requirement_constants.py +0 -0
  130. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  131. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  132. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/meta_data.py +0 -0
  133. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/meta_root.py +0 -0
  134. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/persistence/__init__.py +0 -0
  135. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  136. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  137. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  138. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  139. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  140. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  141. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  142. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  143. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/presentation/__init__.py +0 -0
  144. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  145. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  146. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  147. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  148. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  149. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  150. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  151. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  152. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/provider_extends.py +0 -0
  153. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/template/__init__.py +0 -0
  154. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/template/meta_template.py +0 -0
  155. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  156. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta/template/template_constants.py +0 -0
  157. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/meta_endpoint.py +0 -0
  158. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/naming.py +0 -0
  159. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/naming_refs.py +0 -0
  160. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/parser.py +0 -0
  161. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/parser_yaml.py +0 -0
  162. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/provider.py +0 -0
  163. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/py.typed +0 -0
  164. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/registry.py +0 -0
  165. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/registry_manifest.py +0 -0
  166. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/__init__.py +0 -0
  167. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/email_document.py +0 -0
  168. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/escapers.py +0 -0
  169. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  170. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/__init__.py +0 -0
  171. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/coerce.py +0 -0
  172. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/extract.py +0 -0
  173. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/extract_map.py +0 -0
  174. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  175. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/locate.py +0 -0
  176. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/normalize.py +0 -0
  177. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/strip.py +0 -0
  178. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/types.py +0 -0
  179. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  180. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/filesystem_provider.py +0 -0
  181. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/payload_accessors.py +0 -0
  182. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/prompt/__init__.py +0 -0
  183. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  184. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  185. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  186. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  187. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  188. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/renderer.py +0 -0
  189. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/render/verify.py +0 -0
  190. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/runtime/__init__.py +0 -0
  191. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/runtime/llm_recorder.py +0 -0
  192. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  193. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/runtime/object_manager.py +0 -0
  194. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/runtime/tph.py +0 -0
  195. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/scope.py +0 -0
  196. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/serializer_json.py +0 -0
  197. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/shared/__init__.py +0 -0
  198. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/shared/base_types.py +0 -0
  199. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/shared/separators.py +0 -0
  200. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/shared/structural.py +0 -0
  201. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/source/__init__.py +0 -0
  202. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/source/error_source.py +0 -0
  203. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/source/json_path.py +0 -0
  204. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/source/semantic_diff.py +0 -0
  205. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/source/yaml_positions.py +0 -0
  206. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/source_resolution.py +0 -0
  207. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  208. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/attr.json +0 -0
  209. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/db.json +0 -0
  210. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  211. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/field.json +0 -0
  212. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/identity.json +0 -0
  213. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/index.json +0 -0
  214. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/layout.json +0 -0
  215. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/object.json +0 -0
  216. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/origin.json +0 -0
  217. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  218. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  219. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/requirement.json +0 -0
  220. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/source.json +0 -0
  221. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/template.json +0 -0
  222. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
  223. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/ui.json +0 -0
  224. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/validator.json +0 -0
  225. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/spec_metamodel/view.json +0 -0
  226. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/super_resolve.py +0 -0
  227. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/validation_types.py +0 -0
  228. {metaobjects-1.0.12 → metaobjects-1.0.13}/src/metaobjects/yaml_desugar.py +0 -0
  229. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/__init__.py +0 -0
  230. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/__init__.py +0 -0
  231. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/gen_suite.py +0 -0
  232. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  233. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/extends/expected/Program.py +0 -0
  234. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/extends/meta.json +0 -0
  235. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  236. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  237. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/nested-array/meta.json +0 -0
  238. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  239. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  240. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/scalars/meta.json +0 -0
  241. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  242. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/golden/vanilla/meta.json +0 -0
  243. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_abstract_conformance.py +0 -0
  244. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_api_docs_builder.py +0 -0
  245. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_api_docs_paths.py +0 -0
  246. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli.py +0 -0
  247. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_config_gen.py +0 -0
  248. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_config_verify.py +0 -0
  249. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_dependencies.py +0 -0
  250. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_fmt.py +0 -0
  251. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_gen_unknown_attr.py +0 -0
  252. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_providers.py +0 -0
  253. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_registry.py +0 -0
  254. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_render_helper_template_root.py +0 -0
  255. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  256. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_verify_strict.py +0 -0
  257. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  258. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_codegen_compile_conformance.py +0 -0
  259. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_constants_config.py +0 -0
  260. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_eject.py +0 -0
  261. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_eject_runtime.py +0 -0
  262. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_entity_model.py +0 -0
  263. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_enum_conformance.py +0 -0
  264. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
  265. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_extract_tier_collision.py +0 -0
  266. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_extractor_generator.py +0 -0
  267. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  268. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_fmt_format_file.py +0 -0
  269. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_format.py +0 -0
  270. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  271. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_fr010_output_codegen.py +0 -0
  272. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  273. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_gen_state_keying.py +0 -0
  274. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_generator.py +0 -0
  275. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_generator_extension_seams.py +0 -0
  276. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_golden.py +0 -0
  277. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  278. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_inheritance_conformance.py +0 -0
  279. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_instance_artifacts.py +0 -0
  280. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_m2m_codegen.py +0 -0
  281. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_names_extends_chain.py +0 -0
  282. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_names_generator.py +0 -0
  283. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_no_magic_physical_names.py +0 -0
  284. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_origin_nullability.py +0 -0
  285. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_output_parser_generator.py +0 -0
  286. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_overwrite_policy.py +0 -0
  287. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_overwrite_policy_hash.py +0 -0
  288. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_project_config.py +0 -0
  289. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_projection_compile.py +0 -0
  290. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_recipe_generators.py +0 -0
  291. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_render_helper_conformance.py +0 -0
  292. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_render_helper_generator.py +0 -0
  293. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_reverse_finders.py +0 -0
  294. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_router_autoset.py +0 -0
  295. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_router_generator.py +0 -0
  296. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_router_generator_m2m_target_tph.py +0 -0
  297. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_router_generator_tph_m2m.py +0 -0
  298. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_router_generator_tph_m2m_shadowed.py +0 -0
  299. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_source_resolution.py +0 -0
  300. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_sourceless_entity_surface.py +0 -0
  301. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_sourceless_projection.py +0 -0
  302. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_template_data.py +0 -0
  303. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_template_generator.py +0 -0
  304. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_template_output_pattern.py +0 -0
  305. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_template_scope_helpers.py +0 -0
  306. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_template_scope_walk.py +0 -0
  307. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_template_spec.py +0 -0
  308. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_tph_codegen.py +0 -0
  309. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_trace_helper_generator.py +0 -0
  310. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_type_map.py +0 -0
  311. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_validation_conformance.py +0 -0
  312. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_value_objects.py +0 -0
  313. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/codegen/test_write_through.py +0 -0
  314. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/config/__init__.py +0 -0
  315. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/config/test_dependencies.py +0 -0
  316. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/config/test_library_package_guard.py +0 -0
  317. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/config/test_neutral_config.py +0 -0
  318. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/config/test_source_resolver.py +0 -0
  319. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/__init__.py +0 -0
  320. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/capabilities.py +0 -0
  321. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/conformance-expected-failures.json +0 -0
  322. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/conformance_adapter.py +0 -0
  323. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/corpus.py +0 -0
  324. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/expected_failures.py +0 -0
  325. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/fixture_discovery.py +0 -0
  326. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/navigator.py +0 -0
  327. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  328. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_conformance.py +0 -0
  329. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_dependency_conformance.py +0 -0
  330. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_extract_conformance.py +0 -0
  331. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_extract_object_array_bounds.py +0 -0
  332. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_extract_object_verdict.py +0 -0
  333. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_fmt_conformance.py +0 -0
  334. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  335. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_fr011_attrs.py +0 -0
  336. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_generator_registry_conformance.py +0 -0
  337. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_object_model_conformance.py +0 -0
  338. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_provider_composition_conformance.py +0 -0
  339. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_registry_conformance.py +0 -0
  340. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_runner_hardfail.py +0 -0
  341. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_scope_conformance.py +0 -0
  342. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_source_resolution_conformance.py +0 -0
  343. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  344. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_strict_attr_load.py +0 -0
  345. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_template_codegen_conformance.py +0 -0
  346. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_template_generator_conformance.py +0 -0
  347. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/test_yaml_conformance.py +0 -0
  348. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  349. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/__init__.py +0 -0
  350. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/api_contract_assertions.py +0 -0
  351. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/api_contract_jsonb_server.py +0 -0
  352. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/api_contract_m2m_server.py +0 -0
  353. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/api_contract_server.py +0 -0
  354. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/generated_jsonb_app.py +0 -0
  355. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/generated_m2m_app.py +0 -0
  356. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/generated_projection_app.py +0 -0
  357. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/generated_router_app.py +0 -0
  358. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/generated_tph_app.py +0 -0
  359. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/meta_ai_trace.yaml +0 -0
  360. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/normalization.py +0 -0
  361. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/postgres_container.py +0 -0
  362. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/query_runner.py +0 -0
  363. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/scenarios.py +0 -0
  364. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_api_contract.py +0 -0
  365. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_api_contract_generated.py +0 -0
  366. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_api_contract_jsonb.py +0 -0
  367. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_api_contract_m2m.py +0 -0
  368. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  369. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_api_contract_projection.py +0 -0
  370. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_api_contract_tph_generated.py +0 -0
  371. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_generated_router_uuid_pk.py +0 -0
  372. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_llm_call_trace.py +0 -0
  373. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_m2m_target_tph_generated.py +0 -0
  374. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_normalization.py +0 -0
  375. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_query_scenarios.py +0 -0
  376. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_runtime_return_types.py +0 -0
  377. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_tph_m2m_generated.py +0 -0
  378. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/integration/test_tph_m2m_shadowed_generated.py +0 -0
  379. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/loader/__init__.py +0 -0
  380. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/loader/test_abstract_subtype_authored.py +0 -0
  381. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/loader/test_child_wrapper_body_shape.py +0 -0
  382. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/loader/test_enum_extends_values_conflict.py +0 -0
  383. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/loader/test_field_mutability_order.py +0 -0
  384. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/loader/test_shipped_library_ai.py +0 -0
  385. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/loader/test_template_payload_target_210.py +0 -0
  386. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/open_closed_proof_test.py +0 -0
  387. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/__init__.py +0 -0
  388. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/extract/__init__.py +0 -0
  389. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/extract/test_coerce.py +0 -0
  390. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/extract/test_extract.py +0 -0
  391. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/extract/test_extract_map.py +0 -0
  392. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  393. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/extract/test_locate.py +0 -0
  394. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/extract/test_model.py +0 -0
  395. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/extract/test_normalize.py +0 -0
  396. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/extract/test_strip.py +0 -0
  397. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  398. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/prompt/__init__.py +0 -0
  399. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/prompt/test_output_format_renderer.py +0 -0
  400. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/test_email_document.py +0 -0
  401. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/test_filesystem_provider.py +0 -0
  402. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/test_output_format_renderer_nested.py +0 -0
  403. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/test_output_prompt_conformance.py +0 -0
  404. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/test_render_conformance.py +0 -0
  405. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/test_render_max_chars.py +0 -0
  406. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/test_render_model_payload.py +0 -0
  407. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/test_verify.py +0 -0
  408. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/render/test_verify_conformance.py +0 -0
  409. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/runtime/test_constraint_errors.py +0 -0
  410. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/runtime/test_object_manager_currency_read.py +0 -0
  411. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/runtime/test_object_manager_enum_intvaluemap.py +0 -0
  412. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/runtime/test_object_manager_table_name.py +0 -0
  413. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/source/__init__.py +0 -0
  414. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/source/test_fr5c_merge_attribution.py +0 -0
  415. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/source/test_fr5d_reference_resolution.py +0 -0
  416. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/source/test_fr5e_database_source_shape.py +0 -0
  417. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/source/test_json_path.py +0 -0
  418. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/source/test_semantic_diff.py +0 -0
  419. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/source/test_source_on_node.py +0 -0
  420. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/source/test_yaml_positions.py +0 -0
  421. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/test_api_docs_accuracy.py +0 -0
  422. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/test_meta_endpoint.py +0 -0
  423. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/__init__.py +0 -0
  424. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_agent_context_staleness.py +0 -0
  425. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_capabilities.py +0 -0
  426. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_column_naming_strategy.py +0 -0
  427. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_common_attrs.py +0 -0
  428. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_core_types.py +0 -0
  429. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_derive_m2m_declaring_entity.py +0 -0
  430. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_effective_children_sibling_shadowing.py +0 -0
  431. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_effective_package.py +0 -0
  432. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_errors.py +0 -0
  433. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_field_enum.py +0 -0
  434. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_field_enum_intvaluemap.py +0 -0
  435. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_field_map_validation.py +0 -0
  436. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  437. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_file_source_id.py +0 -0
  438. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_index_lookup.py +0 -0
  439. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_llm_recorder.py +0 -0
  440. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_loader.py +0 -0
  441. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_loader_bom.py +0 -0
  442. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_loader_class.py +0 -0
  443. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_merge.py +0 -0
  444. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_meta_attr.py +0 -0
  445. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_meta_data.py +0 -0
  446. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_meta_source.py +0 -0
  447. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_module_shortcuts.py +0 -0
  448. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_n2m_resolver.py +0 -0
  449. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  450. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_no_dead_config_fields.py +0 -0
  451. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
  452. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  453. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_object_manager_write_through.py +0 -0
  454. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_one_primary_source.py +0 -0
  455. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_parser.py +0 -0
  456. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_parser_yaml.py +0 -0
  457. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_provider.py +0 -0
  458. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_provider_extension.py +0 -0
  459. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_registry.py +0 -0
  460. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_registry_completeness.py +0 -0
  461. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_registry_extend.py +0 -0
  462. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_registry_sealed.py +0 -0
  463. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_relationship_m2m_validation.py +0 -0
  464. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_relationship_references.py +0 -0
  465. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_relationship_referential_actions.py +0 -0
  466. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_resolution_key.py +0 -0
  467. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  468. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_serializer.py +0 -0
  469. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_serializer_shared_document.py +0 -0
  470. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_shared_constants.py +0 -0
  471. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_smoke.py +0 -0
  472. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_source_escapes.py +0 -0
  473. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_sources.py +0 -0
  474. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_strict_child_placement.py +0 -0
  475. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_super_resolve.py +0 -0
  476. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_template_toolcall.py +0 -0
  477. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  478. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_validation_attr_schema.py +0 -0
  479. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_validation_filter_values.py +0 -0
  480. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_validation_origin_195.py +0 -0
  481. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_validation_origin_paths.py +0 -0
  482. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_validation_sort_field.py +0 -0
  483. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_validation_warnings.py +0 -0
  484. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_write_coercion_arrays.py +0 -0
  485. {metaobjects-1.0.12 → metaobjects-1.0.13}/tests/unit/test_yaml_desugar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: metaobjects
3
- Version: 1.0.12
3
+ Version: 1.0.13
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 = "1.0.12"
3
+ version = "1.0.13"
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"
@@ -74,6 +74,52 @@ def _route_snake_case(name: str) -> str:
74
74
  return to_snake_case(name)
75
75
 
76
76
 
77
+ _ALREADY_PLURAL_EXCLUDED_PRECEDING_CHARS = "suia"
78
+ """The four letters that precede a genuinely SINGULAR "...s" ending in the common
79
+ patterns this codebase's entity names hit — status, address, bonus, alias, gas,
80
+ analysis. Anything else before a final "s" reads as already-plural."""
81
+
82
+
83
+ def _is_already_plural(word: str) -> bool:
84
+ """True when ``word`` already reads as a plural noun, so running it through
85
+ the ordinary suffix rule would double it — the real defect this exists to
86
+ fix: ``program_purchase_stats`` -> ``program_purchase_statses`` shipped as a
87
+ REST collection path in an adopter's app.
88
+
89
+ Heuristic, not a dictionary: a word ending in ``s`` is already-plural UNLESS
90
+ the character immediately before that final ``s`` is one of s/u/i/a
91
+ (case-insensitive). Covers stats, settings, details, news, analytics,
92
+ series, photos; leaves status, address, bonus, alias, gas, analysis on the
93
+ ordinary suffix path (unchanged).
94
+
95
+ Known miss, deliberately not fixed here: a genuinely singular word ending in
96
+ "...s" with none of those four letters before it reads as already-plural too
97
+ — ``lens`` -> ``lens`` (correct plural ``lenses``). Fixing that needs a real
98
+ dictionary, which this is not; the heuristic optimizes for the shape the
99
+ doubling defect actually hits (entity names that are already a plural
100
+ concept)."""
101
+ if len(word) < 2:
102
+ return False
103
+ lowered = word.lower()
104
+ if lowered[-1] != "s":
105
+ return False
106
+ return lowered[-2] not in _ALREADY_PLURAL_EXCLUDED_PRECEDING_CHARS
107
+
108
+
109
+ def _pluralize_legacy_suffix_only(name: str) -> str:
110
+ """FROZEN — byte-for-byte the pre-fix suffix-only pluralization rule, with no
111
+ already-plural detection. Exposed only so :func:`pluralize` can fall back to
112
+ it. The DEFAULT PHYSICAL name fallback lives in a completely separate,
113
+ independently-frozen implementation (``meta_source._pluralize``) — this is
114
+ NOT that one, and the two must never be merged into a single call site."""
115
+ lowered = name.lower()
116
+ if lowered.endswith(("s", "x", "z", "ch", "sh")):
117
+ return name + "es"
118
+ if len(name) >= 2 and lowered[-1] == "y" and lowered[-2] not in "aeiou":
119
+ return name[:-1] + "ies"
120
+ return name + "s"
121
+
122
+
77
123
  def pluralize(name: str) -> str:
78
124
  """``post_category`` → ``post_categories``; ``address`` → ``addresses``.
79
125
 
@@ -82,13 +128,16 @@ def pluralize(name: str) -> str:
82
128
  ``y`` becomes ``ies``; anything else takes ``s``. The cross-port
83
129
  byte-identity covers the already-lowercased word :func:`route_path` feeds
84
130
  in — suffix tests here run on a lowercased copy, unlike the JVM port's
85
- case-sensitive ones, so mixed-case input may diverge."""
86
- lowered = name.lower()
87
- if lowered.endswith(("s", "x", "z", "ch", "sh")):
88
- return name + "es"
89
- if len(name) >= 2 and lowered[-1] == "y" and lowered[-2] not in "aeiou":
90
- return name[:-1] + "ies"
91
- return name + "s"
131
+ case-sensitive ones, so mixed-case input may diverge.
132
+
133
+ Adds already-plural detection (:func:`_is_already_plural`) on top of that
134
+ historical suffix rule (:func:`_pluralize_legacy_suffix_only`) — API/code-
135
+ surface pluralization only; the DEFAULT PHYSICAL table name fallback is
136
+ frozen separately (``meta_source._pluralize``), so an existing adopter
137
+ database never sees a proposed rename."""
138
+ if _is_already_plural(name):
139
+ return name
140
+ return _pluralize_legacy_suffix_only(name)
92
141
 
93
142
 
94
143
  def model_class_name(obj: MetaData) -> str:
@@ -0,0 +1,58 @@
1
+ """Collection-name collision refusal — the single choke point every route/router/
2
+ finder generator path goes through (called once, in ``run_gen``, over the full
3
+ entity set BEFORE any generator runs).
4
+
5
+ Two DISTINCT entities/projections pluralizing to the SAME API-surface collection
6
+ name (REST path, generated router module/finder names) is reachable from stock
7
+ metadata now that :func:`metaobjects.apidocs.naming.pluralize` no longer doubles an
8
+ already-plural word: ``Address`` (not already-plural: legacy-pluralizes to
9
+ ``Addresses``) and ``Addresses`` (already-plural: left unchanged, ALSO
10
+ ``Addresses``) collide, where before the fix they did not (``Addresses`` legacy-
11
+ pluralized to ``Addresseses``). Silently letting two entities share one collection
12
+ name would mean whichever generator runs second silently wins, or a straight
13
+ duplicate-module/route registration two files downstream of the real cause. This
14
+ refuses at the one place that can see the WHOLE set, with a message naming both
15
+ entities and the colliding name.
16
+ """
17
+ from __future__ import annotations
18
+
19
+ from metaobjects.apidocs.naming import pluralize
20
+ from metaobjects.errors import ErrorCode
21
+ from metaobjects.meta.core.object.meta_object import MetaObject
22
+ from metaobjects.meta.core.object.object_constants import OBJECT_SUBTYPE_VALUE
23
+
24
+ ERR_COLLECTION_NAME_COLLISION = ErrorCode.ERR_COLLECTION_NAME_COLLISION.value
25
+
26
+
27
+ def assert_no_collection_name_collisions(entities: list[MetaObject]) -> None:
28
+ """Refuse a generation run in which two DISTINCT entities/projections pluralize
29
+ to the same API-surface collection name.
30
+
31
+ Scope: every object EXCEPT ``object.value`` (a value object never gets a route,
32
+ router module, or finder name — it is pure shape, so it cannot collide on any of
33
+ those). A TPH subtype IS included: it gets its own generated names against the
34
+ shared base table, so two subtypes (or a subtype and an unrelated top-level
35
+ entity) sharing a pluralized name collide on those generated symbols exactly as
36
+ two top-level entities would.
37
+
38
+ A PURE function of the entity set — never of traversal order: the reported pair
39
+ is deterministic (first-seen-by-input-order "owner" of a plural; raised as soon
40
+ as a second, different entity claims the same plural).
41
+ """
42
+ owner_of_plural: dict[str, MetaObject] = {}
43
+ for obj in entities:
44
+ if obj.sub_type == OBJECT_SUBTYPE_VALUE:
45
+ continue
46
+ plural = pluralize(obj.name)
47
+ existing = owner_of_plural.get(plural)
48
+ if existing is not None:
49
+ if existing.resolution_key() != obj.resolution_key():
50
+ raise ValueError(
51
+ f"{ERR_COLLECTION_NAME_COLLISION}: \"{existing.name}\" and \"{obj.name}\" both "
52
+ f'pluralize to the API-surface collection name "{plural}" (REST path, generated '
53
+ "router module/finder names) — rename one entity so its pluralized name is "
54
+ "distinct. Default PHYSICAL table names are unaffected by this rule and are not "
55
+ "involved in the collision."
56
+ )
57
+ else:
58
+ owner_of_plural[plural] = obj
@@ -10,6 +10,7 @@ from metaobjects.meta.meta_root import MetaRoot
10
10
  from metaobjects.meta.meta_data import MetaData
11
11
  from metaobjects.meta.core.object.meta_object import MetaObject
12
12
  from metaobjects.shared.base_types import TYPE_OBJECT
13
+ from .collection_name_collision import assert_no_collection_name_collisions
13
14
  from .config import GenConfig
14
15
  from .constants import generated_package_init
15
16
  from .generator import GenContext, Generator
@@ -107,6 +108,10 @@ def run_gen(
107
108
  if not safe:
108
109
  return result
109
110
 
111
+ # Refuse before any generator runs — the one choke point that sees the WHOLE
112
+ # entity set every route/router-emitting generator below reads from.
113
+ assert_no_collection_name_collisions(safe)
114
+
110
115
  emitted: dict[str, tuple[str, str]] = {} # full_path -> (content, generated_by)
111
116
  for gen in generators:
112
117
  ctx = GenContext(
@@ -200,6 +200,12 @@ class ErrorCode(str, Enum):
200
200
  # Codegen (ADR-0044): two value-object FQNs in one payload artifact derive the same
201
201
  # emitted record name even after package-qualification. Peer of ERR_VAR_NOT_ON_PAYLOAD.
202
202
  ERR_PAYLOAD_NAME_COLLISION = "ERR_PAYLOAD_NAME_COLLISION"
203
+ # Codegen: two DISTINCT entities/projections in one generation run pluralize to the
204
+ # SAME API-surface collection name (route path, generated router/finder names) —
205
+ # e.g. "Address" and "Addresses" both resolving to "Addresses". A codegen-time (not
206
+ # loader) error, peer of ERR_PAYLOAD_NAME_COLLISION: refused rather than silently
207
+ # letting one entity's route win.
208
+ ERR_COLLECTION_NAME_COLLISION = "ERR_COLLECTION_NAME_COLLISION"
203
209
  ERR_OUTPUT_TAG_MISSING = "ERR_OUTPUT_TAG_MISSING"
204
210
  # SP-H Unit9 — @filterable: true on a field subtype with no filter-operator
205
211
  # band (e.g. field.object). Would silently generate an empty-ops filter.
@@ -47,7 +47,17 @@ def _to_snake_case(s: str) -> str:
47
47
 
48
48
 
49
49
  def _pluralize(s: str) -> str:
50
- """Trivial cross-port pluralization (matches TS ``pluralize``)."""
50
+ """FROZEN — the DEFAULT PHYSICAL name derivation only (step 4 below, reached
51
+ only when metadata declares no explicit physical name). Matches the TS
52
+ reference ``pluralizeTableNameLegacy`` (metadata/src/naming.ts) — NOT the TS
53
+ ``pluralize``, which now differs for an already-plural word, deliberately
54
+ with NO already-plural detection. An adopter's live database was created
55
+ with this rule; adding the already-plural check here would make a schema
56
+ migration propose a rename for every already-plural entity name on the next
57
+ run. The API-surface analog (``metaobjects.apidocs.naming.pluralize``) has
58
+ that fix — this one must not, and the two are intentionally separate,
59
+ independently-maintained implementations (never merge them into one call
60
+ site)."""
51
61
  if not s:
52
62
  return s
53
63
  lower = s.lower()
@@ -128,7 +138,8 @@ class MetaSource(MetaData):
128
138
  2. Legacy ``@table`` for non-table kind (pre-1.0 fallback).
129
139
  3. Source's bare structural ``name`` via snake_case
130
140
  (no pluralize — the source's name IS the logical name).
131
- 4. Owning entity's name via ``pluralize(snake_case())``.
141
+ 4. Owning entity's name via the frozen legacy ``pluralize(snake_case())``
142
+ — see module-level ``_pluralize``'s docstring for why it is frozen.
132
143
 
133
144
  Callers needing the legacy raw ``@table`` slot only should use
134
145
  :meth:`table_name`; codegen / runtime should use ``physical_name``.
@@ -0,0 +1,109 @@
1
+ """assert_no_collection_name_collisions — the single choke point every route/router
2
+ generator path goes through (called once, in run_gen, over the full entity set
3
+ BEFORE any generator runs). See collection_name_collision.py's module docstring.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ from pathlib import Path
9
+
10
+ import pytest
11
+
12
+ from metaobjects import MetaDataLoader
13
+ from metaobjects.meta.core.object.meta_object import MetaObject
14
+ from metaobjects.shared.base_types import TYPE_OBJECT
15
+ from metaobjects.codegen.collection_name_collision import (
16
+ ERR_COLLECTION_NAME_COLLISION,
17
+ assert_no_collection_name_collisions,
18
+ )
19
+
20
+ _NAMING_CONFORMANCE_FIXTURE = (
21
+ Path(__file__).parents[4] / "fixtures" / "naming-conformance" / "already-plural-pluralize.json"
22
+ )
23
+
24
+
25
+ def _entities(tmp_path: Path, *names: str) -> list:
26
+ children = [
27
+ {
28
+ "object.entity": {
29
+ "name": name,
30
+ "children": [
31
+ {"source.rdb": {"@table": name.lower()}},
32
+ {"field.long": {"name": "id"}},
33
+ {"identity.primary": {"@fields": "id"}},
34
+ ],
35
+ }
36
+ }
37
+ for name in names
38
+ ]
39
+ meta_dir = tmp_path / "meta"
40
+ meta_dir.mkdir(parents=True, exist_ok=True)
41
+ (meta_dir / "meta.json").write_text(
42
+ json.dumps({"metadata.root": {"package": "acme", "children": children}})
43
+ )
44
+ root = MetaDataLoader.from_directory(meta_dir).root
45
+ return [c for c in root.own_children() if c.type == TYPE_OBJECT and isinstance(c, MetaObject)]
46
+
47
+
48
+ def _entity_and_value(tmp_path: Path, entity_name: str, value_name: str) -> list:
49
+ doc = {
50
+ "metadata.root": {
51
+ "package": "acme",
52
+ "children": [
53
+ {
54
+ "object.entity": {
55
+ "name": entity_name,
56
+ "children": [
57
+ {"source.rdb": {"@table": entity_name.lower()}},
58
+ {"field.long": {"name": "id"}},
59
+ {"identity.primary": {"@fields": "id"}},
60
+ ],
61
+ }
62
+ },
63
+ {
64
+ "object.value": {
65
+ "name": value_name,
66
+ "children": [{"field.string": {"name": "text"}}],
67
+ }
68
+ },
69
+ ],
70
+ }
71
+ }
72
+ meta_dir = tmp_path / "meta"
73
+ meta_dir.mkdir(parents=True, exist_ok=True)
74
+ (meta_dir / "meta.json").write_text(json.dumps(doc))
75
+ root = MetaDataLoader.from_directory(meta_dir).root
76
+ return [c for c in root.own_children() if c.type == TYPE_OBJECT and isinstance(c, MetaObject)]
77
+
78
+
79
+ def test_does_not_raise_for_distinct_plurals(tmp_path: Path) -> None:
80
+ assert_no_collection_name_collisions(_entities(tmp_path, "Post", "Author", "Category"))
81
+
82
+
83
+ def test_raises_for_address_and_addresses(tmp_path: Path) -> None:
84
+ with pytest.raises(ValueError) as exc_info:
85
+ assert_no_collection_name_collisions(_entities(tmp_path, "Address", "Addresses"))
86
+ msg = str(exc_info.value)
87
+ assert ERR_COLLECTION_NAME_COLLISION in msg
88
+ assert "Address" in msg
89
+ assert "Addresses" in msg
90
+
91
+
92
+ def test_raises_for_order_and_orders(tmp_path: Path) -> None:
93
+ with pytest.raises(ValueError):
94
+ assert_no_collection_name_collisions(_entities(tmp_path, "Order", "Orders"))
95
+
96
+
97
+ def test_excludes_value_objects(tmp_path: Path) -> None:
98
+ # "Address" (entity) pluralizes to "Addresses"; an unrelated object.value named
99
+ # "Addresses" never gets a route, so it must not trip this.
100
+ assert_no_collection_name_collisions(_entity_and_value(tmp_path, "Address", "Addresses"))
101
+
102
+
103
+ def test_naming_conformance_collision_cases_are_refused(tmp_path: Path) -> None:
104
+ cases = json.loads(_NAMING_CONFORMANCE_FIXTURE.read_text())["collisionCases"]
105
+ for i, case in enumerate(cases):
106
+ with pytest.raises(ValueError):
107
+ assert_no_collection_name_collisions(
108
+ _entities(tmp_path / str(i), case["entityA"], case["entityB"])
109
+ )
@@ -0,0 +1,149 @@
1
+ """The cross-port collection-URL spelling: the ENTITY NAME snake_cased, then pluralized.
2
+
3
+ One rule in all five ports. The multi-word and consonant+y axes are also gated
4
+ end-to-end by ``fixtures/api-contract-conformance/m2m/`` (the ``PostCategory``
5
+ scenario). The ACRONYM case is not — no corpus entity carries one — so it is
6
+ pinned here, against the same rule, in every port that owns a naming seam.
7
+
8
+ ``route_path`` deliberately does NOT reuse :func:`metaobjects.apidocs.naming.snake_case`:
9
+ that one separates before every capital, which is right for a module name and
10
+ would emit ``h_t_t_p_server`` in a URL.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ from pathlib import Path
16
+
17
+ import pytest
18
+
19
+ from metaobjects.apidocs.naming import pluralize, reverse_finder_fn, route_path, snake_case
20
+
21
+ _NAMING_CONFORMANCE_FIXTURE = (
22
+ Path(__file__).parents[4] / "fixtures" / "naming-conformance" / "already-plural-pluralize.json"
23
+ )
24
+
25
+
26
+ @pytest.mark.parametrize(
27
+ ("name", "expected"),
28
+ [
29
+ # Single regular word — every port's old rule already agreed here, which
30
+ # is exactly why four different spellings shipped green.
31
+ ("Author", "authors"),
32
+ ("Post", "posts"),
33
+ ("Tag", "tags"),
34
+ ("Person", "persons"),
35
+ ("Account", "accounts"),
36
+ ("Auth", "auths"),
37
+ # Multi-word: the capitals carry the word boundary, so lowercasing
38
+ # without separating used to yield "postcategorys".
39
+ ("PostCategory", "post_categories"),
40
+ ("OrderSummary", "order_summaries"),
41
+ # consonant + y -> ies; a sibilant takes -es.
42
+ ("Category", "categories"),
43
+ ("Address", "addresses"),
44
+ # Acronym: a run of capitals stays together until the final one that
45
+ # begins a word.
46
+ ("HTTPServer", "http_servers"),
47
+ ],
48
+ )
49
+ def test_route_path_is_entity_name_snake_cased_then_pluralized(name: str, expected: str) -> None:
50
+ assert route_path(name) == expected
51
+
52
+
53
+ def test_route_path_does_not_reuse_the_module_name_snake_case() -> None:
54
+ """The two snake variants genuinely differ, so this is not a tautology."""
55
+ assert snake_case("HTTPServer") == "h_t_t_p_server"
56
+ assert route_path("HTTPServer") == "http_servers"
57
+
58
+
59
+ @pytest.mark.parametrize(
60
+ ("word", "expected"),
61
+ [
62
+ ("post_category", "post_categories"),
63
+ ("address", "addresses"),
64
+ ("box", "boxes"),
65
+ ("buzz", "buzzes"),
66
+ ("match", "matches"),
67
+ ("dish", "dishes"),
68
+ # A VOWEL before the y takes a plain "s" — "days", never "daies".
69
+ ("day", "days"),
70
+ ("author", "authors"),
71
+ ],
72
+ )
73
+ def test_pluralize_contract(word: str, expected: str) -> None:
74
+ assert pluralize(word) == expected
75
+
76
+
77
+ # ---- Already-plural detection --------------------------------
78
+ #
79
+ # An already-plural entity name used to double (program_purchase_stats ->
80
+ # program_purchase_statses) in the REST collection segment and the reverse-finder
81
+ # names. Mirrors the TS fix in metadata/src/naming.ts exactly (same four-letter
82
+ # exclusion set before a final "s").
83
+
84
+ @pytest.mark.parametrize(
85
+ "word",
86
+ ["stats", "settings", "details", "news", "analytics", "series", "photos"],
87
+ )
88
+ def test_pluralize_leaves_already_plural_words_unchanged(word: str) -> None:
89
+ assert pluralize(word) == word
90
+
91
+
92
+ @pytest.mark.parametrize(
93
+ ("name", "expected"),
94
+ [
95
+ ("ProgramPurchaseStats", "program_purchase_stats"),
96
+ ("Settings", "settings"),
97
+ ],
98
+ )
99
+ def test_route_path_does_not_double_pluralize_an_already_plural_entity_name(
100
+ name: str, expected: str
101
+ ) -> None:
102
+ assert route_path(name) == expected
103
+
104
+
105
+ @pytest.mark.parametrize(
106
+ ("word", "expected"),
107
+ [
108
+ ("status", "statuses"),
109
+ ("address", "addresses"),
110
+ ("bonus", "bonuses"),
111
+ ("alias", "aliases"),
112
+ ("gas", "gases"),
113
+ # Documented pre-existing imperfection, explicitly out of scope — not "analyses".
114
+ ("analysis", "analysises"),
115
+ ],
116
+ )
117
+ def test_pluralize_keeps_existing_behavior_for_s_u_i_a_plus_s_endings(
118
+ word: str, expected: str
119
+ ) -> None:
120
+ assert pluralize(word) == expected
121
+
122
+
123
+ def test_pluralize_documented_known_miss_lens_reads_as_already_plural() -> None:
124
+ # Correct plural is "lenses"; this heuristic is not a dictionary. See
125
+ # pluralize's docstring.
126
+ assert pluralize("lens") == "lens"
127
+
128
+
129
+ def test_reverse_finder_fn_does_not_double_pluralize_an_already_plural_source_entity() -> None:
130
+ assert reverse_finder_fn("ProgramPurchaseStats", "programId") == (
131
+ "find_program_purchase_stats_by_program"
132
+ )
133
+
134
+
135
+ # ---------------------------------------------------------------------------
136
+ # fixtures/naming-conformance/ — the shared cross-port data proving every
137
+ # port's API-surface pluralizer agrees on the same inputs (the FROZEN legacy
138
+ # half of the same fixture is checked in test_fr016_source_name_and_kind_aliases.py,
139
+ # against meta_source._pluralize through a loaded entity). See that corpus's
140
+ # README.
141
+ # ---------------------------------------------------------------------------
142
+ _naming_conformance_cases = json.loads(_NAMING_CONFORMANCE_FIXTURE.read_text())["cases"]
143
+
144
+
145
+ @pytest.mark.parametrize(
146
+ "case", _naming_conformance_cases, ids=[c["name"] for c in _naming_conformance_cases]
147
+ )
148
+ def test_naming_conformance_api_plurals_match(case: dict) -> None:
149
+ assert pluralize(case["name"]) == case["apiPlural"]
@@ -210,3 +210,24 @@ def test_the_no_manifest_refusal_leads_with_the_performable_remedy(tmp_path: Pat
210
210
  assert "--baseline=adopt" in msgs[0]
211
211
  # What it must no longer do: prescribe committing a file this project cannot produce.
212
212
  assert "Commit '.metaobjects/.gen-state/.hashes.json' and re-run" not in msgs[0]
213
+
214
+
215
+ def test_run_gen_refuses_a_model_with_colliding_collection_names(tmp_path: Path) -> None:
216
+ """Integration-level: proves the collision gate is wired into the real entry
217
+ point (run_gen), not just callable as a standalone function. Address +
218
+ Addresses both pluralize to "Addresses" — reachable now that an already-
219
+ plural entity name is no longer double-pluralized."""
220
+ root = _load(tmp_path / "meta", {"metadata.root": {"package": "acme", "children": [
221
+ {"object.entity": {"name": "Address", "children": [
222
+ {"source.rdb": {"@table": "address"}},
223
+ {"field.long": {"name": "id"}},
224
+ {"identity.primary": {"@fields": "id"}},
225
+ ]}},
226
+ {"object.entity": {"name": "Addresses", "children": [
227
+ {"source.rdb": {"@table": "addresses"}},
228
+ {"field.long": {"name": "id"}},
229
+ {"identity.primary": {"@fields": "id"}},
230
+ ]}},
231
+ ]}})
232
+ with pytest.raises(ValueError):
233
+ run_gen(GenConfig(out_dir=str(tmp_path / "out")), root, generators=[entity_model()])
@@ -19,6 +19,8 @@ import os
19
19
  import tempfile
20
20
  from pathlib import Path
21
21
 
22
+ import pytest
23
+
22
24
  from metaobjects import MetaDataLoader
23
25
  from metaobjects.core_types import core_provider
24
26
  from metaobjects.meta.persistence.source.meta_source import MetaSource
@@ -160,6 +162,37 @@ def test_physical_name_step4_owning_entity_pluralize_snake_case() -> None:
160
162
  assert _first_source(root).physical_name() == "authors"
161
163
 
162
164
 
165
+ def test_physical_name_step4_frozen_legacy_rule_still_doubles_an_already_plural_owner_name() -> None:
166
+ """The default PHYSICAL table name derivation is FROZEN at the
167
+ pre-fix suffix-only rule, so an adopter's existing database never sees a
168
+ proposed rename for an already-plural entity name. Only the API-surface
169
+ ``metaobjects.apidocs.naming.pluralize`` gets the already-plural fix."""
170
+ _, _, root, _ = _load(_one_source("ProgramPurchaseStats", {}))
171
+ assert _first_source(root).physical_name() == "program_purchase_statses"
172
+
173
+
174
+ # ---------------------------------------------------------------------------
175
+ # fixtures/naming-conformance/ — the shared cross-port data proving every
176
+ # port's FROZEN legacy pluralizer agrees on the same inputs (the API-surface
177
+ # half of the same fixture is checked in test_route_path_naming.py, against
178
+ # metaobjects.apidocs.naming.pluralize directly). See that corpus's README.
179
+ # ---------------------------------------------------------------------------
180
+ _NAMING_CONFORMANCE_FIXTURE = (
181
+ Path(__file__).parents[4] / "fixtures" / "naming-conformance" / "already-plural-pluralize.json"
182
+ )
183
+ _naming_conformance_cases = json.loads(_NAMING_CONFORMANCE_FIXTURE.read_text())["cases"]
184
+
185
+
186
+ @pytest.mark.parametrize(
187
+ "case", _naming_conformance_cases, ids=[c["name"] for c in _naming_conformance_cases]
188
+ )
189
+ def test_naming_conformance_legacy_plurals_match(case: dict) -> None:
190
+ _, _, root, _ = _load(_one_source(case["name"], {}))
191
+ # Every fixture case is a single PascalCase word, so lowercasing is
192
+ # byte-equivalent to the snake_case step physical_name applies first.
193
+ assert _first_source(root).physical_name() == case["legacyPlural"].lower()
194
+
195
+
163
196
  # ---------------------------------------------------------------------------
164
197
  # Validation pass — kind mismatch, multiple aliases, empty string, legacy warn
165
198
  # ---------------------------------------------------------------------------
@@ -250,7 +250,7 @@ wheels = [
250
250
 
251
251
  [[package]]
252
252
  name = "metaobjects"
253
- version = "1.0.12"
253
+ version = "1.0.13"
254
254
  source = { editable = "." }
255
255
  dependencies = [
256
256
  { name = "pyyaml" },
@@ -1,67 +0,0 @@
1
- """The cross-port collection-URL spelling: the ENTITY NAME snake_cased, then pluralized.
2
-
3
- One rule in all five ports. The multi-word and consonant+y axes are also gated
4
- end-to-end by ``fixtures/api-contract-conformance/m2m/`` (the ``PostCategory``
5
- scenario). The ACRONYM case is not — no corpus entity carries one — so it is
6
- pinned here, against the same rule, in every port that owns a naming seam.
7
-
8
- ``route_path`` deliberately does NOT reuse :func:`metaobjects.apidocs.naming.snake_case`:
9
- that one separates before every capital, which is right for a module name and
10
- would emit ``h_t_t_p_server`` in a URL.
11
- """
12
- from __future__ import annotations
13
-
14
- import pytest
15
-
16
- from metaobjects.apidocs.naming import pluralize, route_path, snake_case
17
-
18
-
19
- @pytest.mark.parametrize(
20
- ("name", "expected"),
21
- [
22
- # Single regular word — every port's old rule already agreed here, which
23
- # is exactly why four different spellings shipped green.
24
- ("Author", "authors"),
25
- ("Post", "posts"),
26
- ("Tag", "tags"),
27
- ("Person", "persons"),
28
- ("Account", "accounts"),
29
- ("Auth", "auths"),
30
- # Multi-word: the capitals carry the word boundary, so lowercasing
31
- # without separating used to yield "postcategorys".
32
- ("PostCategory", "post_categories"),
33
- ("OrderSummary", "order_summaries"),
34
- # consonant + y -> ies; a sibilant takes -es.
35
- ("Category", "categories"),
36
- ("Address", "addresses"),
37
- # Acronym: a run of capitals stays together until the final one that
38
- # begins a word.
39
- ("HTTPServer", "http_servers"),
40
- ],
41
- )
42
- def test_route_path_is_entity_name_snake_cased_then_pluralized(name: str, expected: str) -> None:
43
- assert route_path(name) == expected
44
-
45
-
46
- def test_route_path_does_not_reuse_the_module_name_snake_case() -> None:
47
- """The two snake variants genuinely differ, so this is not a tautology."""
48
- assert snake_case("HTTPServer") == "h_t_t_p_server"
49
- assert route_path("HTTPServer") == "http_servers"
50
-
51
-
52
- @pytest.mark.parametrize(
53
- ("word", "expected"),
54
- [
55
- ("post_category", "post_categories"),
56
- ("address", "addresses"),
57
- ("box", "boxes"),
58
- ("buzz", "buzzes"),
59
- ("match", "matches"),
60
- ("dish", "dishes"),
61
- # A VOWEL before the y takes a plain "s" — "days", never "daies".
62
- ("day", "days"),
63
- ("author", "authors"),
64
- ],
65
- )
66
- def test_pluralize_contract(word: str, expected: str) -> None:
67
- assert pluralize(word) == expected
File without changes
File without changes
File without changes