metaobjects 0.12.1__tar.gz → 0.14.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. {metaobjects-0.12.1 → metaobjects-0.14.0}/PKG-INFO +1 -1
  2. {metaobjects-0.12.1 → metaobjects-0.14.0}/pyproject.toml +1 -1
  3. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/cli.py +146 -17
  4. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generator.py +32 -0
  5. metaobjects-0.14.0/src/metaobjects/codegen/generators/template_generator.py +138 -0
  6. metaobjects-0.14.0/src/metaobjects/codegen/template_codegen/output_pattern.py +54 -0
  7. metaobjects-0.14.0/src/metaobjects/codegen/template_codegen/template_data.py +119 -0
  8. metaobjects-0.14.0/src/metaobjects/codegen/template_codegen/template_spec.py +90 -0
  9. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/type_map.py +12 -0
  10. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/escapers.py +4 -0
  11. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_cli.py +57 -0
  12. metaobjects-0.14.0/tests/codegen/test_cli_verify_strict.py +139 -0
  13. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_entity_model.py +14 -0
  14. metaobjects-0.14.0/tests/codegen/test_template_data.py +50 -0
  15. metaobjects-0.14.0/tests/codegen/test_template_output_pattern.py +32 -0
  16. metaobjects-0.14.0/tests/codegen/test_template_scope_helpers.py +52 -0
  17. metaobjects-0.14.0/tests/codegen/test_template_scope_walk.py +52 -0
  18. metaobjects-0.14.0/tests/codegen/test_template_spec.py +60 -0
  19. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_type_map.py +14 -0
  20. metaobjects-0.14.0/tests/conformance/test_template_codegen_conformance.py +51 -0
  21. metaobjects-0.14.0/tests/integration/api_contract_jsonb_server.py +160 -0
  22. metaobjects-0.14.0/tests/integration/generated_jsonb_app.py +135 -0
  23. metaobjects-0.14.0/tests/integration/test_api_contract_jsonb.py +153 -0
  24. metaobjects-0.14.0/tests/unit/__init__.py +0 -0
  25. {metaobjects-0.12.1 → metaobjects-0.14.0}/uv.lock +1 -1
  26. metaobjects-0.12.1/src/metaobjects/codegen/generators/template_generator.py +0 -70
  27. {metaobjects-0.12.1 → metaobjects-0.14.0}/.gitignore +0 -0
  28. {metaobjects-0.12.1 → metaobjects-0.14.0}/LICENSE +0 -0
  29. {metaobjects-0.12.1 → metaobjects-0.14.0}/README.md +0 -0
  30. {metaobjects-0.12.1 → metaobjects-0.14.0}/hatch_build.py +0 -0
  31. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/__init__.py +0 -0
  32. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/agent_context/__init__.py +0 -0
  33. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/agent_context/scaffold.py +0 -0
  34. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/__init__.py +0 -0
  35. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/api_model.py +0 -0
  36. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/builder.py +0 -0
  37. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/naming.py +0 -0
  38. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/paths.py +0 -0
  39. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/apidocs/renderer.py +0 -0
  40. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/attr_class_map.py +0 -0
  41. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
  42. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/__init__.py +0 -0
  43. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/config.py +0 -0
  44. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/constants.py +0 -0
  45. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  46. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  47. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/format.py +0 -0
  48. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  49. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generator_registry.py +0 -0
  50. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/__init__.py +0 -0
  51. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/entity_model.py +0 -0
  52. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  53. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
  54. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  55. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
  56. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  57. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  58. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
  59. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  60. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/router_generator.py +0 -0
  61. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  62. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  63. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  64. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  65. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  66. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/runner.py +0 -0
  67. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  68. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  69. {metaobjects-0.12.1/src/metaobjects/loader → metaobjects-0.14.0/src/metaobjects/codegen/template_codegen}/__init__.py +0 -0
  70. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/core_types.py +0 -0
  71. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/datatype.py +0 -0
  72. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/documentation/__init__.py +0 -0
  73. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/documentation/doc_constants.py +0 -0
  74. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/documentation/doc_provider.py +0 -0
  75. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/documentation/doc_schema.py +0 -0
  76. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/errors.py +0 -0
  77. {metaobjects-0.12.1/src/metaobjects/meta/core → metaobjects-0.14.0/src/metaobjects/loader}/__init__.py +0 -0
  78. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/merge.py +0 -0
  79. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/meta_data_loader.py +0 -0
  80. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/registered_validation.py +0 -0
  81. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/sources/__init__.py +0 -0
  82. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/sources/directory_source.py +0 -0
  83. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/sources/file_source.py +0 -0
  84. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  85. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/sources/uri_source.py +0 -0
  86. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/validate_discriminator.py +0 -0
  87. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/validate_field_readonly.py +0 -0
  88. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  89. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  90. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/loader/validation_passes.py +0 -0
  91. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/__init__.py +0 -0
  92. {metaobjects-0.12.1/src/metaobjects/meta/core/attr → metaobjects-0.14.0/src/metaobjects/meta/core}/__init__.py +0 -0
  93. {metaobjects-0.12.1/src/metaobjects/meta/core/field → metaobjects-0.14.0/src/metaobjects/meta/core/attr}/__init__.py +0 -0
  94. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  95. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  96. {metaobjects-0.12.1/src/metaobjects/meta/core/identity → metaobjects-0.14.0/src/metaobjects/meta/core/field}/__init__.py +0 -0
  97. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  98. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  99. {metaobjects-0.12.1/src/metaobjects/meta/core/object → metaobjects-0.14.0/src/metaobjects/meta/core/identity}/__init__.py +0 -0
  100. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  101. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  102. {metaobjects-0.12.1/src/metaobjects/meta/core/relationship → metaobjects-0.14.0/src/metaobjects/meta/core/object}/__init__.py +0 -0
  103. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  104. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  105. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  106. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  107. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  108. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/object/value_object.py +0 -0
  109. {metaobjects-0.12.1/src/metaobjects/meta/core/validator → metaobjects-0.14.0/src/metaobjects/meta/core/relationship}/__init__.py +0 -0
  110. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  111. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  112. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  113. {metaobjects-0.12.1/src/metaobjects/meta/persistence → metaobjects-0.14.0/src/metaobjects/meta/core/validator}/__init__.py +0 -0
  114. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  115. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/meta_data.py +0 -0
  116. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/meta_root.py +0 -0
  117. {metaobjects-0.12.1/src/metaobjects/meta/persistence/origin → metaobjects-0.14.0/src/metaobjects/meta/persistence}/__init__.py +0 -0
  118. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  119. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  120. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  121. {metaobjects-0.12.1/src/metaobjects/meta/persistence/source → metaobjects-0.14.0/src/metaobjects/meta/persistence/origin}/__init__.py +0 -0
  122. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  123. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  124. {metaobjects-0.12.1/src/metaobjects/meta/presentation → metaobjects-0.14.0/src/metaobjects/meta/persistence/source}/__init__.py +0 -0
  125. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  126. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  127. {metaobjects-0.12.1/src/metaobjects/meta/presentation/layout → metaobjects-0.14.0/src/metaobjects/meta/presentation}/__init__.py +0 -0
  128. {metaobjects-0.12.1/src/metaobjects/meta/presentation/ui → metaobjects-0.14.0/src/metaobjects/meta/presentation/layout}/__init__.py +0 -0
  129. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  130. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  131. {metaobjects-0.12.1/src/metaobjects/meta/presentation/view → metaobjects-0.14.0/src/metaobjects/meta/presentation/ui}/__init__.py +0 -0
  132. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  133. {metaobjects-0.12.1/src/metaobjects/meta/template → metaobjects-0.14.0/src/metaobjects/meta/presentation/view}/__init__.py +0 -0
  134. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  135. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  136. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/provider_extends.py +0 -0
  137. {metaobjects-0.12.1/src/metaobjects/shared → metaobjects-0.14.0/src/metaobjects/meta/template}/__init__.py +0 -0
  138. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/template/meta_template.py +0 -0
  139. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  140. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/meta/template/template_constants.py +0 -0
  141. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/naming_refs.py +0 -0
  142. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/parser.py +0 -0
  143. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/parser_yaml.py +0 -0
  144. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/provider.py +0 -0
  145. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/py.typed +0 -0
  146. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/registry.py +0 -0
  147. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/registry_manifest.py +0 -0
  148. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/__init__.py +0 -0
  149. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/email_document.py +0 -0
  150. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  151. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/__init__.py +0 -0
  152. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/coerce.py +0 -0
  153. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/extract.py +0 -0
  154. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/extract_map.py +0 -0
  155. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  156. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/locate.py +0 -0
  157. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/normalize.py +0 -0
  158. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/strip.py +0 -0
  159. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/types.py +0 -0
  160. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  161. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/filesystem_provider.py +0 -0
  162. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/__init__.py +0 -0
  163. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  164. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  165. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  166. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  167. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  168. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/renderer.py +0 -0
  169. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/render/verify.py +0 -0
  170. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/runtime/__init__.py +0 -0
  171. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/runtime/llm_recorder.py +0 -0
  172. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  173. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/runtime/object_manager.py +0 -0
  174. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/runtime/tph.py +0 -0
  175. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/serializer_json.py +0 -0
  176. {metaobjects-0.12.1/tests → metaobjects-0.14.0/src/metaobjects/shared}/__init__.py +0 -0
  177. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/shared/base_types.py +0 -0
  178. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/shared/separators.py +0 -0
  179. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/shared/structural.py +0 -0
  180. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/source/__init__.py +0 -0
  181. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/source/error_source.py +0 -0
  182. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/source/json_path.py +0 -0
  183. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/source/semantic_diff.py +0 -0
  184. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/source/yaml_positions.py +0 -0
  185. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  186. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/attr.json +0 -0
  187. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/db.json +0 -0
  188. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  189. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/field.json +0 -0
  190. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/identity.json +0 -0
  191. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/layout.json +0 -0
  192. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/object.json +0 -0
  193. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/origin.json +0 -0
  194. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  195. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  196. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/source.json +0 -0
  197. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/template.json +0 -0
  198. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/ui.json +0 -0
  199. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/validator.json +0 -0
  200. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/spec_metamodel/view.json +0 -0
  201. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/super_resolve.py +0 -0
  202. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/validation_types.py +0 -0
  203. {metaobjects-0.12.1 → metaobjects-0.14.0}/src/metaobjects/yaml_desugar.py +0 -0
  204. {metaobjects-0.12.1/tests/codegen → metaobjects-0.14.0/tests}/__init__.py +0 -0
  205. {metaobjects-0.12.1/tests/conformance → metaobjects-0.14.0/tests/codegen}/__init__.py +0 -0
  206. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  207. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/extends/expected/Program.py +0 -0
  208. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/extends/meta.json +0 -0
  209. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  210. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  211. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/nested-array/meta.json +0 -0
  212. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  213. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  214. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/scalars/meta.json +0 -0
  215. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  216. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/golden/vanilla/meta.json +0 -0
  217. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_abstract_conformance.py +0 -0
  218. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_api_docs_builder.py +0 -0
  219. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_api_docs_paths.py +0 -0
  220. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_cli_registry.py +0 -0
  221. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  222. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  223. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_constants_config.py +0 -0
  224. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_enum_conformance.py +0 -0
  225. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_extractor_generator.py +0 -0
  226. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  227. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_format.py +0 -0
  228. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_fr010_output_codegen.py +0 -0
  229. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  230. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_generator.py +0 -0
  231. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_generator_extension_seams.py +0 -0
  232. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_golden.py +0 -0
  233. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  234. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_inheritance_conformance.py +0 -0
  235. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_instance_artifacts.py +0 -0
  236. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_m2m_codegen.py +0 -0
  237. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_output_parser_generator.py +0 -0
  238. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_overwrite_policy.py +0 -0
  239. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_payload_vo_generator.py +0 -0
  240. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_projection_compile.py +0 -0
  241. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_render_helper_conformance.py +0 -0
  242. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_render_helper_generator.py +0 -0
  243. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_router_generator.py +0 -0
  244. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_runner.py +0 -0
  245. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_template_generator.py +0 -0
  246. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_tph_codegen.py +0 -0
  247. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_trace_helper_generator.py +0 -0
  248. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/codegen/test_validation_conformance.py +0 -0
  249. {metaobjects-0.12.1/tests/integration → metaobjects-0.14.0/tests/conformance}/__init__.py +0 -0
  250. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/capabilities.py +0 -0
  251. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/conformance-expected-failures.json +0 -0
  252. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/conformance_adapter.py +0 -0
  253. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/corpus.py +0 -0
  254. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/expected_failures.py +0 -0
  255. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/fixture_discovery.py +0 -0
  256. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/navigator.py +0 -0
  257. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  258. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_conformance.py +0 -0
  259. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_extract_conformance.py +0 -0
  260. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_extract_object_verdict.py +0 -0
  261. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  262. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_fr011_attrs.py +0 -0
  263. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_generator_registry_conformance.py +0 -0
  264. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_object_model_conformance.py +0 -0
  265. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_registry_conformance.py +0 -0
  266. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_runner_hardfail.py +0 -0
  267. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  268. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_strict_attr_load.py +0 -0
  269. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_template_generator_conformance.py +0 -0
  270. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/test_yaml_conformance.py +0 -0
  271. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  272. {metaobjects-0.12.1/tests/render → metaobjects-0.14.0/tests/integration}/__init__.py +0 -0
  273. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/api_contract_assertions.py +0 -0
  274. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/api_contract_m2m_server.py +0 -0
  275. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/api_contract_server.py +0 -0
  276. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/generated_m2m_app.py +0 -0
  277. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/generated_router_app.py +0 -0
  278. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/generated_tph_app.py +0 -0
  279. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/meta_ai_trace.yaml +0 -0
  280. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/normalization.py +0 -0
  281. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/postgres_container.py +0 -0
  282. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/query_runner.py +0 -0
  283. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/scenarios.py +0 -0
  284. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_api_contract.py +0 -0
  285. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_api_contract_generated.py +0 -0
  286. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_api_contract_m2m.py +0 -0
  287. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  288. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_api_contract_tph_generated.py +0 -0
  289. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_llm_call_trace.py +0 -0
  290. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_normalization.py +0 -0
  291. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_query_scenarios.py +0 -0
  292. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/integration/test_runtime_return_types.py +0 -0
  293. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/open_closed_proof_test.py +0 -0
  294. {metaobjects-0.12.1/tests/render/extract → metaobjects-0.14.0/tests/render}/__init__.py +0 -0
  295. {metaobjects-0.12.1/tests/render/prompt → metaobjects-0.14.0/tests/render/extract}/__init__.py +0 -0
  296. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_coerce.py +0 -0
  297. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_extract.py +0 -0
  298. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_extract_map.py +0 -0
  299. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  300. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_locate.py +0 -0
  301. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_model.py +0 -0
  302. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_normalize.py +0 -0
  303. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_strip.py +0 -0
  304. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  305. {metaobjects-0.12.1/tests/source → metaobjects-0.14.0/tests/render/prompt}/__init__.py +0 -0
  306. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/prompt/test_output_format_renderer.py +0 -0
  307. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_email_document.py +0 -0
  308. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_filesystem_provider.py +0 -0
  309. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_output_format_renderer_nested.py +0 -0
  310. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_output_prompt_conformance.py +0 -0
  311. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_render_conformance.py +0 -0
  312. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_render_max_chars.py +0 -0
  313. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_verify.py +0 -0
  314. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/render/test_verify_conformance.py +0 -0
  315. {metaobjects-0.12.1/tests/unit → metaobjects-0.14.0/tests/source}/__init__.py +0 -0
  316. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_fr5c_merge_attribution.py +0 -0
  317. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_fr5d_reference_resolution.py +0 -0
  318. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_fr5e_database_source_shape.py +0 -0
  319. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_json_path.py +0 -0
  320. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_semantic_diff.py +0 -0
  321. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_source_on_node.py +0 -0
  322. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/source/test_yaml_positions.py +0 -0
  323. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/test_api_docs_accuracy.py +0 -0
  324. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_agent_context_staleness.py +0 -0
  325. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_capabilities.py +0 -0
  326. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_common_attrs.py +0 -0
  327. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_core_types.py +0 -0
  328. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_effective_package.py +0 -0
  329. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_errors.py +0 -0
  330. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_field_enum.py +0 -0
  331. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_field_map_validation.py +0 -0
  332. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  333. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  334. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_llm_recorder.py +0 -0
  335. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_loader.py +0 -0
  336. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_loader_bom.py +0 -0
  337. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_loader_class.py +0 -0
  338. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_merge.py +0 -0
  339. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_meta_attr.py +0 -0
  340. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_meta_data.py +0 -0
  341. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_meta_source.py +0 -0
  342. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_module_shortcuts.py +0 -0
  343. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_n2m_resolver.py +0 -0
  344. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  345. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_one_primary_source.py +0 -0
  346. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_parser.py +0 -0
  347. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_parser_yaml.py +0 -0
  348. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_provider.py +0 -0
  349. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_provider_extension.py +0 -0
  350. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_registry.py +0 -0
  351. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_registry_completeness.py +0 -0
  352. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_registry_extend.py +0 -0
  353. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_registry_sealed.py +0 -0
  354. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_relationship_referential_actions.py +0 -0
  355. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_resolution_key.py +0 -0
  356. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  357. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_serializer.py +0 -0
  358. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_shared_constants.py +0 -0
  359. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_smoke.py +0 -0
  360. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_sources.py +0 -0
  361. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_strict_child_placement.py +0 -0
  362. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_super_resolve.py +0 -0
  363. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_template_toolcall.py +0 -0
  364. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  365. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_validation_attr_schema.py +0 -0
  366. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_validation_filter_values.py +0 -0
  367. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_validation_origin_paths.py +0 -0
  368. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_validation_sort_field.py +0 -0
  369. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_validation_warnings.py +0 -0
  370. {metaobjects-0.12.1 → metaobjects-0.14.0}/tests/unit/test_yaml_desugar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metaobjects
