@metaobjectsdev/codegen-ts 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 +1 -1
- package/dist/column-mapper.d.ts +12 -6
- package/dist/column-mapper.d.ts.map +1 -1
- package/dist/column-mapper.js +68 -28
- package/dist/column-mapper.js.map +1 -1
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +16 -0
- package/dist/constants.js.map +1 -1
- package/dist/docs-paths.d.ts +58 -0
- package/dist/docs-paths.d.ts.map +1 -0
- package/dist/docs-paths.js +89 -0
- package/dist/docs-paths.js.map +1 -0
- package/dist/enum-import.d.ts +14 -0
- package/dist/enum-import.d.ts.map +1 -0
- package/dist/enum-import.js +35 -0
- package/dist/enum-import.js.map +1 -0
- package/dist/enum-shared.d.ts +32 -0
- package/dist/enum-shared.d.ts.map +1 -0
- package/dist/enum-shared.js +83 -0
- package/dist/enum-shared.js.map +1 -0
- package/dist/generator-registry.d.ts +22 -0
- package/dist/generator-registry.d.ts.map +1 -0
- package/dist/generator-registry.js +161 -0
- package/dist/generator-registry.js.map +1 -0
- package/dist/generator.d.ts +6 -0
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js.map +1 -1
- package/dist/generators/api-doc-render.d.ts +17 -0
- package/dist/generators/api-doc-render.d.ts.map +1 -0
- package/dist/generators/api-doc-render.js +431 -0
- package/dist/generators/api-doc-render.js.map +1 -0
- package/dist/generators/api-docs-file.d.ts +21 -0
- package/dist/generators/api-docs-file.d.ts.map +1 -0
- package/dist/generators/api-docs-file.js +112 -0
- package/dist/generators/api-docs-file.js.map +1 -0
- package/dist/generators/api-field-shape.d.ts +39 -0
- package/dist/generators/api-field-shape.d.ts.map +1 -0
- package/dist/generators/api-field-shape.js +92 -0
- package/dist/generators/api-field-shape.js.map +1 -0
- package/dist/generators/api-label.d.ts +3 -0
- package/dist/generators/api-label.d.ts.map +1 -0
- package/dist/generators/api-label.js +8 -0
- package/dist/generators/api-label.js.map +1 -0
- package/dist/generators/api-model.d.ts +122 -0
- package/dist/generators/api-model.d.ts.map +1 -0
- package/dist/generators/api-model.js +809 -0
- package/dist/generators/api-model.js.map +1 -0
- package/dist/generators/docs-data-builder.d.ts +26 -4
- package/dist/generators/docs-data-builder.d.ts.map +1 -1
- package/dist/generators/docs-data-builder.js +439 -167
- package/dist/generators/docs-data-builder.js.map +1 -1
- package/dist/generators/docs-data.d.ts +136 -27
- package/dist/generators/docs-data.d.ts.map +1 -1
- package/dist/generators/docs-data.js +1 -1
- package/dist/generators/docs-data.js.map +1 -1
- package/dist/generators/docs-file.d.ts +19 -0
- package/dist/generators/docs-file.d.ts.map +1 -1
- package/dist/generators/docs-file.js +154 -27
- package/dist/generators/docs-file.js.map +1 -1
- package/dist/generators/entity-file.d.ts.map +1 -1
- package/dist/generators/entity-file.js +29 -14
- package/dist/generators/entity-file.js.map +1 -1
- package/dist/generators/extractor-file.d.ts.map +1 -1
- package/dist/generators/extractor-file.js +2 -1
- package/dist/generators/extractor-file.js.map +1 -1
- package/dist/generators/field-anchor.d.ts +7 -0
- package/dist/generators/field-anchor.d.ts.map +1 -0
- package/dist/generators/field-anchor.js +23 -0
- package/dist/generators/field-anchor.js.map +1 -0
- package/dist/generators/index.d.ts +8 -1
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +6 -0
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/mermaid-er.d.ts +14 -0
- package/dist/generators/mermaid-er.d.ts.map +1 -1
- package/dist/generators/mermaid-er.js +14 -0
- package/dist/generators/mermaid-er.js.map +1 -1
- package/dist/generators/output-parser-file.d.ts.map +1 -1
- package/dist/generators/output-parser-file.js +3 -4
- package/dist/generators/output-parser-file.js.map +1 -1
- package/dist/generators/output-prompt-file.d.ts.map +1 -1
- package/dist/generators/output-prompt-file.js +2 -2
- package/dist/generators/output-prompt-file.js.map +1 -1
- package/dist/generators/prompt-render-file.d.ts.map +1 -1
- package/dist/generators/prompt-render-file.js +3 -4
- package/dist/generators/prompt-render-file.js.map +1 -1
- package/dist/generators/queries-file.d.ts.map +1 -1
- package/dist/generators/queries-file.js +8 -3
- package/dist/generators/queries-file.js.map +1 -1
- package/dist/generators/render-helper-file.d.ts.map +1 -1
- package/dist/generators/render-helper-file.js +2 -2
- package/dist/generators/render-helper-file.js.map +1 -1
- package/dist/generators/routes-file-hono.d.ts.map +1 -1
- package/dist/generators/routes-file-hono.js +5 -1
- package/dist/generators/routes-file-hono.js.map +1 -1
- package/dist/generators/routes-file.d.ts +3 -0
- package/dist/generators/routes-file.d.ts.map +1 -1
- package/dist/generators/routes-file.js +6 -1
- package/dist/generators/routes-file.js.map +1 -1
- package/dist/generators/template-doc-builder.d.ts +19 -0
- package/dist/generators/template-doc-builder.d.ts.map +1 -0
- package/dist/generators/template-doc-builder.js +220 -0
- package/dist/generators/template-doc-builder.js.map +1 -0
- package/dist/generators/template-doc-data.d.ts +62 -0
- package/dist/generators/template-doc-data.d.ts.map +1 -0
- package/dist/generators/template-doc-data.js +16 -0
- package/dist/generators/template-doc-data.js.map +1 -0
- package/dist/generators/template-payload-tree.d.ts +15 -0
- package/dist/generators/template-payload-tree.d.ts.map +1 -0
- package/dist/generators/template-payload-tree.js +61 -0
- package/dist/generators/template-payload-tree.js.map +1 -0
- package/dist/generators/template-source-annotate.d.ts +74 -0
- package/dist/generators/template-source-annotate.d.ts.map +1 -0
- package/dist/generators/template-source-annotate.js +184 -0
- package/dist/generators/template-source-annotate.js.map +1 -0
- package/dist/generators/template-source-render.d.ts +24 -0
- package/dist/generators/template-source-render.d.ts.map +1 -0
- package/dist/generators/template-source-render.js +175 -0
- package/dist/generators/template-source-render.js.map +1 -0
- package/dist/generators/trace-helper-file.d.ts +9 -0
- package/dist/generators/trace-helper-file.d.ts.map +1 -0
- package/dist/generators/trace-helper-file.js +196 -0
- package/dist/generators/trace-helper-file.js.map +1 -0
- package/dist/import-path.d.ts +18 -0
- package/dist/import-path.d.ts.map +1 -1
- package/dist/import-path.js +21 -0
- package/dist/import-path.js.map +1 -1
- package/dist/index.d.ts +29 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -2
- package/dist/index.js.map +1 -1
- package/dist/metaobjects-config.d.ts +101 -2
- package/dist/metaobjects-config.d.ts.map +1 -1
- package/dist/metaobjects-config.js +46 -0
- package/dist/metaobjects-config.js.map +1 -1
- package/dist/naming.d.ts +39 -2
- package/dist/naming.d.ts.map +1 -1
- package/dist/naming.js +52 -3
- package/dist/naming.js.map +1 -1
- package/dist/payload-codegen.d.ts.map +1 -1
- package/dist/payload-codegen.js +14 -6
- package/dist/payload-codegen.js.map +1 -1
- package/dist/pk-resolver.d.ts.map +1 -1
- package/dist/pk-resolver.js +4 -2
- package/dist/pk-resolver.js.map +1 -1
- package/dist/projection/extract-view-spec.d.ts.map +1 -1
- package/dist/projection/extract-view-spec.js +52 -26
- package/dist/projection/extract-view-spec.js.map +1 -1
- package/dist/relation-resolver.d.ts +16 -0
- package/dist/relation-resolver.d.ts.map +1 -1
- package/dist/relation-resolver.js +82 -1
- package/dist/relation-resolver.js.map +1 -1
- package/dist/render-context.d.ts +25 -2
- package/dist/render-context.d.ts.map +1 -1
- package/dist/render-context.js +7 -0
- package/dist/render-context.js.map +1 -1
- package/dist/render-engine/embedded-templates.generated.d.ts +2 -0
- package/dist/render-engine/embedded-templates.generated.d.ts.map +1 -0
- package/dist/render-engine/embedded-templates.generated.js +15 -0
- package/dist/render-engine/embedded-templates.generated.js.map +1 -0
- package/dist/render-engine/framework-provider.d.ts.map +1 -1
- package/dist/render-engine/framework-provider.js +26 -13
- package/dist/render-engine/framework-provider.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +20 -0
- package/dist/runner.js.map +1 -1
- package/dist/templates/docs-file.d.ts +2 -6
- package/dist/templates/docs-file.d.ts.map +1 -1
- package/dist/templates/docs-file.js +2 -5
- package/dist/templates/docs-file.js.map +1 -1
- package/dist/templates/drizzle-schema.d.ts.map +1 -1
- package/dist/templates/drizzle-schema.js +72 -23
- package/dist/templates/drizzle-schema.js.map +1 -1
- package/dist/templates/entity-constants.d.ts +7 -0
- package/dist/templates/entity-constants.d.ts.map +1 -1
- package/dist/templates/entity-constants.js +3 -3
- package/dist/templates/entity-constants.js.map +1 -1
- package/dist/templates/entity-file.d.ts.map +1 -1
- package/dist/templates/entity-file.js +16 -5
- package/dist/templates/entity-file.js.map +1 -1
- package/dist/templates/enums-file.d.ts +11 -0
- package/dist/templates/enums-file.d.ts.map +1 -0
- package/dist/templates/enums-file.js +44 -0
- package/dist/templates/enums-file.js.map +1 -0
- package/dist/templates/extract-delegate-emitter.d.ts.map +1 -1
- package/dist/templates/extract-delegate-emitter.js +6 -8
- package/dist/templates/extract-delegate-emitter.js.map +1 -1
- package/dist/templates/extractor.d.ts.map +1 -1
- package/dist/templates/extractor.js +58 -41
- package/dist/templates/extractor.js.map +1 -1
- package/dist/templates/field-meta.d.ts.map +1 -1
- package/dist/templates/field-meta.js +2 -6
- package/dist/templates/field-meta.js.map +1 -1
- package/dist/templates/filter-allowlist.d.ts +7 -2
- package/dist/templates/filter-allowlist.d.ts.map +1 -1
- package/dist/templates/filter-allowlist.js +18 -10
- package/dist/templates/filter-allowlist.js.map +1 -1
- package/dist/templates/filter-shared.js +2 -2
- package/dist/templates/filter-shared.js.map +1 -1
- package/dist/templates/filter-type.d.ts +7 -1
- package/dist/templates/filter-type.d.ts.map +1 -1
- package/dist/templates/filter-type.js +10 -6
- package/dist/templates/filter-type.js.map +1 -1
- package/dist/templates/find-templates.d.ts +4 -0
- package/dist/templates/find-templates.d.ts.map +1 -0
- package/dist/templates/find-templates.js +15 -0
- package/dist/templates/find-templates.js.map +1 -0
- package/dist/templates/fr010-field-mapping.d.ts +2 -0
- package/dist/templates/fr010-field-mapping.d.ts.map +1 -1
- package/dist/templates/fr010-field-mapping.js +15 -11
- package/dist/templates/fr010-field-mapping.js.map +1 -1
- package/dist/templates/inferred-types.d.ts +44 -7
- package/dist/templates/inferred-types.d.ts.map +1 -1
- package/dist/templates/inferred-types.js +121 -19
- package/dist/templates/inferred-types.js.map +1 -1
- package/dist/templates/mermaid-er.d.ts +35 -2
- package/dist/templates/mermaid-er.d.ts.map +1 -1
- package/dist/templates/mermaid-er.js +174 -7
- package/dist/templates/mermaid-er.js.map +1 -1
- package/dist/templates/output-format-spec-emitter.js +1 -1
- package/dist/templates/output-format-spec-emitter.js.map +1 -1
- package/dist/templates/output-parser.d.ts.map +1 -1
- package/dist/templates/output-parser.js +31 -80
- package/dist/templates/output-parser.js.map +1 -1
- package/dist/templates/output-prompt.d.ts.map +1 -1
- package/dist/templates/output-prompt.js +2 -2
- package/dist/templates/output-prompt.js.map +1 -1
- package/dist/templates/queries-file.d.ts.map +1 -1
- package/dist/templates/queries-file.js +113 -5
- package/dist/templates/queries-file.js.map +1 -1
- package/dist/templates/queries.d.ts +7 -2
- package/dist/templates/queries.d.ts.map +1 -1
- package/dist/templates/queries.js +15 -15
- package/dist/templates/queries.js.map +1 -1
- package/dist/templates/relations-block.d.ts.map +1 -1
- package/dist/templates/relations-block.js +12 -3
- package/dist/templates/relations-block.js.map +1 -1
- package/dist/templates/render-helper.d.ts.map +1 -1
- package/dist/templates/render-helper.js +5 -5
- package/dist/templates/render-helper.js.map +1 -1
- package/dist/templates/routes-file-hono.d.ts.map +1 -1
- package/dist/templates/routes-file-hono.js +1 -2
- package/dist/templates/routes-file-hono.js.map +1 -1
- package/dist/templates/routes-file.d.ts.map +1 -1
- package/dist/templates/routes-file.js +184 -7
- package/dist/templates/routes-file.js.map +1 -1
- package/dist/templates/tph-discriminator.d.ts +56 -0
- package/dist/templates/tph-discriminator.d.ts.map +1 -0
- package/dist/templates/tph-discriminator.js +180 -0
- package/dist/templates/tph-discriminator.js.map +1 -0
- package/dist/templates/value-object-file.d.ts +2 -1
- package/dist/templates/value-object-file.d.ts.map +1 -1
- package/dist/templates/value-object-file.js +33 -5
- package/dist/templates/value-object-file.js.map +1 -1
- package/dist/templates/zod-validators.d.ts +65 -2
- package/dist/templates/zod-validators.d.ts.map +1 -1
- package/dist/templates/zod-validators.js +202 -22
- package/dist/templates/zod-validators.js.map +1 -1
- package/package.json +103 -34
- package/src/column-mapper.ts +79 -32
- package/src/constants.ts +18 -0
- package/src/docs-paths.ts +128 -0
- package/src/enum-import.ts +43 -0
- package/src/enum-shared.ts +95 -0
- package/src/generator-registry.ts +204 -0
- package/src/generator.ts +6 -0
- package/src/generators/api-doc-render.ts +572 -0
- package/src/generators/api-docs-file.ts +146 -0
- package/src/generators/api-field-shape.ts +114 -0
- package/src/generators/api-label.ts +7 -0
- package/src/generators/api-model.ts +1067 -0
- package/src/generators/docs-data-builder.ts +483 -189
- package/src/generators/docs-data.ts +139 -28
- package/src/generators/docs-file.ts +205 -39
- package/src/generators/entity-file.ts +31 -15
- package/src/generators/extractor-file.ts +2 -1
- package/src/generators/field-anchor.ts +24 -0
- package/src/generators/index.ts +8 -1
- package/src/generators/mermaid-er.ts +14 -0
- package/src/generators/output-parser-file.ts +3 -4
- package/src/generators/output-prompt-file.ts +2 -1
- package/src/generators/prompt-render-file.ts +3 -4
- package/src/generators/queries-file.ts +9 -3
- package/src/generators/render-helper-file.ts +2 -1
- package/src/generators/routes-file-hono.ts +5 -1
- package/src/generators/routes-file.ts +7 -1
- package/src/generators/template-doc-builder.ts +306 -0
- package/src/generators/template-doc-data.ts +85 -0
- package/src/generators/template-payload-tree.ts +71 -0
- package/src/generators/template-source-annotate.ts +290 -0
- package/src/generators/template-source-render.ts +203 -0
- package/src/generators/trace-helper-file.ts +301 -0
- package/src/import-path.ts +28 -0
- package/src/index.ts +55 -4
- package/src/metaobjects-config.ts +146 -2
- package/src/naming.ts +73 -3
- package/src/payload-codegen.ts +16 -5
- package/src/pk-resolver.ts +4 -2
- package/src/projection/extract-view-spec.ts +50 -31
- package/src/relation-resolver.ts +103 -1
- package/src/render-context.ts +32 -2
- package/src/render-engine/embedded-templates.generated.ts +14 -0
- package/src/render-engine/framework-provider.ts +25 -11
- package/src/runner.ts +24 -0
- package/src/templates/docs-file.ts +2 -9
- package/src/templates/drizzle-schema.ts +80 -28
- package/src/templates/entity-constants.ts +3 -3
- package/src/templates/entity-file.ts +16 -5
- package/src/templates/enums-file.ts +50 -0
- package/src/templates/extract-delegate-emitter.ts +6 -7
- package/src/templates/extractor.ts +70 -40
- package/src/templates/field-meta.ts +1 -7
- package/src/templates/filter-allowlist.ts +18 -11
- package/src/templates/filter-shared.ts +2 -2
- package/src/templates/filter-type.ts +9 -7
- package/src/templates/find-templates.ts +15 -0
- package/src/templates/fr010-field-mapping.ts +15 -13
- package/src/templates/inferred-types.ts +122 -21
- package/src/templates/mermaid-er.ts +176 -8
- package/src/templates/output-format-spec-emitter.ts +1 -1
- package/src/templates/output-parser.ts +31 -80
- package/src/templates/output-prompt.ts +2 -1
- package/src/templates/queries-file.ts +133 -4
- package/src/templates/queries.ts +21 -15
- package/src/templates/relations-block.ts +19 -3
- package/src/templates/render-helper.ts +5 -4
- package/src/templates/routes-file-hono.ts +1 -2
- package/src/templates/routes-file.ts +234 -7
- package/src/templates/tph-discriminator.ts +232 -0
- package/src/templates/value-object-file.ts +39 -5
- package/src/templates/zod-validators.ts +225 -21
- package/templates/api/agent-api.md.mustache +30 -0
- package/templates/api/entity-api.md.mustache +69 -0
- package/templates/api/index.md.mustache +21 -0
- package/templates/docs/entity-page.md.mustache +33 -21
- package/templates/docs/template-page.md.mustache +56 -0
- package/dist/templates/extract-schema-emitter.d.ts +0 -8
- package/dist/templates/extract-schema-emitter.d.ts.map +0 -1
- package/dist/templates/extract-schema-emitter.js +0 -81
- package/dist/templates/extract-schema-emitter.js.map +0 -1
- package/src/templates/extract-schema-emitter.ts +0 -111
package/src/templates/queries.ts
CHANGED
|
@@ -5,13 +5,19 @@ import { code, imp, type Code } from "ts-poet";
|
|
|
5
5
|
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
6
6
|
import { IDENTITY_ATTR_FIELDS } from "@metaobjectsdev/metadata";
|
|
7
7
|
import type { RenderContext } from "../render-context.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
findByIdFnName,
|
|
10
|
+
listFnName,
|
|
11
|
+
createFnName,
|
|
12
|
+
updateFnName,
|
|
13
|
+
deleteByIdFnName,
|
|
14
|
+
} from "../naming.js";
|
|
9
15
|
|
|
10
16
|
/** Get the PK field name and its TS type for a given entity. */
|
|
11
|
-
function getPkInfo(entity: MetaObject, ctx: RenderContext): { fieldName: string; tsType: string } {
|
|
17
|
+
export function getPkInfo(entity: MetaObject, ctx: RenderContext): { fieldName: string; tsType: string } {
|
|
12
18
|
// Use primaryIdentity() to find the primary identity (may be inherited from extends:/super:).
|
|
13
19
|
const primary = entity.primaryIdentity();
|
|
14
|
-
const rawFields = primary?.
|
|
20
|
+
const rawFields = primary?.attr(IDENTITY_ATTR_FIELDS);
|
|
15
21
|
const fields = Array.isArray(rawFields) ? rawFields : (typeof rawFields === "string" ? [rawFields] : undefined);
|
|
16
22
|
const pkFieldName = fields?.[0] ?? "id";
|
|
17
23
|
const pkInfo = ctx.pkMap.get(entity.name);
|
|
@@ -26,11 +32,11 @@ function getPkInfo(entity: MetaObject, ctx: RenderContext): { fieldName: string;
|
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
export function renderFindByIdFn(entity: MetaObject, ctx: RenderContext): Code {
|
|
29
|
-
const varName =
|
|
35
|
+
const varName = ctx.collectionName(entity.name);
|
|
30
36
|
const entityName = entity.name;
|
|
31
37
|
const singularVar = entityName.charAt(0).toLowerCase() + entityName.slice(1);
|
|
32
38
|
const { fieldName: pkField, tsType: pkType } = getPkInfo(entity, ctx);
|
|
33
|
-
const fnName =
|
|
39
|
+
const fnName = findByIdFnName(entityName);
|
|
34
40
|
const eqSym = imp("eq@drizzle-orm");
|
|
35
41
|
|
|
36
42
|
return code`
|
|
@@ -41,12 +47,12 @@ export async function ${fnName}(db: Db, ${pkField}: ${pkType}): Promise<${entity
|
|
|
41
47
|
`;
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
export function renderListFn(entity: MetaObject,
|
|
45
|
-
const varName =
|
|
50
|
+
export function renderListFn(entity: MetaObject, ctx: RenderContext): Code {
|
|
51
|
+
const varName = ctx.collectionName(entity.name);
|
|
46
52
|
const entityName = entity.name;
|
|
47
53
|
// Pluralize the PascalCase entity name, preserving capitalization
|
|
48
54
|
// (e.g., "Category" -> "Categories", not "Categorys").
|
|
49
|
-
const fnName =
|
|
55
|
+
const fnName = listFnName(entityName);
|
|
50
56
|
|
|
51
57
|
return code`
|
|
52
58
|
export async function ${fnName}(db: Db, opts?: { limit?: number; offset?: number }): Promise<${entityName}[]> {
|
|
@@ -58,11 +64,11 @@ export async function ${fnName}(db: Db, opts?: { limit?: number; offset?: number
|
|
|
58
64
|
`;
|
|
59
65
|
}
|
|
60
66
|
|
|
61
|
-
export function renderCreateFn(entity: MetaObject,
|
|
62
|
-
const varName =
|
|
67
|
+
export function renderCreateFn(entity: MetaObject, ctx: RenderContext): Code {
|
|
68
|
+
const varName = ctx.collectionName(entity.name);
|
|
63
69
|
const entityName = entity.name;
|
|
64
70
|
const singularVar = entityName.charAt(0).toLowerCase() + entityName.slice(1);
|
|
65
|
-
const fnName =
|
|
71
|
+
const fnName = createFnName(entityName);
|
|
66
72
|
const schemaName = `${entityName}InsertSchema`;
|
|
67
73
|
|
|
68
74
|
return code`
|
|
@@ -75,11 +81,11 @@ export async function ${fnName}(db: Db, data: unknown): Promise<${entityName}> {
|
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
export function renderUpdateFn(entity: MetaObject, ctx: RenderContext): Code {
|
|
78
|
-
const varName =
|
|
84
|
+
const varName = ctx.collectionName(entity.name);
|
|
79
85
|
const entityName = entity.name;
|
|
80
86
|
const singularVar = entityName.charAt(0).toLowerCase() + entityName.slice(1);
|
|
81
87
|
const { fieldName: pkField, tsType: pkType } = getPkInfo(entity, ctx);
|
|
82
|
-
const fnName =
|
|
88
|
+
const fnName = updateFnName(entityName);
|
|
83
89
|
const schemaName = `${entityName}InsertSchema`;
|
|
84
90
|
const eqSym = imp("eq@drizzle-orm");
|
|
85
91
|
|
|
@@ -93,10 +99,10 @@ export async function ${fnName}(db: Db, ${pkField}: ${pkType}, data: unknown): P
|
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
export function renderDeleteByIdFn(entity: MetaObject, ctx: RenderContext): Code {
|
|
96
|
-
const varName =
|
|
102
|
+
const varName = ctx.collectionName(entity.name);
|
|
97
103
|
const entityName = entity.name;
|
|
98
104
|
const { fieldName: pkField, tsType: pkType } = getPkInfo(entity, ctx);
|
|
99
|
-
const fnName =
|
|
105
|
+
const fnName = deleteByIdFnName(entityName);
|
|
100
106
|
const eqSym = imp("eq@drizzle-orm");
|
|
101
107
|
|
|
102
108
|
return code`
|
|
@@ -6,7 +6,6 @@ import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
|
6
6
|
import { CARDINALITY_ONE, CARDINALITY_MANY } from "@metaobjectsdev/metadata";
|
|
7
7
|
import { type RenderContext } from "../render-context.js";
|
|
8
8
|
import { crossEntitySpecifier } from "../import-path.js";
|
|
9
|
-
import { variableNameFromEntity } from "../naming.js";
|
|
10
9
|
import type { RelationEntry } from "../relation-resolver.js";
|
|
11
10
|
|
|
12
11
|
/**
|
|
@@ -17,7 +16,7 @@ export function renderRelationsBlock(entity: MetaObject, ctx: RenderContext): Co
|
|
|
17
16
|
const entries = ctx.relationMap.get(entity.name);
|
|
18
17
|
if (!entries || entries.length === 0) return null;
|
|
19
18
|
|
|
20
|
-
const varName =
|
|
19
|
+
const varName = ctx.collectionName(entity.name);
|
|
21
20
|
const relationsFn = imp("relations@drizzle-orm");
|
|
22
21
|
const relationsVarName = `${varName}Relations`;
|
|
23
22
|
|
|
@@ -46,6 +45,23 @@ function renderRelationEntry(
|
|
|
46
45
|
thisVarName: string,
|
|
47
46
|
thisEntityPackage: string | undefined,
|
|
48
47
|
): Code {
|
|
48
|
+
// FR-018 M:N: the source navigates through the junction table, so the Drizzle
|
|
49
|
+
// many() targets the JUNCTION, not the target entity (the relational query API
|
|
50
|
+
// then hops junction → target via the junction's one() sides). The
|
|
51
|
+
// mountM2mRoute the routes generator emits performs the flattened two-stage
|
|
52
|
+
// traversal for the REST contract.
|
|
53
|
+
if (entry.cardinality === CARDINALITY_MANY && entry.junctionEntity !== undefined) {
|
|
54
|
+
const junctionSpec = crossEntitySpecifier(
|
|
55
|
+
ctx.outputLayout,
|
|
56
|
+
thisEntityPackage,
|
|
57
|
+
ctx.packageOf.get(entry.junctionEntity),
|
|
58
|
+
entry.junctionEntity,
|
|
59
|
+
ctx.extStyle,
|
|
60
|
+
);
|
|
61
|
+
const junctionVarSym = imp(`${ctx.collectionName(entry.junctionEntity)}@${junctionSpec}`);
|
|
62
|
+
return code` ${entry.name}: many(${junctionVarSym})`;
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
// Use imp() for cross-entity references so ts-poet tracks and emits the import.
|
|
50
66
|
const targetSpec = crossEntitySpecifier(
|
|
51
67
|
ctx.outputLayout,
|
|
@@ -54,7 +70,7 @@ function renderRelationEntry(
|
|
|
54
70
|
entry.targetEntity,
|
|
55
71
|
ctx.extStyle,
|
|
56
72
|
);
|
|
57
|
-
const targetVarSym = imp(`${
|
|
73
|
+
const targetVarSym = imp(`${ctx.collectionName(entry.targetEntity)}@${targetSpec}`);
|
|
58
74
|
|
|
59
75
|
if (entry.cardinality === CARDINALITY_ONE) {
|
|
60
76
|
const pkInfo = ctx.pkMap.get(entry.targetEntity);
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
TEMPLATE_ATTR_SUBJECT_REF,
|
|
44
44
|
TEMPLATE_ATTR_HTML_BODY_REF,
|
|
45
45
|
TEMPLATE_ATTR_TEXT_BODY_REF,
|
|
46
|
+
refMatchesObject,
|
|
46
47
|
} from "@metaobjectsdev/metadata";
|
|
47
48
|
import {
|
|
48
49
|
verify,
|
|
@@ -53,7 +54,7 @@ import {
|
|
|
53
54
|
} from "@metaobjectsdev/render";
|
|
54
55
|
|
|
55
56
|
function findObject(root: MetaData, name: string): MetaData | undefined {
|
|
56
|
-
return root.ownChildren().find((c) => c.type === TYPE_OBJECT && c
|
|
57
|
+
return root.ownChildren().find((c) => c.type === TYPE_OBJECT && refMatchesObject(c, name));
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
function findTemplate(root: MetaData, name: string): MetaData | undefined {
|
|
@@ -80,7 +81,7 @@ function derivePayloadFieldTree(
|
|
|
80
81
|
const fields: PayloadField[] = [];
|
|
81
82
|
for (const f of vo.children().filter((c) => c.type === TYPE_FIELD)) {
|
|
82
83
|
if (f.subType === FIELD_SUBTYPE_OBJECT) {
|
|
83
|
-
const ref = f.
|
|
84
|
+
const ref = f.attr(FIELD_ATTR_OBJECT_REF);
|
|
84
85
|
if (typeof ref === "string") {
|
|
85
86
|
fields.push({ name: f.name, fields: derivePayloadFieldTree(root, ref, nextSeen) });
|
|
86
87
|
continue;
|
|
@@ -192,7 +193,7 @@ export function renderRenderHelper(
|
|
|
192
193
|
|
|
193
194
|
return `import { render } from "@metaobjectsdev/render";
|
|
194
195
|
import type { Provider, EmailDocument } from "@metaobjectsdev/render";
|
|
195
|
-
import type { ${payloadRef} } from "
|
|
196
|
+
import type { ${payloadRef} } from "./${payloadRef}.js";
|
|
196
197
|
|
|
197
198
|
/**
|
|
198
199
|
* Render the ${templateName} email (subject + html body${typeof textBodyRef === "string" ? " + text body" : ""}) from a
|
|
@@ -230,7 +231,7 @@ export function ${fnName}(payload: ${payloadRef}, provider: Provider): EmailDocu
|
|
|
230
231
|
|
|
231
232
|
return `import { render } from "@metaobjectsdev/render";
|
|
232
233
|
import type { Provider } from "@metaobjectsdev/render";
|
|
233
|
-
import type { ${payloadRef} } from "
|
|
234
|
+
import type { ${payloadRef} } from "./${payloadRef}.js";
|
|
234
235
|
|
|
235
236
|
/**
|
|
236
237
|
* Render the ${templateName} document from a typed ${payloadRef} payload. Wraps the
|
|
@@ -29,7 +29,6 @@ import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
|
29
29
|
import { type RenderContext } from "../render-context.js";
|
|
30
30
|
import { entityModuleSpecifier } from "../import-path.js";
|
|
31
31
|
import { GENERATED_HEADER } from "../constants.js";
|
|
32
|
-
import { variableNameFromEntity } from "../naming.js";
|
|
33
32
|
import { isProjection } from "../projection/projection-detector.js";
|
|
34
33
|
|
|
35
34
|
export function renderRoutesFileHono(entity: MetaObject, ctx: RenderContext): string {
|
|
@@ -97,7 +96,7 @@ export function ${handlerName}(app: ${HonoSym}<any, any, any>, deps: { db: unkno
|
|
|
97
96
|
}
|
|
98
97
|
|
|
99
98
|
// --- Vanilla / write-through entity path: full CRUD routes ---
|
|
100
|
-
const tableVar =
|
|
99
|
+
const tableVar = ctx.collectionName(entityName);
|
|
101
100
|
|
|
102
101
|
const HonoSym = imp("t:Hono@hono");
|
|
103
102
|
const mountCrudRoutesSym = imp("mountCrudRoutes@@metaobjectsdev/runtime-ts/hono");
|
|
@@ -15,17 +15,31 @@
|
|
|
15
15
|
// the existing queries-file template). The entity's Drizzle table const is
|
|
16
16
|
// imported alongside the Zod schemas + constants from the sibling Entity.ts.
|
|
17
17
|
|
|
18
|
-
import { code, imp } from "ts-poet";
|
|
18
|
+
import { code, imp, joinCode, type Code } from "ts-poet";
|
|
19
19
|
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
20
|
+
import {
|
|
21
|
+
TYPE_FIELD,
|
|
22
|
+
resolveColumnName,
|
|
23
|
+
} from "@metaobjectsdev/metadata";
|
|
20
24
|
import { type RenderContext } from "../render-context.js";
|
|
21
|
-
import { entityModuleSpecifier, relativeModuleSpecifier } from "../import-path.js";
|
|
25
|
+
import { crossEntitySpecifier, entityModuleSpecifier, relativeModuleSpecifier } from "../import-path.js";
|
|
22
26
|
import { GENERATED_HEADER } from "../constants.js";
|
|
23
|
-
import {
|
|
27
|
+
import { routesHandlerName } from "../naming.js";
|
|
24
28
|
import { isProjection } from "../projection/projection-detector.js";
|
|
29
|
+
import type { RelationEntry } from "../relation-resolver.js";
|
|
30
|
+
import { isTphDiscriminatorBase, tphPlan } from "./tph-discriminator.js";
|
|
25
31
|
|
|
26
32
|
export function renderRoutesFile(entity: MetaObject, ctx: RenderContext): string {
|
|
33
|
+
// FR-017 Tier 2 — a TPH discriminator base mounts polymorphic list/get at the
|
|
34
|
+
// base path plus a full per-subtype CRUD route set scoped to each
|
|
35
|
+
// discriminator value. (Subtype entities are filtered out of the routes
|
|
36
|
+
// generator entirely — their routes live here.)
|
|
37
|
+
if (isTphDiscriminatorBase(entity, ctx.loadedRoot)) {
|
|
38
|
+
return renderTphRoutesFile(entity, ctx);
|
|
39
|
+
}
|
|
40
|
+
|
|
27
41
|
const entityName = entity.name;
|
|
28
|
-
const handlerName =
|
|
42
|
+
const handlerName = routesHandlerName(entityName);
|
|
29
43
|
// Import the entity's own file. Same target → relative "./Entity"; cross
|
|
30
44
|
// target → importBase-qualified package path.
|
|
31
45
|
const entityFileSpec = entityModuleSpecifier(
|
|
@@ -108,11 +122,24 @@ export async function ${handlerName}(fastify: ${FastifyInstanceSym}) {
|
|
|
108
122
|
}
|
|
109
123
|
|
|
110
124
|
// --- Vanilla / write-through entity path: full CRUD routes ---
|
|
111
|
-
const tableVar =
|
|
125
|
+
const tableVar = ctx.collectionName(entityName);
|
|
112
126
|
|
|
113
127
|
const FastifyInstanceSym = imp("t:FastifyInstance@fastify");
|
|
114
128
|
const mountCrudRoutesSym = imp("mountCrudRoutes@@metaobjectsdev/runtime-ts/drizzle-fastify");
|
|
115
129
|
|
|
130
|
+
// FR-018 M:N traversal: for each many-to-many navigation declared on this
|
|
131
|
+
// entity, emit a mountM2mRoute(...) that traverses the junction. The junction
|
|
132
|
+
// FK columns were derived from the junction's identity.reference children (the
|
|
133
|
+
// SSOT) by the relation-resolver pre-pass; here we resolve them to physical
|
|
134
|
+
// column names for the Drizzle two-stage join.
|
|
135
|
+
const m2mEntries = (ctx.relationMap.get(entityName) ?? []).filter(
|
|
136
|
+
(e): e is RelationEntry & { junctionEntity: string } => e.junctionEntity !== undefined,
|
|
137
|
+
);
|
|
138
|
+
// Two fastify-scope variants: under an apiPrefix the mounts live inside the
|
|
139
|
+
// register-block (`instance`); otherwise they bind directly to `fastify`.
|
|
140
|
+
const m2mMountsPrefixed = renderM2mMounts(m2mEntries, entity, ctx, "instance");
|
|
141
|
+
const m2mMountsFlat = renderM2mMounts(m2mEntries, entity, ctx, "fastify");
|
|
142
|
+
|
|
116
143
|
const literalImports = code`
|
|
117
144
|
import { db } from ${JSON.stringify(dbImportSpec)};
|
|
118
145
|
import {
|
|
@@ -148,7 +175,7 @@ export async function ${handlerName}(fastify: ${FastifyInstanceSym}) {
|
|
|
148
175
|
sortAllowlist: ${entityName}SortAllowlist,
|
|
149
176
|
dialect: ${JSON.stringify(ctx.dialect)},
|
|
150
177
|
});
|
|
151
|
-
}, { prefix: ${JSON.stringify(ctx.apiPrefix)} });
|
|
178
|
+
${m2mMountsPrefixed} }, { prefix: ${JSON.stringify(ctx.apiPrefix)} });
|
|
152
179
|
}
|
|
153
180
|
`
|
|
154
181
|
: code`
|
|
@@ -172,8 +199,208 @@ export async function ${handlerName}(fastify: ${FastifyInstanceSym}) {
|
|
|
172
199
|
sortAllowlist: ${entityName}SortAllowlist,
|
|
173
200
|
dialect: ${JSON.stringify(ctx.dialect)},
|
|
174
201
|
});
|
|
175
|
-
}
|
|
202
|
+
${m2mMountsFlat}}
|
|
176
203
|
`;
|
|
177
204
|
|
|
178
205
|
return header + literalImports.toString() + body.toString();
|
|
179
206
|
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Render the M:N traversal mounts for an entity as a single Code fragment to
|
|
210
|
+
* interpolate INTO the handler-body code template (so the junction/target table
|
|
211
|
+
* + mountM2mRoute imports hoist with the rest of the body's imports, not inline
|
|
212
|
+
* mid-function). `fastifyVar` is the in-scope Fastify reference (`instance`
|
|
213
|
+
* under an apiPrefix register-block, else `fastify`). Returns "" when the entity
|
|
214
|
+
* has no M:N relationships — CRUD-only output stays byte-identical to before.
|
|
215
|
+
*/
|
|
216
|
+
function renderM2mMounts(
|
|
217
|
+
entries: ReadonlyArray<RelationEntry & { junctionEntity: string }>,
|
|
218
|
+
source: MetaObject,
|
|
219
|
+
ctx: RenderContext,
|
|
220
|
+
fastifyVar: string,
|
|
221
|
+
): Code | string {
|
|
222
|
+
if (entries.length === 0) return "";
|
|
223
|
+
const mounts = entries.map((e) => renderM2mMount(e, source, ctx, fastifyVar));
|
|
224
|
+
return code`${joinCode(mounts, { on: "\n", trim: false })}
|
|
225
|
+
`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Render one M:N traversal mount. The junction + target Drizzle table consts are
|
|
230
|
+
* imported from their sibling entity files (imp() lets ts-poet track + emit the
|
|
231
|
+
* import). The source/target FK columns + the target PK are resolved to PHYSICAL
|
|
232
|
+
* column names via resolveColumnName (the runtime two-stage join queries by
|
|
233
|
+
* column). mountM2mRoute appends `/:id/<relationName>` to the source $path.
|
|
234
|
+
*/
|
|
235
|
+
function renderM2mMount(
|
|
236
|
+
entry: RelationEntry & { junctionEntity: string },
|
|
237
|
+
source: MetaObject,
|
|
238
|
+
ctx: RenderContext,
|
|
239
|
+
fastifyVar: string,
|
|
240
|
+
): Code {
|
|
241
|
+
const junctionVarSym = imp(
|
|
242
|
+
`${ctx.collectionName(entry.junctionEntity)}@${crossEntitySpecifier(
|
|
243
|
+
ctx.outputLayout,
|
|
244
|
+
source.package,
|
|
245
|
+
ctx.packageOf.get(entry.junctionEntity),
|
|
246
|
+
entry.junctionEntity,
|
|
247
|
+
ctx.extStyle,
|
|
248
|
+
)}`,
|
|
249
|
+
);
|
|
250
|
+
const targetVarSym = imp(
|
|
251
|
+
`${ctx.collectionName(entry.targetEntity)}@${crossEntitySpecifier(
|
|
252
|
+
ctx.outputLayout,
|
|
253
|
+
source.package,
|
|
254
|
+
ctx.packageOf.get(entry.targetEntity),
|
|
255
|
+
entry.targetEntity,
|
|
256
|
+
ctx.extStyle,
|
|
257
|
+
)}`,
|
|
258
|
+
);
|
|
259
|
+
const mountM2mRouteSym = imp("mountM2mRoute@@metaobjectsdev/runtime-ts/drizzle-fastify");
|
|
260
|
+
const junction = ctx.loadedRoot.findObject(entry.junctionEntity);
|
|
261
|
+
const target = ctx.loadedRoot.findObject(entry.targetEntity);
|
|
262
|
+
const sourceColumn = junction
|
|
263
|
+
? resolveJunctionColumn(junction, entry.sourceJoinField!, ctx)
|
|
264
|
+
: entry.sourceJoinField!;
|
|
265
|
+
const targetColumn = junction
|
|
266
|
+
? resolveJunctionColumn(junction, entry.targetJoinField!, ctx)
|
|
267
|
+
: entry.targetJoinField!;
|
|
268
|
+
const targetPkColumn = target
|
|
269
|
+
? resolveJunctionColumn(target, ctx.pkMap.get(entry.targetEntity)?.fieldName ?? "id", ctx)
|
|
270
|
+
: "id";
|
|
271
|
+
|
|
272
|
+
return code` ${mountM2mRouteSym}({
|
|
273
|
+
fastify: ${fastifyVar},
|
|
274
|
+
path: ${source.name}.$path,
|
|
275
|
+
relationName: ${JSON.stringify(entry.name)},
|
|
276
|
+
db,
|
|
277
|
+
junctionTable: ${junctionVarSym},
|
|
278
|
+
targetTable: ${targetVarSym},
|
|
279
|
+
sourceColumn: ${JSON.stringify(sourceColumn)},
|
|
280
|
+
targetColumn: ${JSON.stringify(targetColumn)},
|
|
281
|
+
targetPkColumn: ${JSON.stringify(targetPkColumn)},
|
|
282
|
+
symmetric: ${entry.symmetric ? "true" : "false"},
|
|
283
|
+
});`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Resolve a field's physical column name on an entity (defaults if missing). */
|
|
287
|
+
function resolveJunctionColumn(entity: MetaObject, fieldName: string, ctx: RenderContext): string {
|
|
288
|
+
const field = entity.ownChildren().find((c) => c.type === TYPE_FIELD && c.name === fieldName);
|
|
289
|
+
if (!field) return fieldName;
|
|
290
|
+
return resolveColumnName(field, ctx.columnNamingStrategy);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* FR-017 Tier 2 — the routes file for a TPH discriminator base.
|
|
295
|
+
*
|
|
296
|
+
* Mounts a polymorphic list/get route set at the base path (`GET /auths`,
|
|
297
|
+
* `GET /auths/:id` — rows carry the discriminator by value), then a full
|
|
298
|
+
* per-subtype CRUD route set at `<basePath>/<discriminatorValue lowercased>`
|
|
299
|
+
* (`/auths/bridge`, ...). The per-subtype create body OMITS the discriminator
|
|
300
|
+
* (the URL names the subtype); the runtime helper injects it. The per-subtype
|
|
301
|
+
* route set is scoped to its discriminator value via the `discriminator` option
|
|
302
|
+
* (cross-subtype get/update/delete 404; update strips the discriminator).
|
|
303
|
+
*
|
|
304
|
+
* Subtype route segment defaults to the lowercased `@discriminatorValue`
|
|
305
|
+
* (`"Bridge"` → `bridge`) — a robust, value-derived path that matches the
|
|
306
|
+
* FR-017 design's `/auths/bridge` examples. Fastify resolves the static
|
|
307
|
+
* `/auths/bridge` ahead of the parametric `/auths/:id`, so the two coexist.
|
|
308
|
+
*/
|
|
309
|
+
function renderTphRoutesFile(base: MetaObject, ctx: RenderContext): string {
|
|
310
|
+
const baseName = base.name;
|
|
311
|
+
const handlerName = routesHandlerName(baseName);
|
|
312
|
+
// Single source of truth for the discriminator field + subtypes + route segments.
|
|
313
|
+
const plan = tphPlan(base, ctx.loadedRoot)!;
|
|
314
|
+
const discField = plan.discriminatorField;
|
|
315
|
+
const tableVar = ctx.collectionName(baseName);
|
|
316
|
+
|
|
317
|
+
const baseFileSpec = entityModuleSpecifier(
|
|
318
|
+
ctx.selfTarget, ctx.entityModuleTarget, base.package, baseName, ctx.extStyle,
|
|
319
|
+
);
|
|
320
|
+
const dbImportSpec = relativeModuleSpecifier(ctx.outputLayout, base.package, ctx.dbImport);
|
|
321
|
+
|
|
322
|
+
const FastifyInstanceSym = imp("t:FastifyInstance@fastify");
|
|
323
|
+
const mountCrudRoutesSym = imp("mountCrudRoutes@@metaobjectsdev/runtime-ts/drizzle-fastify");
|
|
324
|
+
const dbSym = imp(`db@${dbImportSpec}`);
|
|
325
|
+
const tableSym = imp(`${tableVar}@${baseFileSpec}`);
|
|
326
|
+
const baseConstSym = imp(`${baseName}@${baseFileSpec}`);
|
|
327
|
+
const baseInsertSym = imp(`${baseName}InsertSchema@${baseFileSpec}`);
|
|
328
|
+
const baseUpdateSym = imp(`${baseName}UpdateSchema@${baseFileSpec}`);
|
|
329
|
+
const baseFilterSym = imp(`${baseName}FilterAllowlist@${baseFileSpec}`);
|
|
330
|
+
const baseSortSym = imp(`${baseName}SortAllowlist@${baseFileSpec}`);
|
|
331
|
+
|
|
332
|
+
const fastifyRef = ctx.apiPrefix ? "instance" : "fastify";
|
|
333
|
+
const dialectLit = JSON.stringify(ctx.dialect);
|
|
334
|
+
|
|
335
|
+
const polymorphic = code`
|
|
336
|
+
${mountCrudRoutesSym}({
|
|
337
|
+
fastify: ${fastifyRef},
|
|
338
|
+
path: ${baseConstSym}.$path,
|
|
339
|
+
db: ${dbSym},
|
|
340
|
+
table: ${tableSym},
|
|
341
|
+
insertSchema: ${baseInsertSym},
|
|
342
|
+
updateSchema: ${baseUpdateSym},
|
|
343
|
+
filterAllowlist: ${baseFilterSym},
|
|
344
|
+
sortAllowlist: ${baseSortSym},
|
|
345
|
+
dialect: ${dialectLit},
|
|
346
|
+
expose: ["list", "get"],
|
|
347
|
+
});`;
|
|
348
|
+
|
|
349
|
+
const subtypeMounts: Code[] = plan.subtypes.map(({ entity: sub, value, routeSegment: segment }) => {
|
|
350
|
+
const subFileSpec = entityModuleSpecifier(
|
|
351
|
+
ctx.selfTarget, ctx.entityModuleTarget, sub.package, sub.name, ctx.extStyle,
|
|
352
|
+
);
|
|
353
|
+
const subInsertSym = imp(`${sub.name}InsertSchema@${subFileSpec}`);
|
|
354
|
+
// FR-017 Tier 3: each subtype carries its OWN filter/sort allowlist
|
|
355
|
+
// (discriminator excluded — it's pinned by this path).
|
|
356
|
+
const subFilterSym = imp(`${sub.name}FilterAllowlist@${subFileSpec}`);
|
|
357
|
+
const subSortSym = imp(`${sub.name}SortAllowlist@${subFileSpec}`);
|
|
358
|
+
return code`
|
|
359
|
+
${mountCrudRoutesSym}({
|
|
360
|
+
fastify: ${fastifyRef},
|
|
361
|
+
path: ${baseConstSym}.$path + ${JSON.stringify("/" + segment)},
|
|
362
|
+
db: ${dbSym},
|
|
363
|
+
table: ${tableSym},
|
|
364
|
+
insertSchema: ${subInsertSym}.omit({ ${discField}: true }),
|
|
365
|
+
updateSchema: ${subInsertSym}.omit({ ${discField}: true }).partial(),
|
|
366
|
+
filterAllowlist: ${subFilterSym},
|
|
367
|
+
sortAllowlist: ${subSortSym},
|
|
368
|
+
dialect: ${dialectLit},
|
|
369
|
+
discriminator: { column: ${JSON.stringify(discField)}, value: ${JSON.stringify(value)} },
|
|
370
|
+
});`;
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const mounts = joinCode([polymorphic, ...subtypeMounts], { on: "\n" });
|
|
374
|
+
|
|
375
|
+
const fn = ctx.apiPrefix
|
|
376
|
+
? code`
|
|
377
|
+
/**
|
|
378
|
+
* Mount polymorphic + per-subtype REST endpoints for the ${baseName} TPH hierarchy.
|
|
379
|
+
*
|
|
380
|
+
* GET ${baseName}.$path (+ /:id) lists/gets the discriminated union; each
|
|
381
|
+
* /${baseName}.$path/<subtype> path is a full per-subtype CRUD set.
|
|
382
|
+
*/
|
|
383
|
+
export async function ${handlerName}(fastify: ${FastifyInstanceSym}) {
|
|
384
|
+
await fastify.register(async (instance) => {
|
|
385
|
+
${mounts}
|
|
386
|
+
}, { prefix: ${JSON.stringify(ctx.apiPrefix)} });
|
|
387
|
+
}
|
|
388
|
+
`
|
|
389
|
+
: code`
|
|
390
|
+
/**
|
|
391
|
+
* Mount polymorphic + per-subtype REST endpoints for the ${baseName} TPH hierarchy.
|
|
392
|
+
*
|
|
393
|
+
* GET ${baseName}.$path (+ /:id) lists/gets the discriminated union; each
|
|
394
|
+
* /${baseName}.$path/<subtype> path is a full per-subtype CRUD set.
|
|
395
|
+
*/
|
|
396
|
+
export async function ${handlerName}(fastify: ${FastifyInstanceSym}) {
|
|
397
|
+
${mounts}
|
|
398
|
+
}
|
|
399
|
+
`;
|
|
400
|
+
|
|
401
|
+
const header =
|
|
402
|
+
`// ${GENERATED_HEADER} — DO NOT EDIT.\n` +
|
|
403
|
+
`// Source metadata: ${baseName} (${base.fqn()}) — TPH discriminator base\n` +
|
|
404
|
+
`// Customize via ${baseName}.extra.ts in this directory (e.g., auth, additional handlers).\n`;
|
|
405
|
+
return header + fn.toString();
|
|
406
|
+
}
|