feat(intent): EXTENSION entities - extends: keyword + cross-model merge into the base table#6383
Merged
Merged
Conversation
…d fields into the base table at generation An EXTENSION entity (already modellable in the EDM editor) had no generation support: it fell through as a normal entity, getting its own table and no UI. Now it is a true field contribution to another entity: - generate.mjs (augmentWithExtensions): before generating a project, scan every sibling project's <name>.model for EXTENSION entities whose extensionReferencedModel names THIS project, and fold them into this model's entity list (deterministic order). This is the cross-model compose step - an extension module (e.g. a localisation adding EGN to Employee) contributes fields to an entity it does not own, resolved at generation time. - generateUtils.generateFiles (mergeExtensionEntities): merge each EXTENSION's properties into its base entity (extensionReferencedEntity), skipping the pk, audit columns, and any name the base already defines (core wins); then drop the EXTENSION entities. The added fields become real columns on the base table, so list filter/sort/search, the form, REST and _LANG all treat them natively - no runtime join. An extension whose base is absent (its own project's generation) owns no table and is dropped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An entity can declare extends: { model: <base-model>, entity: <base-entity> }
(model in uses:; omit for same-model). The EDM generator marks it type=EXTENSION
with extensionReferencedModel/extensionReferencedEntity and emits no
perspective/nav/layout - its fields are folded into the base table by the
model-to-code merge (this branch's generateUtils.mergeExtensionEntities). The
authoring layer over the generation support: a localisation/integration module
contributes fields to an entity another module owns, from .intent. Test asserts
the EXTENSION type + base reference + field emission + no perspective.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Honor EXTENSION entities: cross-model field contribution, from intent to base table
The EDM editor has long offered an Extension Entity type with no generation support. This makes it real, and adds the intent-DSL authoring layer, so a module can contribute fields to an entity another module owns without forking it (a localisation adds a national id to a core
Employee, an integration adds an external key, …).Two layers
generate.mjs+generateUtils.js): before generating a project, scan sibling projects'.models forEXTENSIONentities whoseextensionReferencedModelnames this project and fold them in (augmentWithExtensions); then merge eachEXTENSION's properties into its base entity (mergeExtensionEntities), skipping the PK, audit columns, and any name the base already defines (core wins), and drop theEXTENSION. The added fields become real base-table columns — list filter/sort, form, REST and_LANGall native, no join. An extension whose base is absent owns no table.extends:):extends: { model: <base-model>, entity: <base-entity> }(model inuses:; omit for same-model). The EDM generator marks the entitytype=EXTENSIONwith the base reference and emits no perspective/nav/layout. Documented in the assistant guide. A contributed field may declareafter: <baseField>/before: <baseField>to control where the merged column lands in the base's form/list (default: appended).Backward-compatible: a model with no
EXTENSIONentities is untouched.How it is used (in-workspace)
The merge runs at generation time by scanning the workspace — the base project and the extension project are both present, and the base is (re)generated after the extension exists so its table picks up the added fields. This is the normal dev/build flow: to work on an extension you have both models in your workspace. (How the generated output is subsequently packaged/deployed is outside the platform's concern.)
Verified end-to-end
An intent-authored extension module (
extends: Employeeadding anegnfield) → the EXTENSION.modelis emitted (typeEXTENSION, base reference,Egnproperty) → regenerating the base folds it in: a realEMPLOYEE_BG_EGNcolumn on the base Employee table, present in the entity class, the form and the manage-list, with no separate extension table. Unit tests: EDM emission (EdmIntentGeneratorTest), merge semantics (pk/audit skipped, core not overridden, orphan dropped).Follow-up
_LANGmerge test for a multilingual extension field; optional base-prefixeddataNamefor the contributed column (today it carries the contributing entity's prefix — good for provenance and collision-avoidance).🤖 Generated with Claude Code