3
- Version: 0.12.1
3
+ Version: 0.14.0
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.12.1"
3
+ version = "0.14.0"
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"
@@ -3,9 +3,13 @@
3
3
  Two subcommands:
4
4
 
5
5
  metaobjects gen <metadataDir> --out <dir> [--package <pkg>]
6
+ [--template-spec <json> [--templates <dir>]]
6
7
  Load metadata, run the Python codegen generator suite, and write files
7
8
  under ``--out`` (guarded by the @generated header). Prints each written
8
- file. Non-zero exit on a load error.
9
+ file. Non-zero exit on a load error. ``--template-spec`` appends the
10
+ declarative Mustache template generators (scope perEntity/perPackage/
11
+ perModel + outputPattern) described by a JSON spec, resolving template
12
+ refs under ``--templates`` (default ``templates``).
9
13
 
10
14
  metaobjects verify <metadataDir> [--codegen] [--templates] [--db URL] ...
11
15
  Drift gate with explicit subverbs (ADR-0021 D2 — one verify vocabulary
@@ -99,15 +103,33 @@ def _default_generators() -> list[Generator]:
99
103
  ]
100
104
 
101
105
 
102
- def _load_root(metadata_dir: str) -> tuple[MetaData | None, list[str]]:
103
- """Load metadata; return ``(root, error_messages)``. ``root`` is None on error."""
104
- result = MetaDataLoader.from_directory(metadata_dir)
106
+ def _load_root(
107
+ metadata_dir: str, strict: bool = False
108
+ ) -> tuple[MetaData | None, list[str]]:
109
+ """Load metadata; return ``(root, error_messages)``. ``root`` is None on error.
110
+
111
+ ``strict`` (ADR-0023, #96) — when True, an undeclared own ``@attr`` →
112
+ ``ERR_UNKNOWN_ATTR``. Defaults False so ``gen`` / ``docs`` keep the legacy
113
+ open-attr load; only ``verify`` opts in (strict-by-default with a ``--lax``
114
+ escape).
115
+ """
116
+ result = MetaDataLoader.from_directory(metadata_dir, strict=strict)
105
117
  if result.errors:
