@vertz/codegen 0.2.11 → 0.2.13
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 +15 -1
- package/dist/index.js +81 -10
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -138,10 +138,16 @@ interface SchemaNamingParts {
|
|
|
138
138
|
entity?: string;
|
|
139
139
|
part?: string;
|
|
140
140
|
}
|
|
141
|
+
interface CodegenRelation {
|
|
142
|
+
name: string;
|
|
143
|
+
type: "one" | "many";
|
|
144
|
+
entity: string;
|
|
145
|
+
}
|
|
141
146
|
interface CodegenEntityModule {
|
|
142
147
|
entityName: string;
|
|
143
148
|
operations: CodegenEntityOperation[];
|
|
144
149
|
actions: CodegenEntityAction[];
|
|
150
|
+
relations?: CodegenRelation[];
|
|
145
151
|
}
|
|
146
152
|
interface CodegenEntityOperation {
|
|
147
153
|
kind: "list" | "get" | "create" | "update" | "delete";
|
|
@@ -273,6 +279,14 @@ declare class EntityTypesGenerator implements Generator {
|
|
|
273
279
|
private emitResponseType;
|
|
274
280
|
private generateIndex;
|
|
275
281
|
}
|
|
282
|
+
interface RelationManifestEntry {
|
|
283
|
+
entityType: string;
|
|
284
|
+
schema: Record<string, {
|
|
285
|
+
type: "one" | "many";
|
|
286
|
+
entity: string;
|
|
287
|
+
}>;
|
|
288
|
+
}
|
|
289
|
+
declare function generateRelationManifest(entities: CodegenEntityModule[]): RelationManifestEntry[];
|
|
276
290
|
/**
|
|
277
291
|
* Returns a SHA-256 hex hash of the given content string.
|
|
278
292
|
* Used for comparing generated file content against what is already on disk.
|
|
@@ -302,4 +316,4 @@ declare function toPascalCase(input: string): string;
|
|
|
302
316
|
declare function toCamelCase(input: string): string;
|
|
303
317
|
declare function toKebabCase(input: string): string;
|
|
304
318
|
declare function toSnakeCase(input: string): string;
|
|
305
|
-
export { writeIncremental, validateCodegenConfig, toSnakeCase, toPascalCase, toKebabCase, toCamelCase, resolveCodegenConfig, renderImports, mergeImportsToPackageJson, mergeImports, jsonSchemaToTS, hashContent, generate, formatWithBiome, defineCodegenConfig, createCodegenPipeline, adaptIR, StreamingConfig, SchemaNamingParts, SchemaAnnotations, ResolvedCodegenConfig, OperationSchemaRefs, OperationAuth, OAuthFlows, JsonSchema, IncrementalResult, IncrementalOptions, Import, HttpMethod, GeneratorName, GeneratorConfig, Generator, GeneratedFile, GenerateResult, FileFragment, EntityTypesGenerator, EntitySdkGenerator, EntitySchemaGenerator, ConversionResult, ConversionContext, CodegenTypescriptConfig, CodegenSchema, CodegenResolvedField, CodegenPublishableConfig, CodegenPipeline, CodegenOperation, CodegenModule, CodegenIR, CodegenEntityOperation, CodegenEntityModule, CodegenEntityAction, CodegenConfig, CodegenAuthScheme, CodegenAuth, ClientGenerator };
|
|
319
|
+
export { writeIncremental, validateCodegenConfig, toSnakeCase, toPascalCase, toKebabCase, toCamelCase, resolveCodegenConfig, renderImports, mergeImportsToPackageJson, mergeImports, jsonSchemaToTS, hashContent, generateRelationManifest, generate, formatWithBiome, defineCodegenConfig, createCodegenPipeline, adaptIR, StreamingConfig, SchemaNamingParts, SchemaAnnotations, ResolvedCodegenConfig, RelationManifestEntry, OperationSchemaRefs, OperationAuth, OAuthFlows, JsonSchema, IncrementalResult, IncrementalOptions, Import, HttpMethod, GeneratorName, GeneratorConfig, Generator, GeneratedFile, GenerateResult, FileFragment, EntityTypesGenerator, EntitySdkGenerator, EntitySchemaGenerator, ConversionResult, ConversionContext, CodegenTypescriptConfig, CodegenSchema, CodegenResolvedField, CodegenRelation, CodegenPublishableConfig, CodegenPipeline, CodegenOperation, CodegenModule, CodegenIR, CodegenEntityOperation, CodegenEntityModule, CodegenEntityAction, CodegenConfig, CodegenAuthScheme, CodegenAuth, ClientGenerator };
|
package/dist/index.js
CHANGED
|
@@ -139,6 +139,17 @@ function toSnakeCase(input) {
|
|
|
139
139
|
return splitWords(input).map((w) => w.toLowerCase()).join("_");
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
// src/generators/relation-manifest-generator.ts
|
|
143
|
+
function generateRelationManifest(entities) {
|
|
144
|
+
return entities.map((entity) => {
|
|
145
|
+
const schema = {};
|
|
146
|
+
for (const rel of entity.relations ?? []) {
|
|
147
|
+
schema[rel.name] = { type: rel.type, entity: rel.entity };
|
|
148
|
+
}
|
|
149
|
+
return { entityType: entity.entityName, schema };
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
142
153
|
// src/generators/client-generator.ts
|
|
143
154
|
var FILE_HEADER = `// Generated by @vertz/codegen — do not edit
|
|
144
155
|
|
|
@@ -155,29 +166,70 @@ class ClientGenerator {
|
|
|
155
166
|
}
|
|
156
167
|
generateClient(ir) {
|
|
157
168
|
const entities = ir.entities ?? [];
|
|
169
|
+
const hasMutations = entities.some((e) => e.operations.some((op) => op.kind === "update" || op.kind === "delete"));
|
|
170
|
+
const manifest = generateRelationManifest(entities);
|
|
171
|
+
const hasRelations = manifest.some((entry) => Object.keys(entry.schema).length > 0);
|
|
158
172
|
const lines = [FILE_HEADER];
|
|
159
173
|
if (entities.length > 0) {
|
|
160
|
-
|
|
174
|
+
if (hasMutations) {
|
|
175
|
+
lines.push("import { FetchClient, type OptimisticHandler } from '@vertz/fetch';");
|
|
176
|
+
if (hasRelations) {
|
|
177
|
+
lines.push("import { createOptimisticHandler, getEntityStore, registerRelationSchema } from '@vertz/ui';");
|
|
178
|
+
} else {
|
|
179
|
+
lines.push("import { createOptimisticHandler, getEntityStore } from '@vertz/ui';");
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
lines.push("import { FetchClient } from '@vertz/fetch';");
|
|
183
|
+
if (hasRelations) {
|
|
184
|
+
lines.push("import { registerRelationSchema } from '@vertz/ui';");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
161
187
|
for (const entity of entities) {
|
|
162
188
|
const pascal = toPascalCase(entity.entityName);
|
|
163
189
|
lines.push(`import { create${pascal}Sdk } from './entities/${entity.entityName}';`);
|
|
164
190
|
}
|
|
165
191
|
lines.push("");
|
|
192
|
+
if (hasRelations) {
|
|
193
|
+
for (const entry of manifest) {
|
|
194
|
+
const schemaEntries = Object.entries(entry.schema);
|
|
195
|
+
if (schemaEntries.length === 0) {
|
|
196
|
+
lines.push(`registerRelationSchema('${entry.entityType}', {});`);
|
|
197
|
+
} else {
|
|
198
|
+
lines.push(`registerRelationSchema('${entry.entityType}', {`);
|
|
199
|
+
for (const [field, def] of schemaEntries) {
|
|
200
|
+
lines.push(` ${field}: { type: '${def.type}', entity: '${def.entity}' },`);
|
|
201
|
+
}
|
|
202
|
+
lines.push("});");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
lines.push("");
|
|
206
|
+
}
|
|
166
207
|
}
|
|
167
208
|
lines.push("export interface ClientOptions {");
|
|
168
209
|
lines.push(" baseURL?: string;");
|
|
169
210
|
lines.push(" headers?: Record<string, string>;");
|
|
170
211
|
lines.push(" timeoutMs?: number;");
|
|
212
|
+
if (hasMutations) {
|
|
213
|
+
lines.push(" optimistic?: OptimisticHandler | false;");
|
|
214
|
+
}
|
|
171
215
|
lines.push("}");
|
|
172
216
|
lines.push("");
|
|
173
217
|
lines.push("export function createClient(options: ClientOptions = {}) {");
|
|
174
218
|
if (entities.length > 0) {
|
|
175
219
|
lines.push(` const client = new FetchClient({ baseURL: options.baseURL ?? '/api', headers: options.headers, timeoutMs: options.timeoutMs });`);
|
|
220
|
+
if (hasMutations) {
|
|
221
|
+
lines.push(" const optimistic = options.optimistic !== false ? (options.optimistic ?? createOptimisticHandler(getEntityStore())) : undefined;");
|
|
222
|
+
}
|
|
176
223
|
lines.push(" return {");
|
|
177
224
|
for (const entity of entities) {
|
|
178
225
|
const pascal = toPascalCase(entity.entityName);
|
|
179
226
|
const camel = toCamelCase(entity.entityName);
|
|
180
|
-
|
|
227
|
+
const entityHasMutations = entity.operations.some((op) => op.kind === "update" || op.kind === "delete");
|
|
228
|
+
if (entityHasMutations) {
|
|
229
|
+
lines.push(` ${camel}: create${pascal}Sdk(client, optimistic),`);
|
|
230
|
+
} else {
|
|
231
|
+
lines.push(` ${camel}: create${pascal}Sdk(client),`);
|
|
232
|
+
}
|
|
181
233
|
}
|
|
182
234
|
lines.push(" };");
|
|
183
235
|
} else {
|
|
@@ -379,6 +431,7 @@ class EntitySdkGenerator {
|
|
|
379
431
|
}
|
|
380
432
|
const hasTypes = entity.operations.some((op) => op.outputSchema || op.inputSchema);
|
|
381
433
|
const hasListOp = entity.operations.some((op) => op.kind === "list");
|
|
434
|
+
const hasMutationOp = entity.operations.some((op) => op.kind === "update" || op.kind === "delete");
|
|
382
435
|
if (hasTypes) {
|
|
383
436
|
const typeImports = new Set;
|
|
384
437
|
for (const op of entity.operations) {
|
|
@@ -394,11 +447,22 @@ class EntitySdkGenerator {
|
|
|
394
447
|
typeImports.add(action.outputSchema);
|
|
395
448
|
}
|
|
396
449
|
lines.push(`import type { ${[...typeImports].join(", ")} } from '../types/${entity.entityName}';`);
|
|
397
|
-
const
|
|
398
|
-
|
|
450
|
+
const fetchImportParts = ["type FetchClient"];
|
|
451
|
+
if (hasListOp)
|
|
452
|
+
fetchImportParts.push("type ListResponse");
|
|
453
|
+
if (hasMutationOp)
|
|
454
|
+
fetchImportParts.push("type OptimisticHandler");
|
|
455
|
+
fetchImportParts.push("createDescriptor");
|
|
456
|
+
if (hasMutationOp)
|
|
457
|
+
fetchImportParts.push("createMutationDescriptor");
|
|
458
|
+
lines.push(`import { ${fetchImportParts.join(", ")} } from '@vertz/fetch';`);
|
|
399
459
|
lines.push("");
|
|
400
460
|
}
|
|
401
|
-
|
|
461
|
+
if (hasMutationOp) {
|
|
462
|
+
lines.push(`export function create${pascal}Sdk(client: FetchClient, optimistic?: OptimisticHandler) {`);
|
|
463
|
+
} else {
|
|
464
|
+
lines.push(`export function create${pascal}Sdk(client: FetchClient) {`);
|
|
465
|
+
}
|
|
402
466
|
lines.push(" return {");
|
|
403
467
|
for (const op of entity.operations) {
|
|
404
468
|
const inputType = op.inputSchema ?? "unknown";
|
|
@@ -407,13 +471,13 @@ class EntitySdkGenerator {
|
|
|
407
471
|
switch (op.kind) {
|
|
408
472
|
case "list":
|
|
409
473
|
lines.push(` list: Object.assign(`);
|
|
410
|
-
lines.push(` (query?: Record<string, unknown>) => createDescriptor('GET', '${op.path}', () => client.get<${listOutput}>('${op.path}', { query }), query),`);
|
|
474
|
+
lines.push(` (query?: Record<string, unknown>) => createDescriptor('GET', '${op.path}', () => client.get<${listOutput}>('${op.path}', { query }), query, { entityType: '${entity.entityName}', kind: 'list' as const }),`);
|
|
411
475
|
lines.push(` { url: '${op.path}', method: 'GET' as const },`);
|
|
412
476
|
lines.push(` ),`);
|
|
413
477
|
break;
|
|
414
478
|
case "get":
|
|
415
479
|
lines.push(` get: Object.assign(`);
|
|
416
|
-
lines.push(` (id: string) => createDescriptor('GET', \`${op.path.replace(":id", "${id}")}\`, () => client.get<${outputType}>(\`${op.path.replace(":id", "${id}")}\`)),`);
|
|
480
|
+
lines.push(` (id: string) => createDescriptor('GET', \`${op.path.replace(":id", "${id}")}\`, () => client.get<${outputType}>(\`${op.path.replace(":id", "${id}")}\`), undefined, { entityType: '${entity.entityName}', kind: 'get' as const, id }),`);
|
|
417
481
|
lines.push(` { url: '${op.path}', method: 'GET' as const },`);
|
|
418
482
|
lines.push(` ),`);
|
|
419
483
|
break;
|
|
@@ -437,13 +501,13 @@ class EntitySdkGenerator {
|
|
|
437
501
|
break;
|
|
438
502
|
case "update":
|
|
439
503
|
lines.push(` update: Object.assign(`);
|
|
440
|
-
lines.push(` (id: string, body: ${inputType}) =>
|
|
504
|
+
lines.push(` (id: string, body: ${inputType}) => createMutationDescriptor('PATCH', \`${op.path.replace(":id", "${id}")}\`, () => client.patch<${outputType}>(\`${op.path.replace(":id", "${id}")}\`, body), { entityType: '${entity.entityName}', kind: 'update' as const, id, body }, optimistic),`);
|
|
441
505
|
lines.push(` { url: '${op.path}', method: 'PATCH' as const },`);
|
|
442
506
|
lines.push(` ),`);
|
|
443
507
|
break;
|
|
444
508
|
case "delete":
|
|
445
509
|
lines.push(` delete: Object.assign(`);
|
|
446
|
-
lines.push(` (id: string) =>
|
|
510
|
+
lines.push(` (id: string) => createMutationDescriptor('DELETE', \`${op.path.replace(":id", "${id}")}\`, () => client.delete<${outputType}>(\`${op.path.replace(":id", "${id}")}\`), { entityType: '${entity.entityName}', kind: 'delete' as const, id }, optimistic),`);
|
|
447
511
|
lines.push(` { url: '${op.path}', method: 'DELETE' as const },`);
|
|
448
512
|
lines.push(` ),`);
|
|
449
513
|
break;
|
|
@@ -769,7 +833,13 @@ function adaptIR(appIR) {
|
|
|
769
833
|
resolvedOutputFields
|
|
770
834
|
};
|
|
771
835
|
});
|
|
772
|
-
|
|
836
|
+
const resolvedRelations = entity.relations.filter((r) => !!r.type && !!r.entity).map((r) => ({ name: r.name, type: r.type, entity: r.entity }));
|
|
837
|
+
return {
|
|
838
|
+
entityName: entity.name,
|
|
839
|
+
operations,
|
|
840
|
+
actions,
|
|
841
|
+
relations: resolvedRelations.length > 0 ? resolvedRelations : undefined
|
|
842
|
+
};
|
|
773
843
|
});
|
|
774
844
|
return {
|
|
775
845
|
basePath: appIR.app.basePath,
|
|
@@ -1056,6 +1126,7 @@ export {
|
|
|
1056
1126
|
mergeImports,
|
|
1057
1127
|
jsonSchemaToTS,
|
|
1058
1128
|
hashContent,
|
|
1129
|
+
generateRelationManifest,
|
|
1059
1130
|
generate,
|
|
1060
1131
|
formatWithBiome,
|
|
1061
1132
|
defineCodegenConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/codegen",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
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.12"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^25.3.1",
|