@huanglangjian/specs-ts-codegen 0.9.0
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.mts +21 -0
- package/dist/index.mjs +740 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +36 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Models, RecordModel, RouterModel } from "@huanglangjian/specs";
|
|
2
|
+
|
|
3
|
+
//#region src/server.d.ts
|
|
4
|
+
interface TsServerOptions {
|
|
5
|
+
routers: RouterModel[];
|
|
6
|
+
identifier?: (id: string) => string;
|
|
7
|
+
namespace?: string;
|
|
8
|
+
configuration?: RecordModel<Record<string, Models>, string>;
|
|
9
|
+
}
|
|
10
|
+
declare function generateTsServer(options: TsServerOptions): Record<string, string>;
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/client.d.ts
|
|
13
|
+
interface TsClientOptions {
|
|
14
|
+
routers: RouterModel[];
|
|
15
|
+
identifier?: (id: string) => string;
|
|
16
|
+
namespace?: string;
|
|
17
|
+
}
|
|
18
|
+
declare function generateTsClient(options: TsClientOptions): Record<string, string>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { type TsClientOptions, type TsServerOptions, generateTsClient, generateTsServer };
|
|
21
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
import { collectOperations, collectSchemaMap, groupBy, resolveNamedRoot, topologicalSortSchemaMap } from "@huanglangjian/specs";
|
|
2
|
+
import { camelCase, pascalCase, snakeCase } from "text-case";
|
|
3
|
+
//#region src/shared.ts
|
|
4
|
+
function generateModels(schemaMap, identifier, namespace) {
|
|
5
|
+
const lines = [];
|
|
6
|
+
lines.push(`import { z } from "zod"`);
|
|
7
|
+
lines.push("");
|
|
8
|
+
for (const [id, schemaInfo] of topologicalSortSchemaMap(schemaMap)) {
|
|
9
|
+
const schemaName = camelCase(id) + "Schema";
|
|
10
|
+
const tsName = identifier(id);
|
|
11
|
+
switch (schemaInfo.kind) {
|
|
12
|
+
case "record":
|
|
13
|
+
lines.push(`export const ${schemaName} = z.object({`);
|
|
14
|
+
for (const field of schemaInfo.fields) lines.push(` ${field.name}: ${toZod(field.model, schemaMap)}${field.required ? "" : optionalDefault(field.model)},`);
|
|
15
|
+
lines.push(`})`);
|
|
16
|
+
lines.push("");
|
|
17
|
+
lines.push(`export interface ${tsName} {`);
|
|
18
|
+
for (const field of schemaInfo.fields) lines.push(` ${field.name}${field.required ? "" : "?"}: ${toTs(field.model, schemaMap, identifier, namespace)};`);
|
|
19
|
+
lines.push(`}`);
|
|
20
|
+
lines.push("");
|
|
21
|
+
break;
|
|
22
|
+
case "enums":
|
|
23
|
+
lines.push(`export const ${schemaName} = z.enum(${JSON.stringify(Object.values(schemaInfo.variants))})`);
|
|
24
|
+
lines.push(`export type ${tsName} = z.infer<typeof ${schemaName}>`);
|
|
25
|
+
lines.push("");
|
|
26
|
+
break;
|
|
27
|
+
case "union": {
|
|
28
|
+
const unionItems = Object.entries(schemaInfo.unionVariants).map(([, v]) => toZod(v, schemaMap)).join(", ");
|
|
29
|
+
lines.push(`export const ${schemaName} = z.union([${unionItems}])`);
|
|
30
|
+
lines.push(`export type ${tsName} = z.infer<typeof ${schemaName}>`);
|
|
31
|
+
lines.push("");
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
case "taggedUnion": {
|
|
35
|
+
const discriminator = schemaInfo.discriminator;
|
|
36
|
+
const unionItems = Object.entries(schemaInfo.unionVariants).map(([, v]) => `${toZod(v, schemaMap)}`);
|
|
37
|
+
lines.push(`export const ${schemaName} = z.discriminatedUnion(${JSON.stringify(discriminator)}, [${unionItems.join(", ")}])`);
|
|
38
|
+
lines.push(`export type ${tsName} = z.infer<typeof ${schemaName}>`);
|
|
39
|
+
lines.push("");
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
function toZod(model, schemaMap) {
|
|
47
|
+
switch (model.kind) {
|
|
48
|
+
case "int32": return "z.coerce.number().int()";
|
|
49
|
+
case "float32":
|
|
50
|
+
case "float64": return "z.coerce.number()";
|
|
51
|
+
case "boolean": return "z.coerce.boolean()";
|
|
52
|
+
case "string": return "z.string()";
|
|
53
|
+
case "datetime": return "z.string().datetime()";
|
|
54
|
+
case "date": return "z.string().date()";
|
|
55
|
+
case "duration": return "z.string()";
|
|
56
|
+
case "literal": return `z.literal(${JSON.stringify(model.value)})`;
|
|
57
|
+
case "null": return "z.null()";
|
|
58
|
+
case "array": return `${toZod(model.base, schemaMap)}.array()`;
|
|
59
|
+
case "set": return `${toZod(model.base, schemaMap)}.array()`;
|
|
60
|
+
case "map": return `z.record(z.string(), ${toZod(model.base, schemaMap)})`;
|
|
61
|
+
case "enums": return `z.enum(${JSON.stringify(Object.values(model.variants))})`;
|
|
62
|
+
case "record": return schemaMap.get(model.id)?.fields ? camelCase(model.id) + "Schema" : "z.unknown()";
|
|
63
|
+
case "union":
|
|
64
|
+
if (schemaMap.get(model.id)?.unionVariants) return camelCase(model.id) + "Schema";
|
|
65
|
+
return `z.union([${Object.values(model.variants).map((v) => toZod(v, schemaMap)).join(", ")}])`;
|
|
66
|
+
case "taggedUnion": {
|
|
67
|
+
if (schemaMap.get(model.id)?.unionVariants) return camelCase(model.id) + "Schema";
|
|
68
|
+
const discriminator = model.discriminator;
|
|
69
|
+
const unionItems = Object.values(model.variants).map((v) => `${toZod(v, schemaMap)}`);
|
|
70
|
+
return `z.discriminatedUnion(${JSON.stringify(discriminator)}, [${unionItems.join(", ")}])`;
|
|
71
|
+
}
|
|
72
|
+
default: return "z.unknown()";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function toTs(model, schemaMap, identifier, namespace) {
|
|
76
|
+
switch (model.kind) {
|
|
77
|
+
case "int32":
|
|
78
|
+
case "float32":
|
|
79
|
+
case "float64": return "number";
|
|
80
|
+
case "boolean": return "boolean";
|
|
81
|
+
case "string":
|
|
82
|
+
case "datetime":
|
|
83
|
+
case "date":
|
|
84
|
+
case "duration": return "string";
|
|
85
|
+
case "literal": return JSON.stringify(model.value);
|
|
86
|
+
case "null": return "null";
|
|
87
|
+
case "array":
|
|
88
|
+
case "set": return `${toTs(model.base, schemaMap, identifier, namespace)}[]`;
|
|
89
|
+
case "map": return `Record<string, ${toTs(model.base, schemaMap, identifier, namespace)}>`;
|
|
90
|
+
case "enums":
|
|
91
|
+
if (schemaMap.get(model.id)?.variants) return identifier(model.id);
|
|
92
|
+
return Object.values(model.variants).map((v) => JSON.stringify(v)).join(" | ");
|
|
93
|
+
case "record": return schemaMap.get(model.id)?.fields ? identifier(model.id) : "unknown";
|
|
94
|
+
case "union":
|
|
95
|
+
case "taggedUnion":
|
|
96
|
+
if (schemaMap.get(model.id)?.unionVariants) return identifier(model.id);
|
|
97
|
+
return Object.values(model.variants).map((v) => toTs(v, schemaMap, identifier, namespace)).join(" | ");
|
|
98
|
+
default: return "unknown";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function optionalDefault(model) {
|
|
102
|
+
if ("default" in model && model.default != null) return `.optional().default(${JSON.stringify(model.default)})`;
|
|
103
|
+
return ".optional()";
|
|
104
|
+
}
|
|
105
|
+
function resolveZodSchema(model, schemaMap) {
|
|
106
|
+
switch (model.kind) {
|
|
107
|
+
case "record":
|
|
108
|
+
case "union":
|
|
109
|
+
case "taggedUnion":
|
|
110
|
+
case "enums": return schemaMap.get(model.id) ? camelCase(model.id) + "Schema" : null;
|
|
111
|
+
case "array":
|
|
112
|
+
case "set": {
|
|
113
|
+
const inner = resolveZodSchema(model.base, schemaMap);
|
|
114
|
+
return inner ? `${inner}.array()` : null;
|
|
115
|
+
}
|
|
116
|
+
case "map": {
|
|
117
|
+
const inner = resolveZodSchema(model.base, schemaMap);
|
|
118
|
+
return inner ? `z.record(z.string(), ${inner})` : null;
|
|
119
|
+
}
|
|
120
|
+
default: return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function collectSchemaRefs(model, schemaMap) {
|
|
124
|
+
switch (model.kind) {
|
|
125
|
+
case "record":
|
|
126
|
+
case "union":
|
|
127
|
+
case "taggedUnion":
|
|
128
|
+
case "enums": return schemaMap.get(model.id) ? [camelCase(model.id) + "Schema"] : [];
|
|
129
|
+
case "array":
|
|
130
|
+
case "set": return collectSchemaRefs(model.base, schemaMap);
|
|
131
|
+
case "map": return collectSchemaRefs(model.base, schemaMap);
|
|
132
|
+
default: return [];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function toHonoPath(path) {
|
|
136
|
+
return path.replace(/\{(\w+)\}/g, ":$1");
|
|
137
|
+
}
|
|
138
|
+
function contentTypeForKind(kind) {
|
|
139
|
+
switch (kind) {
|
|
140
|
+
case "binary": return "application/octet-stream";
|
|
141
|
+
case "stream-response": return "application/x-ndjson";
|
|
142
|
+
case "sse-response": return "text/event-stream";
|
|
143
|
+
default: return "application/json";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/server.ts
|
|
148
|
+
function generateTsServer(options) {
|
|
149
|
+
const { routers, identifier = pascalCase, namespace, configuration } = options;
|
|
150
|
+
const operations = collectOperations(routers);
|
|
151
|
+
const schemaMap = collectSchemaMap(operations);
|
|
152
|
+
const files = {};
|
|
153
|
+
files["models.ts"] = generateModels(schemaMap, identifier, namespace);
|
|
154
|
+
for (const operation of operations) files[`${camelCase(operation.group)}/${camelCase(operation.id)}.ts`] = generateOpFile(operation, schemaMap, identifier, namespace);
|
|
155
|
+
files["index.ts"] = generateIndex(operations, identifier);
|
|
156
|
+
if (configuration) files["config.ts"] = generateConfig(configuration);
|
|
157
|
+
return files;
|
|
158
|
+
}
|
|
159
|
+
function generateOpFile(operation, schemaMap, identifier, namespace) {
|
|
160
|
+
const lines = [];
|
|
161
|
+
const OperationName = pascalCase(operation.id);
|
|
162
|
+
const operationName = camelCase(operation.id);
|
|
163
|
+
const hasBody = operation.requestModel != null && operation.requestModel.kind !== "null";
|
|
164
|
+
const hasParams = Object.keys(operation.pathVariables).length > 0;
|
|
165
|
+
const hasQuery = Object.keys(operation.queries).length > 0;
|
|
166
|
+
const hasHeaders = Object.keys(operation.headers).length > 0;
|
|
167
|
+
if (hasParams || hasQuery || hasHeaders) lines.push(`import { z } from "zod"`);
|
|
168
|
+
const schemaImports = [];
|
|
169
|
+
if (hasBody && "id" in operation.requestModel) {
|
|
170
|
+
const root = resolveNamedRoot(operation.requestModel);
|
|
171
|
+
if (root && schemaMap.has(root.id)) schemaImports.push(camelCase(root.id) + "Schema");
|
|
172
|
+
}
|
|
173
|
+
const typeImports = [];
|
|
174
|
+
if (hasBody && "id" in operation.requestModel) {
|
|
175
|
+
const root = resolveNamedRoot(operation.requestModel);
|
|
176
|
+
if (root) {
|
|
177
|
+
const typeName = identifier(root.id);
|
|
178
|
+
if (schemaMap.has(root.id) && !typeImports.includes(typeName)) typeImports.push(typeName);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
for (const responseModel of Object.values(operation.responses)) {
|
|
182
|
+
if (responseModel == null) continue;
|
|
183
|
+
const root = resolveNamedRoot(responseModel);
|
|
184
|
+
if (root) {
|
|
185
|
+
const typeName = identifier(root.id);
|
|
186
|
+
if (schemaMap.has(root.id) && !typeImports.includes(typeName)) typeImports.push(typeName);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if ([...new Set([...schemaImports, ...typeImports])].length > 0 || schemaImports.length > 0) {
|
|
190
|
+
const parts = [];
|
|
191
|
+
if (typeImports.length > 0) parts.push(typeImports.join(", "));
|
|
192
|
+
if (schemaImports.length > 0) parts.push(schemaImports.join(", "));
|
|
193
|
+
lines.push(`import { ${parts.join(", ")} } from "../models"`);
|
|
194
|
+
}
|
|
195
|
+
lines.push("");
|
|
196
|
+
if (hasParams) {
|
|
197
|
+
const fields = Object.entries(operation.pathVariables).map(([key, value]) => ` ${key}: ${toZod(value.model, schemaMap)},`);
|
|
198
|
+
lines.push(`const ${operationName}Params = z.object({`);
|
|
199
|
+
lines.push(...fields);
|
|
200
|
+
lines.push(`})`);
|
|
201
|
+
lines.push("");
|
|
202
|
+
}
|
|
203
|
+
if (hasQuery) {
|
|
204
|
+
const fields = Object.entries(operation.queries).map(([key, query]) => ` ${key}: ${toZod(query.model, schemaMap)}${query.required ? "" : optionalDefault(query.model)},`);
|
|
205
|
+
lines.push(`const ${operationName}Query = z.object({`);
|
|
206
|
+
lines.push(...fields);
|
|
207
|
+
lines.push(`})`);
|
|
208
|
+
lines.push("");
|
|
209
|
+
}
|
|
210
|
+
if (hasHeaders) {
|
|
211
|
+
const fields = Object.entries(operation.headers).map(([key, header]) => ` "${key}": ${toZod(header.model, schemaMap)}${header.required ? "" : optionalDefault(header.model)},`);
|
|
212
|
+
lines.push(`const ${operationName}Headers = z.object({`);
|
|
213
|
+
lines.push(...fields);
|
|
214
|
+
lines.push(`})`);
|
|
215
|
+
lines.push("");
|
|
216
|
+
}
|
|
217
|
+
lines.push(`export namespace ${OperationName}Operation {`);
|
|
218
|
+
lines.push("");
|
|
219
|
+
const requestFields = [];
|
|
220
|
+
if (hasParams) {
|
|
221
|
+
const field = Object.entries(operation.pathVariables).map(([key, value]) => `${key}: ${toTs(value.model, schemaMap, identifier, namespace)}`).join("; ");
|
|
222
|
+
requestFields.push(`params: { ${field} }`);
|
|
223
|
+
}
|
|
224
|
+
if (hasQuery) {
|
|
225
|
+
const field = Object.entries(operation.queries).map(([key, query]) => `${key}${query.required ? "" : "?"}: ${toTs(query.model, schemaMap, identifier, namespace)}`).join("; ");
|
|
226
|
+
requestFields.push(`query: { ${field} }`);
|
|
227
|
+
}
|
|
228
|
+
if (hasHeaders) {
|
|
229
|
+
const field = Object.entries(operation.headers).map(([key, header]) => `"${key}"${header.required ? "" : "?"}: ${toTs(header.model, schemaMap, identifier, namespace)}`).join("; ");
|
|
230
|
+
requestFields.push(`headers: { ${field} }`);
|
|
231
|
+
}
|
|
232
|
+
if (hasBody) requestFields.push(`body: ${toTs(operation.requestModel, schemaMap, identifier, namespace)}`);
|
|
233
|
+
lines.push(` export interface Request {`);
|
|
234
|
+
for (const field of requestFields) lines.push(` ${field}`);
|
|
235
|
+
lines.push(` }`);
|
|
236
|
+
lines.push("");
|
|
237
|
+
const responseEntries = Object.entries(operation.responses);
|
|
238
|
+
const responseKind = (status) => operation.responseKinds[Number(status)] ?? "json-response";
|
|
239
|
+
const responseBodyField = (kind, model) => {
|
|
240
|
+
if (model == null) {
|
|
241
|
+
if (kind === "binary") return "body: Blob";
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
if (kind === "stream-response" || kind === "sse-response") return `stream: ReadableStream<${toTs(model, schemaMap, identifier, namespace)}>`;
|
|
245
|
+
if (kind === "binary") return "body: Blob";
|
|
246
|
+
return `body: ${toTs(model, schemaMap, identifier, namespace)}`;
|
|
247
|
+
};
|
|
248
|
+
if (responseEntries.length === 1) {
|
|
249
|
+
const [status, responseModel] = responseEntries[0];
|
|
250
|
+
const bodyField = responseBodyField(responseKind(status), responseModel);
|
|
251
|
+
if (bodyField != null) lines.push(` export type Response = { status: ${status}; ${bodyField} }`);
|
|
252
|
+
else lines.push(` export type Response = { status: ${status} }`);
|
|
253
|
+
} else {
|
|
254
|
+
lines.push(` export type Response =`);
|
|
255
|
+
const parts = responseEntries.map(([status, responseModel]) => {
|
|
256
|
+
const bodyField = responseBodyField(responseKind(status), responseModel);
|
|
257
|
+
if (bodyField != null) return ` | { status: ${status}; ${bodyField} }`;
|
|
258
|
+
return ` | { status: ${status} }`;
|
|
259
|
+
});
|
|
260
|
+
lines.push(parts.join("\n"));
|
|
261
|
+
}
|
|
262
|
+
lines.push("");
|
|
263
|
+
if (requestFields.length > 0) lines.push(` export type Handler = (req: Request) => Promise<Response>`);
|
|
264
|
+
else lines.push(` export type Handler = () => Promise<Response>`);
|
|
265
|
+
lines.push(`}`);
|
|
266
|
+
lines.push("");
|
|
267
|
+
if (hasParams) {
|
|
268
|
+
lines.push(`export const ${operationName}Pattern = new URLPattern({ pathname: "*/${toHonoPath(operation.path).replace(/^\//, "")}" })`);
|
|
269
|
+
lines.push("");
|
|
270
|
+
}
|
|
271
|
+
lines.push(`export function ${operationName}(handler: ${OperationName}Operation.Handler) {`);
|
|
272
|
+
lines.push(` return {`);
|
|
273
|
+
lines.push(` method: "${operation.method.toUpperCase()}",`);
|
|
274
|
+
lines.push(` path: "${toHonoPath(operation.path)}",`);
|
|
275
|
+
lines.push(` handler: async (request: Request, params?: Record<string, string>): Promise<Response> => {`);
|
|
276
|
+
const requestArgs = [];
|
|
277
|
+
if (hasQuery) lines.push(` const requestUrl = new URL(request.url)`);
|
|
278
|
+
if (hasParams) {
|
|
279
|
+
lines.push(` const p = ${operationName}Params.parse(`);
|
|
280
|
+
lines.push(` params ?? ${operationName}Pattern.exec(request.url)!.pathname.groups`);
|
|
281
|
+
lines.push(` )`);
|
|
282
|
+
requestArgs.push("params: p");
|
|
283
|
+
}
|
|
284
|
+
if (hasQuery) {
|
|
285
|
+
lines.push(` const query = ${operationName}Query.parse(Object.fromEntries(requestUrl.searchParams))`);
|
|
286
|
+
requestArgs.push("query");
|
|
287
|
+
}
|
|
288
|
+
if (hasHeaders) {
|
|
289
|
+
const headerParts = Object.keys(operation.headers).map((key) => ` "${key}": request.headers.get("${key}"),`);
|
|
290
|
+
lines.push(` const headers = ${operationName}Headers.parse({`);
|
|
291
|
+
lines.push(...headerParts);
|
|
292
|
+
lines.push(` })`);
|
|
293
|
+
requestArgs.push("headers");
|
|
294
|
+
}
|
|
295
|
+
if (hasBody) {
|
|
296
|
+
const schemaName = camelCase(operation.requestModel.id) + "Schema";
|
|
297
|
+
lines.push(` const body = ${schemaName}.parse(await request.json())`);
|
|
298
|
+
requestArgs.push("body");
|
|
299
|
+
}
|
|
300
|
+
if (requestArgs.length > 0) lines.push(` const result = await handler({ ${requestArgs.join(", ")} })`);
|
|
301
|
+
else lines.push(` const result = await handler()`);
|
|
302
|
+
lines.push(` switch (result.status) {`);
|
|
303
|
+
for (const [status, responseModel] of Object.entries(operation.responses)) {
|
|
304
|
+
const kind = operation.responseKinds[Number(status)] ?? "json-response";
|
|
305
|
+
if (responseModel != null) if (kind === "json-response") lines.push(` case ${status}: return new Response(JSON.stringify(result.body), { status: ${status}, headers: { "Content-Type": "application/json" } })`);
|
|
306
|
+
else if (kind === "binary") lines.push(` case ${status}: return new Response(result.body, { status: ${status}, headers: { "Content-Type": "${contentTypeForKind(kind)}" } })`);
|
|
307
|
+
else lines.push(` case ${status}: return new Response(result.stream, { status: ${status}, headers: { "Content-Type": "${contentTypeForKind(kind)}" } })`);
|
|
308
|
+
else if (kind === "binary") lines.push(` case ${status}: return new Response(result.body, { status: ${status}, headers: { "Content-Type": "${contentTypeForKind(kind)}" } })`);
|
|
309
|
+
else lines.push(` case ${status}: return new Response(null, { status: ${status} })`);
|
|
310
|
+
}
|
|
311
|
+
lines.push(` default: return new Response(JSON.stringify({ message: \`Unexpected response status \${(result as { status: number }).status}\` }), { status: 500, headers: { "Content-Type": "application/json" } })`);
|
|
312
|
+
lines.push(` }`);
|
|
313
|
+
lines.push(` },`);
|
|
314
|
+
lines.push(` }`);
|
|
315
|
+
lines.push(`}`);
|
|
316
|
+
return lines.join("\n");
|
|
317
|
+
}
|
|
318
|
+
function generateIndex(operations, _identifier) {
|
|
319
|
+
const lines = [];
|
|
320
|
+
lines.push("");
|
|
321
|
+
for (const operation of operations) {
|
|
322
|
+
const operationName = camelCase(operation.id);
|
|
323
|
+
const OperationName = pascalCase(operation.id);
|
|
324
|
+
lines.push(`import { ${operationName}, ${OperationName}Operation } from "./${camelCase(operation.group)}/${operationName}"`);
|
|
325
|
+
}
|
|
326
|
+
lines.push("");
|
|
327
|
+
const groups = groupBy(operations, (operation) => operation.group);
|
|
328
|
+
for (const [group, groupOps] of Object.entries(groups)) {
|
|
329
|
+
const groupPascal = pascalCase(group);
|
|
330
|
+
lines.push(`export interface ${groupPascal}Handlers {`);
|
|
331
|
+
for (const operation of groupOps) {
|
|
332
|
+
const operationName = camelCase(operation.id);
|
|
333
|
+
lines.push(` ${operationName}: ${pascalCase(operation.id)}Operation.Handler`);
|
|
334
|
+
}
|
|
335
|
+
lines.push(`}`);
|
|
336
|
+
lines.push("");
|
|
337
|
+
lines.push(`export function create${groupPascal}Router(handlers: ${groupPascal}Handlers) {`);
|
|
338
|
+
lines.push(` return [`);
|
|
339
|
+
for (const operation of groupOps) {
|
|
340
|
+
const operationName = camelCase(operation.id);
|
|
341
|
+
lines.push(` ${operationName}(handlers.${operationName}),`);
|
|
342
|
+
}
|
|
343
|
+
lines.push(` ]`);
|
|
344
|
+
lines.push(`}`);
|
|
345
|
+
lines.push("");
|
|
346
|
+
}
|
|
347
|
+
return lines.join("\n");
|
|
348
|
+
}
|
|
349
|
+
function generateConfig(config) {
|
|
350
|
+
const root = collectLevel(config.properties, config.required, "");
|
|
351
|
+
const out = [];
|
|
352
|
+
out.push(`import { z } from "zod"`);
|
|
353
|
+
out.push("");
|
|
354
|
+
const schemaName = camelCase(config.id) + "Schema";
|
|
355
|
+
out.push(`export const ${schemaName} = z.object({`);
|
|
356
|
+
for (const v of root.envVars) out.push(` ${v.envName}: ${v.zodExpr},`);
|
|
357
|
+
out.push(`})`);
|
|
358
|
+
out.push("");
|
|
359
|
+
for (const sw of root.switches) emitSwitch(sw, out);
|
|
360
|
+
out.push(`export function get${pascalCase(config.id)}(env: Record<string, string | undefined> = process.env) {`);
|
|
361
|
+
out.push(` const e = ${schemaName}.parse(env)`);
|
|
362
|
+
out.push(` return {`);
|
|
363
|
+
for (const f of root.fields) out.push(` ${f.name}: ${emitFieldExpr(f, "e", "env")},`);
|
|
364
|
+
out.push(` }`);
|
|
365
|
+
out.push(`}`);
|
|
366
|
+
out.push("");
|
|
367
|
+
return out.join("\n");
|
|
368
|
+
}
|
|
369
|
+
function emitSwitch(sw, out) {
|
|
370
|
+
for (const v of sw.variants) {
|
|
371
|
+
for (const nestedSw of v.switches) emitSwitch(nestedSw, out);
|
|
372
|
+
const schemaName = camelCase(v.resolveFnName.replace("_resolve", "")) + "ConfigSchema";
|
|
373
|
+
out.push(`export const ${schemaName} = z.object({`);
|
|
374
|
+
for (const ev of v.envVars) out.push(` ${ev.envName}: ${ev.zodExpr},`);
|
|
375
|
+
out.push(`})`);
|
|
376
|
+
out.push("");
|
|
377
|
+
out.push(`function ${v.resolveFnName}(env: Record<string, string | undefined>) {`);
|
|
378
|
+
out.push(` const e = ${schemaName}.parse(env)`);
|
|
379
|
+
out.push("");
|
|
380
|
+
out.push(` return {`);
|
|
381
|
+
for (const f of v.fields) out.push(` ${f.name}: ${emitFieldExpr(f, "e", "env")},`);
|
|
382
|
+
out.push(` }`);
|
|
383
|
+
out.push(`}`);
|
|
384
|
+
out.push("");
|
|
385
|
+
}
|
|
386
|
+
out.push(`function ${sw.resolveFnName}(env: Record<string, string | undefined>, dv: string) {`);
|
|
387
|
+
out.push(` switch (dv) {`);
|
|
388
|
+
for (const v of sw.variants) if (sw.discriminator != null) out.push(` case "${v.varName}": return ${v.resolveFnName}(env)`);
|
|
389
|
+
else out.push(` case "${v.varName}": return { type: "${v.varName}" as const, ...${v.resolveFnName}(env) }`);
|
|
390
|
+
out.push(` }`);
|
|
391
|
+
out.push(`}`);
|
|
392
|
+
out.push("");
|
|
393
|
+
}
|
|
394
|
+
function emitFieldExpr(field, envRef, rawEnv) {
|
|
395
|
+
switch (field.kind) {
|
|
396
|
+
case "env": return `${envRef}.${field.envName}`;
|
|
397
|
+
case "record": return `{ ${field.childFields.map((f) => `${f.name}: ${emitFieldExpr(f, envRef, rawEnv)}`).join(", ")} }`;
|
|
398
|
+
case "switch": return `${field.switchFnName}(${rawEnv}, ${envRef}.${field.dvEnvName})`;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function collectLevel(properties, required, prefix) {
|
|
402
|
+
const envVars = [];
|
|
403
|
+
const fields = [];
|
|
404
|
+
const switches = [];
|
|
405
|
+
for (const [propName, model] of Object.entries(properties)) {
|
|
406
|
+
const envPrefix = prefix ? `${prefix}_${snakeCase(propName).toUpperCase()}` : snakeCase(propName).toUpperCase();
|
|
407
|
+
switch (model.kind) {
|
|
408
|
+
case "int32":
|
|
409
|
+
case "float32":
|
|
410
|
+
case "float64":
|
|
411
|
+
case "boolean":
|
|
412
|
+
case "string":
|
|
413
|
+
case "datetime":
|
|
414
|
+
case "date":
|
|
415
|
+
case "duration":
|
|
416
|
+
case "literal":
|
|
417
|
+
case "null":
|
|
418
|
+
envVars.push({
|
|
419
|
+
envName: envPrefix,
|
|
420
|
+
zodExpr: toZodEnv(model)
|
|
421
|
+
});
|
|
422
|
+
fields.push({
|
|
423
|
+
name: propName,
|
|
424
|
+
kind: "env",
|
|
425
|
+
envName: envPrefix
|
|
426
|
+
});
|
|
427
|
+
break;
|
|
428
|
+
case "enums":
|
|
429
|
+
envVars.push({
|
|
430
|
+
envName: envPrefix,
|
|
431
|
+
zodExpr: toZodEnv(model)
|
|
432
|
+
});
|
|
433
|
+
fields.push({
|
|
434
|
+
name: propName,
|
|
435
|
+
kind: "env",
|
|
436
|
+
envName: envPrefix
|
|
437
|
+
});
|
|
438
|
+
break;
|
|
439
|
+
case "record": {
|
|
440
|
+
const rec = model;
|
|
441
|
+
const child = collectLevel(rec.properties, rec.required, envPrefix);
|
|
442
|
+
envVars.push(...child.envVars);
|
|
443
|
+
switches.push(...child.switches);
|
|
444
|
+
fields.push({
|
|
445
|
+
name: propName,
|
|
446
|
+
kind: "record",
|
|
447
|
+
childFields: child.fields
|
|
448
|
+
});
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
case "taggedUnion": {
|
|
452
|
+
const dvZod = `z.enum(${JSON.stringify(Object.keys(model.variants))})`;
|
|
453
|
+
envVars.push({
|
|
454
|
+
envName: envPrefix,
|
|
455
|
+
zodExpr: dvZod
|
|
456
|
+
});
|
|
457
|
+
const resolveFnName = `_resolve${pascalCase(propName)}`;
|
|
458
|
+
const variants = [];
|
|
459
|
+
for (const [vKey, vModel] of Object.entries(model.variants)) {
|
|
460
|
+
const vRec = vModel;
|
|
461
|
+
const child = collectLevel(vRec.properties, vRec.required, envPrefix);
|
|
462
|
+
variants.push({
|
|
463
|
+
varName: vKey,
|
|
464
|
+
resolveFnName: `${resolveFnName}${pascalCase(vKey)}`,
|
|
465
|
+
envVars: child.envVars,
|
|
466
|
+
fields: child.fields,
|
|
467
|
+
switches: child.switches
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
switches.push({
|
|
471
|
+
resolveFnName,
|
|
472
|
+
dvEnvName: envPrefix,
|
|
473
|
+
dvZodExpr: dvZod,
|
|
474
|
+
discriminator: model.discriminator,
|
|
475
|
+
variants
|
|
476
|
+
});
|
|
477
|
+
fields.push({
|
|
478
|
+
name: propName,
|
|
479
|
+
kind: "switch",
|
|
480
|
+
switchFnName: resolveFnName,
|
|
481
|
+
dvEnvName: envPrefix
|
|
482
|
+
});
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
485
|
+
case "union": {
|
|
486
|
+
const dvEnvName = `${envPrefix}_TYPE`;
|
|
487
|
+
const dvZod = `z.enum(${JSON.stringify(Object.keys(model.variants))})`;
|
|
488
|
+
envVars.push({
|
|
489
|
+
envName: dvEnvName,
|
|
490
|
+
zodExpr: dvZod
|
|
491
|
+
});
|
|
492
|
+
const resolveFnName = `_resolve${pascalCase(propName)}`;
|
|
493
|
+
const variants = [];
|
|
494
|
+
for (const [vKey, vModel] of Object.entries(model.variants)) {
|
|
495
|
+
const child = collectVariant(vModel, envPrefix);
|
|
496
|
+
variants.push({
|
|
497
|
+
varName: vKey,
|
|
498
|
+
resolveFnName: `${resolveFnName}${pascalCase(vKey)}`,
|
|
499
|
+
envVars: child.envVars,
|
|
500
|
+
fields: child.fields,
|
|
501
|
+
switches: child.switches
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
switches.push({
|
|
505
|
+
resolveFnName,
|
|
506
|
+
dvEnvName,
|
|
507
|
+
dvZodExpr: dvZod,
|
|
508
|
+
discriminator: null,
|
|
509
|
+
variants
|
|
510
|
+
});
|
|
511
|
+
fields.push({
|
|
512
|
+
name: propName,
|
|
513
|
+
kind: "switch",
|
|
514
|
+
switchFnName: resolveFnName,
|
|
515
|
+
dvEnvName
|
|
516
|
+
});
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
case "array":
|
|
520
|
+
case "set": {
|
|
521
|
+
const base = model.base;
|
|
522
|
+
if (!isSimpleType(base)) throw new Error(`unsupported configuration value of kind ${model.kind}<non-simple>, only simple element types are allowed`);
|
|
523
|
+
envVars.push({
|
|
524
|
+
envName: envPrefix,
|
|
525
|
+
zodExpr: toZodEnv(model)
|
|
526
|
+
});
|
|
527
|
+
fields.push({
|
|
528
|
+
name: propName,
|
|
529
|
+
kind: "env",
|
|
530
|
+
envName: envPrefix
|
|
531
|
+
});
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
case "map": throw new Error("unsupported configuration value of kind map");
|
|
535
|
+
default:
|
|
536
|
+
envVars.push({
|
|
537
|
+
envName: envPrefix,
|
|
538
|
+
zodExpr: "z.string()"
|
|
539
|
+
});
|
|
540
|
+
fields.push({
|
|
541
|
+
name: propName,
|
|
542
|
+
kind: "env",
|
|
543
|
+
envName: envPrefix
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return {
|
|
548
|
+
envVars,
|
|
549
|
+
fields,
|
|
550
|
+
switches
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function collectVariant(model, prefix) {
|
|
554
|
+
if (model.kind === "record") {
|
|
555
|
+
const rec = model;
|
|
556
|
+
return collectLevel(rec.properties, rec.required, prefix);
|
|
557
|
+
}
|
|
558
|
+
const envName = prefix;
|
|
559
|
+
return {
|
|
560
|
+
envVars: [{
|
|
561
|
+
envName,
|
|
562
|
+
zodExpr: toZodEnv(model)
|
|
563
|
+
}],
|
|
564
|
+
fields: [{
|
|
565
|
+
name: "value",
|
|
566
|
+
kind: "env",
|
|
567
|
+
envName
|
|
568
|
+
}],
|
|
569
|
+
switches: []
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function isSimpleType(model) {
|
|
573
|
+
return [
|
|
574
|
+
"int32",
|
|
575
|
+
"float32",
|
|
576
|
+
"float64",
|
|
577
|
+
"boolean",
|
|
578
|
+
"string",
|
|
579
|
+
"datetime",
|
|
580
|
+
"date",
|
|
581
|
+
"duration",
|
|
582
|
+
"literal",
|
|
583
|
+
"enums"
|
|
584
|
+
].includes(model.kind);
|
|
585
|
+
}
|
|
586
|
+
function toZodEnv(model) {
|
|
587
|
+
switch (model.kind) {
|
|
588
|
+
case "int32": return "z.coerce.number().int()";
|
|
589
|
+
case "float32":
|
|
590
|
+
case "float64": return "z.coerce.number()";
|
|
591
|
+
case "boolean": return "z.coerce.boolean()";
|
|
592
|
+
case "string": return "z.string()";
|
|
593
|
+
case "datetime": return "z.string().datetime()";
|
|
594
|
+
case "date": return "z.string().date()";
|
|
595
|
+
case "duration": return "z.string()";
|
|
596
|
+
case "literal": return `z.literal(${JSON.stringify(model.value)})`;
|
|
597
|
+
case "null": return "z.null()";
|
|
598
|
+
case "enums": return `z.enum(${JSON.stringify(Object.values(model.variants))})`;
|
|
599
|
+
case "array":
|
|
600
|
+
if (!isSimpleType(model.base)) throw new Error("unsupported configuration value of kind array<non-simple>, only simple element types are allowed");
|
|
601
|
+
return `z.coerce.string().transform(s => s.split(',').filter(Boolean)).pipe(z.array(${toZodEnv(model.base)}))`;
|
|
602
|
+
case "set":
|
|
603
|
+
if (!isSimpleType(model.base)) throw new Error("unsupported configuration value of kind set<non-simple>, only simple element types are allowed");
|
|
604
|
+
return `z.coerce.string().transform(s => new Set(s.split(',').filter(Boolean))).pipe(z.set(${toZodEnv(model.base)}))`;
|
|
605
|
+
case "map": throw new Error("unsupported configuration value of kind map");
|
|
606
|
+
default: return "z.string()";
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
//#endregion
|
|
610
|
+
//#region src/client.ts
|
|
611
|
+
function generateTsClient(options) {
|
|
612
|
+
const { routers, identifier = pascalCase, namespace } = options;
|
|
613
|
+
const operations = collectOperations(routers);
|
|
614
|
+
const schemaMap = collectSchemaMap(operations);
|
|
615
|
+
const files = {};
|
|
616
|
+
files["models.ts"] = generateModels(schemaMap, identifier, namespace);
|
|
617
|
+
for (const operation of operations) files[`${camelCase(operation.group)}/${camelCase(operation.id)}.ts`] = generateClientFn(operation, schemaMap, identifier, namespace);
|
|
618
|
+
files["index.ts"] = generateClientIndex(operations, identifier);
|
|
619
|
+
return files;
|
|
620
|
+
}
|
|
621
|
+
function generateClientFn(operation, schemaMap, identifier, namespace) {
|
|
622
|
+
const lines = [];
|
|
623
|
+
const functionName = camelCase(operation.id);
|
|
624
|
+
const OperationName = pascalCase(operation.id);
|
|
625
|
+
const hasBody = operation.requestModel != null && operation.requestModel.kind !== "null";
|
|
626
|
+
const hasParams = Object.keys(operation.pathVariables).length > 0;
|
|
627
|
+
const hasQuery = Object.keys(operation.queries).length > 0;
|
|
628
|
+
const hasHeaders = Object.keys(operation.headers).length > 0;
|
|
629
|
+
const typeImports = [];
|
|
630
|
+
const addNamedRef = (model) => {
|
|
631
|
+
const root = resolveNamedRoot(model);
|
|
632
|
+
if (root) {
|
|
633
|
+
const typeName = identifier(root.id);
|
|
634
|
+
if (schemaMap.has(root.id) && !typeImports.includes(typeName)) typeImports.push(typeName);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
if (hasBody) addNamedRef(operation.requestModel);
|
|
638
|
+
for (const responseModel of Object.values(operation.responses)) if (responseModel != null) addNamedRef(responseModel);
|
|
639
|
+
const okStatus = Object.keys(operation.responses).find((s) => Number(s) >= 200 && Number(s) < 300);
|
|
640
|
+
const okModel = okStatus ? operation.responses[Number(okStatus)] : null;
|
|
641
|
+
const okKind = okStatus ? operation.responseKinds[Number(okStatus)] ?? "json-response" : "json-response";
|
|
642
|
+
const isStreamLike = okKind === "stream-response" || okKind === "sse-response" || okKind === "binary";
|
|
643
|
+
const okSchema = okModel ? resolveZodSchema(okModel, schemaMap) : null;
|
|
644
|
+
const allModelImports = [...typeImports];
|
|
645
|
+
if (okSchema && !isStreamLike) {
|
|
646
|
+
const schemaRefs = collectSchemaRefs(okModel, schemaMap);
|
|
647
|
+
for (const ref of schemaRefs) if (!allModelImports.includes(ref)) allModelImports.push(ref);
|
|
648
|
+
}
|
|
649
|
+
if (allModelImports.length > 0) lines.push(`import { ${allModelImports.join(", ")} } from "../models"`);
|
|
650
|
+
lines.push("");
|
|
651
|
+
lines.push(`export namespace ${OperationName}Operation {`);
|
|
652
|
+
lines.push("");
|
|
653
|
+
const requestFields = [];
|
|
654
|
+
for (const [n, v] of Object.entries(operation.pathVariables)) requestFields.push(`${n}: ${toTs(v.model, schemaMap, identifier, namespace)}`);
|
|
655
|
+
for (const [n, q] of Object.entries(operation.queries)) {
|
|
656
|
+
const required = q.required ? "" : "?";
|
|
657
|
+
requestFields.push(`${n}${required}: ${toTs(q.model, schemaMap, identifier, namespace)}`);
|
|
658
|
+
}
|
|
659
|
+
if (hasBody) requestFields.push(`body: ${toTs(operation.requestModel, schemaMap, identifier, namespace)}`);
|
|
660
|
+
if (hasHeaders) {
|
|
661
|
+
const field = Object.entries(operation.headers).map(([key, header]) => `"${key}"${header.required ? "" : "?"}: ${toTs(header.model, schemaMap, identifier, namespace)}`).join("; ");
|
|
662
|
+
requestFields.push(`headers?: { ${field} } & Record<string, string>`);
|
|
663
|
+
} else requestFields.push("headers?: Record<string, string>");
|
|
664
|
+
requestFields.push("baseUrl?: string");
|
|
665
|
+
lines.push(` export interface Request {`);
|
|
666
|
+
for (const field of requestFields) lines.push(` ${field}`);
|
|
667
|
+
lines.push(` }`);
|
|
668
|
+
lines.push("");
|
|
669
|
+
const responseEntries = Object.entries(operation.responses);
|
|
670
|
+
const responseKind = (status) => operation.responseKinds[Number(status)] ?? "json-response";
|
|
671
|
+
const responseBodyField = (kind, responseModel) => {
|
|
672
|
+
if (responseModel == null) {
|
|
673
|
+
if (kind === "binary") return "body: Blob";
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
if (kind === "stream-response" || kind === "sse-response") return `stream: ReadableStream<${toTs(responseModel, schemaMap, identifier, namespace)}>`;
|
|
677
|
+
if (kind === "binary") return "body: Blob";
|
|
678
|
+
return `body: ${toTs(responseModel, schemaMap, identifier, namespace)}`;
|
|
679
|
+
};
|
|
680
|
+
if (responseEntries.length === 1) {
|
|
681
|
+
const [status, responseModel] = responseEntries[0];
|
|
682
|
+
const bodyField = responseBodyField(responseKind(status), responseModel);
|
|
683
|
+
if (bodyField != null) lines.push(` export type Response = { status: ${status}; ${bodyField} }`);
|
|
684
|
+
else lines.push(` export type Response = { status: ${status} }`);
|
|
685
|
+
} else {
|
|
686
|
+
lines.push(` export type Response =`);
|
|
687
|
+
for (const [status, responseModel] of responseEntries) {
|
|
688
|
+
const bodyField = responseBodyField(responseKind(status), responseModel);
|
|
689
|
+
if (bodyField != null) lines.push(` | { status: ${status}; ${bodyField} }`);
|
|
690
|
+
else lines.push(` | { status: ${status} }`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
lines.push("");
|
|
694
|
+
lines.push(`}`);
|
|
695
|
+
lines.push("");
|
|
696
|
+
const reqParam = hasParams || hasBody ? `req: ${OperationName}Operation.Request` : `req?: ${OperationName}Operation.Request`;
|
|
697
|
+
const retType = okModel && !isStreamLike ? toTs(okModel, schemaMap, identifier, namespace) : "Response";
|
|
698
|
+
const pathExpr = operation.path.replace(/\{(\w+)\}/g, (_, name) => `\${encodeURIComponent(req.${name})}`);
|
|
699
|
+
lines.push(`export async function ${functionName}(${reqParam}): Promise<${retType}> {`);
|
|
700
|
+
lines.push(` const baseUrl = req?.baseUrl ?? ""`);
|
|
701
|
+
if (hasQuery) {
|
|
702
|
+
lines.push(` const parts: string[] = []`);
|
|
703
|
+
for (const [n, q] of Object.entries(operation.queries)) if (q.required) lines.push(` parts.push("${n}=" + encodeURIComponent(req.${n}))`);
|
|
704
|
+
else lines.push(` if (req?.${n} != null) parts.push("${n}=" + encodeURIComponent(req.${n}))`);
|
|
705
|
+
lines.push(` const qs = parts.length > 0 ? "?" + parts.join("&") : ""`);
|
|
706
|
+
lines.push(` const url = \`\${baseUrl}${pathExpr}\${qs}\``);
|
|
707
|
+
} else lines.push(` const url = \`\${baseUrl}${pathExpr}\``);
|
|
708
|
+
lines.push("");
|
|
709
|
+
lines.push(` const res = await fetch(url, {`);
|
|
710
|
+
lines.push(` method: "${operation.method}",`);
|
|
711
|
+
if (hasBody) {
|
|
712
|
+
lines.push(` headers: { "Content-Type": "application/json", ...req.headers },`);
|
|
713
|
+
lines.push(` body: JSON.stringify(req.body),`);
|
|
714
|
+
} else lines.push(` headers: req?.headers,`);
|
|
715
|
+
lines.push(` })`);
|
|
716
|
+
lines.push(` if (!res.ok) throw new Error(\`${operation.method} ${operation.path} failed: \${res.status}\`)`);
|
|
717
|
+
if (okModel && !isStreamLike) if (okSchema) lines.push(` return ${okSchema}.parse(await res.json())`);
|
|
718
|
+
else lines.push(` return res.json()`);
|
|
719
|
+
else lines.push(` return res`);
|
|
720
|
+
lines.push(`}`);
|
|
721
|
+
return lines.join("\n");
|
|
722
|
+
}
|
|
723
|
+
function generateClientIndex(operations, _identifier) {
|
|
724
|
+
const lines = [];
|
|
725
|
+
const groups = groupBy(operations, (operation) => operation.group);
|
|
726
|
+
for (const [group, groupOps] of Object.entries(groups)) {
|
|
727
|
+
lines.push(`// ── ${group} ──`);
|
|
728
|
+
for (const operation of groupOps) {
|
|
729
|
+
const n = camelCase(operation.id);
|
|
730
|
+
const OperationName = pascalCase(operation.id);
|
|
731
|
+
lines.push(`export { ${n}, ${OperationName}Operation } from "./${camelCase(operation.group)}/${n}"`);
|
|
732
|
+
}
|
|
733
|
+
lines.push("");
|
|
734
|
+
}
|
|
735
|
+
return lines.join("\n");
|
|
736
|
+
}
|
|
737
|
+
//#endregion
|
|
738
|
+
export { generateTsClient, generateTsServer };
|
|
739
|
+
|
|
740
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/shared.ts","../src/server.ts","../src/client.ts"],"sourcesContent":["import type { Models } from \"@huanglangjian/specs\"\nimport { topologicalSortSchemaMap } from \"@huanglangjian/specs\"\nimport type { SchemaMap } from \"@huanglangjian/specs\"\nimport { camelCase } from \"text-case\"\n\nexport function generateModels(schemaMap: SchemaMap, identifier: (s: string) => string, namespace?: string): string {\n const lines: string[] = []\n lines.push(`import { z } from \"zod\"`)\n lines.push(\"\")\n\n for (const [id, schemaInfo] of topologicalSortSchemaMap(schemaMap)) {\n const schemaName = camelCase(id) + \"Schema\"\n const tsName = identifier(id)\n\n switch (schemaInfo.kind) {\n case \"record\": {\n lines.push(`export const ${schemaName} = z.object({`)\n for (const field of schemaInfo.fields!) {\n lines.push(\n ` ${field.name}: ${toZod(field.model, schemaMap)}${field.required ? \"\" : optionalDefault(field.model)},`,\n )\n }\n lines.push(`})`)\n lines.push(\"\")\n\n lines.push(`export interface ${tsName} {`)\n for (const field of schemaInfo.fields!) {\n lines.push(\n ` ${field.name}${field.required ? \"\" : \"?\"}: ${toTs(field.model, schemaMap, identifier, namespace)};`,\n )\n }\n lines.push(`}`)\n lines.push(\"\")\n break\n }\n case \"enums\": {\n lines.push(`export const ${schemaName} = z.enum(${JSON.stringify(Object.values(schemaInfo.variants!))})`)\n lines.push(`export type ${tsName} = z.infer<typeof ${schemaName}>`)\n lines.push(\"\")\n break\n }\n case \"union\": {\n const variants = Object.entries(schemaInfo.unionVariants!)\n const unionItems = variants.map(([, v]) => toZod(v as Models, schemaMap)).join(\", \")\n lines.push(`export const ${schemaName} = z.union([${unionItems}])`)\n lines.push(`export type ${tsName} = z.infer<typeof ${schemaName}>`)\n lines.push(\"\")\n break\n }\n case \"taggedUnion\": {\n const discriminator = schemaInfo.discriminator!\n const unionItems = Object.entries(schemaInfo.unionVariants!).map(([, v]) => `${toZod(v as Models, schemaMap)}`)\n lines.push(\n `export const ${schemaName} = z.discriminatedUnion(${JSON.stringify(discriminator)}, [${unionItems.join(\", \")}])`,\n )\n lines.push(`export type ${tsName} = z.infer<typeof ${schemaName}>`)\n lines.push(\"\")\n break\n }\n }\n }\n\n return lines.join(\"\\n\")\n}\n\nexport function toZod(model: Models, schemaMap: SchemaMap): string {\n switch (model.kind) {\n case \"int32\":\n return \"z.coerce.number().int()\"\n case \"float32\":\n case \"float64\":\n return \"z.coerce.number()\"\n case \"boolean\":\n return \"z.coerce.boolean()\"\n case \"string\":\n return \"z.string()\"\n case \"datetime\":\n return \"z.string().datetime()\"\n case \"date\":\n return \"z.string().date()\"\n case \"duration\":\n return \"z.string()\"\n case \"literal\":\n return `z.literal(${JSON.stringify(model.value)})`\n case \"null\":\n return \"z.null()\"\n case \"array\":\n return `${toZod(model.base, schemaMap)}.array()`\n case \"set\":\n return `${toZod(model.base, schemaMap)}.array()`\n case \"map\":\n return `z.record(z.string(), ${toZod(model.base, schemaMap)})`\n case \"enums\":\n return `z.enum(${JSON.stringify(Object.values(model.variants))})`\n case \"record\": {\n const schemaInfo = schemaMap.get(model.id)\n return schemaInfo?.fields ? camelCase(model.id) + \"Schema\" : \"z.unknown()\"\n }\n case \"union\": {\n const schemaInfo = schemaMap.get(model.id)\n if (schemaInfo?.unionVariants) return camelCase(model.id) + \"Schema\"\n const unionItems = Object.values(model.variants).map((v) => toZod(v as Models, schemaMap))\n return `z.union([${unionItems.join(\", \")}])`\n }\n case \"taggedUnion\": {\n const schemaInfo = schemaMap.get(model.id)\n if (schemaInfo?.unionVariants) return camelCase(model.id) + \"Schema\"\n const discriminator = model.discriminator as string\n const unionItems = Object.values(model.variants).map((v) => `${toZod(v as Models, schemaMap)}`)\n return `z.discriminatedUnion(${JSON.stringify(discriminator)}, [${unionItems.join(\", \")}])`\n }\n default:\n return \"z.unknown()\"\n }\n}\n\nexport function toTs(\n model: Models,\n schemaMap: SchemaMap,\n identifier: (s: string) => string,\n namespace?: string,\n): string {\n void namespace\n switch (model.kind) {\n case \"int32\":\n case \"float32\":\n case \"float64\":\n return \"number\"\n case \"boolean\":\n return \"boolean\"\n case \"string\":\n case \"datetime\":\n case \"date\":\n case \"duration\":\n return \"string\"\n case \"literal\":\n return JSON.stringify(model.value)\n case \"null\":\n return \"null\"\n case \"array\":\n case \"set\":\n return `${toTs(model.base, schemaMap, identifier, namespace)}[]`\n case \"map\":\n return `Record<string, ${toTs(model.base, schemaMap, identifier, namespace)}>`\n case \"enums\": {\n const schemaInfo = schemaMap.get(model.id)\n if (schemaInfo?.variants) return identifier(model.id)\n return Object.values(model.variants)\n .map((v) => JSON.stringify(v))\n .join(\" | \")\n }\n case \"record\": {\n const schemaInfo = schemaMap.get(model.id)\n return schemaInfo?.fields ? identifier(model.id) : \"unknown\"\n }\n case \"union\":\n case \"taggedUnion\": {\n const schemaInfo = schemaMap.get(model.id)\n if (schemaInfo?.unionVariants) return identifier(model.id)\n return Object.values(model.variants)\n .map((v) => toTs(v as Models, schemaMap, identifier, namespace))\n .join(\" | \")\n }\n default:\n return \"unknown\"\n }\n}\n\nexport function optionalDefault(model: Models): string {\n if (\"default\" in model && model.default != null) {\n return `.optional().default(${JSON.stringify(model.default)})`\n }\n return \".optional()\"\n}\n\nexport function resolveZodSchema(model: Models, schemaMap: SchemaMap): string | null {\n switch (model.kind) {\n case \"record\":\n case \"union\":\n case \"taggedUnion\":\n case \"enums\": {\n const schemaInfo = schemaMap.get(model.id)\n return schemaInfo ? camelCase(model.id) + \"Schema\" : null\n }\n case \"array\":\n case \"set\": {\n const inner = resolveZodSchema(model.base, schemaMap)\n return inner ? `${inner}.array()` : null\n }\n case \"map\": {\n const inner = resolveZodSchema(model.base, schemaMap)\n return inner ? `z.record(z.string(), ${inner})` : null\n }\n default:\n return null\n }\n}\n\nexport function collectSchemaRefs(model: Models, schemaMap: SchemaMap): string[] {\n switch (model.kind) {\n case \"record\":\n case \"union\":\n case \"taggedUnion\":\n case \"enums\": {\n const schemaInfo = schemaMap.get(model.id)\n return schemaInfo ? [camelCase(model.id) + \"Schema\"] : []\n }\n case \"array\":\n case \"set\":\n return collectSchemaRefs(model.base, schemaMap)\n case \"map\":\n return collectSchemaRefs(model.base, schemaMap)\n default:\n return []\n }\n}\n\nexport function toHonoPath(path: string): string {\n return path.replace(/\\{(\\w+)\\}/g, \":$1\")\n}\n\nexport function contentTypeForKind(kind: string): string {\n switch (kind) {\n case \"binary\":\n return \"application/octet-stream\"\n case \"stream-response\":\n return \"application/x-ndjson\"\n case \"sse-response\":\n return \"text/event-stream\"\n default:\n return \"application/json\"\n }\n}\n","import type { RouterModel, Models, RecordModel } from \"@huanglangjian/specs\"\nimport { collectOperations, collectSchemaMap, resolveNamedRoot } from \"@huanglangjian/specs\"\nimport type { OperationDescriptor, SchemaMap } from \"@huanglangjian/specs\"\nimport { groupBy } from \"@huanglangjian/specs\"\nimport { camelCase, pascalCase, snakeCase } from \"text-case\"\n\nimport { generateModels, toZod, toTs, optionalDefault, toHonoPath, contentTypeForKind } from \"./shared\"\n\nexport interface TsServerOptions {\n routers: RouterModel[]\n identifier?: (id: string) => string\n namespace?: string\n configuration?: RecordModel<Record<string, Models>, string>\n}\n\nexport function generateTsServer(options: TsServerOptions): Record<string, string> {\n const { routers, identifier = pascalCase, namespace, configuration } = options\n const operations = collectOperations(routers)\n const schemaMap = collectSchemaMap(operations)\n\n const files: Record<string, string> = {}\n\n files[\"models.ts\"] = generateModels(schemaMap, identifier, namespace)\n\n for (const operation of operations) {\n files[`${camelCase(operation.group)}/${camelCase(operation.id)}.ts`] = generateOpFile(\n operation,\n schemaMap,\n identifier,\n namespace,\n )\n }\n\n files[\"index.ts\"] = generateIndex(operations, identifier)\n\n if (configuration) {\n files[\"config.ts\"] = generateConfig(configuration)\n }\n\n return files\n}\n\n// ---- per-operation file ----\n\nfunction generateOpFile(\n operation: OperationDescriptor,\n schemaMap: SchemaMap,\n identifier: (s: string) => string,\n namespace: string | undefined,\n): string {\n const lines: string[] = []\n const OperationName = pascalCase(operation.id)\n const operationName = camelCase(operation.id)\n\n const hasBody = operation.requestModel != null && operation.requestModel.kind !== \"null\"\n const hasParams = Object.keys(operation.pathVariables).length > 0\n const hasQuery = Object.keys(operation.queries).length > 0\n const hasHeaders = Object.keys(operation.headers).length > 0\n\n const needsZod = hasParams || hasQuery || hasHeaders\n if (needsZod) {\n lines.push(`import { z } from \"zod\"`)\n }\n\n const schemaImports: string[] = []\n if (hasBody && \"id\" in operation.requestModel!) {\n const root = resolveNamedRoot(operation.requestModel!)\n if (root && schemaMap.has(root.id)) {\n schemaImports.push(camelCase(root.id) + \"Schema\")\n }\n }\n\n const typeImports: string[] = []\n if (hasBody && \"id\" in operation.requestModel!) {\n const root = resolveNamedRoot(operation.requestModel!)\n if (root) {\n const typeName = identifier(root.id)\n if (schemaMap.has(root.id) && !typeImports.includes(typeName)) {\n typeImports.push(typeName)\n }\n }\n }\n for (const responseModel of Object.values(operation.responses)) {\n if (responseModel == null) continue\n const root = resolveNamedRoot(responseModel)\n if (root) {\n const typeName = identifier(root.id)\n if (schemaMap.has(root.id) && !typeImports.includes(typeName)) {\n typeImports.push(typeName)\n }\n }\n }\n\n const allImports = [...new Set([...schemaImports, ...typeImports])]\n if (allImports.length > 0 || schemaImports.length > 0) {\n const parts: string[] = []\n if (typeImports.length > 0) parts.push(typeImports.join(\", \"))\n if (schemaImports.length > 0) parts.push(schemaImports.join(\", \"))\n lines.push(`import { ${parts.join(\", \")} } from \"../models\"`)\n }\n lines.push(\"\")\n\n if (hasParams) {\n const fields = Object.entries(operation.pathVariables).map(\n ([key, value]) => ` ${key}: ${toZod(value.model, schemaMap)},`,\n )\n lines.push(`const ${operationName}Params = z.object({`)\n lines.push(...fields)\n lines.push(`})`)\n lines.push(\"\")\n }\n if (hasQuery) {\n const fields = Object.entries(operation.queries).map(\n ([key, query]) =>\n ` ${key}: ${toZod(query.model, schemaMap)}${query.required ? \"\" : optionalDefault(query.model)},`,\n )\n lines.push(`const ${operationName}Query = z.object({`)\n lines.push(...fields)\n lines.push(`})`)\n lines.push(\"\")\n }\n if (hasHeaders) {\n const fields = Object.entries(operation.headers).map(\n ([key, header]) =>\n ` \"${key}\": ${toZod(header.model, schemaMap)}${header.required ? \"\" : optionalDefault(header.model)},`,\n )\n lines.push(`const ${operationName}Headers = z.object({`)\n lines.push(...fields)\n lines.push(`})`)\n lines.push(\"\")\n }\n\n lines.push(`export namespace ${OperationName}Operation {`)\n lines.push(\"\")\n\n const requestFields: string[] = []\n if (hasParams) {\n const field = Object.entries(operation.pathVariables)\n .map(([key, value]) => `${key}: ${toTs(value.model, schemaMap, identifier, namespace)}`)\n .join(\"; \")\n requestFields.push(`params: { ${field} }`)\n }\n if (hasQuery) {\n const field = Object.entries(operation.queries)\n .map(\n ([key, query]) => `${key}${query.required ? \"\" : \"?\"}: ${toTs(query.model, schemaMap, identifier, namespace)}`,\n )\n .join(\"; \")\n requestFields.push(`query: { ${field} }`)\n }\n if (hasHeaders) {\n const field = Object.entries(operation.headers)\n .map(\n ([key, header]) =>\n `\"${key}\"${header.required ? \"\" : \"?\"}: ${toTs(header.model, schemaMap, identifier, namespace)}`,\n )\n .join(\"; \")\n requestFields.push(`headers: { ${field} }`)\n }\n if (hasBody) {\n requestFields.push(`body: ${toTs(operation.requestModel!, schemaMap, identifier, namespace)}`)\n }\n lines.push(` export interface Request {`)\n for (const field of requestFields) lines.push(` ${field}`)\n lines.push(` }`)\n lines.push(\"\")\n\n const responseEntries = Object.entries(operation.responses)\n const responseKind = (status: string) => operation.responseKinds[Number(status)] ?? \"json-response\"\n const responseBodyField = (kind: string, model: Models | null) => {\n if (model == null) {\n if (kind === \"binary\") return \"body: Blob\"\n return null\n }\n if (kind === \"stream-response\" || kind === \"sse-response\") {\n return `stream: ReadableStream<${toTs(model, schemaMap, identifier, namespace)}>`\n }\n if (kind === \"binary\") return \"body: Blob\"\n return `body: ${toTs(model, schemaMap, identifier, namespace)}`\n }\n if (responseEntries.length === 1) {\n const [status, responseModel] = responseEntries[0]\n const kind = responseKind(status)\n const bodyField = responseBodyField(kind, responseModel)\n if (bodyField != null) {\n lines.push(` export type Response = { status: ${status}; ${bodyField} }`)\n } else {\n lines.push(` export type Response = { status: ${status} }`)\n }\n } else {\n lines.push(` export type Response =`)\n const parts = responseEntries.map(([status, responseModel]) => {\n const kind = responseKind(status)\n const bodyField = responseBodyField(kind, responseModel)\n if (bodyField != null) return ` | { status: ${status}; ${bodyField} }`\n return ` | { status: ${status} }`\n })\n lines.push(parts.join(\"\\n\"))\n }\n lines.push(\"\")\n\n if (requestFields.length > 0) {\n lines.push(` export type Handler = (req: Request) => Promise<Response>`)\n } else {\n lines.push(` export type Handler = () => Promise<Response>`)\n }\n lines.push(`}`)\n lines.push(\"\")\n\n if (hasParams) {\n lines.push(\n `export const ${operationName}Pattern = new URLPattern({ pathname: \"*/${toHonoPath(operation.path).replace(/^\\//, \"\")}\" })`,\n )\n lines.push(\"\")\n }\n\n lines.push(`export function ${operationName}(handler: ${OperationName}Operation.Handler) {`)\n lines.push(` return {`)\n lines.push(` method: \"${operation.method.toUpperCase()}\",`)\n lines.push(` path: \"${toHonoPath(operation.path)}\",`)\n lines.push(` handler: async (request: Request, params?: Record<string, string>): Promise<Response> => {`)\n\n const requestArgs: string[] = []\n\n if (hasQuery) {\n lines.push(` const requestUrl = new URL(request.url)`)\n }\n if (hasParams) {\n lines.push(` const p = ${operationName}Params.parse(`)\n lines.push(` params ?? ${operationName}Pattern.exec(request.url)!.pathname.groups`)\n lines.push(` )`)\n requestArgs.push(\"params: p\")\n }\n if (hasQuery) {\n lines.push(` const query = ${operationName}Query.parse(Object.fromEntries(requestUrl.searchParams))`)\n requestArgs.push(\"query\")\n }\n if (hasHeaders) {\n const headerParts = Object.keys(operation.headers).map((key) => ` \"${key}\": request.headers.get(\"${key}\"),`)\n lines.push(` const headers = ${operationName}Headers.parse({`)\n lines.push(...headerParts)\n lines.push(` })`)\n requestArgs.push(\"headers\")\n }\n if (hasBody) {\n const schemaName = camelCase((operation.requestModel as any).id) + \"Schema\"\n lines.push(` const body = ${schemaName}.parse(await request.json())`)\n requestArgs.push(\"body\")\n }\n\n if (requestArgs.length > 0) {\n lines.push(` const result = await handler({ ${requestArgs.join(\", \")} })`)\n } else {\n lines.push(` const result = await handler()`)\n }\n\n lines.push(` switch (result.status) {`)\n for (const [status, responseModel] of Object.entries(operation.responses)) {\n const kind = operation.responseKinds[Number(status)] ?? \"json-response\"\n if (responseModel != null) {\n if (kind === \"json-response\") {\n lines.push(\n ` case ${status}: return new Response(JSON.stringify(result.body), { status: ${status}, headers: { \"Content-Type\": \"application/json\" } })`,\n )\n } else if (kind === \"binary\") {\n lines.push(\n ` case ${status}: return new Response(result.body, { status: ${status}, headers: { \"Content-Type\": \"${contentTypeForKind(kind)}\" } })`,\n )\n } else {\n lines.push(\n ` case ${status}: return new Response(result.stream, { status: ${status}, headers: { \"Content-Type\": \"${contentTypeForKind(kind)}\" } })`,\n )\n }\n } else if (kind === \"binary\") {\n lines.push(\n ` case ${status}: return new Response(result.body, { status: ${status}, headers: { \"Content-Type\": \"${contentTypeForKind(kind)}\" } })`,\n )\n } else {\n lines.push(` case ${status}: return new Response(null, { status: ${status} })`)\n }\n }\n lines.push(\n ` default: return new Response(JSON.stringify({ message: \\`Unexpected response status \\${(result as { status: number }).status}\\` }), { status: 500, headers: { \"Content-Type\": \"application/json\" } })`,\n )\n lines.push(` }`)\n\n lines.push(` },`)\n lines.push(` }`)\n lines.push(`}`)\n\n return lines.join(\"\\n\")\n}\n\n// ---- index.ts ----\n\nfunction generateIndex(operations: OperationDescriptor[], _identifier: (s: string) => string): string {\n const lines: string[] = []\n lines.push(\"\")\n\n for (const operation of operations) {\n const operationName = camelCase(operation.id)\n const OperationName = pascalCase(operation.id)\n lines.push(\n `import { ${operationName}, ${OperationName}Operation } from \"./${camelCase(operation.group)}/${operationName}\"`,\n )\n }\n\n lines.push(\"\")\n\n const groups = groupBy(operations, (operation) => operation.group)\n\n for (const [group, groupOps] of Object.entries(groups)) {\n const groupPascal = pascalCase(group)\n lines.push(`export interface ${groupPascal}Handlers {`)\n for (const operation of groupOps) {\n const operationName = camelCase(operation.id)\n lines.push(` ${operationName}: ${pascalCase(operation.id)}Operation.Handler`)\n }\n lines.push(`}`)\n lines.push(\"\")\n lines.push(`export function create${groupPascal}Router(handlers: ${groupPascal}Handlers) {`)\n lines.push(` return [`)\n for (const operation of groupOps) {\n const operationName = camelCase(operation.id)\n lines.push(` ${operationName}(handlers.${operationName}),`)\n }\n lines.push(` ]`)\n lines.push(`}`)\n lines.push(\"\")\n }\n\n return lines.join(\"\\n\")\n}\n\n// ---- config.ts ----\n\ninterface EnvVar {\n envName: string\n zodExpr: string\n}\n\ninterface FieldNode {\n name: string\n kind: \"env\" | \"record\" | \"switch\"\n envName?: string\n childFields?: FieldNode[]\n switchFnName?: string\n dvEnvName?: string\n}\n\ninterface VariantNode {\n varName: string\n resolveFnName: string\n envVars: EnvVar[]\n fields: FieldNode[]\n switches: SwitchNode[]\n}\n\ninterface SwitchNode {\n resolveFnName: string\n dvEnvName: string\n dvZodExpr: string\n discriminator: string | null\n variants: VariantNode[]\n}\n\ninterface CollectResult {\n envVars: EnvVar[]\n fields: FieldNode[]\n switches: SwitchNode[]\n}\n\nlet _switchIdCounter = 0\n\nfunction generateConfig(config: RecordModel<Record<string, Models>, string>): string {\n _switchIdCounter = 0\n const root = collectLevel(config.properties as Record<string, Models>, config.required as string[], \"\")\n\n const out: string[] = []\n\n out.push(`import { z } from \"zod\"`)\n out.push(\"\")\n\n const schemaName = camelCase(config.id) + \"Schema\"\n out.push(`export const ${schemaName} = z.object({`)\n for (const v of root.envVars) {\n out.push(` ${v.envName}: ${v.zodExpr},`)\n }\n out.push(`})`)\n out.push(\"\")\n\n for (const sw of root.switches) {\n emitSwitch(sw, out)\n }\n\n out.push(`export function get${pascalCase(config.id)}(env: Record<string, string | undefined> = process.env) {`)\n out.push(` const e = ${schemaName}.parse(env)`)\n out.push(` return {`)\n for (const f of root.fields) {\n out.push(` ${f.name}: ${emitFieldExpr(f, \"e\", \"env\")},`)\n }\n out.push(` }`)\n out.push(`}`)\n out.push(\"\")\n\n return out.join(\"\\n\")\n}\n\nfunction emitSwitch(sw: SwitchNode, out: string[]): void {\n for (const v of sw.variants) {\n for (const nestedSw of v.switches) {\n emitSwitch(nestedSw, out)\n }\n\n const schemaName = camelCase(v.resolveFnName.replace(\"_resolve\", \"\")) + \"ConfigSchema\"\n out.push(`export const ${schemaName} = z.object({`)\n for (const ev of v.envVars) {\n out.push(` ${ev.envName}: ${ev.zodExpr},`)\n }\n out.push(`})`)\n out.push(\"\")\n out.push(`function ${v.resolveFnName}(env: Record<string, string | undefined>) {`)\n out.push(` const e = ${schemaName}.parse(env)`)\n out.push(\"\")\n out.push(` return {`)\n for (const f of v.fields) {\n out.push(` ${f.name}: ${emitFieldExpr(f, \"e\", \"env\")},`)\n }\n out.push(` }`)\n out.push(`}`)\n out.push(\"\")\n }\n\n out.push(`function ${sw.resolveFnName}(env: Record<string, string | undefined>, dv: string) {`)\n out.push(` switch (dv) {`)\n for (const v of sw.variants) {\n if (sw.discriminator != null) {\n out.push(` case \"${v.varName}\": return ${v.resolveFnName}(env)`)\n } else {\n out.push(` case \"${v.varName}\": return { type: \"${v.varName}\" as const, ...${v.resolveFnName}(env) }`)\n }\n }\n out.push(` }`)\n out.push(`}`)\n out.push(\"\")\n}\n\nfunction emitFieldExpr(field: FieldNode, envRef: string, rawEnv: string): string {\n switch (field.kind) {\n case \"env\":\n return `${envRef}.${field.envName}`\n case \"record\":\n return `{ ${field.childFields!.map((f) => `${f.name}: ${emitFieldExpr(f, envRef, rawEnv)}`).join(\", \")} }`\n case \"switch\":\n return `${field.switchFnName}(${rawEnv}, ${envRef}.${field.dvEnvName})`\n }\n}\n\nfunction collectLevel(properties: Record<string, Models>, required: string[], prefix: string): CollectResult {\n const envVars: EnvVar[] = []\n const fields: FieldNode[] = []\n const switches: SwitchNode[] = []\n\n for (const [propName, model] of Object.entries(properties)) {\n const envPrefix = prefix ? `${prefix}_${snakeCase(propName).toUpperCase()}` : snakeCase(propName).toUpperCase()\n\n switch (model.kind) {\n case \"int32\":\n case \"float32\":\n case \"float64\":\n case \"boolean\":\n case \"string\":\n case \"datetime\":\n case \"date\":\n case \"duration\":\n case \"literal\":\n case \"null\":\n envVars.push({ envName: envPrefix, zodExpr: toZodEnv(model as Models) })\n fields.push({ name: propName, kind: \"env\", envName: envPrefix })\n break\n\n case \"enums\":\n envVars.push({ envName: envPrefix, zodExpr: toZodEnv(model as Models) })\n fields.push({ name: propName, kind: \"env\", envName: envPrefix })\n break\n\n case \"record\": {\n const rec = model as RecordModel<Record<string, Models>, string>\n const child = collectLevel(rec.properties as Record<string, Models>, rec.required as string[], envPrefix)\n envVars.push(...child.envVars)\n switches.push(...child.switches)\n fields.push({ name: propName, kind: \"record\", childFields: child.fields })\n break\n }\n\n case \"taggedUnion\": {\n const dvZod = `z.enum(${JSON.stringify(Object.keys(model.variants))})`\n envVars.push({ envName: envPrefix, zodExpr: dvZod })\n\n const resolveFnName = `_resolve${pascalCase(propName)}`\n\n const variants: VariantNode[] = []\n for (const [vKey, vModel] of Object.entries(model.variants)) {\n const vRec = vModel as RecordModel<Record<string, Models>, string>\n const child = collectLevel(vRec.properties as Record<string, Models>, vRec.required as string[], envPrefix)\n variants.push({\n varName: vKey,\n resolveFnName: `${resolveFnName}${pascalCase(vKey)}`,\n envVars: child.envVars,\n fields: child.fields,\n switches: child.switches,\n })\n }\n\n switches.push({\n resolveFnName,\n dvEnvName: envPrefix,\n dvZodExpr: dvZod,\n discriminator: model.discriminator as string,\n variants,\n })\n fields.push({ name: propName, kind: \"switch\", switchFnName: resolveFnName, dvEnvName: envPrefix })\n break\n }\n\n case \"union\": {\n const dvEnvName = `${envPrefix}_TYPE`\n const dvZod = `z.enum(${JSON.stringify(Object.keys(model.variants))})`\n envVars.push({ envName: dvEnvName, zodExpr: dvZod })\n\n const resolveFnName = `_resolve${pascalCase(propName)}`\n\n const variants: VariantNode[] = []\n for (const [vKey, vModel] of Object.entries(model.variants)) {\n const child = collectVariant(vModel as Models, envPrefix)\n variants.push({\n varName: vKey,\n resolveFnName: `${resolveFnName}${pascalCase(vKey)}`,\n envVars: child.envVars,\n fields: child.fields,\n switches: child.switches,\n })\n }\n\n switches.push({ resolveFnName, dvEnvName, dvZodExpr: dvZod, discriminator: null, variants })\n fields.push({ name: propName, kind: \"switch\", switchFnName: resolveFnName, dvEnvName })\n break\n }\n\n case \"array\":\n case \"set\": {\n const base = (model as { base: Models }).base\n if (!isSimpleType(base)) {\n throw new Error(\n `unsupported configuration value of kind ${model.kind}<non-simple>, only simple element types are allowed`,\n )\n }\n envVars.push({ envName: envPrefix, zodExpr: toZodEnv(model) })\n fields.push({ name: propName, kind: \"env\", envName: envPrefix })\n break\n }\n\n case \"map\":\n throw new Error(\"unsupported configuration value of kind map\")\n\n default:\n envVars.push({ envName: envPrefix, zodExpr: \"z.string()\" })\n fields.push({ name: propName, kind: \"env\", envName: envPrefix })\n }\n }\n\n return { envVars, fields, switches }\n}\n\nfunction collectVariant(model: Models, prefix: string): CollectResult {\n if (model.kind === \"record\") {\n const rec = model as RecordModel<Record<string, Models>, string>\n return collectLevel(rec.properties as Record<string, Models>, rec.required as string[], prefix)\n }\n const envName = prefix\n return {\n envVars: [{ envName, zodExpr: toZodEnv(model) }],\n fields: [{ name: \"value\", kind: \"env\", envName }],\n switches: [],\n }\n}\n\nfunction isSimpleType(model: Models): boolean {\n return [\n \"int32\",\n \"float32\",\n \"float64\",\n \"boolean\",\n \"string\",\n \"datetime\",\n \"date\",\n \"duration\",\n \"literal\",\n \"enums\",\n ].includes(model.kind)\n}\n\nfunction toZodEnv(model: Models): string {\n switch (model.kind) {\n case \"int32\":\n return \"z.coerce.number().int()\"\n case \"float32\":\n case \"float64\":\n return \"z.coerce.number()\"\n case \"boolean\":\n return \"z.coerce.boolean()\"\n case \"string\":\n return \"z.string()\"\n case \"datetime\":\n return \"z.string().datetime()\"\n case \"date\":\n return \"z.string().date()\"\n case \"duration\":\n return \"z.string()\"\n case \"literal\":\n return `z.literal(${JSON.stringify(model.value)})`\n case \"null\":\n return \"z.null()\"\n case \"enums\":\n return `z.enum(${JSON.stringify(Object.values(model.variants))})`\n case \"array\": {\n if (!isSimpleType(model.base))\n throw new Error(\n \"unsupported configuration value of kind array<non-simple>, only simple element types are allowed\",\n )\n return `z.coerce.string().transform(s => s.split(',').filter(Boolean)).pipe(z.array(${toZodEnv(model.base)}))`\n }\n case \"set\": {\n if (!isSimpleType(model.base))\n throw new Error(\n \"unsupported configuration value of kind set<non-simple>, only simple element types are allowed\",\n )\n return `z.coerce.string().transform(s => new Set(s.split(',').filter(Boolean))).pipe(z.set(${toZodEnv(model.base)}))`\n }\n case \"map\":\n throw new Error(\"unsupported configuration value of kind map\")\n default:\n return \"z.string()\"\n }\n}\n","import type { RouterModel, Models } from \"@huanglangjian/specs\"\nimport { collectOperations, collectSchemaMap, resolveNamedRoot } from \"@huanglangjian/specs\"\nimport type { OperationDescriptor, SchemaMap } from \"@huanglangjian/specs\"\nimport { groupBy } from \"@huanglangjian/specs\"\nimport { camelCase, pascalCase } from \"text-case\"\n\nimport { generateModels, toTs, resolveZodSchema, collectSchemaRefs } from \"./shared\"\n\nexport interface TsClientOptions {\n routers: RouterModel[]\n identifier?: (id: string) => string\n namespace?: string\n}\n\nexport function generateTsClient(options: TsClientOptions): Record<string, string> {\n const { routers, identifier = pascalCase, namespace } = options\n const operations = collectOperations(routers)\n const schemaMap = collectSchemaMap(operations)\n const files: Record<string, string> = {}\n\n files[\"models.ts\"] = generateModels(schemaMap, identifier, namespace)\n\n for (const operation of operations) {\n files[`${camelCase(operation.group)}/${camelCase(operation.id)}.ts`] = generateClientFn(\n operation,\n schemaMap,\n identifier,\n namespace,\n )\n }\n\n files[\"index.ts\"] = generateClientIndex(operations, identifier)\n\n return files\n}\n\n// ---- per-operation file ----\n\nfunction generateClientFn(\n operation: OperationDescriptor,\n schemaMap: SchemaMap,\n identifier: (s: string) => string,\n namespace: string | undefined,\n): string {\n const lines: string[] = []\n const functionName = camelCase(operation.id)\n const OperationName = pascalCase(operation.id)\n const hasBody = operation.requestModel != null && operation.requestModel.kind !== \"null\"\n const hasParams = Object.keys(operation.pathVariables).length > 0\n const hasQuery = Object.keys(operation.queries).length > 0\n const hasHeaders = Object.keys(operation.headers).length > 0\n\n const typeImports: string[] = []\n const addNamedRef = (model: Models) => {\n const root = resolveNamedRoot(model)\n if (root) {\n const typeName = identifier(root.id)\n if (schemaMap.has(root.id) && !typeImports.includes(typeName)) {\n typeImports.push(typeName)\n }\n }\n }\n if (hasBody) addNamedRef(operation.requestModel!)\n for (const responseModel of Object.values(operation.responses)) {\n if (responseModel != null) addNamedRef(responseModel)\n }\n\n const okStatus = Object.keys(operation.responses).find((s) => Number(s) >= 200 && Number(s) < 300)\n const okModel: Models | null = okStatus ? operation.responses[Number(okStatus)] : null\n const okKind = okStatus ? (operation.responseKinds[Number(okStatus)] ?? \"json-response\") : \"json-response\"\n const isStreamLike = okKind === \"stream-response\" || okKind === \"sse-response\" || okKind === \"binary\"\n const okSchema = okModel ? resolveZodSchema(okModel, schemaMap) : null\n\n const allModelImports = [...typeImports]\n if (okSchema && !isStreamLike) {\n const schemaRefs = collectSchemaRefs(okModel!, schemaMap)\n for (const ref of schemaRefs) {\n if (!allModelImports.includes(ref)) allModelImports.push(ref)\n }\n }\n if (allModelImports.length > 0) {\n lines.push(`import { ${allModelImports.join(\", \")} } from \"../models\"`)\n }\n\n lines.push(\"\")\n lines.push(`export namespace ${OperationName}Operation {`)\n lines.push(\"\")\n\n const requestFields: string[] = []\n for (const [n, v] of Object.entries(operation.pathVariables)) {\n requestFields.push(`${n}: ${toTs(v.model, schemaMap, identifier, namespace)}`)\n }\n for (const [n, q] of Object.entries(operation.queries)) {\n const required = q.required ? \"\" : \"?\"\n requestFields.push(`${n}${required}: ${toTs(q.model, schemaMap, identifier, namespace)}`)\n }\n if (hasBody) {\n requestFields.push(`body: ${toTs(operation.requestModel!, schemaMap, identifier, namespace)}`)\n }\n if (hasHeaders) {\n const field = Object.entries(operation.headers)\n .map(\n ([key, header]) =>\n `\"${key}\"${header.required ? \"\" : \"?\"}: ${toTs(header.model, schemaMap, identifier, namespace)}`,\n )\n .join(\"; \")\n requestFields.push(`headers?: { ${field} } & Record<string, string>`)\n } else {\n requestFields.push(\"headers?: Record<string, string>\")\n }\n requestFields.push(\"baseUrl?: string\")\n\n lines.push(` export interface Request {`)\n for (const field of requestFields) lines.push(` ${field}`)\n lines.push(` }`)\n lines.push(\"\")\n\n const responseEntries = Object.entries(operation.responses)\n const responseKind = (status: string) => operation.responseKinds[Number(status)] ?? \"json-response\"\n const responseBodyField = (kind: string, responseModel: Models | null) => {\n if (responseModel == null) {\n if (kind === \"binary\") return \"body: Blob\"\n return null\n }\n if (kind === \"stream-response\" || kind === \"sse-response\") {\n return `stream: ReadableStream<${toTs(responseModel, schemaMap, identifier, namespace)}>`\n }\n if (kind === \"binary\") return \"body: Blob\"\n return `body: ${toTs(responseModel, schemaMap, identifier, namespace)}`\n }\n if (responseEntries.length === 1) {\n const [status, responseModel] = responseEntries[0]\n const kind = responseKind(status)\n const bodyField = responseBodyField(kind, responseModel)\n if (bodyField != null) {\n lines.push(` export type Response = { status: ${status}; ${bodyField} }`)\n } else {\n lines.push(` export type Response = { status: ${status} }`)\n }\n } else {\n lines.push(` export type Response =`)\n for (const [status, responseModel] of responseEntries) {\n const kind = responseKind(status)\n const bodyField = responseBodyField(kind, responseModel)\n if (bodyField != null) lines.push(` | { status: ${status}; ${bodyField} }`)\n else lines.push(` | { status: ${status} }`)\n }\n }\n lines.push(\"\")\n lines.push(`}`)\n lines.push(\"\")\n\n const hasRequired = hasParams || hasBody\n const reqParam = hasRequired ? `req: ${OperationName}Operation.Request` : `req?: ${OperationName}Operation.Request`\n\n const retType = okModel && !isStreamLike ? toTs(okModel, schemaMap, identifier, namespace) : \"Response\"\n\n const pathExpr = operation.path.replace(/\\{(\\w+)\\}/g, (_, name) => `\\${encodeURIComponent(req.${name})}`)\n\n lines.push(`export async function ${functionName}(${reqParam}): Promise<${retType}> {`)\n lines.push(` const baseUrl = req?.baseUrl ?? \"\"`)\n\n if (hasQuery) {\n lines.push(` const parts: string[] = []`)\n for (const [n, q] of Object.entries(operation.queries)) {\n if (q.required) {\n lines.push(` parts.push(\"${n}=\" + encodeURIComponent(req.${n}))`)\n } else {\n lines.push(` if (req?.${n} != null) parts.push(\"${n}=\" + encodeURIComponent(req.${n}))`)\n }\n }\n lines.push(` const qs = parts.length > 0 ? \"?\" + parts.join(\"&\") : \"\"`)\n lines.push(` const url = \\`\\${baseUrl}${pathExpr}\\${qs}\\``)\n } else {\n lines.push(` const url = \\`\\${baseUrl}${pathExpr}\\``)\n }\n\n lines.push(\"\")\n lines.push(` const res = await fetch(url, {`)\n lines.push(` method: \"${operation.method}\",`)\n if (hasBody) {\n lines.push(` headers: { \"Content-Type\": \"application/json\", ...req.headers },`)\n lines.push(` body: JSON.stringify(req.body),`)\n } else {\n lines.push(` headers: req?.headers,`)\n }\n lines.push(` })`)\n lines.push(` if (!res.ok) throw new Error(\\`${operation.method} ${operation.path} failed: \\${res.status}\\`)`)\n\n if (okModel && !isStreamLike) {\n if (okSchema) {\n lines.push(` return ${okSchema}.parse(await res.json())`)\n } else {\n lines.push(` return res.json()`)\n }\n } else {\n lines.push(` return res`)\n }\n lines.push(`}`)\n\n return lines.join(\"\\n\")\n}\n\n// ---- index.ts ----\n\nfunction generateClientIndex(operations: OperationDescriptor[], _identifier: (s: string) => string): string {\n const lines: string[] = []\n\n const groups = groupBy(operations, (operation) => operation.group)\n for (const [group, groupOps] of Object.entries(groups)) {\n lines.push(`// ── ${group} ──`)\n for (const operation of groupOps) {\n const n = camelCase(operation.id)\n const OperationName = pascalCase(operation.id)\n lines.push(`export { ${n}, ${OperationName}Operation } from \"./${camelCase(operation.group)}/${n}\"`)\n }\n lines.push(\"\")\n }\n\n return lines.join(\"\\n\")\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@huanglangjian/specs-ts-codegen",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"text-case": "^1.2.11",
|
|
20
|
+
"zod": "^4.4.3",
|
|
21
|
+
"@huanglangjian/specs": "0.9.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^25.9.3",
|
|
25
|
+
"tsdown": "^0.22.2",
|
|
26
|
+
"tsx": "^4.19.2",
|
|
27
|
+
"typescript": "^6.0.3",
|
|
28
|
+
"vitest": "^4.1.8"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsdown",
|
|
32
|
+
"dev": "tsdown --watch",
|
|
33
|
+
"test": "vitest",
|
|
34
|
+
"test:openapi": "tsx src/test.ts"
|
|
35
|
+
}
|
|
36
|
+
}
|