106
118
  msgs = [f"{e.code}: {e.message}" for e in result.errors]
107
119
  return None, msgs
108
120
  return result.root, []
109
121
 
110
122
 
123
+ def _strict_load_hint() -> str:
124
+ """Actionable next-steps when strict verify rejects an undeclared @attr."""
125
+ return (
126
+ "verify is strict (ADR-0023): every authored @attr must be declared. Fix: "
127
+ "register the attr on a metadata provider, OR move arbitrary author-supplied "
128
+ "properties into an `attr.properties` bag, OR re-run with `--lax` to keep the "
129
+ "legacy open-attr load."
130
+ )
131
+
132
+
111
133
  def _resolve_generators(names: str) -> tuple[list[Generator], list[str]]:
112
134
  """Resolve a comma-separated list of STABLE generator names via the registry.
113
135
 
@@ -138,11 +160,39 @@ def _parse_entities(value: str | None) -> list[str] | None:
138
160
  return names or None
139
161
 
140
162
 
163
+ def _run_suite(
164
+ root: MetaData,
165
+ out_dir: str,
166
+ generators: list[Generator] | None = None,
167
+ entity_filter: list[str] | None = None,
168
+ emit_package_init: bool = True,
169
+ ) -> list[str]:
170
+ """Run a generator suite against an ALREADY-LOADED ``root`` into ``out_dir``.
171
+
172
+ Split out from :func:`_generate` so a single gen invocation can load the
173
+ metadata once and run multiple passes against it (the default Python suite +
174
+ the ``--template-spec`` pass). See :func:`_generate` for the parameter
175
+ semantics. Returns the written paths.
176
+
177
+ NOTE: ``run_gen``'s output-path collision guard is per-pass — a
178
+ ``--template-spec`` ``outputPattern`` that collides with a default-suite path
179
+ is not flagged across the two passes (it would overwrite, since default files
180
+ carry the ``@generated`` header). Accepted marginal limitation (requires user
181
+ misconfiguration).
182
+ """
183
+ config = GenConfig(out_dir=out_dir, emit_package_init=emit_package_init)
184
+ suite = generators if generators is not None else _default_generators()
185
+ result = run_gen(config, root, generators=suite, entity_filter=entity_filter)
186
+ return [path for path, status in result.files if status != "refused"]
187
+
188
+
141
189
  def _generate(
142
190
  metadata_dir: str,
143
191
  out_dir: str,
144
192
  generators: list[Generator] | None = None,
145
193
  entity_filter: list[str] | None = None,
194
+ emit_package_init: bool = True,
195
+ strict: bool = False,
146
196
  ) -> tuple[list[str], list[str]]:
147
197
  """Run the generator suite into ``out_dir``.
