Repository navigation
fix!: generated code survives colliding names or refuses them clearly - #210
Merged
Merged
Conversation
The resource model is built for every schema, and its rules (a String id, legal and distinct member names, relation targets in the schema) are checked only when the build generates an HTTP server or an HTTP TypeScript client. An IPC-only or MCP-only build, with or without the admin registry, accepts a relation named `type` or `_tag`. A refused member name now states the whole JSON:API rule.
A generated IPC or MCP file brings into scope bare only the consumer's API modules, the types its fns name, its state and its store. Runtime items are written by `::`-rooted paths, so an entity named `Value`, `State`, `Arc` or `Future` no longer collides with an import and a module named `log` shadows no crate. The MCP handler alias is `OntogenHandlerFn`; the items public in 0.8.0 keep their names. A module named after a Rust keyword is written raw (`r#match`) in imports and calls, and a type named like a std container without arguments (`Arc`) is imported as the consumer's own. An MCP tool reads an argument named after a keyword under serde's name for it (`type` for `r#type`), the key its input schema advertises, and names it so in its errors. A tool description is an escaped literal, so a doc that quotes compiles. BREAKING CHANGE: an MCP tool argument named after a Rust keyword is read under the key without `r#` (`type`, not `r#type`), as its input schema already said.
- The IPC transport and the IPC wire-key checks derive each argument's invoke key as Tauri does: the name without its `r#`, lowerCamelCased by heck (`_kind` is `kind`, `r#type` is `type`), so `sort`/`_sort` and `project_id`/`_project_id` are refused as one key. - TS parameters are named by that key, with a trailing `_` for a reserved word or a name a method body calls; HTTP calls send each argument under its serde name, IPC calls under its invoke key. - The bindings, the admin registry and long-tail types name a raw field as serde writes it (`loop`, not `r#loop`). - The clients write `Error`, `Event`, `EventSource`, `Map`, `MessageEvent` and `Response` through `globalThis` and import Tauri's `Channel` as `IpcChannel`, so entities may take those names; a type named after another global or a declaration the client makes is refused. - `just test` type-checks the name-safety fixture's TypeScript (HTTP client, transport, bindings, admin registry) under `tsc --strict`.
The generated HTTP file names nothing bare but the consumer's types and API modules. Runtime types are imported under Ontogen aliases, runtime fns, modules and std/serde items are named by path, and every helper the file defines is Ontogen- or ontogen_-prefixed. Entities named Document, Response, State or Method and API modules named request or response no longer clash with the runtime's imports. Module and relationship names that are Rust keywords are written as raw idents (r#match::list, linked.r#ref), and an API fn parameter written r#type is read from the wire as type. Attribute types are named through types_import_path. BREAKING CHANGE: the generated HTTP file's pub PaginatedResult is now the private OntogenPaginatedResult.
# Conflicts: # src/servers/generators/mod.rs # src/servers/parse.rs
# Conflicts: # site/src/content/docs/guides/server-transports.mdx
…name
Entities named like the generator's own locals or imports (`Doc`, `Order`),
after a Rust keyword (`Match`), and keyword relationships (`r#in`,
`r#loop`) now generate a store, DTOs, persistence and API forwarders that
compile on both backends.
- The store binds the record as `record`, never under the entity's snake
name, so `Doc` no longer shadows the markdown store's vault document and
a field named like the entity no longer shadows the record in `apply`.
Create reads relationship lists from the record instead of copying them
into field-named locals.
- The SeaORM store imports its traits unnamed and writes `Select`,
`sea_query::Order` and `sea_query::NullOrdering` by path, so an entity
named `Order` or `Select` no longer clashes.
- Module paths and `pub mod` lines use raw identifiers (`r#match`); file,
table, column and directory names stay bare. Junction tables, relation
and column variants, `{field}_changed` flags and NaN/range messages use
a keyword field's bare name.
- The SeaORM set-parent `UPDATE` quotes its identifiers, so a keyword
foreign key (`in`) is valid SQL.
- API forwarders call `store.create_x(X::from(input))` with no local.
- The schema parser refuses an entity whose snake name is `crate`,
`self`, `super` or `Self`, and reads `foreign_key = "in"` as `r#in`.
- The markdown populate for an entity with only many_to_many lists no
longer emits `let _ = &*self`, which clippy rejects.
The parity schema gains `Doc`, `Order` and `Match` so both backends'
generated code is compiled, and a runtime parity scenario drives them.
# Conflicts: # crates/markdown-pilot/src/api/transport/http/generated.rs # crates/markdown-pilot/src/api/transport/http_scoped/generated.rs # examples/iron-log-md/src/api/transport/http/generated.rs # examples/iron-log/src-tauri/src/api/transport/http/generated.rs # examples/notes-kb/src/api/transport/http/generated.rs # examples/tasks-tracker/src/api/transport/http/generated.rs # site/src/content/docs/reference/upgrading.mdx # src/servers/generators/http.rs # src/servers/generators/http/relationship.rs # src/servers/tests.rs # src/snapshots/ontogen__snapshots__servers_jsonapi_ops_http.snap # src/snapshots/ontogen__snapshots__servers_jsonapi_ops_scoped_http.snap # src/snapshots/ontogen__snapshots__servers_jsonapi_resources_http.snap # tests/builder.rs
Deploying ontogen with
|
| Latest commit: |
ff3097a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://7c8a525a.ontogen.pages.dev |
| Branch Preview URL: | https://fix-pre090-codegen-names.ontogen.pages.dev |
The HTTP server roots every extern-crate path (`::axum`, `::serde`, `::ontogen_jsonapi`, `::std`, ...), including a route prefix type the consumer wrote (`::uuid::Uuid`), so an API module named after a crate (entity `Serde`, `Axum`) no longer shadows it. The markdown `open_vault` roots `::std` and `::markdown_store` for an entity `MarkdownStore`. The ids a create or update document links are held under the relation fields' names, so relations `crate_id`, `self_id` and `super_id` (whose relationships have no raw spelling) are served. `Path` and `PathBuf` are no longer treated as prelude names: a bare one is imported from the types module like any consumer type, the owned companions of `&Path`, `&CStr` and `&OsStr` are written by path, and the TS clients read an entity named like a std string type as that entity. The IPC server writes each line of a multi-line event doc as its own `///` line. The generated HTTP, MCP and IPC files and the TS clients state their naming convention in their headers.
Entity, field, API fn and argument names that collide with what the generated code names bare are refused with an error naming the item, the clash and a rename, at the earliest stage that knows: schema parse (prelude items, the Ontogen prefix, crate-shadowing modules, ontogen_ fields), the store stage (Store, AppError, ChangeOp, EntityKind, OrderBy), the api stage (hooks/generated and the output directory's name), the servers stage (state and store types, MCP and IPC public items, Ontogen-prefixed types, MCP modules std/schemars) and the API scan (raw fn names, unnamed, keyless and ontogen_ arguments). The lists live in src/ident.rs with the naming convention every emitter follows. The SeaORM set_parent helper builds its UPDATE with sea_query, which quotes identifiers for the connection's backend.
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed and why
Goal: a schema whose names collide with what the generated code uses either builds, or fails the build with an error naming the item and a rename. Generated code used to bind runtime items under bare names that a consumer's entity names, relationship names and API arguments could take. The audit found these failures (AUDIT-FINDINGS B4 quality, B4 architecture, B6 quality):
Document,Response,Stateand so on gave E0252 in the HTTP server;Responsebroke the TS clients;Docbroke the markdown store;HTTP server (
src/servers/generators/http.rs,http/)axumandontogen_jsonapiis imported asOntogen{Name}. Every other extern-crate path (::std,::serde,::axum,::ontogen_jsonapi,::ontogen_core, …) is::-rooted, so an entity module namedserdeoraxumcannot shadow it. A consumer-written route-prefix type is rooted too.OntogenPaginatedResult,OntogenListParams,OntogenPagedListParams,OntogenGetParams,OntogenPageOpArgs,OntogenFrameData,Ontogen{X}LinkedIds,ontogen_allow,ontogen_pageand so on.PaginatedResultis now private.request::attribute::<T>names T throughtypes_import_path.crate::ident::rust_ident. That covers keyword API modules (r#match).Ontogen{X}LinkedIdsmembers are named after the relation field (crate_id,r#loop), so relationships namedcrate,selforsuperwork.Ontogen/ontogen_prefixes,::-rooted paths, and only your types and API modules named bare.r#: op-arg keys, bare filter keys, custom-op path segments and SSE route path parameters.MCP and IPC servers
::-rooted paths. The bare imports left are the consumer's own: API modules, its types,state_importandstore_import.HandlerFnis nowOntogenHandlerFn. No item that was public in 0.8.0 is renamed.type, notr#type).collect_type_importnow imports a consumer type named like a std container (Arc) orPath.PathandPathBufare no longer treated as prelude types: owned&Path/&CStr/&OsStrcompanions are written by path. An entityPathis the entity in TS too.///line per line.open_vaultroots its paths, so an entityMarkdownStorebuilds.Stores, DTOs, persistence, API forwarders, hooks
record,value,records), so an entity or field named like the store's own locals (Docagainst the markdowndoc) no longer shadows them.sea_ormitems by::-rooted path: an entity namedOrderused to collide, and one namedSeaOrm(modulesea_orm) shadowed the crate.compare_*helpers root::std::cmp.set_parentbuilds its UPDATE with sea_query (Query::update,Alias), so names are quoted per backend; the rawUPDATE docs SET in = ?was a syntax error.Match) get raw module paths in every emitter.foreign_key = "in"spelling is normalised to the fieldr#in.TS clients
Error,Map,Response,Event,MessageEventandEventSourceare written asglobalThis.X, and Tauri'sChannelis imported asIpcChannel.Promise,Record, …) or like a client declaration (PaginatedResult,Transport, …) is refused with a clear message, instead of producing TS that does not compile.types.ts, the admin registry andontogen-tslong-tail structs emit serde member names (loop, notr#loop).class_,new_,in_). HTTP keys use the serde name, and IPC keys use Tauri's.IPC invoke keys
ipc_arg_key, matches Tauri exactly: heck 0.5to_lower_camel_caseafterunraw(), as intauri-macros2.6.3.heckis added as a dependency.check_wire_keysand the TS IPC transport both use it, so_sort/sortand_project_id/project_idcollisions are refused, and_kindtravels askind.Refused names
Each refusal is a
CodegenErrorthat names the item, says what it would clash with, and suggests a rename. The lists live insrc/ident.rswith the naming convention, so one place governs every emitter.Result,Option,Vec,String,Box,From,Default,Clone,Send,Sync,FnStd,Schemarscrate,self,superOntogen; fields startingontogen_Store,AppError,ChangeOp,EntityKind,OrderBystate_typeorstore_typeof the api, servers or clients stagehooks,generated, the API output directory's namestate_typeorstore_type; with MCPMcpToolDef,GetByIdInput,ByIntIdInput,EmptyInput,SimpleToolDef; with paginated IPCPaginatedResult; with MCP an API modulestd/schemarsr#…; arguments_,__(no letter or digit, so an empty Tauri key); arguments startingontogen_Promise,Record,Array,JSON,Object,String, …) or a client declaration (PaginatedResult,ListOptions,Transport,JsonApi…, …)Escaped instead of refused: keyword entities, modules and relationship fields become raw identifiers (
r#match,r#loop). Keyword and reserved-word arguments get valid TS names (class_,in_), and their wire keys drop ther#. Entities that shadow imported items, and entity modules that shadow crates the output roots (Serde,Axum,MarkdownStore,SeaOrm,SeaQuery), build.JSON:API checks
ResourceModel::buildis total. Its rules moved toResourceModel::check(), which runs only whenHttpAxum,HttpTsorHttpTauriIpcSplitis configured.Fixture
crates/name-safetyis a new workspace member (release = false,publish = falsein release-plz.toml). Its build script runs the whole pipeline: markdown store, API, HTTP and MCP servers,HttpTs,HttpTauriIpcSplitand the admin registry.Axum,Doc,Document,Endpoint,Event,Links,Map,MarkdownStore,Match,Method,Path,Relationship,Request,Response,Serde,StateandValue.match,ref,loop,in,crate,self,super.r#type,r#in,class,_kind,new,default, plusPathBufand&Path.just test-ts-clients, whichjust testruns, type-checks its TS withtsc --strict;typescriptis added as a root devDependency.Doc,Order,Match,SeaOrmandSeaQuery, with a keyword FK and a keyword m2m, so SeaORM output is compiled and run too. It has a runtime scenario on both backends.Breaking changes (vs 0.8.0)
Each has a
BREAKING CHANGE:footer and an upgrading.mdx entry.pub struct PaginatedResultis now the privateOntogenPaginatedResult.type, where it readr#type.subscribeXmethod'sargsmember for an underscore-led event argument isargs.kind, where it wasargs.Kind.Ontogen*orontogen_*items. Others:OrderBy, prelude names in layers 0.8.0 did not write them in (Defaultin most builds;Send,SyncorClonewithout a server), and__arguments in Rust-only builds. Each was built atontogen-v0.8.0. upgrading.mdx's "Names the generator refuses" section lists them.Not breaks:
ontogen-jsonapi's public API is unchanged.crates/ontogen-tsgets a bug fix (raw field names); its API is unchanged.Snapshots and examples
servers_jsonapi_ops_http,servers_jsonapi_ops_scoped_http,servers_jsonapi_resources_http,servers_two_surfaces_http. Changes are the aliases, path-qualified items, prefixed helpers and rustfmt reflow.servers_two_surfaces_ipc, for the::-rooted paths.markdown_store_complex_entity,markdown_store_has_many_entity,markdown_store_has_many_required_parent,store_crud_complex_entity,store_crud_has_many_entity, andtests/golden/markdown-backend/store/note.rs.golden. Changes are the fixed bindings, path-qualified SeaORM items and quotedset_parentSQL.servers_name_safety_ipc,servers_name_safety_mcp. In the review round, the HTTP and IPC/MCP snapshots also gained the::rooting and header sentence, plusmarkdown_open_vault_with_okf_options;store_crud_has_many_entitygained the sea_queryset_parent.just regen-examples). Example lockfiles gainheck.Verification
Docshadowing and the TSResponse/r#errors. It now builds, andtsc --strictpasses.src/servers/tests.rs,src/clients/tests.rs,src/store/tests.rs,src/schema/parse.rs,src/persistence/**,src/resource.rs,tests/builder.rs,crates/ontogen-tsandcrates/parity/checkcover each item. Each fails on the old emitters.just full-checkpasses, includingjust test-ts-clients.just regen-examplesleaves no drift.typescriptat the root only adds a(typescript@5.9.3)peer tag to vue and nuxt entries in pnpm-lock.yaml; no package version changes. The admin-layer vitest suite passes (75/75).Squash commit message