@metaobjectsdev/codegen-ts-tanstack 0.9.0 → 0.11.0-rc.1

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.
package/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # @metaobjectsdev/codegen-ts-tanstack
2
2
 
3
- TanStack codegen for MetaObjects. Provides `tanstackQuery()` (per-entity `<Entity>.hooks.ts` — 5 React Query hooks), `tanstackGrid()` (`<Entity>.columns.tsx` for `@tanstack/react-table`), and `tanstackGridHook()`.
3
+ TanStack codegen for MetaObjects. Provides `tanstackQuery()` (per-entity `<Entity>.hooks.ts` — 5 React Query hooks, plus a `use<Source><Relation>(sourceId, opts?)` collection hook per many-to-many relationship), `tanstackGrid()` (`<Entity>.columns.tsx` for `@tanstack/react-table`), and `tanstackGridHook()`.
4
+
5
+ ### M:N collection hooks (FR-018)
6
+
7
+ For each many-to-many relationship a source entity declares (`@cardinality: "many"` + `@through`), `tanstackQuery()` emits a `use<Source><Relation>(sourceId, opts?)` hook. It is a `useQuery` that fetches the REST sub-resource `GET /<source-plural>/{sourceId}/<relationName>` (the exact URL the generated route serves) and returns the typed target collection (`<Target>[]`). The query is enabled only when `sourceId` is present, so it is safe to call before the parent row loads. A symmetric self-join still produces a single collection hook (the server unions both junction columns on read).
4
8
 
5
9
  ## Install
6
10
 
