feat(ogar-render-askama): T2 — TsInterface askama template + emitter#80
feat(ogar-render-askama): T2 — TsInterface askama template + emitter#80AdaWorldAPI wants to merge 1 commit into
Conversation
Second real emitter in the artifact-kind kit (T2 per Northstar plan §3). Mirrors the shape of T1 (RustStruct, PR #78): same canonical input (`&ogar_vocab::Class`), same dispatch path through `for_kind`, different template + binding-struct + type mapping. Surface (additive): - `templates/dispatch/ts_interface.askama` — emits an `export interface X { ... }` + `export const CLASS_ID = 0xDDCC as const;` + canonical concept const. - `artifact_kinds::ts_interface::TsInterfaceEmitter` — typed binding via `#[derive(askama::Template)]`. Maps Rails-side type names to TS types (string/number/boolean/unknown), family edges to `number | null` (belongs_to/has_one) or `ReadonlyArray<number>` (has_many/habtm). - `escape_ts_property` — quotes JS reserved + TS contextual keywords + non-bare-identifier names (Odoo `account.move.line` etc.). TS-side companion of Rust's `r#type` escape from codex P1 on #78. - Dispatcher wires the real emitter in place of the stub. Tests (+5): - ts_interface_emits_class_id_and_canonical_concept — proof of shape on project_work_item (CLASS_ID = 0x0102 as const, etc.). - ts_interface_maps_rails_types_to_ts — integer→number, string→string, text→string, against project_role. - ts_interface_emits_family_edges_as_arrays_and_nullable_ids — belongs_to/has_one as `number | null`, has_many as `ReadonlyArray<number>`, asserted on billable_work_entry + project_actor. - ts_interface_handles_keyword_property_names_safely — project_actor's `type` attribute emits `"type": string;` (quoted JS-contextual), never unquoted `type: string;`. - ts_interface_quotes_dotted_property_names — fabricated `account.move.line` attribute must be quoted in the emitter output. Stub coverage updated: 3 kinds remain stubbed (SurrealqlTable, OpenapiSchema, NodeGuidRoutingArm) for T3-T5. 12/12 unit tests + 1 ignored doctest; workspace check + workspace test green. Per Northstar §1.6: template is mass-mail simple (bag of variables named by the template, supplied by `TsInterfaceCtx`). No template hardcodes a class name.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3f549f45f3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /** Canonical concept name as in the OGAR codebook. */ | ||
| export const CANONICAL_CONCEPT = "{{ canonical_concept }}" as const; | ||
|
|
||
| export interface {{ name }} { |
There was a problem hiding this comment.
Sanitize interface names before emitting
When rendering a supported Class whose name is not a TypeScript identifier, this splices the raw source name into the declaration and produces invalid TS such as export interface account.move { (the Class::name docs explicitly preserve dotted ORM names in crates/ogar-vocab/src/lib.rs:89-91, and aliases like account.analytic.line are part of the codebook flow). Since this emitter accepts arbitrary ogar_vocab::Class values, derive or sanitize a valid TS interface identifier instead of using the source name directly.
Useful? React with 👍 / 👎.
|
Closing — misread the architecture. Askama is the render output (server-side HTML, per WoA-rs's The Northstar plan §3 will be updated to drop TS-side targets (TsInterface, OpenapiSchema as a separate kind) and replace them with askama HTML render templates in the WoA list_view / detail / form shape. Follow-up PR coming. Anti-pattern added to plan §4: "emit TS source from askama" — wrong layer; askama is the output. |
What
T2 —
TsInterfacefrom the Northstar plan §3. Second real emitter in the artifact-kind kit after T1 (RustStruct, in PR #78). Same&Classinput, same dispatch path, different template + binding-struct + type mapping. Validates the kit's claim that adding a new target = one template + one emitter.What it emits
Sample output rendering
project_work_item():Same shape as the Rust output, different syntax.
as constso consumers get the literal type, not a widenednumber.Mapping
string/textstringinteger/big_integer/bigintnumberbooleanbooleandate/datetime/timestampstring(ISO-8601; consumers parse toDate)json/jsonbunknownbelongs_to/has_onenumber | nullhas_many/has_and_belongs_to_manyReadonlyArray<number>Keyword escape (TS-side companion of Rust's
r#type)escape_ts_property()quotes JS reserved + TS contextual keywords + non-bare-identifier names. The same hazard codex caught on #78 (project_actor()declares an attribute namedtype) gets resolved by quoting:Also handles Odoo-style dotted identifiers (
"account.move.line": string;).Conservative quote list: strict JS reserved (
new,delete,function, …) + ES strict-mode reserved (let,static,interface,public, …) + TS contextual (type,async,await,as,from,of,is,infer,keyof,namespace,satisfies,readonly). Zero downside; removes a class of "compiles here, breaks at consumer" footguns.Tests (12/12, +5 for T2)
ts_interface_emits_class_id_and_canonical_concept— proof of shape onproject_work_item(CLASS_ID = 0x0102, etc.).ts_interface_maps_rails_types_to_ts— integer/string/text mapping verified againstproject_role.ts_interface_emits_family_edges_as_arrays_and_nullable_ids— both edge shapes pinned (belongs_to vs has_many).ts_interface_handles_keyword_property_names_safely—project_actor.typemust emit"type":, never baretype:.ts_interface_quotes_dotted_property_names— Odooaccount.move.linemust be quoted.Workspace check + workspace test green.
Per Northstar §1.6
The template is mass-mail simple — bag of variables the template names, supplied by
TsInterfaceCtx. No template hardcodes a class name. Adding a future TS-specific variant (e.g. zod schemas, generated from the same source) = one newArtifactKindvariant + one new template;TsInterfaceCtx's field set is the reference shape.Remaining in this kit
3 stubs left for T3 (
SurrealqlTable), T4 (OpenapiSchema), T5 (NodeGuidRoutingArm). The dispatcher mod doc updated to reflect 2/5 real emitters.