@vertz/codegen 0.2.18 → 0.2.20
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/dist/index.d.ts +13 -0
- package/dist/index.js +115 -11
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -168,6 +168,17 @@ interface CodegenRelation {
|
|
|
168
168
|
type: "one" | "many";
|
|
169
169
|
entity: string;
|
|
170
170
|
}
|
|
171
|
+
interface CodegenExposeField {
|
|
172
|
+
name: string;
|
|
173
|
+
conditional: boolean;
|
|
174
|
+
}
|
|
175
|
+
interface CodegenExposeRelation {
|
|
176
|
+
name: string;
|
|
177
|
+
entity: string;
|
|
178
|
+
type: "one" | "many";
|
|
179
|
+
select?: CodegenExposeField[];
|
|
180
|
+
resolvedFields?: CodegenResolvedField[];
|
|
181
|
+
}
|
|
171
182
|
interface CodegenEntityModule {
|
|
172
183
|
entityName: string;
|
|
173
184
|
operations: CodegenEntityOperation[];
|
|
@@ -178,6 +189,8 @@ interface CodegenEntityModule {
|
|
|
178
189
|
primaryKey?: string;
|
|
179
190
|
hiddenFields?: string[];
|
|
180
191
|
responseFields?: CodegenResolvedField[];
|
|
192
|
+
exposeSelect?: CodegenExposeField[];
|
|
193
|
+
exposeInclude?: CodegenExposeRelation[];
|
|
181
194
|
relationSelections?: Record<string, "all" | string[]>;
|
|
182
195
|
relationQueryConfig?: Record<string, {
|
|
183
196
|
allowWhere?: string[];
|
package/dist/index.js
CHANGED
|
@@ -575,9 +575,17 @@ class EntitySdkGenerator {
|
|
|
575
575
|
switch (op.kind) {
|
|
576
576
|
case "list": {
|
|
577
577
|
lines.push(` list: Object.assign(`);
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
578
|
+
if (op.outputSchema) {
|
|
579
|
+
lines.push(` <K extends keyof ${outputType} = keyof ${outputType}>(`);
|
|
580
|
+
lines.push(` query?: { select?: Record<K, true> } & Record<string, unknown>,`);
|
|
581
|
+
lines.push(` ) => {`);
|
|
582
|
+
lines.push(` const resolvedQuery = resolveVertzQL(query);`);
|
|
583
|
+
lines.push(` return createDescriptor('GET', '${op.path}', () => client.get<ListResponse<Pick<${outputType}, K>>>('${op.path}', { query: resolvedQuery }), resolvedQuery, { entityType: '${entity.entityName}', kind: 'list' as const });`);
|
|
584
|
+
} else {
|
|
585
|
+
lines.push(` (query?: Record<string, unknown>) => {`);
|
|
586
|
+
lines.push(` const resolvedQuery = resolveVertzQL(query);`);
|
|
587
|
+
lines.push(` return createDescriptor('GET', '${op.path}', () => client.get<${listOutput}>('${op.path}', { query: resolvedQuery }), resolvedQuery, { entityType: '${entity.entityName}', kind: 'list' as const });`);
|
|
588
|
+
}
|
|
581
589
|
lines.push(` },`);
|
|
582
590
|
lines.push(` { url: '${op.path}', method: 'GET' as const },`);
|
|
583
591
|
lines.push(` ),`);
|
|
@@ -586,9 +594,17 @@ class EntitySdkGenerator {
|
|
|
586
594
|
case "get": {
|
|
587
595
|
const getPathExpr = `\`${op.path.replace(":id", "${id}")}\``;
|
|
588
596
|
lines.push(` get: Object.assign(`);
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
597
|
+
if (op.outputSchema) {
|
|
598
|
+
lines.push(` <K extends keyof ${outputType} = keyof ${outputType}>(`);
|
|
599
|
+
lines.push(` id: string, options?: { select?: Record<K, true> },`);
|
|
600
|
+
lines.push(` ) => {`);
|
|
601
|
+
lines.push(` const resolvedQuery = resolveVertzQL(options);`);
|
|
602
|
+
lines.push(` return createDescriptor('GET', ${getPathExpr}, () => client.get<Pick<${outputType}, K>>(${getPathExpr}, { query: resolvedQuery }), resolvedQuery, { entityType: '${entity.entityName}', kind: 'get' as const, id });`);
|
|
603
|
+
} else {
|
|
604
|
+
lines.push(` (id: string, options?: { select?: Record<string, true> }) => {`);
|
|
605
|
+
lines.push(` const resolvedQuery = resolveVertzQL(options);`);
|
|
606
|
+
lines.push(` return createDescriptor('GET', ${getPathExpr}, () => client.get<${outputType}>(${getPathExpr}, { query: resolvedQuery }), resolvedQuery, { entityType: '${entity.entityName}', kind: 'get' as const, id });`);
|
|
607
|
+
}
|
|
592
608
|
lines.push(` },`);
|
|
593
609
|
lines.push(` { url: '${op.path}', method: 'GET' as const },`);
|
|
594
610
|
lines.push(` ),`);
|
|
@@ -711,7 +727,7 @@ class EntityTypesGenerator {
|
|
|
711
727
|
}
|
|
712
728
|
}
|
|
713
729
|
if (op.outputSchema && !emitted.has(op.outputSchema)) {
|
|
714
|
-
const outputType = this.emitResponseType(op.outputSchema, op.responseFields);
|
|
730
|
+
const outputType = this.emitResponseType(op.outputSchema, op.responseFields, entity);
|
|
715
731
|
if (outputType) {
|
|
716
732
|
lines.push(outputType);
|
|
717
733
|
lines.push("");
|
|
@@ -719,6 +735,13 @@ class EntityTypesGenerator {
|
|
|
719
735
|
}
|
|
720
736
|
}
|
|
721
737
|
}
|
|
738
|
+
const responseSchema = entity.operations.find((op) => (op.kind === "list" || op.kind === "get") && op.outputSchema)?.outputSchema;
|
|
739
|
+
if (responseSchema && !emitted.has(`${responseSchema}Fields`)) {
|
|
740
|
+
const fieldsTypeName = responseSchema.replace(/Response$/, "Fields");
|
|
741
|
+
lines.push(`export type ${fieldsTypeName} = keyof ${responseSchema};`);
|
|
742
|
+
lines.push("");
|
|
743
|
+
emitted.add(`${responseSchema}Fields`);
|
|
744
|
+
}
|
|
722
745
|
for (const action of entity.actions) {
|
|
723
746
|
if (action.inputSchema && !emitted.has(action.inputSchema)) {
|
|
724
747
|
const inputType = this.emitBodyType(action.inputSchema, action.resolvedInputFields);
|
|
@@ -729,7 +752,7 @@ class EntityTypesGenerator {
|
|
|
729
752
|
}
|
|
730
753
|
}
|
|
731
754
|
if (action.outputSchema && !emitted.has(action.outputSchema)) {
|
|
732
|
-
const outputType = this.
|
|
755
|
+
const outputType = this.emitBodyType(action.outputSchema, action.resolvedOutputFields);
|
|
733
756
|
if (outputType) {
|
|
734
757
|
lines.push(outputType);
|
|
735
758
|
lines.push("");
|
|
@@ -756,17 +779,38 @@ class EntityTypesGenerator {
|
|
|
756
779
|
${props};
|
|
757
780
|
}`;
|
|
758
781
|
}
|
|
759
|
-
emitResponseType(typeName, fields) {
|
|
782
|
+
emitResponseType(typeName, fields, entity) {
|
|
760
783
|
if (!fields || fields.length === 0)
|
|
761
784
|
return;
|
|
785
|
+
const conditionalFields = new Set(entity.exposeSelect?.filter((f) => f.conditional).map((f) => f.name) ?? []);
|
|
762
786
|
const props = fields.map((f) => {
|
|
763
787
|
const tsType = TS_TYPE_MAP[f.tsType] ?? "unknown";
|
|
764
788
|
const optional = f.optional ? "?" : "";
|
|
765
|
-
|
|
789
|
+
const nullable = conditionalFields.has(f.name) ? " | null" : "";
|
|
790
|
+
return ` ${f.name}${optional}: ${tsType}${nullable}`;
|
|
766
791
|
}).join(`;
|
|
767
792
|
`);
|
|
793
|
+
const relationProps = [];
|
|
794
|
+
if (entity.exposeInclude) {
|
|
795
|
+
for (const rel of entity.exposeInclude) {
|
|
796
|
+
if (!rel.resolvedFields || rel.resolvedFields.length === 0)
|
|
797
|
+
continue;
|
|
798
|
+
const relFields = rel.resolvedFields.map((f) => {
|
|
799
|
+
const tsType = TS_TYPE_MAP[f.tsType] ?? "unknown";
|
|
800
|
+
return `${f.name}: ${tsType}`;
|
|
801
|
+
}).join("; ");
|
|
802
|
+
if (rel.type === "many") {
|
|
803
|
+
relationProps.push(` ${rel.name}: Array<{ ${relFields} }>`);
|
|
804
|
+
} else {
|
|
805
|
+
relationProps.push(` ${rel.name}?: { ${relFields} }`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const allProps = relationProps.length > 0 ? `${props};
|
|
810
|
+
${relationProps.join(`;
|
|
811
|
+
`)}` : props;
|
|
768
812
|
return `export interface ${typeName} {
|
|
769
|
-
${
|
|
813
|
+
${allProps};
|
|
770
814
|
}`;
|
|
771
815
|
}
|
|
772
816
|
generateIndex(entities) {
|
|
@@ -1038,6 +1082,19 @@ function adaptIR(appIR) {
|
|
|
1038
1082
|
};
|
|
1039
1083
|
});
|
|
1040
1084
|
const allSchemas = [...schemas].sort((a, b) => a.name.localeCompare(b.name));
|
|
1085
|
+
const entityResponseFieldsMap = new Map;
|
|
1086
|
+
for (const entity of appIR.entities ?? []) {
|
|
1087
|
+
const respRef = entity.modelRef.schemaRefs.response;
|
|
1088
|
+
if (respRef?.kind === "inline") {
|
|
1089
|
+
const fields = respRef.resolvedFields?.map((f) => ({
|
|
1090
|
+
name: f.name,
|
|
1091
|
+
tsType: f.tsType,
|
|
1092
|
+
optional: f.optional
|
|
1093
|
+
}));
|
|
1094
|
+
if (fields)
|
|
1095
|
+
entityResponseFieldsMap.set(entity.name, fields);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1041
1098
|
const entities = (appIR.entities ?? []).map((entity) => {
|
|
1042
1099
|
const entityPascal = toPascalCase(entity.name);
|
|
1043
1100
|
const operations = [];
|
|
@@ -1136,6 +1193,51 @@ function adaptIR(appIR) {
|
|
|
1136
1193
|
optional: f.optional
|
|
1137
1194
|
}));
|
|
1138
1195
|
}
|
|
1196
|
+
let exposeSelect;
|
|
1197
|
+
let exposeInclude;
|
|
1198
|
+
if (entity.expose) {
|
|
1199
|
+
exposeSelect = entity.expose.select.map((f) => ({
|
|
1200
|
+
name: f.name,
|
|
1201
|
+
conditional: f.conditional
|
|
1202
|
+
}));
|
|
1203
|
+
if (entityResponseFields) {
|
|
1204
|
+
const exposedNames = new Set(entity.expose.select.map((f) => f.name));
|
|
1205
|
+
const hiddenNames = new Set(entity.modelRef.hiddenFields ?? []);
|
|
1206
|
+
entityResponseFields = entityResponseFields.filter((f) => exposedNames.has(f.name) && !hiddenNames.has(f.name));
|
|
1207
|
+
for (const op of operations) {
|
|
1208
|
+
if (op.responseFields) {
|
|
1209
|
+
op.responseFields = op.responseFields.filter((f) => exposedNames.has(f.name) && !hiddenNames.has(f.name));
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
if (entity.expose.include && entity.expose.include.length > 0) {
|
|
1214
|
+
const relationsLookup = new Map(entity.relations.filter((r) => r.type && r.entity).map((r) => [r.name, { type: r.type, entity: r.entity }]));
|
|
1215
|
+
const resolved = [];
|
|
1216
|
+
for (const rel of entity.expose.include) {
|
|
1217
|
+
const relEntity = rel.entity ?? relationsLookup.get(rel.name)?.entity;
|
|
1218
|
+
const relType = rel.type ?? relationsLookup.get(rel.name)?.type;
|
|
1219
|
+
if (!relEntity || !relType)
|
|
1220
|
+
continue;
|
|
1221
|
+
const targetFields = entityResponseFieldsMap.get(relEntity);
|
|
1222
|
+
let resolvedFields;
|
|
1223
|
+
if (targetFields && rel.select) {
|
|
1224
|
+
const selectedNames = new Set(rel.select.map((f) => f.name));
|
|
1225
|
+
resolvedFields = targetFields.filter((f) => selectedNames.has(f.name));
|
|
1226
|
+
} else if (targetFields) {
|
|
1227
|
+
resolvedFields = targetFields;
|
|
1228
|
+
}
|
|
1229
|
+
resolved.push({
|
|
1230
|
+
name: rel.name,
|
|
1231
|
+
entity: relEntity,
|
|
1232
|
+
type: relType,
|
|
1233
|
+
...rel.select ? { select: rel.select.map((f) => ({ name: f.name, conditional: f.conditional })) } : {},
|
|
1234
|
+
...resolvedFields ? { resolvedFields } : {}
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
if (resolved.length > 0)
|
|
1238
|
+
exposeInclude = resolved;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1139
1241
|
return {
|
|
1140
1242
|
entityName: entity.name,
|
|
1141
1243
|
operations,
|
|
@@ -1146,6 +1248,8 @@ function adaptIR(appIR) {
|
|
|
1146
1248
|
primaryKey: entity.modelRef.primaryKey,
|
|
1147
1249
|
hiddenFields: entity.modelRef.hiddenFields,
|
|
1148
1250
|
responseFields: entityResponseFields,
|
|
1251
|
+
exposeSelect,
|
|
1252
|
+
exposeInclude,
|
|
1149
1253
|
relationSelections: Object.keys(relationSelections).length > 0 ? relationSelections : undefined,
|
|
1150
1254
|
relationQueryConfig: Object.keys(relationQueryConfig).length > 0 ? relationQueryConfig : undefined
|
|
1151
1255
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/codegen",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Vertz code generation — internal, no stability guarantee",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@vertz/compiler": "^0.2.
|
|
30
|
+
"@vertz/compiler": "^0.2.18"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^25.3.1",
|