@@ -1 +1 @@
1
- {"version":3,"file":"tanstack-grid-hook.d.ts","sourceRoot":"","sources":["../src/tanstack-grid-hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,EAA6B,KAAK,gBAAgB,EAAsD,MAAM,4BAA4B,CAAC;AAGlJ,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EA6BxB,gBAAgB,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"tanstack-grid-hook.d.ts","sourceRoot":"","sources":["../src/tanstack-grid-hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,EAA6B,KAAK,gBAAgB,EAAkF,MAAM,4BAA4B,CAAC;AAG9K,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EA6BxB,gBAAgB,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { LAYOUT_SUBTYPE_DATA_GRID } from "@metaobjectsdev/metadata";
2
- import { perEntity, formatTs, entityOutputPath, emitsInstanceArtifacts } from "@metaobjectsdev/codegen-ts";
2
+ import { perEntity, formatTs, entityOutputPath, emitsInstanceArtifacts, CODEGEN_ATTR_EMIT_TANSTACK } from "@metaobjectsdev/codegen-ts";
3
3
  import { renderGridHookFile } from "./templates/grid-hook-file.js";
4
4
  function hasDataGridLayout(entity) {
5
5
  // layouts() is effective — own + inherited layouts (from extends:/super:).
@@ -19,7 +19,7 @@ export const tanstackGridHook = function tanstackGridHook(opts) {
19
19
  // AND-composes the framework instance-artifact guard (skips abstract types),
20
20
  // opt-out, user filter, and dataGrid layout presence.
21
21
  filter: (e) => emitsInstanceArtifacts(e)
22
- && e.ownAttr("emitTanstack") !== false
22
+ && e.ownAttr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
23
23
  && userFilter(e)
24
24
  && hasDataGridLayout(e),
25
25
  generate: perEntity(async (entity, ctx) => {
@@ -1 +1 @@
1
- {"version":3,"file":"tanstack-grid-hook.js","sourceRoot":"","sources":["../src/tanstack-grid-hook.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,SAAS,EAAyC,QAAQ,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAClJ,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAOnE,SAAS,iBAAiB,CAAC,MAAkB;IAC3C,2EAA2E;IAC3E,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,wBAAwB,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,gBAAgB,CAAC,IAA2B;IACnF,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,oBAAoB;QAC1B,6EAA6E;QAC7E,sDAAsD;QACtD,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE,CACxB,sBAAsB,CAAC,CAAC,CAAC;eACtB,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,KAAK;eACnC,UAAU,CAAC,CAAC,CAAC;eACb,iBAAiB,CAAC,CAAC,CAAC;QACzB,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YACxC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;YACxF,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,gBAAgB,CACpB,GAAG,CAAC,aAAa,CAAC,YAAY,EAC9B,MAAM,CAAC,OAAO,EACd,GAAG,MAAM,CAAC,IAAI,UAAU,CACzB;gBACD,OAAO,EAAE,MAAM,QAAQ,CAAC,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;aACvE,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAkD,CAAC"}
1
+ {"version":3,"file":"tanstack-grid-hook.js","sourceRoot":"","sources":["../src/tanstack-grid-hook.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,SAAS,EAAyC,QAAQ,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC9K,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAOnE,SAAS,iBAAiB,CAAC,MAAkB;IAC3C,2EAA2E;IAC3E,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,wBAAwB,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,gBAAgB,CAAC,IAA2B;IACnF,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,oBAAoB;QAC1B,6EAA6E;QAC7E,sDAAsD;QACtD,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE,CACxB,sBAAsB,CAAC,CAAC,CAAC;eACtB,CAAC,CAAC,OAAO,CAAC,0BAA0B,CAAC,KAAK,KAAK;eAC/C,UAAU,CAAC,CAAC,CAAC;eACb,iBAAiB,CAAC,CAAC,CAAC;QACzB,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YACxC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;YACxF,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,gBAAgB,CACpB,GAAG,CAAC,aAAa,CAAC,YAAY,EAC9B,MAAM,CAAC,OAAO,EACd,GAAG,MAAM,CAAC,IAAI,UAAU,CACzB;gBACD,OAAO,EAAE,MAAM,QAAQ,CAAC,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;aACvE,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAkD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tanstack-grid.d.ts","sourceRoot":"","sources":["../src/tanstack-grid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,EAA6B,KAAK,gBAAgB,EAAsD,MAAM,4BAA4B,CAAC;AAGlJ,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAyBpB,gBAAgB,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"tanstack-grid.d.ts","sourceRoot":"","sources":["../src/tanstack-grid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,EAA6B,KAAK,gBAAgB,EAAwH,MAAM,4BAA4B,CAAC;AAGpN,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD;;;;GAIG;AACH,eAAO,MAAM,YAAY,EA8BpB,gBAAgB,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { LAYOUT_SUBTYPE_DATA_GRID } from "@metaobjectsdev/metadata";
2
- import { perEntity, formatTs, entityOutputPath, emitsInstanceArtifacts } from "@metaobjectsdev/codegen-ts";
2
+ import { perEntity, formatTs, entityOutputPath, emitsInstanceArtifacts, isTphSubtype, CODEGEN_ATTR_EMIT_TANSTACK, CODEGEN_ATTR_EMIT_GRID } from "@metaobjectsdev/codegen-ts";
3
3
  import { renderColumnsFile } from "./templates/columns-file.js";
4
4
  function hasDataGridLayout(entity) {
5
5
  // layouts() is effective — own + inherited layouts (from extends:/super:).
@@ -16,10 +16,15 @@ export const tanstackGrid = function tanstackGrid(opts) {
16
16
  name: "tanstack-grid",
17
17
  // Always set: AND-composes the framework instance-artifact guard (skips
18
18
  // abstract types), opt-out, user filter, and dataGrid layout presence.
19
+ // FR-017 Tier 3: a TPH discriminator base emits ONE polymorphic grid. Its
20
+ // subtypes inherit the base's dataGrid layout via extends, but per-subtype
21
+ // grids are opt-IN only (own `@emitGrid: true`) — otherwise the polymorphic
22
+ // grid is the single source of truth.
19
23
  filter: (e) => emitsInstanceArtifacts(e)
20
- && e.ownAttr("emitTanstack") !== false
24
+ && e.ownAttr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
21
25
  && userFilter(e)
22
- && hasDataGridLayout(e),
26
+ && hasDataGridLayout(e)
27
+ && (!isTphSubtype(e) || e.ownAttr(CODEGEN_ATTR_EMIT_GRID) === true),
23
28
  generate: perEntity(async (entity, ctx) => {
24
29
  if (!ctx.renderContext) {
25
30
  throw new Error("tanstack-grid: renderContext is required (provided by runGen)");
@@ -1 +1 @@
1
- {"version":3,"file":"tanstack-grid.js","sourceRoot":"","sources":["../src/tanstack-grid.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,SAAS,EAAyC,QAAQ,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAClJ,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAOhE,SAAS,iBAAiB,CAAC,MAAkB;IAC3C,2EAA2E;IAC3E,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,wBAAwB,CAAC,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,YAAY,CAAC,IAAuB;IACvE,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,eAAe;QACrB,wEAAwE;QACxE,uEAAuE;QACvE,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE,CACxB,sBAAsB,CAAC,CAAC,CAAC;eACtB,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,KAAK;eACnC,UAAU,CAAC,CAAC,CAAC;eACb,iBAAiB,CAAC,CAAC,CAAC;QACzB,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YACxC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;YACnF,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,cAAc,CAAC;gBACpG,OAAO,EAAE,MAAM,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;aACtE,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAA8C,CAAC"}
1
+ {"version":3,"file":"tanstack-grid.js","sourceRoot":"","sources":["../src/tanstack-grid.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,SAAS,EAAyC,QAAQ,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,YAAY,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpN,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAOhE,SAAS,iBAAiB,CAAC,MAAkB;IAC3C,2EAA2E;IAC3E,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,wBAAwB,CAAC,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,YAAY,CAAC,IAAuB;IACvE,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,eAAe;QACrB,wEAAwE;QACxE,uEAAuE;QACvE,0EAA0E;QAC1E,2EAA2E;QAC3E,4EAA4E;QAC5E,sCAAsC;QACtC,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE,CACxB,sBAAsB,CAAC,CAAC,CAAC;eACtB,CAAC,CAAC,OAAO,CAAC,0BAA0B,CAAC,KAAK,KAAK;eAC/C,UAAU,CAAC,CAAC,CAAC;eACb,iBAAiB,CAAC,CAAC,CAAC;eACpB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,KAAK,IAAI,CAAC;QACrE,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YACxC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;YACnF,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,cAAc,CAAC;gBACpG,OAAO,EAAE,MAAM,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;aACtE,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAA8C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tanstack-query.d.ts","sourceRoot":"","sources":["../src/tanstack-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAA6B,KAAK,gBAAgB,EAAsD,MAAM,4BAA4B,CAAC;AAGlJ,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,EAyBrB,gBAAgB,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"tanstack-query.d.ts","sourceRoot":"","sources":["../src/tanstack-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAA6B,KAAK,gBAAgB,EAAgG,MAAM,4BAA4B,CAAC;AAG5L,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,EA2BrB,gBAAgB,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC"}
@@ -1,4 +1,4 @@
1
- import { perEntity, formatTs, entityOutputPath, emitsInstanceArtifacts } from "@metaobjectsdev/codegen-ts";
1
+ import { perEntity, formatTs, entityOutputPath, emitsInstanceArtifacts, isTphSubtype, CODEGEN_ATTR_EMIT_TANSTACK } from "@metaobjectsdev/codegen-ts";
2
2
  import { renderHooksFile } from "./templates/hooks-file.js";
3
3
  /**
4
4
  * Per-entity generator that emits <Entity>.hooks.ts — a query-key factory
@@ -15,7 +15,9 @@ export const tanstackQuery = function tanstackQuery(opts) {
15
15
  // they contribute shape via inheritance only and have no instance to query),
16
16
  // the metadata opt-out, and the optional user filter. Projections still pass
17
17
  // here and get read-only hooks via renderHooksFile's isProjection branch.
18
- filter: (e) => emitsInstanceArtifacts(e) && e.ownAttr("emitTanstack") !== false && userFilter(e),
18
+ // FR-017 Tier 3: TPH subtypes get no standalone hooks file their per-subtype
19
+ // hooks live in the discriminator base's hooks file (polymorphic + per-subtype).
20
+ filter: (e) => emitsInstanceArtifacts(e) && e.ownAttr(CODEGEN_ATTR_EMIT_TANSTACK) !== false && !isTphSubtype(e) && userFilter(e),
19
21
  generate: perEntity(async (entity, ctx) => {
20
22
  if (!ctx.renderContext) {
21
23
  throw new Error("tanstack-query: renderContext is required (provided by runGen)");
@@ -1 +1 @@
1
- {"version":3,"file":"tanstack-query.js","sourceRoot":"","sources":["../src/tanstack-query.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAyC,QAAQ,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAClJ,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAO5D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,aAAa,CAAC,IAAwB;IAC1E,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,gBAAgB;QACtB,6EAA6E;QAC7E,6EAA6E;QAC7E,6EAA6E;QAC7E,0EAA0E;QAC1E,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,KAAK,IAAI,UAAU,CAAC,CAAC,CAAC;QAC5G,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YACxC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CACb,gEAAgE,CACjE,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,WAAW,CAAC;gBACjG,OAAO,EAAE,MAAM,QAAQ,CAAC,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;aACpE,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAA+C,CAAC"}
1
+ {"version":3,"file":"tanstack-query.js","sourceRoot":"","sources":["../src/tanstack-query.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAyC,QAAQ,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC5L,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAO5D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,aAAa,CAAC,IAAwB;IAC1E,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,SAAS,GAAc;QAC3B,IAAI,EAAE,gBAAgB;QACtB,6EAA6E;QAC7E,6EAA6E;QAC7E,6EAA6E;QAC7E,0EAA0E;QAC1E,+EAA+E;QAC/E,iFAAiF;QACjF,MAAM,EAAE,CAAC,CAAa,EAAE,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,0BAA0B,CAAC,KAAK,KAAK,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC;QAC5I,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YACxC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CACb,gEAAgE,CACjE,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,WAAW,CAAC;gBACjG,OAAO,EAAE,MAAM,QAAQ,CAAC,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;aACpE,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;IACF,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAA+C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"columns-file.d.ts","sourceRoot":"","sources":["../../src/templates/columns-file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,0BAA0B,CAAC;AAUtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AA6GhE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAmEhF"}
1
+ {"version":3,"file":"columns-file.d.ts","sourceRoot":"","sources":["../../src/templates/columns-file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,0BAA0B,CAAC;AAWtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAwIhE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAgFhF"}
@@ -1,6 +1,6 @@
1
1
  import { code, imp, joinCode } from "ts-poet";
2
- import { LAYOUT_SUBTYPE_DATA_GRID, LAYOUT_DATA_GRID_ATTR_PAGE_SIZE, LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_FIELD, LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_ORDER, LAYOUT_DATA_GRID_ATTR_FILTERABLE, LAYOUT_DATA_GRID_ATTR_FILTER, LAYOUT_DATA_GRID_ATTR_COLUMNS, } from "@metaobjectsdev/metadata";
3
- import { GENERATED_HEADER, entityModuleSpecifier } from "@metaobjectsdev/codegen-ts";
2
+ import { LAYOUT_SUBTYPE_DATA_GRID, LAYOUT_DATA_GRID_ATTR_PAGE_SIZE, LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_FIELD, LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_ORDER, LAYOUT_DATA_GRID_ATTR_FILTERABLE, LAYOUT_DATA_GRID_ATTR_FILTER, LAYOUT_DATA_GRID_ATTR_COLUMNS, OBJECT_ATTR_DISCRIMINATOR, } from "@metaobjectsdev/metadata";
3
+ import { GENERATED_HEADER, entityModuleSpecifier, isTphDiscriminatorBase, collectTphSubtypeFields, } from "@metaobjectsdev/codegen-ts";
4
4
  function humanize(s) {
5
5
  return s
6
6
  .replace(/([a-z])([A-Z])/g, "$1 $2")
@@ -25,9 +25,11 @@ function fieldLabel(field) {
25
25
  * fall back to all fields on the entity (pre-E-T2 behaviour, kept for
26
26
  * backwards compat with metadata not yet migrated by E-T4).
27
27
  */
28
- function extractGrids(entity) {
28
+ function extractGrids(entity, tph) {
29
29
  // fields() and layouts() are both effective (own + inherited via extends:/super:).
30
- const fieldsByName = new Map(entity.fields().map((f) => [f.name, f]));
30
+ // For a TPH base, also index the subtype-only fields so they can be folded
31
+ // into the polymorphic grid's column set.
32
+ const fieldsByName = new Map([...entity.fields(), ...(tph?.subtypeFields ?? [])].map((f) => [f.name, f]));
31
33
  const grids = [];
32
34
  for (const layout of entity.layouts()) {
33
35
  if (layout.subType !== LAYOUT_SUBTYPE_DATA_GRID)
@@ -38,6 +40,16 @@ function extractGrids(entity) {
38
40
  const columnNames = Array.isArray(columnsAttr)
39
41
  ? columnsAttr.filter((x) => typeof x === "string")
40
42
  : [...fieldsByName.keys()];
43
+ // FR-017: a polymorphic (TPH base) grid folds in every subtype-only column
44
+ // so mixed rows can render their subtype fields (the runtime grid renders a
45
+ // null cell as an em-dash for rows of other subtypes). Appended after the
46
+ // declared columns, deduped.
47
+ if (tph) {
48
+ for (const f of tph.subtypeFields) {
49
+ if (!columnNames.includes(f.name))
50
+ columnNames.push(f.name);
51
+ }
52
+ }
41
53
  const columns = columnNames.flatMap((name) => {
42
54
  const field = fieldsByName.get(name);
43
55
  if (!field)
@@ -47,6 +59,9 @@ function extractGrids(entity) {
47
59
  header: fieldLabel(field),
48
60
  viewKind: fieldViewKind(field),
49
61
  };
62
+ // FR-017: the discriminator column renders as a subtype badge.
63
+ if (tph && name === tph.discField)
64
+ spec.renderer = "badge";
50
65
  return [spec];
51
66
  });
52
67
  const sortField = layout.ownAttr(LAYOUT_DATA_GRID_ATTR_DEFAULT_SORT_FIELD);
@@ -87,7 +102,17 @@ function renderColumnDef(col) {
87
102
  export function renderColumnsFile(entity, ctx) {
88
103
  const entityName = entity.name;
89
104
  const lcEntity = entityName.charAt(0).toLowerCase() + entityName.slice(1);
90
- const grids = extractGrids(entity);
105
+ // FR-017: a TPH discriminator base emits a polymorphic grid typed against the
106
+ // raw single-table row (<Base>Row — all columns, subtype-only nullable),
107
+ // NOT the discriminated union (whose members lack the other subtypes' fields).
108
+ const tphBase = isTphDiscriminatorBase(entity, ctx.loadedRoot);
109
+ const tph = tphBase
110
+ ? {
111
+ discField: entity.ownAttr(OBJECT_ATTR_DISCRIMINATOR) ?? "",
112
+ subtypeFields: collectTphSubtypeFields(entity, ctx.loadedRoot),
113
+ }
114
+ : undefined;
115
+ const grids = extractGrids(entity, tph);
91
116
  const ColumnDefSym = imp("t:ColumnDef@@tanstack/react-table");
92
117
  // Track whether any grid emits a filter const so we know to import <Entity>Filter.
93
118
  let hasFilterConst = false;
@@ -130,9 +155,12 @@ export const ${filterConstName}: ${entityName}Filter = ${JSON.stringify(grid.fil
130
155
  // target → importBase-qualified package path.
131
156
  const entityModule = entityModuleSpecifier(ctx.selfTarget, ctx.entityModuleTarget, entity.package, entityName, ctx.extStyle);
132
157
  // Import <Entity>Row always; import <Entity>Filter only when a filter const is emitted.
158
+ // TPH base: <Base>Row is a distinct export (the all-columns row); import it
159
+ // directly. Non-TPH: the entity type IS the row, imported under the Row alias.
160
+ const rowImport = tphBase ? `${entityName}Row` : `${entityName} as ${entityName}Row`;
133
161
  const entityImportCode = hasFilterConst
134
- ? code `import type { ${entityName} as ${entityName}Row, ${entityName}Filter } from ${JSON.stringify(entityModule)};`
135
- : code `import type { ${entityName} as ${entityName}Row } from ${JSON.stringify(entityModule)};`;
162
+ ? code `import type { ${rowImport}, ${entityName}Filter } from ${JSON.stringify(entityModule)};`
163
+ : code `import type { ${rowImport} } from ${JSON.stringify(entityModule)};`;
136
164
  const body = joinCode(sections, { on: "\n" });
137
165
  return header + entityImportCode.toString() + "\n" + body.toString();
138
166
  }
@@ -1 +1 @@
1
- {"version":3,"file":"columns-file.js","sourceRoot":"","sources":["../../src/templates/columns-file.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"columns-file.js","sourceRoot":"","sources":["../../src/templates/columns-file.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-file.d.ts","sourceRoot":"","sources":["../../src/templates/hooks-file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAGhE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAc9E"}
1
+ {"version":3,"file":"hooks-file.d.ts","sourceRoot":"","sources":["../../src/templates/hooks-file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,4BAA4B,CAAC;AAU/E;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAmB9E"}
@@ -1,5 +1,5 @@
1
- import { code, imp, joinCode } from "ts-poet";
2
- import { GENERATED_HEADER, isProjection, pluralize, entityModuleSpecifier } from "@metaobjectsdev/codegen-ts";
1
+ import { code, imp, joinCode, Import } from "ts-poet";
2
+ import { GENERATED_HEADER, isProjection, pluralize, entityModuleSpecifier, isTphDiscriminatorBase, tphPlan, } from "@metaobjectsdev/codegen-ts";
3
3
  /**
4
4
  * Render <Entity>.hooks.ts — query-key factory + 2 query hooks + (for non-projections) 3 mutation hooks.
5
5
  *
@@ -20,19 +20,97 @@ export function renderHooksFile(entity, ctx) {
20
20
  // Import the entity's own file. Same target → relative "./Entity"; cross
21
21
  // target → importBase-qualified package path.
22
22
  const entityModule = entityModuleSpecifier(ctx.selfTarget, ctx.entityModuleTarget, entity.package, entity.name, ctx.extStyle);
23
+ // FR-017 Tier 3: a TPH discriminator base gets a polymorphic + per-subtype
24
+ // hooks file (the subtype entities are filtered out of this generator).
25
+ if (isTphDiscriminatorBase(entity, ctx.loadedRoot)) {
26
+ return renderTphHooksFile(entity, ctx, entityModule);
27
+ }
23
28
  if (isProjection(entity)) {
24
- return renderReadOnlyHooksFile(entity, entityModule);
29
+ return renderReadOnlyHooksFile(entity, entityModule, ctx);
25
30
  }
26
- return renderFullHooksFile(entity, entityModule);
31
+ return renderFullHooksFile(entity, entityModule, ctx);
32
+ }
33
+ // ---------------------------------------------------------------------------
34
+ // FR-018 — M:N collection hook(s).
35
+ //
36
+ // For each many-to-many relationship the source declares (`@cardinality: "many"`
37
+ // + `@through`), emit `use<Source><Relation>(sourceId, opts?)` — a useQuery that
38
+ // fetches the REST sub-resource `GET /<source-plural>/{sourceId}/<relationName>`
39
+ // (the exact URL mountM2mRoute serves) and returns the typed target collection
40
+ // (`Target[]`). The query is enabled only when sourceId is present, so callers
41
+ // can pass `undefined` before the parent row loads. A symmetric self-join is
42
+ // still ONE collection hook (the server unions both junction columns on read).
43
+ // ---------------------------------------------------------------------------
44
+ /** The M:N relation entries for an entity (cardinality 'many' + a junction). */
45
+ function m2mEntriesFor(entity, ctx) {
46
+ return (ctx.relationMap.get(entity.name) ?? []).filter((e) => e.cardinality === "many" && e.junctionEntity !== undefined);
47
+ }
48
+ /** The `relation: (relation, sourceId) => ...` query-key factory line, included
49
+ * in the keys factory ONLY when the entity has M:N relationships. */
50
+ function m2mKeyLine(keysVar) {
51
+ return (` relation: (relation: string, sourceId: number | undefined) =>\n` +
52
+ ` [...${keysVar}.all(), "relation", relation, sourceId ?? null] as const,`);
53
+ }
54
+ /**
55
+ * Render `use<Source><Relation>(sourceId, opts?)` per M:N relationship. Returns
56
+ * null when the entity has no M:N relationships (no extra hooks emitted).
57
+ */
58
+ function renderM2mHooks(entity, ctx, keysVar, entries) {
59
+ if (entries.length === 0)
60
+ return null;
61
+ const useQuerySym = imp("useQuery@@tanstack/react-query");
62
+ const useQueryOptionsSym = imp("t:UseQueryOptions@@tanstack/react-query");
63
+ const useQueryResultSym = imp("t:UseQueryResult@@tanstack/react-query");
64
+ const useEntityFetcherSym = imp("useEntityFetcher@@metaobjectsdev/tanstack");
65
+ const source = entity.name;
66
+ // Distinct target row types, imported (aliased) from each target's entity
67
+ // module. ts-poet's imp() tracks + hoists these into the import block. The
68
+ // <Target>RelRow alias avoids colliding with the source file's own
69
+ // `type <Source> as <Source>Row` import on a self-join (source === target).
70
+ const targetTypeSym = new Map();
71
+ for (const e of entries) {
72
+ if (targetTypeSym.has(e.targetEntity))
73
+ continue;
74
+ const mod = entityModuleSpecifier(ctx.selfTarget, ctx.entityModuleTarget, ctx.packageOf.get(e.targetEntity), e.targetEntity, ctx.extStyle);
75
+ // `import { type <Target> as <Target>RelRow } from "<mod>"` — the RelRow
76
+ // alias avoids colliding with the source file's own `type <Source> as
77
+ // <Source>Row` import on a self-join (source === target).
78
+ targetTypeSym.set(e.targetEntity, Import.importsName(`${e.targetEntity}RelRow`, mod, true, e.targetEntity));
79
+ }
80
+ const hooks = entries.map((e) => {
81
+ const targetSym = targetTypeSym.get(e.targetEntity);
82
+ const hookName = `use${source}${capitalize(e.name)}`;
83
+ const relLit = JSON.stringify(e.name);
84
+ return code `
85
+ export function ${hookName}(
86
+ sourceId: number | undefined,
87
+ opts?: Omit<${useQueryOptionsSym}<${targetSym}[]>, "queryKey" | "queryFn">,
88
+ ): ${useQueryResultSym}<${targetSym}[]> {
89
+ const fetcher = ${useEntityFetcherSym}();
90
+ return ${useQuerySym}<${targetSym}[]>({
91
+ queryKey: ${keysVar}.relation(${relLit}, sourceId),
92
+ queryFn: () => fetcher<${targetSym}[]>(\`\${${source}.$apiPrefix}\${${source}.$path}/\${sourceId}/${e.name}\`),
93
+ enabled: sourceId != null && (opts?.enabled ?? true),
94
+ ...opts,
95
+ });
96
+ }
97
+ `;
98
+ });
99
+ return joinCode(hooks, { on: "\n" });
100
+ }
101
+ function capitalize(s) {
102
+ return s.charAt(0).toUpperCase() + s.slice(1);
27
103
  }
28
104
  // ---------------------------------------------------------------------------
29
105
  // Read-only path (projections)
30
106
  // ---------------------------------------------------------------------------
31
- function renderReadOnlyHooksFile(entity, entityModule) {
107
+ function renderReadOnlyHooksFile(entity, entityModule, ctx) {
32
108
  const entityName = entity.name;
33
109
  const entityNamePlural = pluralize(entityName);
34
110
  const lcEntity = entityName.charAt(0).toLowerCase() + entityName.slice(1);
35
111
  const keysVar = `${lcEntity}Keys`;
112
+ const m2mEntries = m2mEntriesFor(entity, ctx);
113
+ const relationKeyLine = m2mEntries.length > 0 ? `\n${m2mKeyLine(keysVar)}` : "";
36
114
  const useQuerySym = imp("useQuery@@tanstack/react-query");
37
115
  const useQueryOptionsSym = imp("t:UseQueryOptions@@tanstack/react-query");
38
116
  const useQueryResultSym = imp("t:UseQueryResult@@tanstack/react-query");
@@ -51,7 +129,7 @@ export const ${keysVar} = {
51
129
  lists: () => [...${keysVar}.all(), "list"] as const,
52
130
  list: (filter?: ${entityName}Filter) => [...${keysVar}.lists(), filter ?? {}] as const,
53
131
  details: () => [...${keysVar}.all(), "detail"] as const,
54
- detail: (id: number) => [...${keysVar}.details(), id] as const,
132
+ detail: (id: number) => [...${keysVar}.details(), id] as const,${relationKeyLine}
55
133
  };
56
134
  `;
57
135
  const queries = code `
@@ -80,7 +158,8 @@ export function use${entityNamePlural}(
80
158
  });
81
159
  }
82
160
  `;
83
- const body = joinCode([queryKeys, queries], { on: "\n" });
161
+ const m2mHooks = renderM2mHooks(entity, ctx, keysVar, m2mEntries);
162
+ const body = joinCode(m2mHooks ? [queryKeys, queries, m2mHooks] : [queryKeys, queries], { on: "\n" });
84
163
  const header = `// ${GENERATED_HEADER}-tanstack — DO NOT EDIT.\n` +
85
164
  `// Source metadata: ${entityName} (${entity.fqn()})\n`;
86
165
  return header + entityImports.toString() + body.toString();
@@ -88,11 +167,13 @@ export function use${entityNamePlural}(
88
167
  // ---------------------------------------------------------------------------
89
168
  // Full path (writable entities — table-backed or write-through)
90
169
  // ---------------------------------------------------------------------------
91
- function renderFullHooksFile(entity, entityModule) {
170
+ function renderFullHooksFile(entity, entityModule, ctx) {
92
171
  const entityName = entity.name;
93
172
  const entityNamePlural = pluralize(entityName);
94
173
  const lcEntity = entityName.charAt(0).toLowerCase() + entityName.slice(1);
95
174
  const keysVar = `${lcEntity}Keys`;
175
+ const m2mEntries = m2mEntriesFor(entity, ctx);
176
+ const relationKeyLine = m2mEntries.length > 0 ? `\n${m2mKeyLine(keysVar)}` : "";
96
177
  const useMutationSym = imp("useMutation@@tanstack/react-query");
97
178
  const useQuerySym = imp("useQuery@@tanstack/react-query");
98
179
  const useQueryClientSym = imp("useQueryClient@@tanstack/react-query");
@@ -117,7 +198,7 @@ export const ${keysVar} = {
117
198
  lists: () => [...${keysVar}.all(), "list"] as const,
118
199
  list: (filter?: ${entityName}Filter) => [...${keysVar}.lists(), filter ?? {}] as const,
119
200
  details: () => [...${keysVar}.all(), "detail"] as const,
120
- detail: (id: number) => [...${keysVar}.details(), id] as const,
201
+ detail: (id: number) => [...${keysVar}.details(), id] as const,${relationKeyLine}
121
202
  };
122
203
  `;
123
204
  const queries = code `
@@ -146,6 +227,7 @@ export function use${entityNamePlural}(
146
227
  });
147
228
  }
148
229
  `;
230
+ const m2mHooks = renderM2mHooks(entity, ctx, keysVar, m2mEntries);
149
231
  const mutations = code `
150
232
  export function useCreate${entityName}(
151
233
  opts?: Omit<${useMutationOptionsSym}<${entityName}Row, Error, ${entityName}Insert>, "mutationFn">,
@@ -200,9 +282,178 @@ export function useDelete${entityName}(
200
282
  });
201
283
  }
202
284
  `;
203
- const body = joinCode([queryKeys, queries, mutations], { on: "\n" });
285
+ const body = joinCode(m2mHooks ? [queryKeys, queries, m2mHooks, mutations] : [queryKeys, queries, mutations], { on: "\n" });
204
286
  const header = `// ${GENERATED_HEADER}-tanstack — DO NOT EDIT.\n` +
205
287
  `// Source metadata: ${entityName} (${entity.fqn()})\n`;
206
288
  return header + entityImports.toString() + body.toString();
207
289
  }
290
+ // ---------------------------------------------------------------------------
291
+ // FR-017 Tier 3 — TPH discriminator base: polymorphic + per-subtype hooks.
292
+ // ---------------------------------------------------------------------------
293
+ function renderTphHooksFile(base, ctx, baseModule) {
294
+ const baseName = base.name;
295
+ const lcBase = baseName.charAt(0).toLowerCase() + baseName.slice(1);
296
+ const keysVar = `${lcBase}Keys`;
297
+ // Single source of truth for discriminator field + subtypes + route segments.
298
+ const plan = tphPlan(base, ctx.loadedRoot);
299
+ const discField = plan.discriminatorField;
300
+ const useQuerySym = imp("useQuery@@tanstack/react-query");
301
+ const useMutationSym = imp("useMutation@@tanstack/react-query");
302
+ const useQueryClientSym = imp("useQueryClient@@tanstack/react-query");
303
+ const useQueryOptionsSym = imp("t:UseQueryOptions@@tanstack/react-query");
304
+ const useMutationOptionsSym = imp("t:UseMutationOptions@@tanstack/react-query");
305
+ const useQueryResultSym = imp("t:UseQueryResult@@tanstack/react-query");
306
+ const useMutationResultSym = imp("t:UseMutationResult@@tanstack/react-query");
307
+ const useEntityFetcherSym = imp("useEntityFetcher@@metaobjectsdev/tanstack");
308
+ const buildFilterQsSym = imp("buildFilterQs@@metaobjectsdev/runtime-web");
309
+ const subtypes = plan.subtypes;
310
+ // `${baseName}` imports BOTH the constants value (for $path/$apiPrefix) and the
311
+ // discriminated-union type (declaration merge). Each subtype contributes its
312
+ // interface type AND its own filter type (discriminator-excluded — the route
313
+ // pins it), so per-subtype hooks filter on the fields the per-subtype
314
+ // allowlist actually permits.
315
+ const subImportLines = subtypes
316
+ .map((s) => {
317
+ const m = entityModuleSpecifier(ctx.selfTarget, ctx.entityModuleTarget, s.entity.package, s.entity.name, ctx.extStyle);
318
+ return `import { type ${s.entity.name}, type ${s.entity.name}Filter } from ${JSON.stringify(m)};`;
319
+ })
320
+ .join("\n");
321
+ const entityImports = code `
322
+ import { ${baseName}, type ${baseName}Filter } from ${JSON.stringify(baseModule)};
323
+ ${subImportLines}
324
+ `;
325
+ const queryKeys = code `
326
+ export const ${keysVar} = {
327
+ all: () => [${JSON.stringify(lcBase)}] as const,
328
+ lists: () => [...${keysVar}.all(), "list"] as const,
329
+ list: (filter?: ${baseName}Filter) => [...${keysVar}.lists(), filter ?? {}] as const,
330
+ details: () => [...${keysVar}.all(), "detail"] as const,
331
+ detail: (id: number) => [...${keysVar}.details(), id] as const,
332
+ subtypeLists: (sub: string) => [...${keysVar}.all(), sub, "list"] as const,
333
+ // filter is loosely typed here (cache-key identity only); the per-subtype
334
+ // hooks below type it precisely as <Sub>Filter.
335
+ subtypeList: (sub: string, filter?: unknown) => [...${keysVar}.subtypeLists(sub), filter ?? {}] as const,
336
+ subtypeDetails:(sub: string) => [...${keysVar}.all(), sub, "detail"] as const,
337
+ subtypeDetail: (sub: string, id: number) => [...${keysVar}.subtypeDetails(sub), id] as const,
338
+ };
339
+ `;
340
+ // Polymorphic reads — return the discriminated union.
341
+ const polymorphic = code `
342
+ export function use${baseName}(
343
+ id: number,
344
+ opts?: Omit<${useQueryOptionsSym}<${baseName}>, "queryKey" | "queryFn">,
345
+ ): ${useQueryResultSym}<${baseName}> {
346
+ const fetcher = ${useEntityFetcherSym}();
347
+ return ${useQuerySym}<${baseName}>({
348
+ queryKey: ${keysVar}.detail(id),
349
+ queryFn: () => fetcher<${baseName}>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/\${id}\`),
350
+ ...opts,
351
+ });
352
+ }
353
+
354
+ export function use${pluralize(baseName)}(
355
+ filter?: ${baseName}Filter,
356
+ opts?: Omit<${useQueryOptionsSym}<${baseName}[]>, "queryKey" | "queryFn">,
357
+ ): ${useQueryResultSym}<${baseName}[]> {
358
+ const fetcher = ${useEntityFetcherSym}();
359
+ const qs = filter ? "?" + ${buildFilterQsSym}(filter as Record<string, unknown>) : "";
360
+ return ${useQuerySym}<${baseName}[]>({
361
+ queryKey: ${keysVar}.list(filter),
362
+ queryFn: () => fetcher<${baseName}[]>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}\${qs}\`),
363
+ ...opts,
364
+ });
365
+ }
366
+ `;
367
+ // Per-subtype hooks — scoped to each discriminator value's REST sub-path.
368
+ const subtypeSections = subtypes.map(({ entity: subEntity, value, routeSegment: seg }) => {
369
+ const subName = subEntity.name;
370
+ const valueLit = JSON.stringify(value);
371
+ const createInput = `Omit<${subName}, ${JSON.stringify(discField)}>`;
372
+ const updateInput = `Partial<${createInput}>`;
373
+ const subPath = `\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/${seg}\``;
374
+ return code `
375
+ export function use${pluralize(subName)}(
376
+ filter?: ${subName}Filter,
377
+ opts?: Omit<${useQueryOptionsSym}<${subName}[]>, "queryKey" | "queryFn">,
378
+ ): ${useQueryResultSym}<${subName}[]> {
379
+ const fetcher = ${useEntityFetcherSym}();
380
+ const qs = filter ? "?" + ${buildFilterQsSym}(filter as Record<string, unknown>) : "";
381
+ return ${useQuerySym}<${subName}[]>({
382
+ queryKey: ${keysVar}.subtypeList(${valueLit}, filter),
383
+ queryFn: () => fetcher<${subName}[]>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/${seg}\${qs}\`),
384
+ ...opts,
385
+ });
386
+ }
387
+
388
+ export function use${subName}(
389
+ id: number,
390
+ opts?: Omit<${useQueryOptionsSym}<${subName}>, "queryKey" | "queryFn">,
391
+ ): ${useQueryResultSym}<${subName}> {
392
+ const fetcher = ${useEntityFetcherSym}();
393
+ return ${useQuerySym}<${subName}>({
394
+ queryKey: ${keysVar}.subtypeDetail(${valueLit}, id),
395
+ queryFn: () => fetcher<${subName}>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/${seg}/\${id}\`),
396
+ ...opts,
397
+ });
398
+ }
399
+
400
+ export function useCreate${subName}(
401
+ opts?: Omit<${useMutationOptionsSym}<${subName}, Error, ${createInput}>, "mutationFn">,
402
+ ): ${useMutationResultSym}<${subName}, Error, ${createInput}> {
403
+ const fetcher = ${useEntityFetcherSym}();
404
+ const qc = ${useQueryClientSym}();
405
+ return ${useMutationSym}<${subName}, Error, ${createInput}>({
406
+ mutationFn: (input) => fetcher<${subName}>(${subPath}, {
407
+ method: "POST",
408
+ headers: { "Content-Type": "application/json" },
409
+ body: JSON.stringify(input),
410
+ }),
411
+ ...opts,
412
+ onSuccess: (...args) => {
413
+ qc.invalidateQueries({ queryKey: ${keysVar}.all() });
414
+ opts?.onSuccess?.(...args);
415
+ },
416
+ });
417
+ }
418
+
419
+ export function useUpdate${subName}(
420
+ opts?: Omit<${useMutationOptionsSym}<${subName}, Error, { id: number; input: ${updateInput} }>, "mutationFn">,
421
+ ): ${useMutationResultSym}<${subName}, Error, { id: number; input: ${updateInput} }> {
422
+ const fetcher = ${useEntityFetcherSym}();
423
+ const qc = ${useQueryClientSym}();
424
+ return ${useMutationSym}({
425
+ mutationFn: ({ id, input }) => fetcher<${subName}>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/${seg}/\${id}\`, {
426
+ method: "PATCH",
427
+ headers: { "Content-Type": "application/json" },
428
+ body: JSON.stringify(input),
429
+ }),
430
+ ...opts,
431
+ onSuccess: (...args) => {
432
+ qc.invalidateQueries({ queryKey: ${keysVar}.all() });
433
+ opts?.onSuccess?.(...args);
434
+ },
435
+ });
436
+ }
437
+
438
+ export function useDelete${subName}(
439
+ opts?: Omit<${useMutationOptionsSym}<void, Error, number>, "mutationFn">,
440
+ ): ${useMutationResultSym}<void, Error, number> {
441
+ const fetcher = ${useEntityFetcherSym}();
442
+ const qc = ${useQueryClientSym}();
443
+ return ${useMutationSym}({
444
+ mutationFn: (id) => fetcher<void>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/${seg}/\${id}\`, { method: "DELETE" }),
445
+ ...opts,
446
+ onSuccess: (...args) => {
447
+ qc.invalidateQueries({ queryKey: ${keysVar}.all() });
448
+ opts?.onSuccess?.(...args);
449
+ },
450
+ });
451
+ }
452
+ `;
453
+ });
454
+ const body = joinCode([queryKeys, polymorphic, ...subtypeSections], { on: "\n" });
455
+ const header = `// ${GENERATED_HEADER}-tanstack — DO NOT EDIT.\n` +
456
+ `// Source metadata: ${baseName} (${base.fqn()}) — TPH discriminator base\n`;
457
+ return header + entityImports.toString() + body.toString();
458
+ }
208
459
  //# sourceMappingURL=hooks-file.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-file.js","sourceRoot":"","sources":["../../src/templates/hooks-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAa,MAAM,SAAS,CAAC;AAGzD,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAE9G;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAAC,MAAkB,EAAE,GAAkB;IACpE,yEAAyE;IACzE,8CAA8C;IAC9C,MAAM,YAAY,GAAG,qBAAqB,CACxC,GAAG,CAAC,UAAU,EACd,GAAG,CAAC,kBAAkB,EACtB,MAAM,CAAC,OAAO,EACd,MAAM,CAAC,IAAI,EACX,GAAG,CAAC,QAAQ,CACb,CAAC;IACF,IAAI,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,uBAAuB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,mBAAmB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACnD,CAAC;AAED,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E,SAAS,uBAAuB,CAAC,MAAkB,EAAE,YAAoB;IACvE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;IAC/B,MAAM,gBAAgB,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,OAAO,GAAG,GAAG,QAAQ,MAAM,CAAC;IAElC,MAAM,WAAW,GAAG,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC1D,MAAM,kBAAkB,GAAG,GAAG,CAAC,yCAAyC,CAAC,CAAC;IAC1E,MAAM,iBAAiB,GAAG,GAAG,CAAC,wCAAwC,CAAC,CAAC;IACxE,MAAM,mBAAmB,GAAG,GAAG,CAAC,2CAA2C,CAAC,CAAC;IAC7E,MAAM,gBAAgB,GAAG,GAAG,CAAC,2CAA2C,CAAC,CAAC;IAE1E,MAAM,aAAa,GAAS,IAAI,CAAA;;IAE9B,UAAU;SACL,UAAU,OAAO,UAAU;SAC3B,UAAU;SACV,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC;CACpC,CAAC;IAEA,MAAM,SAAS,GAAS,IAAI,CAAA;eACf,OAAO;oBACF,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;uBACrB,OAAO;uBACP,UAAU,kBAAkB,OAAO;uBACnC,OAAO;iCACG,OAAO;;CAEvC,CAAC;IAEA,MAAM,OAAO,GAAS,IAAI,CAAA;qBACP,UAAU;;gBAEf,kBAAkB,IAAI,UAAU;KAC3C,iBAAiB,IAAI,UAAU;oBAChB,mBAAmB;WAC5B,WAAW,IAAI,UAAU;gBACpB,OAAO;6BACM,UAAU,aAAa,UAAU,kBAAkB,UAAU;;;;;qBAKrE,gBAAgB;aACxB,UAAU;gBACP,kBAAkB,IAAI,UAAU;KAC3C,iBAAiB,IAAI,UAAU;oBAChB,mBAAmB;8BACT,gBAAgB;WACnC,WAAW,IAAI,UAAU;gBACpB,OAAO;6BACM,UAAU,eAAe,UAAU,kBAAkB,UAAU;;;;CAI3F,CAAC;IAEA,MAAM,IAAI,GAAS,QAAQ,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAEhE,MAAM,MAAM,GACV,MAAM,gBAAgB,4BAA4B;QAClD,uBAAuB,UAAU,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC;IAC1D,OAAO,MAAM,GAAG,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;AAC7D,CAAC;AAED,8EAA8E;AAC9E,gEAAgE;AAChE,8EAA8E;AAE9E,SAAS,mBAAmB,CAAC,MAAkB,EAAE,YAAoB;IACnE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;IAC/B,MAAM,gBAAgB,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,OAAO,GAAG,GAAG,QAAQ,MAAM,CAAC;IAElC,MAAM,cAAc,GAAG,GAAG,CAAC,mCAAmC,CAAC,CAAC;IAChE,MAAM,WAAW,GAAG,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC1D,MAAM,iBAAiB,GAAG,GAAG,CAAC,sCAAsC,CAAC,CAAC;IACtE,MAAM,kBAAkB,GAAG,GAAG,CAAC,yCAAyC,CAAC,CAAC;IAC1E,MAAM,qBAAqB,GAAG,GAAG,CAAC,4CAA4C,CAAC,CAAC;IAChF,MAAM,iBAAiB,GAAG,GAAG,CAAC,wCAAwC,CAAC,CAAC;IACxE,MAAM,oBAAoB,GAAG,GAAG,CAAC,2CAA2C,CAAC,CAAC;IAC9E,MAAM,mBAAmB,GAAG,GAAG,CAAC,2CAA2C,CAAC,CAAC;IAC7E,MAAM,gBAAgB,GAAG,GAAG,CAAC,2CAA2C,CAAC,CAAC;IAE1E,MAAM,aAAa,GAAS,IAAI,CAAA;;IAE9B,UAAU;SACL,UAAU,OAAO,UAAU;SAC3B,UAAU;SACV,UAAU;SACV,UAAU;SACV,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC;CACpC,CAAC;IAEA,MAAM,SAAS,GAAS,IAAI,CAAA;eACf,OAAO;oBACF,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;uBACrB,OAAO;uBACP,UAAU,kBAAkB,OAAO;uBACnC,OAAO;iCACG,OAAO;;CAEvC,CAAC;IAEA,MAAM,OAAO,GAAS,IAAI,CAAA;qBACP,UAAU;;gBAEf,kBAAkB,IAAI,UAAU;KAC3C,iBAAiB,IAAI,UAAU;oBAChB,mBAAmB;WAC5B,WAAW,IAAI,UAAU;gBACpB,OAAO;6BACM,UAAU,aAAa,UAAU,kBAAkB,UAAU;;;;;qBAKrE,gBAAgB;aACxB,UAAU;gBACP,kBAAkB,IAAI,UAAU;KAC3C,iBAAiB,IAAI,UAAU;oBAChB,mBAAmB;8BACT,gBAAgB;WACnC,WAAW,IAAI,UAAU;gBACpB,OAAO;6BACM,UAAU,eAAe,UAAU,kBAAkB,UAAU;;;;CAI3F,CAAC;IAEA,MAAM,SAAS,GAAS,IAAI,CAAA;2BACH,UAAU;gBACrB,qBAAqB,IAAI,UAAU,eAAe,UAAU;KACvE,oBAAoB,IAAI,UAAU,eAAe,UAAU;oBAC5C,mBAAmB;eACxB,iBAAiB;WACrB,cAAc,IAAI,UAAU,eAAe,UAAU;qCAC3B,UAAU,aAAa,UAAU,kBAAkB,UAAU;;;;;;;yCAOzD,OAAO;;;;;;2BAMrB,UAAU;gBACrB,qBAAqB,IAAI,UAAU,oCAAoC,UAAU;KAC5F,oBAAoB,IAAI,UAAU,oCAAoC,UAAU;oBACjE,mBAAmB;eACxB,iBAAiB;WACrB,cAAc;6CACoB,UAAU,aAAa,UAAU,kBAAkB,UAAU;;;;;;;yCAOjE,OAAO;;;;;;2BAMrB,UAAU;gBACrB,qBAAqB;KAChC,oBAAoB;oBACL,mBAAmB;eACxB,iBAAiB;WACrB,cAAc;6CACoB,UAAU,kBAAkB,UAAU;;;yCAG1C,OAAO;;;;;CAK/C,CAAC;IAEA,MAAM,IAAI,GAAS,QAAQ,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3E,MAAM,MAAM,GACV,MAAM,gBAAgB,4BAA4B;QAClD,uBAAuB,UAAU,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC;IAC1D,OAAO,MAAM,GAAG,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"hooks-file.js","sourceRoot":"","sources":["../../src/templates/hooks-file.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/codegen-ts-tanstack",
3
- "version": "0.9.0",
3
+ "version": "0.11.0-rc.1",
4
4
  "description": "TanStack codegen for metaobjects — emits hooks and column definitions for TanStack Query and Table.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,7 +12,12 @@
12
12
  "default": "./dist/index.js"
13
13
  }
14
14
  },
15
- "files": ["dist", "src", "README.md", "LICENSE"],
15
+ "files": [
16
+ "dist",
17
+ "src",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
16
21
  "scripts": {
17
22
  "build": "tsc -p .",
18
23
  "typecheck": "tsc -p tsconfig.typecheck.json"
@@ -21,28 +26,34 @@
21
26
  "author": "Doug Mealing <doug@dougmealing.com>",
22
27
  "homepage": "https://metaobjects.dev",
23
28
  "bugs": {
24
- "url": "https://github.com/metaobjectsdev/metaobjects/issues"
25
- },
26
- "repository": {
27
- "type": "git",
28
- "url": "https://github.com/metaobjectsdev/metaobjects.git",
29
- "directory": "server/typescript/packages/codegen-ts-tanstack"
30
- },
31
- "keywords": ["metaobjects", "codegen", "tanstack", "react-query", "react-table"],
32
- "publishConfig": {
33
- "access": "public"
34
- },
35
- "dependencies": {
36
- "@metaobjectsdev/metadata": "0.9.0",
37
- "@metaobjectsdev/codegen-ts": "0.9.0",
38
- "ts-poet": "^6.10.0"
39
- },
40
- "peerDependencies": {
41
- "@biomejs/biome": ">=1.9.0"
42
- },
43
- "devDependencies": {
44
- "@biomejs/biome": "^1.9.0",
45
- "bun-types": "latest",
46
- "typescript": "^5.6.0"
47
- }
29
+ "url": "https://github.com/metaobjectsdev/metaobjects/issues"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/metaobjectsdev/metaobjects.git",
34
+ "directory": "server/typescript/packages/codegen-ts-tanstack"
35
+ },
36
+ "keywords": [
37
+ "metaobjects",
38
+ "codegen",
39
+ "tanstack",
40
+ "react-query",
41
+ "react-table"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "dependencies": {
47
+ "@metaobjectsdev/metadata": "0.11.0-rc.1",
48
+ "@metaobjectsdev/codegen-ts": "0.11.0-rc.1",
49
+ "ts-poet": "^6.10.0"
50
+ },
51
+ "peerDependencies": {
52
+ "@biomejs/biome": ">=1.9.0"
53
+ },
54
+ "devDependencies": {
55
+ "@biomejs/biome": "^1.9.0",
56
+ "bun-types": "latest",
57
+ "typescript": "^5.6.0"
58
+ }
48
59
  }
@@ -1,6 +1,6 @@
1
1
  import type { MetaObject } from "@metaobjectsdev/metadata";
2
2
  import { LAYOUT_SUBTYPE_DATA_GRID } from "@metaobjectsdev/metadata";
3
- import { perEntity, type Generator, type GeneratorFactory, formatTs, entityOutputPath, emitsInstanceArtifacts } from "@metaobjectsdev/codegen-ts";
3
+ import { perEntity, type Generator, type GeneratorFactory, formatTs, entityOutputPath, emitsInstanceArtifacts, CODEGEN_ATTR_EMIT_TANSTACK } from "@metaobjectsdev/codegen-ts";
4
4
  import { renderGridHookFile } from "./templates/grid-hook-file.js";
5
5
 
6
6
  export interface TanstackGridHookOpts {
@@ -28,7 +28,7 @@ export const tanstackGridHook = function tanstackGridHook(opts?: TanstackGridHoo
28
28
  // opt-out, user filter, and dataGrid layout presence.
29
29
  filter: (e: MetaObject) =>
30
30
  emitsInstanceArtifacts(e)
31
- && e.ownAttr("emitTanstack") !== false
31
+ && e.ownAttr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
32
32
  && userFilter(e)
33
33
  && hasDataGridLayout(e),
34
34
  generate: perEntity(async (entity, ctx) => {
@@ -1,6 +1,6 @@
1
1
  import type { MetaObject } from "@metaobjectsdev/metadata";
2
2
  import { LAYOUT_SUBTYPE_DATA_GRID } from "@metaobjectsdev/metadata";
3
- import { perEntity, type Generator, type GeneratorFactory, formatTs, entityOutputPath, emitsInstanceArtifacts } from "@metaobjectsdev/codegen-ts";
3
+ import { perEntity, type Generator, type GeneratorFactory, formatTs, entityOutputPath, emitsInstanceArtifacts, isTphSubtype, CODEGEN_ATTR_EMIT_TANSTACK, CODEGEN_ATTR_EMIT_GRID } from "@metaobjectsdev/codegen-ts";
4
4
  import { renderColumnsFile } from "./templates/columns-file.js";
5
5
 
6
6
  export interface TanstackGridOpts {
@@ -24,11 +24,16 @@ export const tanstackGrid = function tanstackGrid(opts?: TanstackGridOpts): Gene
24
24
  name: "tanstack-grid",
25
25
  // Always set: AND-composes the framework instance-artifact guard (skips
26
26
  // abstract types), opt-out, user filter, and dataGrid layout presence.
27
+ // FR-017 Tier 3: a TPH discriminator base emits ONE polymorphic grid. Its
28
+ // subtypes inherit the base's dataGrid layout via extends, but per-subtype
29
+ // grids are opt-IN only (own `@emitGrid: true`) — otherwise the polymorphic
30
+ // grid is the single source of truth.
27
31
  filter: (e: MetaObject) =>
28
32
  emitsInstanceArtifacts(e)
29
- && e.ownAttr("emitTanstack") !== false
33
+ && e.ownAttr(CODEGEN_ATTR_EMIT_TANSTACK) !== false
30
34
  && userFilter(e)
31
- && hasDataGridLayout(e),
35
+ && hasDataGridLayout(e)
36
+ && (!isTphSubtype(e) || e.ownAttr(CODEGEN_ATTR_EMIT_GRID) === true),
32
37
  generate: perEntity(async (entity, ctx) => {
33
38
  if (!ctx.renderContext) {
34
39
  throw new Error("tanstack-grid: renderContext is required (provided by runGen)");
@@ -1,5 +1,5 @@
1
1
  import type { MetaObject } from "@metaobjectsdev/metadata";
2
- import { perEntity, type Generator, type GeneratorFactory, formatTs, entityOutputPath, emitsInstanceArtifacts } from "@metaobjectsdev/codegen-ts";
2
+ import { perEntity, type Generator, type GeneratorFactory, formatTs, entityOutputPath, emitsInstanceArtifacts, isTphSubtype, CODEGEN_ATTR_EMIT_TANSTACK } from "@metaobjectsdev/codegen-ts";
3
3
  import { renderHooksFile } from "./templates/hooks-file.js";
4
4
 
5
5
  export interface TanstackQueryOpts {
@@ -22,7 +22,9 @@ export const tanstackQuery = function tanstackQuery(opts?: TanstackQueryOpts): G
22
22
  // they contribute shape via inheritance only and have no instance to query),
23
23
  // the metadata opt-out, and the optional user filter. Projections still pass
24
24
  // here and get read-only hooks via renderHooksFile's isProjection branch.
25
- filter: (e: MetaObject) => emitsInstanceArtifacts(e) && e.ownAttr("emitTanstack") !== false && userFilter(e),
25
+ // FR-017 Tier 3: TPH subtypes get no standalone hooks file their per-subtype
26
+ // hooks live in the discriminator base's hooks file (polymorphic + per-subtype).
27
+ filter: (e: MetaObject) => emitsInstanceArtifacts(e) && e.ownAttr(CODEGEN_ATTR_EMIT_TANSTACK) !== false && !isTphSubtype(e) && userFilter(e),
26
28
  generate: perEntity(async (entity, ctx) => {
27
29
  if (!ctx.renderContext) {
28
30
  throw new Error(
@@ -8,9 +8,23 @@ import {
8
8
  LAYOUT_DATA_GRID_ATTR_FILTERABLE,
9
9
  LAYOUT_DATA_GRID_ATTR_FILTER,
10
10
  LAYOUT_DATA_GRID_ATTR_COLUMNS,
11
+ OBJECT_ATTR_DISCRIMINATOR,
11
12
  } from "@metaobjectsdev/metadata";
12
13
  import type { RenderContext } from "@metaobjectsdev/codegen-ts";
13
- import { GENERATED_HEADER, entityModuleSpecifier } from "@metaobjectsdev/codegen-ts";
14
+ import {
15
+ GENERATED_HEADER,
16
+ entityModuleSpecifier,
17
+ isTphDiscriminatorBase,
18
+ collectTphSubtypeFields,
19
+ } from "@metaobjectsdev/codegen-ts";
20
+
21
+ /** FR-017 TPH grid context, threaded into extractGrids when the entity is a
22
+ * discriminator base: the discriminator field name (badged in the polymorphic
23
+ * grid) and the subtype-only fields folded in as extra columns. */
24
+ interface TphGridInfo {
25
+ discField: string;
26
+ subtypeFields: MetaField[];
27
+ }
14
28
 
15
29
  interface ColumnSpec {
16
30
  id: string;
@@ -57,10 +71,12 @@ function fieldLabel(field: MetaField): string {
57
71
  * fall back to all fields on the entity (pre-E-T2 behaviour, kept for
58
72
  * backwards compat with metadata not yet migrated by E-T4).
59
73
  */
60
- function extractGrids(entity: MetaObject): GridSpec[] {
74
+ function extractGrids(entity: MetaObject, tph?: TphGridInfo): GridSpec[] {
61
75
  // fields() and layouts() are both effective (own + inherited via extends:/super:).
76
+ // For a TPH base, also index the subtype-only fields so they can be folded
77
+ // into the polymorphic grid's column set.
62
78
  const fieldsByName = new Map(
63
- entity.fields().map((f) => [f.name, f] as const),
79
+ [...entity.fields(), ...(tph?.subtypeFields ?? [])].map((f) => [f.name, f] as const),
64
80
  );
65
81
 
66
82
  const grids: GridSpec[] = [];
@@ -74,6 +90,16 @@ function extractGrids(entity: MetaObject): GridSpec[] {
74
90
  ? (columnsAttr as unknown[]).filter((x): x is string => typeof x === "string")
75
91
  : [...fieldsByName.keys()];
76
92
 
93
+ // FR-017: a polymorphic (TPH base) grid folds in every subtype-only column
94
+ // so mixed rows can render their subtype fields (the runtime grid renders a
95
+ // null cell as an em-dash for rows of other subtypes). Appended after the
96
+ // declared columns, deduped.
97
+ if (tph) {
98
+ for (const f of tph.subtypeFields) {
99
+ if (!columnNames.includes(f.name)) columnNames.push(f.name);
100
+ }
101
+ }
102
+
77
103
  const columns: ColumnSpec[] = columnNames.flatMap((name) => {
78
104
  const field = fieldsByName.get(name);
79
105
  if (!field) return []; // columns ref that doesn't exist on entity; defensive skip
@@ -82,6 +108,8 @@ function extractGrids(entity: MetaObject): GridSpec[] {
82
108
  header: fieldLabel(field),
83
109
  viewKind: fieldViewKind(field),
84
110
  };
111
+ // FR-017: the discriminator column renders as a subtype badge.
112
+ if (tph && name === tph.discField) spec.renderer = "badge";
85
113
  return [spec];
86
114
  });
87
115
 
@@ -121,7 +149,17 @@ function renderColumnDef(col: ColumnSpec): string {
121
149
  export function renderColumnsFile(entity: MetaObject, ctx: RenderContext): string {
122
150
  const entityName = entity.name;
123
151
  const lcEntity = entityName.charAt(0).toLowerCase() + entityName.slice(1);
124
- const grids = extractGrids(entity);
152
+ // FR-017: a TPH discriminator base emits a polymorphic grid typed against the
153
+ // raw single-table row (<Base>Row — all columns, subtype-only nullable),
154
+ // NOT the discriminated union (whose members lack the other subtypes' fields).
155
+ const tphBase = isTphDiscriminatorBase(entity, ctx.loadedRoot);
156
+ const tph: TphGridInfo | undefined = tphBase
157
+ ? {
158
+ discField: (entity.ownAttr(OBJECT_ATTR_DISCRIMINATOR) as string) ?? "",
159
+ subtypeFields: collectTphSubtypeFields(entity, ctx.loadedRoot),
160
+ }
161
+ : undefined;
162
+ const grids = extractGrids(entity, tph);
125
163
 
126
164
  const ColumnDefSym = imp("t:ColumnDef@@tanstack/react-table");
127
165
 
@@ -179,9 +217,12 @@ export const ${filterConstName}: ${entityName}Filter = ${JSON.stringify(grid.fil
179
217
  ctx.extStyle,
180
218
  );
181
219
  // Import <Entity>Row always; import <Entity>Filter only when a filter const is emitted.
220
+ // TPH base: <Base>Row is a distinct export (the all-columns row); import it
221
+ // directly. Non-TPH: the entity type IS the row, imported under the Row alias.
222
+ const rowImport = tphBase ? `${entityName}Row` : `${entityName} as ${entityName}Row`;
182
223
  const entityImportCode = hasFilterConst
183
- ? code`import type { ${entityName} as ${entityName}Row, ${entityName}Filter } from ${JSON.stringify(entityModule)};`
184
- : code`import type { ${entityName} as ${entityName}Row } from ${JSON.stringify(entityModule)};`;
224
+ ? code`import type { ${rowImport}, ${entityName}Filter } from ${JSON.stringify(entityModule)};`
225
+ : code`import type { ${rowImport} } from ${JSON.stringify(entityModule)};`;
185
226
 
186
227
  const body: Code = joinCode(sections, { on: "\n" });
187
228
  return header + entityImportCode.toString() + "\n" + body.toString();
@@ -1,7 +1,14 @@
1
- import { code, imp, joinCode, type Code } from "ts-poet";
1
+ import { code, imp, joinCode, Import, type Code } from "ts-poet";
2
2
  import type { MetaObject } from "@metaobjectsdev/metadata";
3
- import type { RenderContext } from "@metaobjectsdev/codegen-ts";
4
- import { GENERATED_HEADER, isProjection, pluralize, entityModuleSpecifier } from "@metaobjectsdev/codegen-ts";
3
+ import type { RenderContext, RelationEntry } from "@metaobjectsdev/codegen-ts";
4
+ import {
5
+ GENERATED_HEADER,
6
+ isProjection,
7
+ pluralize,
8
+ entityModuleSpecifier,
9
+ isTphDiscriminatorBase,
10
+ tphPlan,
11
+ } from "@metaobjectsdev/codegen-ts";
5
12
 
6
13
  /**
7
14
  * Render <Entity>.hooks.ts — query-key factory + 2 query hooks + (for non-projections) 3 mutation hooks.
@@ -29,21 +36,125 @@ export function renderHooksFile(entity: MetaObject, ctx: RenderContext): string
29
36
  entity.name,
30
37
  ctx.extStyle,
31
38
  );
39
+ // FR-017 Tier 3: a TPH discriminator base gets a polymorphic + per-subtype
40
+ // hooks file (the subtype entities are filtered out of this generator).
41
+ if (isTphDiscriminatorBase(entity, ctx.loadedRoot)) {
42
+ return renderTphHooksFile(entity, ctx, entityModule);
43
+ }
32
44
  if (isProjection(entity)) {
33
- return renderReadOnlyHooksFile(entity, entityModule);
45
+ return renderReadOnlyHooksFile(entity, entityModule, ctx);
46
+ }
47
+ return renderFullHooksFile(entity, entityModule, ctx);
48
+ }
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // FR-018 — M:N collection hook(s).
52
+ //
53
+ // For each many-to-many relationship the source declares (`@cardinality: "many"`
54
+ // + `@through`), emit `use<Source><Relation>(sourceId, opts?)` — a useQuery that
55
+ // fetches the REST sub-resource `GET /<source-plural>/{sourceId}/<relationName>`
56
+ // (the exact URL mountM2mRoute serves) and returns the typed target collection
57
+ // (`Target[]`). The query is enabled only when sourceId is present, so callers
58
+ // can pass `undefined` before the parent row loads. A symmetric self-join is
59
+ // still ONE collection hook (the server unions both junction columns on read).
60
+ // ---------------------------------------------------------------------------
61
+
62
+ /** The M:N relation entries for an entity (cardinality 'many' + a junction). */
63
+ function m2mEntriesFor(entity: MetaObject, ctx: RenderContext): RelationEntry[] {
64
+ return (ctx.relationMap.get(entity.name) ?? []).filter(
65
+ (e) => e.cardinality === "many" && e.junctionEntity !== undefined,
66
+ );
67
+ }
68
+
69
+ /** The `relation: (relation, sourceId) => ...` query-key factory line, included
70
+ * in the keys factory ONLY when the entity has M:N relationships. */
71
+ function m2mKeyLine(keysVar: string): string {
72
+ return (
73
+ ` relation: (relation: string, sourceId: number | undefined) =>\n` +
74
+ ` [...${keysVar}.all(), "relation", relation, sourceId ?? null] as const,`
75
+ );
76
+ }
77
+
78
+ /**
79
+ * Render `use<Source><Relation>(sourceId, opts?)` per M:N relationship. Returns
80
+ * null when the entity has no M:N relationships (no extra hooks emitted).
81
+ */
82
+ function renderM2mHooks(
83
+ entity: MetaObject,
84
+ ctx: RenderContext,
85
+ keysVar: string,
86
+ entries: RelationEntry[],
87
+ ): Code | null {
88
+ if (entries.length === 0) return null;
89
+
90
+ const useQuerySym = imp("useQuery@@tanstack/react-query");
91
+ const useQueryOptionsSym = imp("t:UseQueryOptions@@tanstack/react-query");
92
+ const useQueryResultSym = imp("t:UseQueryResult@@tanstack/react-query");
93
+ const useEntityFetcherSym = imp("useEntityFetcher@@metaobjectsdev/tanstack");
94
+
95
+ const source = entity.name;
96
+
97
+ // Distinct target row types, imported (aliased) from each target's entity
98
+ // module. ts-poet's imp() tracks + hoists these into the import block. The
99
+ // <Target>RelRow alias avoids colliding with the source file's own
100
+ // `type <Source> as <Source>Row` import on a self-join (source === target).
101
+ const targetTypeSym = new Map<string, Import>();
102
+ for (const e of entries) {
103
+ if (targetTypeSym.has(e.targetEntity)) continue;
104
+ const mod = entityModuleSpecifier(
105
+ ctx.selfTarget,
106
+ ctx.entityModuleTarget,
107
+ ctx.packageOf.get(e.targetEntity),
108
+ e.targetEntity,
109
+ ctx.extStyle,
110
+ );
111
+ // `import { type <Target> as <Target>RelRow } from "<mod>"` — the RelRow
112
+ // alias avoids colliding with the source file's own `type <Source> as
113
+ // <Source>Row` import on a self-join (source === target).
114
+ targetTypeSym.set(
115
+ e.targetEntity,
116
+ Import.importsName(`${e.targetEntity}RelRow`, mod, true, e.targetEntity),
117
+ );
34
118
  }
35
- return renderFullHooksFile(entity, entityModule);
119
+
120
+ const hooks = entries.map((e) => {
121
+ const targetSym = targetTypeSym.get(e.targetEntity)!;
122
+ const hookName = `use${source}${capitalize(e.name)}`;
123
+ const relLit = JSON.stringify(e.name);
124
+ return code`
125
+ export function ${hookName}(
126
+ sourceId: number | undefined,
127
+ opts?: Omit<${useQueryOptionsSym}<${targetSym}[]>, "queryKey" | "queryFn">,
128
+ ): ${useQueryResultSym}<${targetSym}[]> {
129
+ const fetcher = ${useEntityFetcherSym}();
130
+ return ${useQuerySym}<${targetSym}[]>({
131
+ queryKey: ${keysVar}.relation(${relLit}, sourceId),
132
+ queryFn: () => fetcher<${targetSym}[]>(\`\${${source}.$apiPrefix}\${${source}.$path}/\${sourceId}/${e.name}\`),
133
+ enabled: sourceId != null && (opts?.enabled ?? true),
134
+ ...opts,
135
+ });
136
+ }
137
+ `;
138
+ });
139
+
140
+ return joinCode(hooks, { on: "\n" });
141
+ }
142
+
143
+ function capitalize(s: string): string {
144
+ return s.charAt(0).toUpperCase() + s.slice(1);
36
145
  }
37
146
 
38
147
  // ---------------------------------------------------------------------------
39
148
  // Read-only path (projections)
40
149
  // ---------------------------------------------------------------------------
41
150
 
42
- function renderReadOnlyHooksFile(entity: MetaObject, entityModule: string): string {
151
+ function renderReadOnlyHooksFile(entity: MetaObject, entityModule: string, ctx: RenderContext): string {
43
152
  const entityName = entity.name;
44
153
  const entityNamePlural = pluralize(entityName);
45
154
  const lcEntity = entityName.charAt(0).toLowerCase() + entityName.slice(1);
46
155
  const keysVar = `${lcEntity}Keys`;
156
+ const m2mEntries = m2mEntriesFor(entity, ctx);
157
+ const relationKeyLine = m2mEntries.length > 0 ? `\n${m2mKeyLine(keysVar)}` : "";
47
158
 
48
159
  const useQuerySym = imp("useQuery@@tanstack/react-query");
49
160
  const useQueryOptionsSym = imp("t:UseQueryOptions@@tanstack/react-query");
@@ -65,7 +176,7 @@ export const ${keysVar} = {
65
176
  lists: () => [...${keysVar}.all(), "list"] as const,
66
177
  list: (filter?: ${entityName}Filter) => [...${keysVar}.lists(), filter ?? {}] as const,
67
178
  details: () => [...${keysVar}.all(), "detail"] as const,
68
- detail: (id: number) => [...${keysVar}.details(), id] as const,
179
+ detail: (id: number) => [...${keysVar}.details(), id] as const,${relationKeyLine}
69
180
  };
70
181
  `;
71
182
 
@@ -96,7 +207,8 @@ export function use${entityNamePlural}(
96
207
  }
97
208
  `;
98
209
 
99
- const body: Code = joinCode([queryKeys, queries], { on: "\n" });
210
+ const m2mHooks = renderM2mHooks(entity, ctx, keysVar, m2mEntries);
211
+ const body: Code = joinCode(m2mHooks ? [queryKeys, queries, m2mHooks] : [queryKeys, queries], { on: "\n" });
100
212
 
101
213
  const header =
102
214
  `// ${GENERATED_HEADER}-tanstack — DO NOT EDIT.\n` +
@@ -108,11 +220,13 @@ export function use${entityNamePlural}(
108
220
  // Full path (writable entities — table-backed or write-through)
109
221
  // ---------------------------------------------------------------------------
110
222
 
111
- function renderFullHooksFile(entity: MetaObject, entityModule: string): string {
223
+ function renderFullHooksFile(entity: MetaObject, entityModule: string, ctx: RenderContext): string {
112
224
  const entityName = entity.name;
113
225
  const entityNamePlural = pluralize(entityName);
114
226
  const lcEntity = entityName.charAt(0).toLowerCase() + entityName.slice(1);
115
227
  const keysVar = `${lcEntity}Keys`;
228
+ const m2mEntries = m2mEntriesFor(entity, ctx);
229
+ const relationKeyLine = m2mEntries.length > 0 ? `\n${m2mKeyLine(keysVar)}` : "";
116
230
 
117
231
  const useMutationSym = imp("useMutation@@tanstack/react-query");
118
232
  const useQuerySym = imp("useQuery@@tanstack/react-query");
@@ -140,7 +254,7 @@ export const ${keysVar} = {
140
254
  lists: () => [...${keysVar}.all(), "list"] as const,
141
255
  list: (filter?: ${entityName}Filter) => [...${keysVar}.lists(), filter ?? {}] as const,
142
256
  details: () => [...${keysVar}.all(), "detail"] as const,
143
- detail: (id: number) => [...${keysVar}.details(), id] as const,
257
+ detail: (id: number) => [...${keysVar}.details(), id] as const,${relationKeyLine}
144
258
  };
145
259
  `;
146
260
 
@@ -171,6 +285,8 @@ export function use${entityNamePlural}(
171
285
  }
172
286
  `;
173
287
 
288
+ const m2mHooks = renderM2mHooks(entity, ctx, keysVar, m2mEntries);
289
+
174
290
  const mutations: Code = code`
175
291
  export function useCreate${entityName}(
176
292
  opts?: Omit<${useMutationOptionsSym}<${entityName}Row, Error, ${entityName}Insert>, "mutationFn">,
@@ -226,10 +342,192 @@ export function useDelete${entityName}(
226
342
  }
227
343
  `;
228
344
 
229
- const body: Code = joinCode([queryKeys, queries, mutations], { on: "\n" });
345
+ const body: Code = joinCode(
346
+ m2mHooks ? [queryKeys, queries, m2mHooks, mutations] : [queryKeys, queries, mutations],
347
+ { on: "\n" },
348
+ );
230
349
 
231
350
  const header =
232
351
  `// ${GENERATED_HEADER}-tanstack — DO NOT EDIT.\n` +
233
352
  `// Source metadata: ${entityName} (${entity.fqn()})\n`;
234
353
  return header + entityImports.toString() + body.toString();
235
354
  }
355
+
356
+ // ---------------------------------------------------------------------------
357
+ // FR-017 Tier 3 — TPH discriminator base: polymorphic + per-subtype hooks.
358
+ // ---------------------------------------------------------------------------
359
+
360
+ function renderTphHooksFile(base: MetaObject, ctx: RenderContext, baseModule: string): string {
361
+ const baseName = base.name;
362
+ const lcBase = baseName.charAt(0).toLowerCase() + baseName.slice(1);
363
+ const keysVar = `${lcBase}Keys`;
364
+ // Single source of truth for discriminator field + subtypes + route segments.
365
+ const plan = tphPlan(base, ctx.loadedRoot)!;
366
+ const discField = plan.discriminatorField;
367
+
368
+ const useQuerySym = imp("useQuery@@tanstack/react-query");
369
+ const useMutationSym = imp("useMutation@@tanstack/react-query");
370
+ const useQueryClientSym = imp("useQueryClient@@tanstack/react-query");
371
+ const useQueryOptionsSym = imp("t:UseQueryOptions@@tanstack/react-query");
372
+ const useMutationOptionsSym = imp("t:UseMutationOptions@@tanstack/react-query");
373
+ const useQueryResultSym = imp("t:UseQueryResult@@tanstack/react-query");
374
+ const useMutationResultSym = imp("t:UseMutationResult@@tanstack/react-query");
375
+ const useEntityFetcherSym = imp("useEntityFetcher@@metaobjectsdev/tanstack");
376
+ const buildFilterQsSym = imp("buildFilterQs@@metaobjectsdev/runtime-web");
377
+
378
+ const subtypes = plan.subtypes;
379
+
380
+ // `${baseName}` imports BOTH the constants value (for $path/$apiPrefix) and the
381
+ // discriminated-union type (declaration merge). Each subtype contributes its
382
+ // interface type AND its own filter type (discriminator-excluded — the route
383
+ // pins it), so per-subtype hooks filter on the fields the per-subtype
384
+ // allowlist actually permits.
385
+ const subImportLines = subtypes
386
+ .map((s) => {
387
+ const m = entityModuleSpecifier(ctx.selfTarget, ctx.entityModuleTarget, s.entity.package, s.entity.name, ctx.extStyle);
388
+ return `import { type ${s.entity.name}, type ${s.entity.name}Filter } from ${JSON.stringify(m)};`;
389
+ })
390
+ .join("\n");
391
+ const entityImports: Code = code`
392
+ import { ${baseName}, type ${baseName}Filter } from ${JSON.stringify(baseModule)};
393
+ ${subImportLines}
394
+ `;
395
+
396
+ const queryKeys: Code = code`
397
+ export const ${keysVar} = {
398
+ all: () => [${JSON.stringify(lcBase)}] as const,
399
+ lists: () => [...${keysVar}.all(), "list"] as const,
400
+ list: (filter?: ${baseName}Filter) => [...${keysVar}.lists(), filter ?? {}] as const,
401
+ details: () => [...${keysVar}.all(), "detail"] as const,
402
+ detail: (id: number) => [...${keysVar}.details(), id] as const,
403
+ subtypeLists: (sub: string) => [...${keysVar}.all(), sub, "list"] as const,
404
+ // filter is loosely typed here (cache-key identity only); the per-subtype
405
+ // hooks below type it precisely as <Sub>Filter.
406
+ subtypeList: (sub: string, filter?: unknown) => [...${keysVar}.subtypeLists(sub), filter ?? {}] as const,
407
+ subtypeDetails:(sub: string) => [...${keysVar}.all(), sub, "detail"] as const,
408
+ subtypeDetail: (sub: string, id: number) => [...${keysVar}.subtypeDetails(sub), id] as const,
409
+ };
410
+ `;
411
+
412
+ // Polymorphic reads — return the discriminated union.
413
+ const polymorphic: Code = code`
414
+ export function use${baseName}(
415
+ id: number,
416
+ opts?: Omit<${useQueryOptionsSym}<${baseName}>, "queryKey" | "queryFn">,
417
+ ): ${useQueryResultSym}<${baseName}> {
418
+ const fetcher = ${useEntityFetcherSym}();
419
+ return ${useQuerySym}<${baseName}>({
420
+ queryKey: ${keysVar}.detail(id),
421
+ queryFn: () => fetcher<${baseName}>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/\${id}\`),
422
+ ...opts,
423
+ });
424
+ }
425
+
426
+ export function use${pluralize(baseName)}(
427
+ filter?: ${baseName}Filter,
428
+ opts?: Omit<${useQueryOptionsSym}<${baseName}[]>, "queryKey" | "queryFn">,
429
+ ): ${useQueryResultSym}<${baseName}[]> {
430
+ const fetcher = ${useEntityFetcherSym}();
431
+ const qs = filter ? "?" + ${buildFilterQsSym}(filter as Record<string, unknown>) : "";
432
+ return ${useQuerySym}<${baseName}[]>({
433
+ queryKey: ${keysVar}.list(filter),
434
+ queryFn: () => fetcher<${baseName}[]>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}\${qs}\`),
435
+ ...opts,
436
+ });
437
+ }
438
+ `;
439
+
440
+ // Per-subtype hooks — scoped to each discriminator value's REST sub-path.
441
+ const subtypeSections: Code[] = subtypes.map(({ entity: subEntity, value, routeSegment: seg }) => {
442
+ const subName = subEntity.name;
443
+ const valueLit = JSON.stringify(value);
444
+ const createInput = `Omit<${subName}, ${JSON.stringify(discField)}>`;
445
+ const updateInput = `Partial<${createInput}>`;
446
+ const subPath = `\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/${seg}\``;
447
+ return code`
448
+ export function use${pluralize(subName)}(
449
+ filter?: ${subName}Filter,
450
+ opts?: Omit<${useQueryOptionsSym}<${subName}[]>, "queryKey" | "queryFn">,
451
+ ): ${useQueryResultSym}<${subName}[]> {
452
+ const fetcher = ${useEntityFetcherSym}();
453
+ const qs = filter ? "?" + ${buildFilterQsSym}(filter as Record<string, unknown>) : "";
454
+ return ${useQuerySym}<${subName}[]>({
455
+ queryKey: ${keysVar}.subtypeList(${valueLit}, filter),
456
+ queryFn: () => fetcher<${subName}[]>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/${seg}\${qs}\`),
457
+ ...opts,
458
+ });
459
+ }
460
+
461
+ export function use${subName}(
462
+ id: number,
463
+ opts?: Omit<${useQueryOptionsSym}<${subName}>, "queryKey" | "queryFn">,
464
+ ): ${useQueryResultSym}<${subName}> {
465
+ const fetcher = ${useEntityFetcherSym}();
466
+ return ${useQuerySym}<${subName}>({
467
+ queryKey: ${keysVar}.subtypeDetail(${valueLit}, id),
468
+ queryFn: () => fetcher<${subName}>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/${seg}/\${id}\`),
469
+ ...opts,
470
+ });
471
+ }
472
+
473
+ export function useCreate${subName}(
474
+ opts?: Omit<${useMutationOptionsSym}<${subName}, Error, ${createInput}>, "mutationFn">,
475
+ ): ${useMutationResultSym}<${subName}, Error, ${createInput}> {
476
+ const fetcher = ${useEntityFetcherSym}();
477
+ const qc = ${useQueryClientSym}();
478
+ return ${useMutationSym}<${subName}, Error, ${createInput}>({
479
+ mutationFn: (input) => fetcher<${subName}>(${subPath}, {
480
+ method: "POST",
481
+ headers: { "Content-Type": "application/json" },
482
+ body: JSON.stringify(input),
483
+ }),
484
+ ...opts,
485
+ onSuccess: (...args) => {
486
+ qc.invalidateQueries({ queryKey: ${keysVar}.all() });
487
+ opts?.onSuccess?.(...args);
488
+ },
489
+ });
490
+ }
491
+
492
+ export function useUpdate${subName}(
493
+ opts?: Omit<${useMutationOptionsSym}<${subName}, Error, { id: number; input: ${updateInput} }>, "mutationFn">,
494
+ ): ${useMutationResultSym}<${subName}, Error, { id: number; input: ${updateInput} }> {
495
+ const fetcher = ${useEntityFetcherSym}();
496
+ const qc = ${useQueryClientSym}();
497
+ return ${useMutationSym}({
498
+ mutationFn: ({ id, input }) => fetcher<${subName}>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/${seg}/\${id}\`, {
499
+ method: "PATCH",
500
+ headers: { "Content-Type": "application/json" },
501
+ body: JSON.stringify(input),
502
+ }),
503
+ ...opts,
504
+ onSuccess: (...args) => {
505
+ qc.invalidateQueries({ queryKey: ${keysVar}.all() });
506
+ opts?.onSuccess?.(...args);
507
+ },
508
+ });
509
+ }
510
+
511
+ export function useDelete${subName}(
512
+ opts?: Omit<${useMutationOptionsSym}<void, Error, number>, "mutationFn">,
513
+ ): ${useMutationResultSym}<void, Error, number> {
514
+ const fetcher = ${useEntityFetcherSym}();
515
+ const qc = ${useQueryClientSym}();
516
+ return ${useMutationSym}({
517
+ mutationFn: (id) => fetcher<void>(\`\${${baseName}.$apiPrefix}\${${baseName}.$path}/${seg}/\${id}\`, { method: "DELETE" }),
518
+ ...opts,
519
+ onSuccess: (...args) => {
520
+ qc.invalidateQueries({ queryKey: ${keysVar}.all() });
521
+ opts?.onSuccess?.(...args);
522
+ },
523
+ });
524
+ }
525
+ `;
526
+ });
527
+
528
+ const body: Code = joinCode([queryKeys, polymorphic, ...subtypeSections], { on: "\n" });
529
+ const header =
530
+ `// ${GENERATED_HEADER}-tanstack — DO NOT EDIT.\n` +
531
+ `// Source metadata: ${baseName} (${base.fqn()}) — TPH discriminator base\n`;
532
+ return header + entityImports.toString() + body.toString();
533
+ }