metaobjects 0.9.0__py3-none-any.whl

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 (181) hide show
  1. metaobjects/__init__.py +75 -0
  2. metaobjects/agent_context/__init__.py +55 -0
  3. metaobjects/agent_context/_content/README.md +14 -0
  4. metaobjects/agent_context/_content/servers/csharp.meta.json +5 -0
  5. metaobjects/agent_context/_content/servers/java.meta.json +5 -0
  6. metaobjects/agent_context/_content/servers/kotlin.meta.json +5 -0
  7. metaobjects/agent_context/_content/servers/python.meta.json +5 -0
  8. metaobjects/agent_context/_content/servers/typescript.meta.json +5 -0
  9. metaobjects/agent_context/_content/skills/metaobjects-authoring/SKILL.md +301 -0
  10. metaobjects/agent_context/_content/skills/metaobjects-codegen/SKILL.md +99 -0
  11. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/csharp.md +87 -0
  12. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/java.md +94 -0
  13. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/kotlin.md +110 -0
  14. metaobjects/agent_context/_content/skills/metaobjects-codegen/references/typescript.md +135 -0
  15. metaobjects/agent_context/_content/skills/metaobjects-prompts/SKILL.md +148 -0
  16. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/csharp.md +110 -0
  17. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/java.md +108 -0
  18. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/kotlin.md +130 -0
  19. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/python.md +116 -0
  20. metaobjects/agent_context/_content/skills/metaobjects-prompts/references/typescript.md +150 -0
  21. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/SKILL.md +130 -0
  22. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/java.md +96 -0
  23. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/kotlin.md +99 -0
  24. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/react.md +86 -0
  25. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/tanstack.md +119 -0
  26. metaobjects/agent_context/_content/skills/metaobjects-runtime-ui/references/typescript.md +92 -0
  27. metaobjects/agent_context/_content/skills/metaobjects-verify/SKILL.md +107 -0
  28. metaobjects/agent_context/_content/skills/metaobjects-verify/references/migration.md +72 -0
  29. metaobjects/agent_context/_content/templates/always-on.md.mustache +27 -0
  30. metaobjects/agent_context/assemble.py +133 -0
  31. metaobjects/agent_context/content_root.py +54 -0
  32. metaobjects/agent_context/scaffold.py +191 -0
  33. metaobjects/agent_context/types.py +44 -0
  34. metaobjects/attr_class_map.py +23 -0
  35. metaobjects/cli.py +696 -0
  36. metaobjects/codegen/__init__.py +0 -0
  37. metaobjects/codegen/config.py +11 -0
  38. metaobjects/codegen/constants.py +13 -0
  39. metaobjects/codegen/extract_delegate_emitter.py +384 -0
  40. metaobjects/codegen/extract_schema_emitter.py +139 -0
  41. metaobjects/codegen/format.py +31 -0
  42. metaobjects/codegen/fr010_field_mapping.py +220 -0
  43. metaobjects/codegen/generator.py +62 -0
  44. metaobjects/codegen/generator_registry.py +163 -0
  45. metaobjects/codegen/generators/__init__.py +0 -0
  46. metaobjects/codegen/generators/entity_model.py +263 -0
  47. metaobjects/codegen/generators/extractor_generator.py +317 -0
  48. metaobjects/codegen/generators/filter_allowlist_generator.py +309 -0
  49. metaobjects/codegen/generators/m2m_codegen.py +192 -0
  50. metaobjects/codegen/generators/output_parser_generator.py +272 -0
  51. metaobjects/codegen/generators/output_prompt_generator.py +192 -0
  52. metaobjects/codegen/generators/payload_vo_generator.py +672 -0
  53. metaobjects/codegen/generators/render_helper_generator.py +451 -0
  54. metaobjects/codegen/generators/router_generator.py +635 -0
  55. metaobjects/codegen/generators/template_generator.py +70 -0
  56. metaobjects/codegen/generators/tph_plan.py +120 -0
  57. metaobjects/codegen/generators/trace_helper_generator.py +336 -0
  58. metaobjects/codegen/instance_artifacts.py +15 -0
  59. metaobjects/codegen/output_format_spec_emitter.py +79 -0
  60. metaobjects/codegen/overwrite_policy.py +27 -0
  61. metaobjects/codegen/runner.py +110 -0
  62. metaobjects/codegen/runtime/__init__.py +6 -0
  63. metaobjects/codegen/runtime/filter_parser.py +193 -0
  64. metaobjects/codegen/type_map.py +84 -0
  65. metaobjects/core_types.py +809 -0
  66. metaobjects/datatype.py +19 -0
  67. metaobjects/documentation/__init__.py +28 -0
  68. metaobjects/documentation/doc_constants.py +20 -0
  69. metaobjects/documentation/doc_provider.py +20 -0
  70. metaobjects/documentation/doc_schema.py +24 -0
  71. metaobjects/errors.py +124 -0
  72. metaobjects/loader/__init__.py +0 -0
  73. metaobjects/loader/merge.py +287 -0
  74. metaobjects/loader/meta_data_loader.py +245 -0
  75. metaobjects/loader/sources/__init__.py +24 -0
  76. metaobjects/loader/sources/directory_source.py +50 -0
  77. metaobjects/loader/sources/file_source.py +41 -0
  78. metaobjects/loader/sources/meta_data_source.py +67 -0
  79. metaobjects/loader/sources/uri_source.py +56 -0
  80. metaobjects/loader/validate_discriminator.py +181 -0
  81. metaobjects/loader/validate_field_readonly.py +146 -0
  82. metaobjects/loader/validate_source_parameter_ref.py +159 -0
  83. metaobjects/loader/validate_source_physical_names.py +140 -0
  84. metaobjects/loader/validation_passes.py +1513 -0
  85. metaobjects/meta/__init__.py +1 -0
  86. metaobjects/meta/core/__init__.py +0 -0
  87. metaobjects/meta/core/attr/__init__.py +0 -0
  88. metaobjects/meta/core/attr/attr_constants.py +31 -0
  89. metaobjects/meta/core/attr/meta_attr.py +136 -0
  90. metaobjects/meta/core/field/__init__.py +0 -0
  91. metaobjects/meta/core/field/field_constants.py +105 -0
  92. metaobjects/meta/core/field/meta_field.py +76 -0
  93. metaobjects/meta/core/identity/__init__.py +0 -0
  94. metaobjects/meta/core/identity/identity_constants.py +19 -0
  95. metaobjects/meta/core/identity/meta_identity.py +8 -0
  96. metaobjects/meta/core/object/__init__.py +0 -0
  97. metaobjects/meta/core/object/meta_object.py +65 -0
  98. metaobjects/meta/core/object/meta_object_aware.py +43 -0
  99. metaobjects/meta/core/object/object_class_registry.py +56 -0
  100. metaobjects/meta/core/object/object_constants.py +13 -0
  101. metaobjects/meta/core/object/object_extract.py +400 -0
  102. metaobjects/meta/core/object/value_object.py +70 -0
  103. metaobjects/meta/core/relationship/__init__.py +0 -0
  104. metaobjects/meta/core/relationship/derive_m2m_fields.py +180 -0
  105. metaobjects/meta/core/relationship/meta_relationship.py +54 -0
  106. metaobjects/meta/core/relationship/relationship_constants.py +51 -0
  107. metaobjects/meta/core/validator/__init__.py +0 -0
  108. metaobjects/meta/core/validator/validator_constants.py +18 -0
  109. metaobjects/meta/meta_data.py +206 -0
  110. metaobjects/meta/meta_root.py +8 -0
  111. metaobjects/meta/persistence/__init__.py +0 -0
  112. metaobjects/meta/persistence/db/__init__.py +1 -0
  113. metaobjects/meta/persistence/db/db_constants.py +41 -0
  114. metaobjects/meta/persistence/db/db_provider.py +60 -0
  115. metaobjects/meta/persistence/origin/__init__.py +0 -0
  116. metaobjects/meta/persistence/origin/meta_origin.py +8 -0
  117. metaobjects/meta/persistence/origin/origin_constants.py +20 -0
  118. metaobjects/meta/persistence/source/__init__.py +0 -0
  119. metaobjects/meta/persistence/source/meta_source.py +137 -0
  120. metaobjects/meta/persistence/source/source_constants.py +115 -0
  121. metaobjects/meta/presentation/__init__.py +0 -0
  122. metaobjects/meta/presentation/layout/__init__.py +0 -0
  123. metaobjects/meta/presentation/layout/layout_constants.py +13 -0
  124. metaobjects/meta/presentation/layout/meta_layout.py +8 -0
  125. metaobjects/meta/presentation/view/__init__.py +0 -0
  126. metaobjects/meta/presentation/view/meta_view.py +8 -0
  127. metaobjects/meta/presentation/view/view_constants.py +22 -0
  128. metaobjects/meta/template/__init__.py +0 -0
  129. metaobjects/meta/template/meta_template.py +46 -0
  130. metaobjects/meta/template/template_constants.py +112 -0
  131. metaobjects/meta/template/template_provider.py +43 -0
  132. metaobjects/parser.py +380 -0
  133. metaobjects/parser_yaml.py +82 -0
  134. metaobjects/provider.py +111 -0
  135. metaobjects/py.typed +0 -0
  136. metaobjects/registry.py +210 -0
  137. metaobjects/registry_manifest.py +223 -0
  138. metaobjects/render/__init__.py +74 -0
  139. metaobjects/render/email_document.py +14 -0
  140. metaobjects/render/escapers.py +109 -0
  141. metaobjects/render/extract/__init__.py +59 -0
  142. metaobjects/render/extract/coerce.py +279 -0
  143. metaobjects/render/extract/extract.py +211 -0
  144. metaobjects/render/extract/extract_map.py +61 -0
  145. metaobjects/render/extract/json_forgiving_reader.py +203 -0
  146. metaobjects/render/extract/locate.py +65 -0
  147. metaobjects/render/extract/normalize.py +96 -0
  148. metaobjects/render/extract/strip.py +20 -0
  149. metaobjects/render/extract/types.py +332 -0
  150. metaobjects/render/extract/xml_forgiving_reader.py +162 -0
  151. metaobjects/render/filesystem_provider.py +51 -0
  152. metaobjects/render/prompt/__init__.py +32 -0
  153. metaobjects/render/prompt/output_format_renderer.py +340 -0
  154. metaobjects/render/prompt/output_format_spec.py +28 -0
  155. metaobjects/render/prompt/prompt_field.py +29 -0
  156. metaobjects/render/prompt/prompt_overrides.py +29 -0
  157. metaobjects/render/prompt/prompt_style.py +38 -0
  158. metaobjects/render/renderer.py +358 -0
  159. metaobjects/render/verify.py +266 -0
  160. metaobjects/runtime/__init__.py +39 -0
  161. metaobjects/runtime/llm_recorder.py +210 -0
  162. metaobjects/runtime/n2m_resolver.py +155 -0
  163. metaobjects/runtime/object_manager.py +715 -0
  164. metaobjects/runtime/tph.py +50 -0
  165. metaobjects/serializer_json.py +172 -0
  166. metaobjects/shared/__init__.py +0 -0
  167. metaobjects/shared/base_types.py +16 -0
  168. metaobjects/shared/separators.py +4 -0
  169. metaobjects/shared/structural.py +9 -0
  170. metaobjects/source/__init__.py +79 -0
  171. metaobjects/source/error_source.py +266 -0
  172. metaobjects/source/json_path.py +106 -0
  173. metaobjects/source/semantic_diff.py +98 -0
  174. metaobjects/source/yaml_positions.py +174 -0
  175. metaobjects/super_resolve.py +128 -0
  176. metaobjects/yaml_desugar.py +481 -0
  177. metaobjects-0.9.0.dist-info/METADATA +97 -0
  178. metaobjects-0.9.0.dist-info/RECORD +181 -0
  179. metaobjects-0.9.0.dist-info/WHEEL +4 -0
  180. metaobjects-0.9.0.dist-info/entry_points.txt +2 -0
  181. metaobjects-0.9.0.dist-info/licenses/LICENSE +189 -0