148
198
 
@@ -150,18 +200,18 @@ def _generate(
150
200
  resolved subset for ``--generators``. ``entity_filter`` (the ``--entities``
151
201
  allowlist) restricts which entities are EMITTED while the WHOLE model is still
152
202
  loaded, so cross-entity references (``extends`` bases, ``@objectRef`` VOs)
153
- resolve — emit a subset without splitting the metadata. Returns
203
+ resolve — emit a subset without splitting the metadata. ``emit_package_init``
204
+ is ``False`` for the format-agnostic ``--template-spec`` generators (whose
205
+ output is text/markdown/csv/json/xml/html, never a Python package) so no
206
+ spurious ``__init__.py`` is scattered through their output tree. Returns
154
207
  ``(written_paths, errors)``. On a load error, ``errors`` is non-empty and no
155
- files are written.
208
+ files are written. ``strict`` (ADR-0023, #96) is threaded to the load — the
209
+ ``verify --codegen`` caller passes True; ``gen`` keeps the default False.
156
210
  """
157
- root, errors = _load_root(metadata_dir)
211
+ root, errors = _load_root(metadata_dir, strict=strict)
158
212
  if root is None:
159
213
  return [], errors
160
- config = GenConfig(out_dir=out_dir)
161
- suite = generators if generators is not None else _default_generators()
162
- result = run_gen(config, root, generators=suite, entity_filter=entity_filter)
163
- written = [path for path, status in result.files if status != "refused"]
164
- return written, []
214
+ return _run_suite(root, out_dir, generators, entity_filter, emit_package_init), []
165
215
 
166
216
 
167
217
  #: The default api-surface subdir (the cross-port contract's ``api/python``).
@@ -293,13 +343,54 @@ def _cmd_gen(args: argparse.Namespace) -> int:
293
343
  print(f" {msg}", file=sys.stderr)
294
344
  return 1
295
345
 
346
+ # SP-1: declarative Mustache generators from a JSON template-spec. Their output
347
+ # is format-agnostic (text/markdown/csv/json/xml/html), so they run as a SECOND,
348
+ # SEPARATE pass with emit_package_init=False — no Python __init__.py is injected
349
+ # into their (possibly non-Python) output tree. Templates resolve under
350
+ # --templates via a FilesystemProvider.
351
+ spec_gens: list[Generator] = []
352
+ if getattr(args, "template_spec", None):
353
+ from metaobjects.codegen.template_codegen.template_spec import (
354
+ parse_template_spec,
355
+ template_spec_to_generators,
356
+ )
357
+
358
+ try:
359
+ spec = parse_template_spec(json.loads(Path(args.template_spec).read_text(encoding="utf-8")))
360
+ except (OSError, ValueError) as exc:
361
+ print(f"error: invalid --template-spec: {exc}", file=sys.stderr)
362
+ return 1
363
+ provider = FilesystemProvider(args.templates)
364
+ spec_gens = template_spec_to_generators(spec, provider)
365
+
296
366
  entities = _parse_entities(getattr(args, "entities", None))
297
- written, errors = _generate(args.metadata_dir, args.out, generators, entities)
298
- if errors:
367
+ # Load the metadata ONCE; both the default suite and the (optional)
368
+ # --template-spec pass run against this single loaded root.
369
+ root, load_errors = _load_root(args.metadata_dir)
370
+ if root is None:
299
371
  print("error: failed to load metadata:", file=sys.stderr)
300
- for msg in errors:
372
+ for msg in load_errors:
301
373
  print(f" {msg}", file=sys.stderr)
302
374
  return 1
375
+ written = _run_suite(root, args.out, generators, entities)
376
+ if spec_gens:
377
+ # The template-spec pass renders user-supplied templates: a bad ref or a
378
+ # wrong --templates dir raises RenderError (not OSError/ValueError, so it
379
+ # escapes the parse-time guard above). Report it as a clean error.
380
+ from metaobjects.render.renderer import RenderError
381
+
382
+ try:
383
+ spec_written = _run_suite(
384
+ root, args.out, spec_gens, entities, emit_package_init=False
385
+ )
386
+ except RenderError as exc:
387
+ print(
388
+ f"error: --template-spec render failed (check the template refs and "
389
+ f"--templates dir {args.templates!r}): {exc}",
390
+ file=sys.stderr,
391
+ )
392
+ return 1
393
+ written = list(written) + spec_written
303
394
  for path in written:
304
395
  print(path)
305
396
  print(f"metaobjects gen: wrote {len(written)} file(s) to {args.out}")
@@ -337,13 +428,20 @@ def _verify_codegen(args: argparse.Namespace) -> int:
337
428
  # Reuse the exact gen code path — regenerate into a throwaway temp dir. The
338
429
  # --entities filter must match the `gen` that produced --out, or the diff
339
430
  # reports the un-emitted entities as spurious drift.
431
+ # ADR-0023 strict-by-default (#96): verify loads strict unless --lax is given,
432
+ # so an undeclared/typo'd own @attr fails verify (matching Java's Maven goal).
433
+ strict = not getattr(args, "lax", False)
340
434
  with tempfile.TemporaryDirectory() as tmp:
341
435
  entities = _parse_entities(getattr(args, "entities", None))
342
- written, errors = _generate(args.metadata_dir, tmp, None, entities)
436
+ written, errors = _generate(
437
+ args.metadata_dir, tmp, None, entities, strict=strict
438
+ )
343
439
  if errors:
344
440
  print("error: failed to load metadata:", file=sys.stderr)
345
441
  for msg in errors:
346
442
  print(f" {msg}", file=sys.stderr)
443
+ if strict and any("ERR_UNKNOWN_ATTR" in m for m in errors):
444
+ print(_strict_load_hint(), file=sys.stderr)
347
445
  return 1
348
446
 
349
447
  expected = _relative_set(Path(tmp))
@@ -407,11 +505,16 @@ def _verify_templates(args: argparse.Namespace) -> int:
407
505
  )
408
506
  return 2
409
507
 
410
- root, errors = _load_root(args.metadata_dir)
508
+ # ADR-0023 strict-by-default (#96) — same as --codegen: an undeclared @attr
509
+ # fails verify unless --lax is passed.
510
+ strict = not getattr(args, "lax", False)
511
+ root, errors = _load_root(args.metadata_dir, strict=strict)
411
512
  if root is None:
412
513
  print("error: failed to load metadata:", file=sys.stderr)
413
514
  for msg in errors:
414
515
  print(f" {msg}", file=sys.stderr)
516
+ if strict and any("ERR_UNKNOWN_ATTR" in m for m in errors):
517
+ print(_strict_load_hint(), file=sys.stderr)
415
518
  return 1
416
519
 
417
520
  provider = FilesystemProvider(template_root)
@@ -581,6 +684,23 @@ def _build_parser() -> argparse.ArgumentParser:
581
684
  action="store_true",
582
685
  help="list registered generators (stable name + description) and exit",
583
686
  )
687
+ gen.add_argument(
688
+ "--template-spec",
689
+ dest="template_spec",
690
+ default=None,
691
+ help=(
692
+ "path to a JSON template-spec ({\"generators\":[{name,template,scope,"
693
+ "outputPattern,format?}]}) — declarative Mustache generators appended "
694
+ "to the suite (SP-1). Templates resolve under --templates. For output "
695
+ "to be regenerable, the template must emit the @generated header itself "
696
+ "(the codegen write path refuses to overwrite files lacking it)."
697
+ ),
698
+ )
699
+ gen.add_argument(
700
+ "--templates",
701
+ default="templates",
702
+ help="templates root for --template-spec (default: templates)",
703
+ )
584
704
  gen.add_argument(
585
705
  "--package",
586
706
  default=None,
@@ -672,6 +792,15 @@ def _build_parser() -> argparse.ArgumentParser:
672
792
  default=None,
673
793
  help="comma-separated entity allowlist for --codegen drift (match `gen --entities`)",
674
794
  )
795
+ verify.add_argument(
796
+ "--lax",
797
+ action="store_true",
798
+ help=(
799
+ "opt out of strict-attr load (ADR-0023, #96): verify is strict by "
800
+ "default — an undeclared/typo'd @attr fails. --lax restores the legacy "
801
+ "open-attr load."
802
+ ),
803
+ )
675
804
  verify.set_defaults(func=_cmd_verify)
676
805
 
677
806
  agent_docs = sub.add_parser(
@@ -60,3 +60,35 @@ def once_per_run(
60
60
  return r if isinstance(r, list) else [r]
61
61
 
62
62
  return run
63
+
64
+
65
+ def per_package(
66
+ fn: Callable[[str, list[MetaObject], GenContext], "EmittedFile | list[EmittedFile]"],
67
+ ) -> Callable[[GenContext], list[EmittedFile]]:
68
+ """One-file-per-package convenience. Groups matched entities by effective
69
+ package, runs ``fn(pkg, entities, ctx)`` once per package (packages ascending,
70
+ entities keeping ``ctx.entities`` order). The package scope from
71
+ codegen-concepts §10 (object + model already exist via per_entity + per_model)."""
72
+
73
+ def run(ctx: GenContext) -> list[EmittedFile]:
74
+ # Local import avoids a module cycle (template_data imports nothing from here).
75
+ from metaobjects.codegen.template_codegen.template_data import package_of
76
+
77
+ by_pkg: dict[str, list[MetaObject]] = {}
78
+ for e in ctx.entities:
79
+ if not ctx.matches(e):
80
+ continue
81
+ by_pkg.setdefault(package_of(e), []).append(e)
82
+ out: list[EmittedFile] = []
83
+ for pkg in sorted(by_pkg):
84
+ r = fn(pkg, by_pkg[pkg], ctx)
85
+ out.extend(r if isinstance(r, list) else [r])
86
+ return out
87
+
88
+ return run
89
+
90
+
91
+ #: App-scope alias — run once over the whole model. The canonical name for the
92
+ #: one-shot scope (``once_per_run`` stays as a soft-deprecated alias; "run" is
93
+ #: ambiguous under multi-target output, ``per_model`` names the data scope).
94
+ per_model = once_per_run
@@ -0,0 +1,138 @@
1
+ """template_generator() — Python port of the TS rc.12 factory.
2
+
3
+ Walks the loaded MetaRoot -> renders shared Mustache templates via the
4
+ metaobjects.render engine -> returns EmittedFile[]. Same Generator Protocol
5
+ as the per-entity hand-coded generators; just adds the "Mustache template"
6
+ + "walk that yields a data dict per output" primitives.
7
+
8
+ Design: spec/design-docs/2026-05-28-cross-port-template-generator.md.
9
+ Cross-port byte-equivalence verified via fixtures/render-conformance/template-generator/.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass
14
+ from typing import Any, Callable, Iterable, Sequence
15
+
16
+ from metaobjects.codegen.generator import EmittedFile, GenContext, Generator
17
+ from metaobjects.codegen.template_codegen.output_pattern import expand_output_pattern
18
+ from metaobjects.codegen.template_codegen.template_data import (
19
+ build_entity_template_data,
20
+ build_model_template_data,
21
+ build_package_template_data,
22
+ bare_name,
23
+ is_concrete,
24
+ package_of,
25
+ )
26
+ from metaobjects.render import escapers
27
+ from metaobjects.render.renderer import RenderRequest, render
28
+ from metaobjects.render.verify import Provider
29
+
30
+ #: The three built-in walk scopes (SP-1 §3.1).
31
+ SCOPES = ("perEntity", "perPackage", "perModel")
32
+
33
+
34
+ def _scope_walk(scope: str, pattern: str) -> Callable[[Any], list[dict]]:
35
+ """Derive a walk from a built-in scope + output pattern. Each scope yields the
36
+ neutral data dict for its unit and names the file via the pattern."""
37
+
38
+ def walk(root: Any) -> list[dict]:
39
+ from metaobjects.meta.core.object.meta_object import MetaObject
40
+
41
+ objects = [c for c in root.children() if isinstance(c, MetaObject)]
42
+ concrete = [o for o in objects if is_concrete(o)]
43
+ if scope == "perEntity":
44
+ return [
45
+ {
46
+ "data": build_entity_template_data(o),
47
+ "output_path": expand_output_pattern(pattern, bare_name(o), package_of(o)),
48
+ }
49
+ for o in concrete
50
+ ]
51
+ if scope == "perPackage":
52
+ by_pkg: dict[str, list[Any]] = {}
53
+ for o in concrete:
54
+ by_pkg.setdefault(package_of(o), []).append(o)
55
+ return [
56
+ {
57
+ "data": build_package_template_data(pkg, by_pkg[pkg]),
58
+ "output_path": expand_output_pattern(pattern, None, pkg),
59
+ }
60
+ for pkg in sorted(by_pkg)
61
+ ]
62
+ # perModel — one file over the whole model.
63
+ return [
64
+ {
65
+ "data": build_model_template_data(objects),
66
+ "output_path": expand_output_pattern(pattern, None, None),
67
+ }
68
+ ]
69
+
70
+ return walk
71
+
72
+
73
+ @dataclass
74
+ class _TemplateGenerator:
75
+ name: str
76
+ template: str
77
+ walk: Callable[[Any], Sequence[dict]]
78
+ provider: Provider
79
+ format: str = escapers.FORMAT_TEXT
80
+
81
+ def generate(self, ctx: GenContext) -> list[EmittedFile]:
82
+ walk_results: Iterable[dict] = self.walk(ctx.loaded_root)
83
+ files: list[EmittedFile] = []
84
+ for entry in walk_results:
85
+ content = render(
86
+ RenderRequest(
87
+ payload=entry["data"],
88
+ provider=self.provider,
89
+ ref=self.template,
90
+ format=self.format,
91
+ )
92
+ )
93
+ files.append(EmittedFile(path=entry["output_path"], content=content))
94
+ return files
95
+
96
+
97
+ def template_generator(
98
+ *,
99
+ name: str,
100
+ template: str,
101
+ provider: Provider,
102
+ walk: Callable[[Any], Sequence[dict]] | None = None,
103
+ scope: str | None = None,
104
+ output_pattern: str | None = None,
105
+ format: str = escapers.FORMAT_TEXT,
106
+ ) -> Generator:
107
+ """Build a Generator that renders a Mustache template per walk entry.
108
+
109
+ Provide exactly one of ``walk`` (the power-user escape hatch) or
110
+ (``scope`` + ``output_pattern``) — the declarative built-in walk.
111
+
112
+ Args:
113
+ name: kebab-case identifier; surfaces in diagnostics.
114
+ template: ref resolved by the provider (e.g. "custom/hello").
115
+ provider: ref-resolver for the template.
116
+ walk: callback taking the loaded MetaRoot, returning dicts shaped
117
+ {"data": <payload>, "output_path": <relative path>}.
118
+ scope: built-in walk scope (perEntity/perPackage/perModel).
119
+ output_pattern: output path pattern for the built-in scope walk.
120
+ format: render format ("text", "html", "markdown", ...). Defaults to text.
121
+ """
122
+ has_walk = walk is not None
123
+ has_scope = scope is not None
124
+ if has_walk == has_scope:
125
+ raise ValueError(
126
+ f"template_generator({name}): provide exactly one of `walk` or "
127
+ "(`scope` + `output_pattern`)"
128
+ )
129
+ if has_scope and not output_pattern:
130
+ raise ValueError(f"template_generator({name}): `scope` requires `output_pattern`")
131
+ resolved_walk = walk if has_walk else _scope_walk(scope, output_pattern) # type: ignore[arg-type]
132
+ return _TemplateGenerator(
133
+ name=name,
134
+ template=template,
135
+ walk=resolved_walk,
136
+ provider=provider,
137
+ format=format,
138
+ )
@@ -0,0 +1,54 @@
1
+ """Expands the cross-port output-pattern grammar (SP-1 §3.3): ``{name}``,
2
+ ``{Name}`` (PascalCase), ``{package}`` (``::`` -> ``/``). An empty ``{package}``
3
+ collapses its trailing/leading slash so ``{package}/{name}`` with no package
4
+ yields just ``{name}``. Unknown placeholders raise.
5
+
6
+ Byte-equivalent to the TypeScript ``output-pattern.ts`` and the JVM
7
+ ``OutputPattern``.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+
14
+ _TOKEN = re.compile(r"\{(\w+)\}")
15
+
16
+
17
+ def _pascal(s: str) -> str:
18
+ return "".join(w[:1].upper() + w[1:] for w in re.split(r"[^A-Za-z0-9]+", s) if w)
19
+
20
+
21
+ def expand_output_pattern(
22
+ pattern: str, name: str | None, package: str | None
23
+ ) -> str:
24
+ """Expand ``pattern``. ``name`` may be ``None`` (perPackage/perModel);
25
+ ``package`` may be ``None``/empty."""
26
+ pkg_empty = False
27
+
28
+ def repl(m: re.Match[str]) -> str:
29
+ nonlocal pkg_empty
30
+ token = m.group(1)
31
+ if token == "package":
32
+ p = (package or "").replace("::", "/")
33
+ if p == "":
34
+ pkg_empty = True
35
+ return p
36
+ if token == "name":
37
+ if name is None:
38
+ raise ValueError(
39
+ f"output pattern {pattern!r} uses {{name}} but no entity name is in scope"
40
+ )
41
+ return name
42
+ if token == "Name":
43
+ if name is None:
44
+ raise ValueError(
45
+ f"output pattern {pattern!r} uses {{Name}} but no entity name is in scope"
46
+ )
47
+ return _pascal(name)
48
+ raise ValueError(f"unknown placeholder {{{token}}} in output pattern {pattern!r}")
49
+
50
+ out = _TOKEN.sub(repl, pattern)
51
+ if pkg_empty:
52
+ out = re.sub(r"^/+", "", out)
53
+ out = re.sub(r"/{2,}", "/", out)
54
+ return out
@@ -0,0 +1,119 @@
1
+ """The NEUTRAL, structural codegen template data dict (SP-1 §3.2) for Python.
2
+
3
+ Plain ``dict``/``list`` mirroring the TS ``EntityTemplateData`` keys EXACTLY — a
4
+ byte-gated cross-port contract (verified against the TS-produced
5
+ ``fixtures/template-codegen-conformance/expected/``). Optional keys
6
+ (``maxLength``, ``enumValues``) are OMITTED when absent so a ``{{#maxLength}}``
7
+ section gates identically to TS.
8
+
9
+ Own-vs-effective discipline (matching the TS ``ownAttr`` semantics, per the JVM
10
+ review): ``is_abstract`` per-node; ``@required`` attr + ``maxLength`` read from
11
+ ``own_attrs()`` (own-only); the required-*validator* branch effective; enum
12
+ ``values`` from ``attrs()`` (effective); effective package derived from
13
+ ``resolution_key()`` (matching the TS ``effectivePackage``).
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Any
19
+
20
+ from metaobjects.shared.base_types import (
21
+ TYPE_IDENTITY,
22
+ TYPE_RELATIONSHIP,
23
+ TYPE_VALIDATOR,
24
+ )
25
+ from metaobjects.shared.separators import PACKAGE_SEP
26
+
27
+ _SUBTYPE_ENUM = "enum"
28
+ _VALIDATOR_REQUIRED = "required"
29
+
30
+
31
+ def bare_name(o: Any) -> str:
32
+ """The object name. Python objects already carry the bare leaf name."""
33
+ return o.name
34
+
35
+
36
+ def package_of(o: Any) -> str:
37
+ """Effective package, matching the TS ``effectivePackage(resolutionKey)``.
38
+
39
+ Derived from ``resolution_key()`` (own ``package`` → ``file_default_package`` →
40
+ nearest ancestor's ``package``) by stripping the trailing ``::<name>`` suffix —
41
+ so programmatically-built / plugin trees (no ``file_default_package``, package
42
+ only on an ancestor) resolve the ancestor package like TS does. "" when the
43
+ resolution key carries no package segment.
44
+ """
45
+ key = o.resolution_key()
46
+ idx = key.rfind(PACKAGE_SEP)
47
+ return key[:idx] if idx >= 0 else ""
48
+
49
+
50
+ def is_concrete(o: Any) -> bool:
51
+ return not o.is_abstract
52
+
53
+
54
+ def _is_required(f: Any) -> bool:
55
+ if f.own_attrs().get(_VALIDATOR_REQUIRED) is True:
56
+ return True
57
+ return any(
58
+ c.type == TYPE_VALIDATOR and c.sub_type == _VALIDATOR_REQUIRED
59
+ for c in f.children()
60
+ )
61
+
62
+
63
+ def _field_data(f: Any) -> dict[str, Any]:
64
+ d: dict[str, Any] = {
65
+ "name": f.name,
66
+ "type": f.sub_type,
67
+ "required": _is_required(f),
68
+ "isArray": bool(f.is_array),
69
+ }
70
+ own = f.own_attrs()
71
+ if "maxLength" in own:
72
+ d["maxLength"] = int(own["maxLength"])
73
+ if f.sub_type == _SUBTYPE_ENUM:
74
+ values = f.attrs().get("values")
75
+ if isinstance(values, list):
76
+ d["enumValues"] = [str(v) for v in values]
77
+ return d
78
+
79
+
80
+ def build_entity_template_data(o: Any) -> dict[str, Any]:
81
+ fields = [_field_data(f) for f in o.fields()]
82
+ identities: list[dict[str, Any]] = []
83
+ relationships: list[dict[str, Any]] = []
84
+ for c in o.children():
85
+ if c.type == TYPE_IDENTITY:
86
+ id_fields = c.own_attrs().get("fields")
87
+ identities.append(
88
+ {"kind": c.sub_type, "fields": list(id_fields) if isinstance(id_fields, list) else []}
89
+ )
90
+ elif c.type == TYPE_RELATIONSHIP:
91
+ own = c.own_attrs()
92
+ relationships.append(
93
+ {
94
+ "name": c.name,
95
+ "cardinality": own.get("cardinality", "") or "",
96
+ "targetRef": own.get("objectRef", "") or "",
97
+ }
98
+ )
99
+ return {
100
+ "name": bare_name(o),
101
+ "package": package_of(o),
102
+ "fields": fields,
103
+ "identities": identities,
104
+ "relationships": relationships,
105
+ }
106
+
107
+
108
+ def build_package_template_data(pkg: str, entities: list[Any]) -> dict[str, Any]:
109
+ return {"package": pkg, "entities": [build_entity_template_data(o) for o in entities]}
110
+
111
+
112
+ def build_model_template_data(objects: list[Any]) -> dict[str, Any]:
113
+ by_pkg: dict[str, list[Any]] = {}
114
+ for o in objects:
115
+ if not is_concrete(o):
116
+ continue
117
+ by_pkg.setdefault(package_of(o), []).append(o)
118
+ packages = [build_package_template_data(p, by_pkg[p]) for p in sorted(by_pkg)]
119
+ return {"packages": packages}