@@ -0,0 +1,94 @@
1
+ # Java codegen specifics
2
+
3
+ The Java port targets Spring Boot consumers on Maven. Codegen runs as a build-time
4
+ Maven plugin goal — there is no standalone `meta` binary on the Java side (the Node
5
+ `meta` is for schema migrations only; see the migration reference).
6
+
7
+ ## Maven coordinates
8
+
9
+ All artifacts share `groupId` `com.metaobjects` and one version property:
10
+
11
+ | Artifact | Purpose |
12
+ |---|---|
13
+ | `metaobjects-metadata` | metamodel + loader |
14
+ | `metaobjects-omdb` | runtime persistence (ObjectManagerDb) |
15
+ | `metaobjects-render` | FR-004 render + `Verify` (prompt/template drift) |
16
+ | `metaobjects-codegen-spring` | the Spring codegen generators |
17
+ | `metaobjects-maven-plugin` | the build-time codegen plugin |
18
+ | `metaobjects-core-spring` | optional Spring wiring |
19
+
20
+ ## Plugin config in `pom.xml`
21
+
22
+ The plugin's `generate` goal binds to the `generate-sources` phase. Point its
23
+ `<loader><sourceDir>` at your metadata and list one `<generator>` per output you
24
+ want, by fully-qualified class name.
25
+
26
+ ```xml
27
+ <build>
28
+ <plugins>
29
+ <plugin>
30
+ <groupId>com.metaobjects</groupId>
31
+ <artifactId>metaobjects-maven-plugin</artifactId>
32
+ <version>${metaobjects.version}</version>
33
+ <executions>
34
+ <execution>
35
+ <id>generate</id>
36
+ <phase>generate-sources</phase>
37
+ <goals><goal>generate</goal></goals>
38
+ <configuration>
39
+ <loader>
40
+ <sourceDir>src/main/metaobjects</sourceDir>
41
+ </loader>
42
+ <generators>
43
+ <generator>
44
+ <classname>com.metaobjects.generator.spring.SpringControllerGenerator</classname>
45
+ <args>
46
+ <outputDir>${project.build.directory}/generated-sources/java</outputDir>
47
+ </args>
48
+ </generator>
49
+ <generator>
50
+ <classname>com.metaobjects.generator.spring.SpringDtoGenerator</classname>
51
+ <args><outputDir>${project.build.directory}/generated-sources/java</outputDir></args>
52
+ </generator>
53
+ <generator>
54
+ <classname>com.metaobjects.generator.spring.SpringRepositoryGenerator</classname>
55
+ <args><outputDir>${project.build.directory}/generated-sources/java</outputDir></args>
56
+ </generator>
57
+ </generators>
58
+ </configuration>
59
+ </execution>
60
+ </executions>
61
+ </plugin>
62
+ </plugins>
63
+ </build>
64
+ ```
65
+
66
+ ## Run
67
+
68
+ ```bash
69
+ mvn metaobjects:generate # the codegen goal directly (goalPrefix=metaobjects, goal=generate)
70
+ mvn compile # also runs it (the goal is bound to generate-sources)
71
+ ```
72
+
73
+ A `metaobjects:verify` Maven goal exists for **codegen-drift** (re-generate and diff vs
74
+ committed output). Schema migration and live-DB drift are NOT Java goals — they run
75
+ through the Node `meta` tool (see the migration reference).
76
+
77
+ ## `codegen-spring` generators
78
+
79
+ All live in `metaobjects-codegen-spring` under
80
+ `com.metaobjects.generator.spring.*`; wire any subset, typically all three of the
81
+ first group together:
82
+
83
+ | Generator | Output |
84
+ |---|---|
85
+ | `SpringControllerGenerator` | `<Entity>Controller.java` per writable entity (`source.rdb` `@kind="table"`) — Spring Web MVC, five CRUD endpoints on the cross-port REST contract (`?sort`, `?limit`/`?offset`, `?withCount=1` envelope, 404/400 envelopes) |
86
+ | `SpringDtoGenerator` | `<Entity>Dto.java` as a Java 21 `record`; wrapped primitives (`Long`/`Integer`/`Boolean`) so missing JSON props deserialise to `null`; currency = `Long` (integer minor units) |
87
+ | `SpringRepositoryGenerator` | `<Entity>Repository.java` — a hand-stubbed `interface` the consumer implements with their persistence layer (Spring Data JPA / jOOQ / JDBC) |
88
+ | `SpringPayloadGenerator` | a Java 21 `record` per template payload VO |
89
+ | `SpringOutputParserGenerator` | the `template.output` parser-on-receipt (see the prompts reference) |
90
+ | `SpringFilterAllowlistGenerator` | per-entity filter allowlist |
91
+
92
+ Metadata lives under `src/main/metaobjects/` in the same canonical JSON the other
93
+ ports read — fused-key form, `source.rdb` + `@table`, `@column` for a renamed
94
+ physical column.
@@ -0,0 +1,110 @@
1
+ # Kotlin codegen specifics
2
+
3
+ The Kotlin port is a **codegen tier built on top of the Java port**. The loader,
4
+ render engine, and Maven plugin are all Java; `codegen-kotlin` emits idiomatic
5
+ Kotlin (`@Serializable data class`, Exposed `Table` objects, Spring
6
+ `@RestController`/`@Configuration`) via KotlinPoet. Codegen runs as the same
7
+ build-time Maven plugin goal the Java port uses — there is no standalone `meta`
8
+ binary on the JVM side (the Node `meta` is for schema migrations only; see the
9
+ migration reference).
10
+
11
+ ## Contents
12
+ - Maven coordinates
13
+ - Plugin config in `pom.xml`
14
+ - Run
15
+ - `codegen-kotlin` generators
16
+
17
+ ## Maven coordinates
18
+
19
+ All artifacts share `groupId` `com.metaobjects` and one version property:
20
+
21
+ | Artifact | Purpose |
22
+ |---|---|
23
+ | `metaobjects-metadata` | metamodel + loader |
24
+ | `metaobjects-metadata-ktx` | thin Kotlin facade over the Java loader + render engine |
25
+ | `metaobjects-render` | render + `Verify` + tolerant `extract` (prompt/template) |
26
+ | `metaobjects-codegen-kotlin` | the Kotlin (KotlinPoet) generators |
27
+ | `metaobjects-maven-plugin` | the build-time codegen plugin |
28
+
29
+ Generated entities and Exposed tables need the consumer's own runtime deps —
30
+ `org.jetbrains.exposed:exposed-core` and
31
+ `org.jetbrains.kotlinx:kotlinx-serialization-json` (the latter for
32
+ `@Serializable`/parser output).
33
+
34
+ ## Plugin config in `pom.xml`
35
+
36
+ The plugin's `generate` goal binds to the `generate-sources` phase. Point its
37
+ `<loader><sourceDir>` at your metadata and list one `<generator>` per output you
38
+ want, by fully-qualified class name. The Kotlin generators run through the same
39
+ Java plugin via the shared SPI:
40
+
41
+ ```xml
42
+ <build>
43
+ <plugins>
44
+ <plugin>
45
+ <groupId>com.metaobjects</groupId>
46
+ <artifactId>metaobjects-maven-plugin</artifactId>
47
+ <version>${metaobjects.version}</version>
48
+ <executions>
49
+ <execution>
50
+ <id>generate</id>
51
+ <phase>generate-sources</phase>
52
+ <goals><goal>generate</goal></goals>
53
+ <configuration>
54
+ <loader>
55
+ <sourceDir>src/main/metaobjects</sourceDir>
56
+ </loader>
57
+ <generators>
58
+ <generator>
59
+ <classname>com.metaobjects.generator.kotlin.KotlinEntityGenerator</classname>
60
+ <args><outputDir>${project.build.directory}/generated-sources/kotlin</outputDir></args>
61
+ </generator>
62
+ <generator>
63
+ <classname>com.metaobjects.generator.kotlin.KotlinExposedTableGenerator</classname>
64
+ <args><outputDir>${project.build.directory}/generated-sources/kotlin</outputDir></args>
65
+ </generator>
66
+ <generator>
67
+ <classname>com.metaobjects.generator.kotlin.KotlinSpringControllerGenerator</classname>
68
+ <args><outputDir>${project.build.directory}/generated-sources/kotlin</outputDir></args>
69
+ </generator>
70
+ </generators>
71
+ </configuration>
72
+ </execution>
73
+ </executions>
74
+ </plugin>
75
+ </plugins>
76
+ </build>
77
+ ```
78
+
79
+ ## Run
80
+
81
+ ```bash
82
+ mvn metaobjects:generate # the codegen goal directly (goalPrefix=metaobjects, goal=generate)
83
+ mvn compile # also runs it (the goal is bound to generate-sources)
84
+ ```
85
+
86
+ A `metaobjects:verify` Maven goal exists for **codegen-drift** (re-generate and diff
87
+ vs committed output). Schema migration and live-DB drift are NOT JVM goals — they run
88
+ through the Node `meta` tool (see the migration reference).
89
+
90
+ ## `codegen-kotlin` generators
91
+
92
+ All live in `metaobjects-codegen-kotlin` under
93
+ `com.metaobjects.generator.kotlin.*`; wire the subset you need by FQ class name:
94
+
95
+ | Generator | Output |
96
+ |---|---|
97
+ | `KotlinEntityGenerator` | `<Entity>.kt` — `@Serializable data class` per `object.entity` / `object.value` |
98
+ | `KotlinExposedTableGenerator` | `<Entity>Table.kt` — Exposed `Table` object (PK + FK + `@storage` columns) for entities with `source.rdb` |
99
+ | `KotlinRelationsGenerator` | `<Entity>Relations.kt` — extension fns for `@cardinality="many"` query helpers |
100
+ | `KotlinSpringControllerGenerator` | `<Entity>Controller.kt` — Spring `@RestController`, five CRUD endpoints on the cross-port REST contract, for writable entities (`source.rdb` `@kind="table"`) |
101
+ | `KotlinPayloadGenerator` | `<Template>Payload.kt` — `@Serializable` payload data class from a template's `@payloadRef` |
102
+ | `KotlinOutputParserGenerator` | the `template.output` parser-on-receipt (see the prompts reference) |
103
+ | `KotlinValidatorGenerator` | `MetadataStartupValidator.kt` + `ExposedTableValidator.kt` (once per project) |
104
+ | `KotlinSpringConfigGenerator` | `MetadataExposedConfig.kt` — `@Configuration` wiring `Database.connect()` + the startup validator (once per project) |
105
+ | `KotlinStoredProcGenerator` | stored-procedure call wrappers for `source.rdb` `@kind="storedProc"` |
106
+ | `KotlinFilterAllowlistGenerator` | per-entity filter allowlist |
107
+
108
+ Metadata lives under `src/main/metaobjects/` in the same canonical JSON the other
109
+ ports read — fused-key form, `source.rdb` + `@table`, `@column` for a renamed
110
+ physical column.
@@ -0,0 +1,135 @@
1
+ # TypeScript codegen specifics
2
+
3
+ The TS port is the reference implementation, published to npm as `@metaobjectsdev/*`
4
+ packages. Codegen runs through the Node `meta` CLI (`@metaobjectsdev/cli`, binary
5
+ `meta`).
6
+
7
+ ## Contents
8
+ - Install
9
+ - `metaobjects.config.ts`
10
+ - The generators
11
+ - Run
12
+ - Multiple output targets
13
+ - Field subtype → column mapping
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install --save-dev @metaobjectsdev/cli @metaobjectsdev/codegen-ts
19
+ npm install @metaobjectsdev/metadata @metaobjectsdev/runtime-ts
20
+ ```
21
+
22
+ For the React + TanStack codegen packages, also:
23
+
24
+ ```bash
25
+ npm install --save-dev @metaobjectsdev/codegen-ts-react @metaobjectsdev/codegen-ts-tanstack
26
+ ```
27
+
28
+ ## `metaobjects.config.ts`
29
+
30
+ Codegen is wired in a type-checked TS config at the project root. `defineConfig`
31
+ comes from `@metaobjectsdev/cli`; the generators come from their packages.
32
+
33
+ ```ts
34
+ import { defineConfig } from "@metaobjectsdev/cli";
35
+ import { entityFile, queriesFile, routesFile, barrel } from "@metaobjectsdev/codegen-ts/generators";
36
+ import { formFile } from "@metaobjectsdev/codegen-ts-react";
37
+ import { tanstackQuery, tanstackGrid } from "@metaobjectsdev/codegen-ts-tanstack";
38
+
39
+ export default defineConfig({
40
+ outDir: "src/generated",
41
+ dialect: "postgres", // "postgres" | "sqlite" | "d1" (D1 is TS-only)
42
+ apiPrefix: "/api", // flows to routes AND client fetch URLs
43
+ columnNamingStrategy: "snake_case", // "snake_case" (default) | "literal" | "kebab-case"
44
+ generators: [
45
+ entityFile(), queriesFile(), routesFile(), barrel(),
46
+ formFile(), tanstackQuery(), tanstackGrid(),
47
+ ],
48
+ });
49
+ ```
50
+
51
+ A second file, `.metaobjects/config.json`, holds static project state parseable by
52
+ non-TS tooling; `meta init` scaffolds both plus the `metaobjects/` source dir.
53
+
54
+ ## The generators
55
+
56
+ From `@metaobjectsdev/codegen-ts/generators` (server-side, framework-neutral):
57
+
58
+ | Generator | Emits per entity |
59
+ |---|---|
60
+ | `entityFile()` | `<Entity>.ts` — Drizzle table + FK `.references()` + `relations()` + inferred types + Zod insert/update schemas + `<Entity>FilterAllowlist` / `<Entity>SortAllowlist` |
61
+ | `queriesFile()` | `<Entity>.queries.ts` — typed CRUD (`findPostById`, `listPosts`, `createPost`, `updatePost`, `deletePostById`) |
62
+ | `routesFile()` | `<Entity>.routes.ts` — Fastify CRUD routes on the cross-port REST contract. `routesFileHono()` is the Hono/Workers variant |
63
+ | `barrel()` | `index.ts` re-exporting each `<Entity>.ts` (one-shot, not per-entity) |
64
+ | `promptRender()` | `render<Name>()` per `template.prompt` |
65
+ | `outputParser()` | `<Name>.output.ts` (`parse*` / `safeParse*`) per `template.output` |
66
+
67
+ ## Docs — `meta docs` (one door, two surfaces)
68
+
69
+ Documentation is NOT a `meta gen` generator. The single door is the `meta docs`
70
+ command, which emits two cross-linked **surfaces** under one output dir (default
71
+ `./docs`):
72
+
73
+ - **model surface** (`./docs/<Entity>.md`, `./docs/<Template>.md`) — the neutral
74
+ metadata reference: one page per entity and per template, including the linked
75
+ template-source section.
76
+ - **api surface** (`./docs/api/<Entity>.md`, `./docs/api/README.md`,
77
+ `./docs/api/AGENT-API.md`) — the SDK/API reference: the concrete imports,
78
+ function signatures, payload field shapes, and runnable examples for *this*
79
+ project's generated code.
80
+
81
+ ```bash
82
+ npx meta docs # both surfaces → ./docs (model) + ./docs/api (api)
83
+ npx meta docs --model # model surface only
84
+ npx meta docs --api # api surface only
85
+ npx meta docs --out ./site-docs # write under a different root
86
+ ```
87
+
88
+ Other flags: `--layout flat|package`, `--base-url <url>`. Configure defaults in a
89
+ `docs:` block in `metaobjects.config.ts` (`outDir`, `layout`, `baseUrl`,
90
+ `surfaces`); CLI flags override it. The api surface needs the gen config
91
+ (it documents what the codegen produced); with no config it is skipped with a note,
92
+ and the model surface still emits from metadata alone.
93
+
94
+ **Before calling any generated code, read `./docs/api/AGENT-API.md`** — it has the
95
+ exact imports, signatures, payload field shapes, and runnable examples for this
96
+ project's generated API, so you don't have to guess them.
97
+
98
+ From `@metaobjectsdev/codegen-ts-react`: `formFile()` → `<Entity>.form.tsx`.
99
+ From `@metaobjectsdev/codegen-ts-tanstack`: `tanstackQuery()` → `<Entity>.hooks.ts`
100
+ (5 React Query hooks), `tanstackGrid()` → `<Entity>.columns.tsx`,
101
+ `tanstackGridHook()` → `<Entity>.grid.tsx`.
102
+
103
+ `entityFile({ allowlists: false })` drops the `runtime-ts/drizzle-fastify` import
104
+ for edge/worker consumers that don't mount server routes. Per-entity opt-out:
105
+ `@emitTanstack: false` on the entity skips its hook + column files.
106
+
107
+ ## Run
108
+
109
+ ```bash
110
+ npx meta gen # load metadata → render → 3-way merge → write
111
+ npx meta gen --dry-run # preview without writing
112
+ npx meta gen --watch # re-run on metadata file change
113
+ npx meta gen Author Post # scope to named entities
114
+ ```
115
+
116
+ Generated files carry an `@generated by @metaobjectsdev/codegen-ts` header; the
117
+ runner overwrites those and refuses to touch files without it. Hand-customizations
118
+ that metadata can't express live in sibling `<Entity>.extra.ts` files.
119
+
120
+ ## Multiple output targets
121
+
122
+ A `targets: { web: { outDir }, api: { outDir } }` registry plus a per-generator
123
+ `target` routes each artifact to its own package (model → database package, routes →
124
+ API app, hooks/forms → web app). The top-level `outDir` is the implicit `default`
125
+ (entity-module) target; set `entityModuleImportBase` on it when generators route
126
+ elsewhere so cross-target imports resolve. With no `targets`, output is
127
+ byte-identical to a single-`outDir` project.
128
+
129
+ ## Field subtype → column mapping
130
+
131
+ Deterministic per dialect: `field.string` + `@maxLength` → `varchar(N)`,
132
+ `field.currency` → integer minor units (`bigint`), `field.uuid` → native `uuid`
133
+ (Postgres) + `gen_random_uuid()`, `field.enum` → `varchar` + `CHECK`. Override a
134
+ field's physical column name with `@column` on the field; the DB schema name lives
135
+ on `source.rdb` via `@schema`.
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: metaobjects-prompts
3
+ description: Use when declaring or using MetaObjects prompt construction: template.prompt/template.output, typed payload projections, provider-resolved text, deterministic render, prompt-drift verify, and parser-on-receipt.
4
+ ---
5
+
6
+ # MetaObjects prompt construction
7
+
8
+ The fourth pillar: **a prompt is code, not a string scattered across services.**
9
+ Declare a prompt's payload as a typed projection (payload bloat becomes a diff),
10
+ keep its text external and provider-resolved, and render it deterministically —
11
+ snapshot-testable, cache-stable, and drift-checked at build time. The same
12
+ machinery renders any text artifact: emails, exports, docs, `llms.txt`.
13
+
14
+ This skill is port-agnostic. The exact render/parse API for *this* project's server
15
+ language lives in a reference fragment (pointed to at the bottom).
16
+
17
+ ## The two template subtypes
18
+
19
+ A **template** is a typed pair: a logical reference to external text + a payload
20
+ value-object declaring exactly what data the text expects.
21
+
22
+ | Subtype | Use | Extra attrs |
23
+ |---|---|---|
24
+ | `template.prompt` | LLM-targeted | `@maxTokens`, `@requiredSlots`, `@model` |
25
+ | `template.output` | email / docs / config / export | (generic only) |
26
+
27
+ Both carry the generic attrs:
28
+
29
+ | Attr | Required | Purpose |
30
+ |---|---|---|
31
+ | `@payloadRef` | yes | the `object.value` declaring the payload shape |
32
+ | `@textRef` | yes | the 2-layer logical text reference `group/source`, resolved by a provider |
33
+ | `@format` | no | `text` (default) / `html` / `xml` / `csv` / `json` / `markdown` / `spreadsheet` — drives the escaper |
34
+ | `@maxChars` | no | build-time size budget |
35
+
36
+ ## The payload is an `object.value` projection
37
+
38
+ The payload is **not** an entity — it's an `object.value` whose every field carries
39
+ an `origin.*` child saying where its value comes from. Three origin subtypes:
40
+
41
+ | Origin | Behavior |
42
+ |---|---|
43
+ | `origin.passthrough @from "Entity.field"` | payload property matches the source field |
44
+ | `origin.aggregate @agg <count\|sum\|avg\|min\|max>` | `count`→long, `avg`→double, others match source |
45
+ | `origin.collection @via "Parent.rel"` | a list of a nested payload, assembled from a relationship |
46
+
47
+ Declaring the payload as a projection is what makes payload bloat visible: adding a
48
+ field to the prompt is a diff on the `object.value`, and a renamed source field
49
+ breaks the build instead of silently degrading the prompt.
50
+
51
+ ```json
52
+ {
53
+ "metadata.root": {
54
+ "package": "acme::blog",
55
+ "children": [
56
+ {
57
+ "object.value": {
58
+ "name": "WelcomePayload",
59
+ "children": [
60
+ { "field.string": { "name": "displayName",
61
+ "children": [ { "origin.passthrough": { "@from": "Author.name" } } ] } },
62
+ { "field.long": { "name": "postCount",
63
+ "children": [ { "origin.aggregate": { "@agg": "count", "@of": "Post.id", "@via": "Author.posts" } } ] } },
64
+ { "field.object": { "name": "posts", "@objectRef": "PostSummary",
65
+ "children": [ { "origin.collection": { "@via": "Author.posts" } } ] } }
66
+ ]
67
+ }
68
+ },
69
+ {
70
+ "object.value": {
71
+ "name": "PostSummary",
72
+ "children": [
73
+ { "field.string": { "name": "title",
74
+ "children": [ { "origin.passthrough": { "@from": "Post.title" } } ] } }
75
+ ]
76
+ }
77
+ },
78
+ {
79
+ "template.prompt": {
80
+ "name": "WelcomePrompt",
81
+ "@payloadRef": "WelcomePayload",
82
+ "@textRef": "lobby/welcome",
83
+ "@format": "xml",
84
+ "@maxTokens": 500
85
+ }
86
+ }
87
+ ]
88
+ }
89
+ }
90
+ ```
91
+
92
+ ## Prompt text is external + provider-resolved (never inlined)
93
+
94
+ `@textRef` is a 2-layer logical reference `group/source` (folder/file,
95
+ table/key, collection/document). The prompt text itself **never lives in
96
+ metadata** — at runtime a configured **provider** resolves the reference to the
97
+ actual Mustache text:
98
+
99
+ - a filesystem provider (L1 = folder, L2 = file) — the dev default;
100
+ - an in-memory provider (a string map) — tests;
101
+ - a classpath/resource provider on the JVM;
102
+ - or a consumer-supplied provider (RDB / vector store / …).
103
+
104
+ Locale, A/B, dynamic, and evolutionary prompt variants all live behind the
105
+ provider seam without touching metadata.
106
+
107
+ ## `render()` is deterministic + byte-stable
108
+
109
+ Rendering is a pure function: `(payload VO, resolved text) → string`. Same inputs,
110
+ byte-identical output — across runs and across every language port. That stability
111
+ is what protects **exact-prefix prompt-cache hits**: a stray whitespace change can't
112
+ silently break a cache prefix because the output doesn't drift. Determinism rules
113
+ the engine enforces:
114
+
115
+ - arrays only for iteration (no object-key iteration);
116
+ - no locale/number/date formatting in the engine — pre-format on the payload;
117
+ - pinned trailing-newline + Mustache standalone-tag whitespace;
118
+ - `@format` drives an engine-owned escaper (CSV/spreadsheet escapers neutralize a
119
+ leading `= + - @ \t \r` per the OWASP CSV-injection guard).
120
+
121
+ For the `xml`-format example above with payload `{ displayName: "Ada", postCount:
122
+ 12, posts: [{title:"Hello"}, {title:"Mustache"}] }`, every port renders the same
123
+ bytes. You render the prompt, call your LLM client (provider-agnostic — codegen
124
+ emits no provider-side schema), then parse the response.
125
+
126
+ ## `verify` fails the build on prompt-drift
127
+
128
+ For every template, the verify step resolves the text, parses each `{{...}}`
129
+ reference, and checks it exists on the payload VO. If the text references
130
+ `{{authorName}}` but the payload only has `displayName`, **the build fails.** This
131
+ is the prompt-vs-payload drift gate — run it in CI. It walks both `template.prompt`
132
+ and `template.output` nodes the same way.
133
+
134
+ ## `template.output` also generates a parser-on-receipt
135
+
136
+ For every `template.output`, codegen emits a **typed parser** that turns an LLM/raw
137
+ response back into the `@payloadRef` value-object — the reverse direction, reusing
138
+ the same payload VO (no new authoring). Each port emits it idiomatically: a
139
+ throw-on-invalid parse plus, where the language has the precedent, a Result-style
140
+ "safe" variant that doesn't throw. The parser file is a companion to the payload-VO
141
+ file; `verify` catches payload-VO ↔ parser drift at build time too.
142
+
143
+ The three-step consumer pattern is identical everywhere: render the prompt → call
144
+ your LLM client → parse the response with the generated parser.
145
+
146
+ ---
147
+
148
+ For this project's server-language parser specifics, read every `references/*.md` file in this skill's directory (one per server language in this project's stack).
@@ -0,0 +1,110 @@
1
+ # C# parser-on-receipt
2
+
3
+ For every `template.output`, `MetaObjects.Codegen`'s `OutputParserGenerator` emits a
4
+ **typed parser** that validates an LLM/raw response against the template's
5
+ `@payloadRef` payload record. This is the receive side only — codegen emits **no**
6
+ provider/LLM-call layer; you compose the call yourself. The payload record comes from
7
+ the payload generator, so the parser and the payload VO can't silently drift.
8
+
9
+ ## Contents
10
+ - Wire the generator
11
+ - What it emits
12
+ - The three-step consumer pattern
13
+ - Recommended LLM caller (bring-your-own)
14
+ - Consumer dependency
15
+ - Drift gate
16
+
17
+ ## Wire the generator
18
+
19
+ `OutputParserGenerator` (stable name `output-parser`) runs as part of
20
+ `dotnet meta gen`, alongside the payload generator that emits the record it parses
21
+ into:
22
+
23
+ ```bash
24
+ dotnet meta gen ./metadata --out ./Generated --namespace Acme.Blog
25
+ ```
26
+
27
+ ## What it emits
28
+
29
+ Per `template.output`, `dotnet meta gen` writes one `<TemplateName>.output.cs` with a
30
+ static `<TemplateName>Parser` following the .NET BCL `Parse`/`TryParse` dual API —
31
+ `Parse` throws, `TryParse` returns a bool plus an out-error:
32
+
33
+ ```csharp
34
+ // generated <TemplateName>.output.cs (shape)
35
+ public static class NpcResponseParser
36
+ {
37
+ /// <exception cref="JsonException">malformed JSON or schema mismatch.</exception>
38
+ public static NpcResponse Parse(string text) =>
39
+ JsonSerializer.Deserialize<NpcResponse>(text, Options)
40
+ ?? throw new JsonException("deserialized to null");
41
+
42
+ public static bool TryParse(string text,
43
+ [NotNullWhen(true)] out NpcResponse? value,
44
+ [NotNullWhen(false)] out string? error) { /* ... */ }
45
+ }
46
+ ```
47
+
48
+ The `[NotNullWhen]` attrs let nullable-flow analysis use `value` without a null-check
49
+ after a `true` return (and `error` after `false`). For `@format: json|xml` outputs the
50
+ generator also emits a tolerant `Extract(string[, ExtractOptions])` (self-contained) +
51
+ `Extract(MetaObject, string, ...)` (runtime-delegating, fully populating nested
52
+ components) returning an `ExtractionResult` with a nullable `<Payload>Extracted` mirror
53
+ — a classified per-field report rather than a throw.
54
+
55
+ ## The three-step consumer pattern
56
+
57
+ Render the prompt → call your LLM client (provider-agnostic; nothing is generated
58
+ here) → parse the response with the generated parser:
59
+
60
+ ```csharp
61
+ string llmResponse = await myLlmClient.CompleteAsync(promptText); // YOUR code — no generated provider
62
+
63
+ // Throwing path
64
+ var npc = NpcResponseParser.Parse(llmResponse);
65
+
66
+ // TryParse for explicit error handling
67
+ if (NpcResponseParser.TryParse(llmResponse, out var npc, out var error))
68
+ return Ok(npc);
69
+ return BadRequest(new { error });
70
+ ```
71
+
72
+ ## Recommended LLM caller (bring-your-own)
73
+
74
+ `dotnet meta gen` emits **no** provider/LLM-call layer and never will — calling is a
75
+ commodity the ecosystem already solves (ADR-0024). You bring the caller; MetaObjects
76
+ owns the typed render → parse (above) → record. For the call step use the idiomatic
77
+ .NET library:
78
+
79
+ ```csharp
80
+ using Microsoft.Extensions.AI; // recommended — IChatClient is provider-agnostic
81
+
82
+ IChatClient client = /* an Anthropic / OpenAI / Azure IChatClient */;
83
+ ChatResponse resp = await client.GetResponseAsync(promptText);
84
+ string text = resp.Text;
85
+
86
+ var npc = NpcResponseParser.Parse(text); // the generated parser, above
87
+ ```
88
+
89
+ **Recommended: Microsoft.Extensions.AI** (`IChatClient`) — the emerging
90
+ provider-agnostic .NET standard; its one-method shape matches MetaObjects' call seam.
91
+ For higher-level agent orchestration, **Semantic Kernel** layers on top of the same
92
+ `IChatClient`.
93
+
94
+ > The typed-trace recorder + render→call→record convenience loop ship in TypeScript
95
+ > today; the C# port is planned (ADR-0024). Until then the call is your code and the
96
+ > generated parser is the typed receive side.
97
+
98
+ ## Consumer dependency
99
+
100
+ `System.Text.Json` ships in the .NET 8 BCL — no NuGet to add. The generated parser
101
+ uses strict (case-sensitive) options.
102
+
103
+ ## Drift gate
104
+
105
+ `MetaObjects.Render`'s `Verify.Check(string templateText, IReadOnlyList<PayloadField>
106
+ fields, VerifyOptions? options = null) -> IReadOnlyList<VerifyError>` walks a Mustache
107
+ template's tokens against the payload field tree — each `{{...}}` that doesn't resolve
108
+ yields a `VerifyError(Code, Path)` (empty list = no drift). Assert it is empty in an
109
+ xUnit test to fail the build on prompt/payload drift; `dotnet meta verify` additionally
110
+ catches a stale committed parser.