@contractkit/plugin-typescript 0.28.2 → 0.30.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/.turbo/turbo-build$colon$ci.log +5 -5
- package/.turbo/turbo-test$colon$ci.log +19 -19
- package/CHANGELOG.md +23 -0
- package/README.md +4 -0
- package/dist/codegen-contract.d.ts +7 -0
- package/dist/codegen-contract.d.ts.map +1 -1
- package/dist/codegen-operation.d.ts +9 -1
- package/dist/codegen-operation.d.ts.map +1 -1
- package/dist/codegen-plain-types.d.ts +3 -0
- package/dist/codegen-plain-types.d.ts.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +144 -120
- package/dist/index.js.map +1 -1
- package/dist/ts-render.d.ts +21 -3
- package/dist/ts-render.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/codegen-contract.ts +7 -0
- package/src/codegen-operation.ts +103 -78
- package/src/codegen-plain-types.ts +47 -22
- package/src/index.ts +21 -1
- package/src/ts-render.ts +52 -32
- package/tests/codegen-operation.test.ts +134 -3
- package/tests/codegen-plain-types.test.ts +48 -0
- package/tests/codegen-server.test.ts +37 -0
package/dist/index.js
CHANGED
|
@@ -31,41 +31,41 @@ function headerNameToProperty(name) {
|
|
|
31
31
|
}).join("");
|
|
32
32
|
}
|
|
33
33
|
__name(headerNameToProperty, "headerNameToProperty");
|
|
34
|
-
function renderTsType(type) {
|
|
34
|
+
function renderTsType(type, target = "client") {
|
|
35
35
|
switch (type.kind) {
|
|
36
36
|
case "scalar":
|
|
37
|
-
return renderTsScalar(type.name);
|
|
37
|
+
return renderTsScalar(type.name, target);
|
|
38
38
|
case "array": {
|
|
39
|
-
const inner = renderTsType(type.item);
|
|
39
|
+
const inner = renderTsType(type.item, target);
|
|
40
40
|
const needsParens = type.item.kind === "union" || type.item.kind === "discriminatedUnion" || type.item.kind === "intersection" || type.item.kind === "enum";
|
|
41
41
|
return needsParens ? `(${inner})[]` : `${inner}[]`;
|
|
42
42
|
}
|
|
43
43
|
case "tuple":
|
|
44
|
-
return `[${type.items.map(renderTsType).join(", ")}]`;
|
|
44
|
+
return `[${type.items.map((i) => renderTsType(i, target)).join(", ")}]`;
|
|
45
45
|
case "record":
|
|
46
|
-
return `Record<${renderTsType(type.key)}, ${renderTsType(type.value)}>`;
|
|
46
|
+
return `Record<${renderTsType(type.key, target)}, ${renderTsType(type.value, target)}>`;
|
|
47
47
|
case "enum":
|
|
48
48
|
return type.values.map((v) => `'${escapeSingleQuoted(v)}'`).join(" | ");
|
|
49
49
|
case "literal":
|
|
50
50
|
return typeof type.value === "string" ? `'${escapeSingleQuoted(type.value)}'` : String(type.value);
|
|
51
51
|
case "union":
|
|
52
|
-
return type.members.map(renderTsType).join(" | ");
|
|
52
|
+
return type.members.map((m) => renderTsType(m, target)).join(" | ");
|
|
53
53
|
case "discriminatedUnion":
|
|
54
|
-
return type.members.map(renderTsType).join(" | ");
|
|
54
|
+
return type.members.map((m) => renderTsType(m, target)).join(" | ");
|
|
55
55
|
case "intersection":
|
|
56
|
-
return type.members.map(renderTsType).join(" & ");
|
|
56
|
+
return type.members.map((m) => renderTsType(m, target)).join(" & ");
|
|
57
57
|
case "ref":
|
|
58
58
|
return type.name;
|
|
59
59
|
case "lazy":
|
|
60
|
-
return renderTsType(type.inner);
|
|
60
|
+
return renderTsType(type.inner, target);
|
|
61
61
|
case "inlineObject":
|
|
62
|
-
return renderTsInlineObject(type.fields);
|
|
62
|
+
return renderTsInlineObject(type.fields, target);
|
|
63
63
|
default:
|
|
64
64
|
return "unknown";
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
__name(renderTsType, "renderTsType");
|
|
68
|
-
function renderTsScalar(name) {
|
|
68
|
+
function renderTsScalar(name, target) {
|
|
69
69
|
switch (name) {
|
|
70
70
|
case "string":
|
|
71
71
|
case "email":
|
|
@@ -92,7 +92,7 @@ function renderTsScalar(name) {
|
|
|
92
92
|
case "object":
|
|
93
93
|
return "Record<string, unknown>";
|
|
94
94
|
case "binary":
|
|
95
|
-
return "Blob";
|
|
95
|
+
return target === "server" ? "Buffer" : "Blob";
|
|
96
96
|
case "json":
|
|
97
97
|
return "JsonValue";
|
|
98
98
|
default: {
|
|
@@ -102,61 +102,61 @@ function renderTsScalar(name) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
__name(renderTsScalar, "renderTsScalar");
|
|
105
|
-
function renderTsInlineObject(fields) {
|
|
105
|
+
function renderTsInlineObject(fields, target) {
|
|
106
106
|
const entries = fields.map((f) => {
|
|
107
107
|
const opt = f.optional ? "?" : "";
|
|
108
|
-
return `${quoteKey(f.name)}${opt}: ${renderTsType(f.type)}`;
|
|
108
|
+
return `${quoteKey(f.name)}${opt}: ${renderTsType(f.type, target)}`;
|
|
109
109
|
});
|
|
110
110
|
return `{ ${entries.join("; ")} }`;
|
|
111
111
|
}
|
|
112
112
|
__name(renderTsInlineObject, "renderTsInlineObject");
|
|
113
|
-
function renderInputTsType(type, modelsWithInput) {
|
|
114
|
-
if (!modelsWithInput || modelsWithInput.size === 0) return renderTsType(type);
|
|
113
|
+
function renderInputTsType(type, modelsWithInput, target = "client") {
|
|
114
|
+
if (!modelsWithInput || modelsWithInput.size === 0) return renderTsType(type, target);
|
|
115
115
|
switch (type.kind) {
|
|
116
116
|
case "ref":
|
|
117
117
|
return modelsWithInput.has(type.name) ? `${type.name}Input` : type.name;
|
|
118
118
|
case "array": {
|
|
119
|
-
const inner = renderInputTsType(type.item, modelsWithInput);
|
|
119
|
+
const inner = renderInputTsType(type.item, modelsWithInput, target);
|
|
120
120
|
const needsParens = type.item.kind === "union" || type.item.kind === "discriminatedUnion" || type.item.kind === "intersection" || type.item.kind === "enum";
|
|
121
121
|
return needsParens ? `(${inner})[]` : `${inner}[]`;
|
|
122
122
|
}
|
|
123
123
|
case "intersection":
|
|
124
|
-
return type.members.map((m) => renderInputTsType(m, modelsWithInput)).join(" & ");
|
|
124
|
+
return type.members.map((m) => renderInputTsType(m, modelsWithInput, target)).join(" & ");
|
|
125
125
|
case "union":
|
|
126
|
-
return type.members.map((m) => renderInputTsType(m, modelsWithInput)).join(" | ");
|
|
126
|
+
return type.members.map((m) => renderInputTsType(m, modelsWithInput, target)).join(" | ");
|
|
127
127
|
case "discriminatedUnion":
|
|
128
|
-
return type.members.map((m) => renderInputTsType(m, modelsWithInput)).join(" | ");
|
|
128
|
+
return type.members.map((m) => renderInputTsType(m, modelsWithInput, target)).join(" | ");
|
|
129
129
|
case "inlineObject":
|
|
130
|
-
return `{ ${type.fields.map((f) => `${quoteKey(f.name)}${f.optional ? "?" : ""}: ${renderInputTsType(f.type, modelsWithInput)}`).join("; ")} }`;
|
|
130
|
+
return `{ ${type.fields.map((f) => `${quoteKey(f.name)}${f.optional ? "?" : ""}: ${renderInputTsType(f.type, modelsWithInput, target)}`).join("; ")} }`;
|
|
131
131
|
case "lazy":
|
|
132
|
-
return renderInputTsType(type.inner, modelsWithInput);
|
|
132
|
+
return renderInputTsType(type.inner, modelsWithInput, target);
|
|
133
133
|
default:
|
|
134
|
-
return renderTsType(type);
|
|
134
|
+
return renderTsType(type, target);
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
__name(renderInputTsType, "renderInputTsType");
|
|
138
|
-
function renderOutputTsType(type, modelsWithOutput) {
|
|
139
|
-
if (!modelsWithOutput || modelsWithOutput.size === 0) return renderTsType(type);
|
|
138
|
+
function renderOutputTsType(type, modelsWithOutput, target = "client") {
|
|
139
|
+
if (!modelsWithOutput || modelsWithOutput.size === 0) return renderTsType(type, target);
|
|
140
140
|
switch (type.kind) {
|
|
141
141
|
case "ref":
|
|
142
142
|
return modelsWithOutput.has(type.name) ? `${type.name}Output` : type.name;
|
|
143
143
|
case "array": {
|
|
144
|
-
const inner = renderOutputTsType(type.item, modelsWithOutput);
|
|
144
|
+
const inner = renderOutputTsType(type.item, modelsWithOutput, target);
|
|
145
145
|
const needsParens = type.item.kind === "union" || type.item.kind === "discriminatedUnion" || type.item.kind === "intersection" || type.item.kind === "enum";
|
|
146
146
|
return needsParens ? `(${inner})[]` : `${inner}[]`;
|
|
147
147
|
}
|
|
148
148
|
case "intersection":
|
|
149
|
-
return type.members.map((m) => renderOutputTsType(m, modelsWithOutput)).join(" & ");
|
|
149
|
+
return type.members.map((m) => renderOutputTsType(m, modelsWithOutput, target)).join(" & ");
|
|
150
150
|
case "union":
|
|
151
|
-
return type.members.map((m) => renderOutputTsType(m, modelsWithOutput)).join(" | ");
|
|
151
|
+
return type.members.map((m) => renderOutputTsType(m, modelsWithOutput, target)).join(" | ");
|
|
152
152
|
case "discriminatedUnion":
|
|
153
|
-
return type.members.map((m) => renderOutputTsType(m, modelsWithOutput)).join(" | ");
|
|
153
|
+
return type.members.map((m) => renderOutputTsType(m, modelsWithOutput, target)).join(" | ");
|
|
154
154
|
case "inlineObject":
|
|
155
|
-
return `{ ${type.fields.map((f) => `${quoteKey(f.name)}${f.optional ? "?" : ""}: ${renderOutputTsType(f.type, modelsWithOutput)}`).join("; ")} }`;
|
|
155
|
+
return `{ ${type.fields.map((f) => `${quoteKey(f.name)}${f.optional ? "?" : ""}: ${renderOutputTsType(f.type, modelsWithOutput, target)}`).join("; ")} }`;
|
|
156
156
|
case "lazy":
|
|
157
|
-
return renderOutputTsType(type.inner, modelsWithOutput);
|
|
157
|
+
return renderOutputTsType(type.inner, modelsWithOutput, target);
|
|
158
158
|
default:
|
|
159
|
-
return renderTsType(type);
|
|
159
|
+
return renderTsType(type, target);
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
__name(renderOutputTsType, "renderOutputTsType");
|
|
@@ -1182,33 +1182,6 @@ function generateOp(root, options = {}) {
|
|
|
1182
1182
|
const types = collectTypes(root, options.modelsWithInput, options.modelsWithOutput);
|
|
1183
1183
|
const services = collectServices(root);
|
|
1184
1184
|
const routerName = deriveRouterName(root.file);
|
|
1185
|
-
const needsParseAndValidate = routeNeedsValidation(root);
|
|
1186
|
-
const body = [];
|
|
1187
|
-
const needsSignature = fileNeedsSignature(root);
|
|
1188
|
-
const needsPolicy = fileNeedsPolicy(root);
|
|
1189
|
-
const koaImports = [
|
|
1190
|
-
"ServerKitRouter",
|
|
1191
|
-
"bodyParserMiddleware"
|
|
1192
|
-
];
|
|
1193
|
-
if (needsPolicy) koaImports.push("requirePolicy");
|
|
1194
|
-
if (needsSignature) koaImports.push("requireSignature");
|
|
1195
|
-
body.push(`import { ${koaImports.join(", ")} } from '@maroonedsoftware/koa';`);
|
|
1196
|
-
for (const svc of services) {
|
|
1197
|
-
const modulePath = root.services?.[svc] ?? root.meta[svc] ?? deriveModulePath(svc, options.servicePathTemplate);
|
|
1198
|
-
body.push(`import { ${svc} } from '${modulePath}';`);
|
|
1199
|
-
}
|
|
1200
|
-
if (types.length > 0) {
|
|
1201
|
-
body.push(...generateTypeImports(types, root.file, options));
|
|
1202
|
-
}
|
|
1203
|
-
if (opNeedsDateTime(root)) {
|
|
1204
|
-
body.push(`import { DateTime } from 'luxon';`);
|
|
1205
|
-
}
|
|
1206
|
-
if (needsParseAndValidate) {
|
|
1207
|
-
body.push(`import { parseAndValidate } from '@maroonedsoftware/zod';`);
|
|
1208
|
-
}
|
|
1209
|
-
if (fileUsesMultipart(root)) {
|
|
1210
|
-
body.push(`import { MultipartBody } from '@maroonedsoftware/multipart';`);
|
|
1211
|
-
}
|
|
1212
1185
|
const helpers = [];
|
|
1213
1186
|
if (opNeedsScalar(root, "binary")) {
|
|
1214
1187
|
helpers.push(`const _ZodBinary = z.custom<Buffer>((val) => Buffer.isBuffer(val), { error: 'Must be binary data' });`);
|
|
@@ -1216,6 +1189,9 @@ function generateOp(root, options = {}) {
|
|
|
1216
1189
|
if (opNeedsScalar(root, "datetime")) {
|
|
1217
1190
|
helpers.push(`const _ZodDatetime = z.preprocess((val) => typeof val === 'string' ? DateTime.fromISO(val) : val, z.custom<DateTime>((val) => val instanceof DateTime && val.isValid, { message: 'Must be in ISO 8601 format' }));`);
|
|
1218
1191
|
}
|
|
1192
|
+
if (opNeedsScalar(root, "interval")) {
|
|
1193
|
+
helpers.push(`const _ZodInterval = z.preprocess((val) => typeof val === 'string' ? Interval.fromISO(val) : val, z.custom<Interval>((val) => val instanceof Interval && val.isValid, { message: 'Must be an ISO 8601 interval' })).transform(val => val.toISO()!);`);
|
|
1194
|
+
}
|
|
1219
1195
|
if (opNeedsScalar(root, "json")) {
|
|
1220
1196
|
helpers.push(`type _JsonValue = string | number | boolean | null | _JsonValue[] | { [key: string]: _JsonValue };`);
|
|
1221
1197
|
helpers.push(`const _ZodJson: z.ZodType<_JsonValue> = z.lazy(() => z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(_ZodJson), z.record(z.string(), _ZodJson)]));`);
|
|
@@ -1236,6 +1212,45 @@ function generateOp(root, options = {}) {
|
|
|
1236
1212
|
lines.push("");
|
|
1237
1213
|
}
|
|
1238
1214
|
}
|
|
1215
|
+
const generated = [
|
|
1216
|
+
...helpers.length ? [
|
|
1217
|
+
"",
|
|
1218
|
+
...helpers
|
|
1219
|
+
] : [],
|
|
1220
|
+
...lines
|
|
1221
|
+
].join("\n");
|
|
1222
|
+
const uses = /* @__PURE__ */ __name((symbol) => new RegExp(`\\b${symbol}\\b`).test(generated), "uses");
|
|
1223
|
+
const body = [];
|
|
1224
|
+
const koaImports = [
|
|
1225
|
+
"ServerKitRouter",
|
|
1226
|
+
"bodyParserMiddleware",
|
|
1227
|
+
"requirePolicy",
|
|
1228
|
+
"requireSignature"
|
|
1229
|
+
].filter(uses);
|
|
1230
|
+
if (koaImports.length > 0) {
|
|
1231
|
+
body.push(`import { ${koaImports.join(", ")} } from '@maroonedsoftware/koa';`);
|
|
1232
|
+
}
|
|
1233
|
+
for (const svc of services) {
|
|
1234
|
+
const modulePath = root.services?.[svc] ?? root.meta[svc] ?? deriveModulePath(svc, options.servicePathTemplate);
|
|
1235
|
+
body.push(`import { ${svc} } from '${modulePath}';`);
|
|
1236
|
+
}
|
|
1237
|
+
if (types.length > 0) {
|
|
1238
|
+
body.push(...generateTypeImports(types, root.file, options));
|
|
1239
|
+
}
|
|
1240
|
+
const luxonImports = [
|
|
1241
|
+
"DateTime",
|
|
1242
|
+
"Duration",
|
|
1243
|
+
"Interval"
|
|
1244
|
+
].filter(uses);
|
|
1245
|
+
if (luxonImports.length > 0) {
|
|
1246
|
+
body.push(`import { ${luxonImports.join(", ")} } from 'luxon';`);
|
|
1247
|
+
}
|
|
1248
|
+
if (uses("parseAndValidate")) {
|
|
1249
|
+
body.push(`import { parseAndValidate } from '@maroonedsoftware/zod';`);
|
|
1250
|
+
}
|
|
1251
|
+
if (uses("MultipartBody")) {
|
|
1252
|
+
body.push(`import { MultipartBody } from '@maroonedsoftware/multipart';`);
|
|
1253
|
+
}
|
|
1239
1254
|
const allContent = [
|
|
1240
1255
|
...body,
|
|
1241
1256
|
...helpers.length ? [
|
|
@@ -1325,7 +1340,7 @@ function generateHandler(route, op, root, options) {
|
|
|
1325
1340
|
const serviceParts = inferService(op, route, file);
|
|
1326
1341
|
const respHeaders = primaryResponse2?.headers ?? [];
|
|
1327
1342
|
const hasRespHeaders = respHeaders.length > 0;
|
|
1328
|
-
const headersAnnotation = hasRespHeaders ? `{ ${respHeaders.map((h) => `${quoteKey(headerNameToProperty(h.name))}${h.optional ? "?" : ""}: ${renderOutputTsType(h.type, options.modelsWithOutput)}`).join("; ")} }` : "";
|
|
1343
|
+
const headersAnnotation = hasRespHeaders ? `{ ${respHeaders.map((h) => `${quoteKey(headerNameToProperty(h.name))}${h.optional ? "?" : ""}: ${renderOutputTsType(h.type, options.modelsWithOutput, "server")}`).join("; ")} }` : "";
|
|
1329
1344
|
if (primaryResponse2?.bodyType) {
|
|
1330
1345
|
const { annotation, prelude } = formatTypeAnnotation(primaryResponse2.bodyType, options.modelsWithOutput);
|
|
1331
1346
|
if (prelude) {
|
|
@@ -1419,6 +1434,45 @@ function buildArgs(route, op) {
|
|
|
1419
1434
|
return args.join(", ");
|
|
1420
1435
|
}
|
|
1421
1436
|
__name(buildArgs, "buildArgs");
|
|
1437
|
+
function serverTsScalar(name) {
|
|
1438
|
+
switch (name) {
|
|
1439
|
+
case "string":
|
|
1440
|
+
case "email":
|
|
1441
|
+
case "url":
|
|
1442
|
+
case "uuid":
|
|
1443
|
+
return "string";
|
|
1444
|
+
case "number":
|
|
1445
|
+
case "int":
|
|
1446
|
+
return "number";
|
|
1447
|
+
case "bigint":
|
|
1448
|
+
return "bigint";
|
|
1449
|
+
case "boolean":
|
|
1450
|
+
return "boolean";
|
|
1451
|
+
case "date":
|
|
1452
|
+
case "time":
|
|
1453
|
+
case "datetime":
|
|
1454
|
+
return "DateTime";
|
|
1455
|
+
case "duration":
|
|
1456
|
+
return "Duration";
|
|
1457
|
+
case "interval":
|
|
1458
|
+
return "string";
|
|
1459
|
+
case "binary":
|
|
1460
|
+
return "Buffer";
|
|
1461
|
+
case "json":
|
|
1462
|
+
return "_JsonValue";
|
|
1463
|
+
case "object":
|
|
1464
|
+
return "Record<string, unknown>";
|
|
1465
|
+
case "null":
|
|
1466
|
+
return "null";
|
|
1467
|
+
case "unknown":
|
|
1468
|
+
return "unknown";
|
|
1469
|
+
default: {
|
|
1470
|
+
const _exhaustive = name;
|
|
1471
|
+
throw new Error(`plugin-typescript: unmapped scalar '${String(_exhaustive)}' \u2014 add a case`);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
__name(serverTsScalar, "serverTsScalar");
|
|
1422
1476
|
function formatTypeAnnotation(bodyType, modelsWithOutput) {
|
|
1423
1477
|
if (bodyType.kind === "array") {
|
|
1424
1478
|
const inner = formatTypeAnnotation(bodyType.item, modelsWithOutput);
|
|
@@ -1434,7 +1488,7 @@ function formatTypeAnnotation(bodyType, modelsWithOutput) {
|
|
|
1434
1488
|
};
|
|
1435
1489
|
}
|
|
1436
1490
|
if (bodyType.kind === "scalar") return {
|
|
1437
|
-
annotation: bodyType.name
|
|
1491
|
+
annotation: serverTsScalar(bodyType.name)
|
|
1438
1492
|
};
|
|
1439
1493
|
const schema = renderType(bodyType);
|
|
1440
1494
|
return {
|
|
@@ -1670,17 +1724,6 @@ function collectTypeNodeRefs(type, out) {
|
|
|
1670
1724
|
}
|
|
1671
1725
|
}
|
|
1672
1726
|
__name(collectTypeNodeRefs, "collectTypeNodeRefs");
|
|
1673
|
-
function paramSourceNeedsDateTime(source) {
|
|
1674
|
-
if (!source) return false;
|
|
1675
|
-
if (source.kind === "ref") return false;
|
|
1676
|
-
if (source.kind === "params") return source.nodes.some((p) => typeNeedsDateTime(p.type));
|
|
1677
|
-
return typeNeedsDateTime(source.node);
|
|
1678
|
-
}
|
|
1679
|
-
__name(paramSourceNeedsDateTime, "paramSourceNeedsDateTime");
|
|
1680
|
-
function opNeedsDateTime(root) {
|
|
1681
|
-
return root.routes.some((route) => paramSourceNeedsDateTime(route.params) || route.operations.some((op) => !!op.request?.bodies.some((b) => typeNeedsDateTime(b.bodyType)) || op.responses.some((r) => r.bodyType && typeNeedsDateTime(r.bodyType)) || paramSourceNeedsDateTime(op.query) || paramSourceNeedsDateTime(op.headers)));
|
|
1682
|
-
}
|
|
1683
|
-
__name(opNeedsDateTime, "opNeedsDateTime");
|
|
1684
1727
|
function paramSourceNeedsScalar(source, name) {
|
|
1685
1728
|
if (!source) return false;
|
|
1686
1729
|
if (source.kind === "ref") return false;
|
|
@@ -1709,29 +1752,6 @@ function collectServices(root) {
|
|
|
1709
1752
|
].sort();
|
|
1710
1753
|
}
|
|
1711
1754
|
__name(collectServices, "collectServices");
|
|
1712
|
-
function hasParamSource(source) {
|
|
1713
|
-
if (!source) return false;
|
|
1714
|
-
if (source.kind === "ref") return true;
|
|
1715
|
-
if (source.kind === "params") return source.nodes.length > 0;
|
|
1716
|
-
return true;
|
|
1717
|
-
}
|
|
1718
|
-
__name(hasParamSource, "hasParamSource");
|
|
1719
|
-
function routeNeedsValidation(root) {
|
|
1720
|
-
return root.routes.some((r) => hasParamSource(r.params) || r.operations.some((op) => !!op.request || hasParamSource(op.query) || hasParamSource(op.headers)));
|
|
1721
|
-
}
|
|
1722
|
-
__name(routeNeedsValidation, "routeNeedsValidation");
|
|
1723
|
-
function fileNeedsPolicy(root) {
|
|
1724
|
-
return root.routes.some((route) => route.operations.some((op) => resolveSecurity(route, op, root) !== SECURITY_NONE));
|
|
1725
|
-
}
|
|
1726
|
-
__name(fileNeedsPolicy, "fileNeedsPolicy");
|
|
1727
|
-
function fileNeedsSignature(root) {
|
|
1728
|
-
return root.routes.some((route) => route.operations.some((op) => !!op.signature));
|
|
1729
|
-
}
|
|
1730
|
-
__name(fileNeedsSignature, "fileNeedsSignature");
|
|
1731
|
-
function fileUsesMultipart(root) {
|
|
1732
|
-
return root.routes.some((route) => route.operations.some((op) => (op.request?.bodies ?? []).some((b) => b.contentType === "multipart/form-data")));
|
|
1733
|
-
}
|
|
1734
|
-
__name(fileUsesMultipart, "fileUsesMultipart");
|
|
1735
1755
|
function isValidIdentifier2(name) {
|
|
1736
1756
|
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
1737
1757
|
}
|
|
@@ -2807,6 +2827,7 @@ __name(generateSdkAggregator, "generateSdkAggregator");
|
|
|
2807
2827
|
import { relative as relative4, dirname as dirname4 } from "path";
|
|
2808
2828
|
import { computeModelsWithOutput, collectExternalOutputRefs } from "@contractkit/core";
|
|
2809
2829
|
function generatePlainTypes(root, context) {
|
|
2830
|
+
const target = context?.target ?? "client";
|
|
2810
2831
|
const externalRefs = collectExternalRefs(root);
|
|
2811
2832
|
const lines = [];
|
|
2812
2833
|
const externalModelsWithInput = context?.modelsWithInput ?? /* @__PURE__ */ new Set();
|
|
@@ -2848,21 +2869,21 @@ function generatePlainTypes(root, context) {
|
|
|
2848
2869
|
m
|
|
2849
2870
|
]));
|
|
2850
2871
|
for (const model of topoSortModels(root.models)) {
|
|
2851
|
-
lines.push(...generateModel2(model, context?.currentOutPath, allModelsWithInput, allModelsWithOutput, modelMap));
|
|
2872
|
+
lines.push(...generateModel2(model, target, context?.currentOutPath, allModelsWithInput, allModelsWithOutput, modelMap));
|
|
2852
2873
|
lines.push("");
|
|
2853
2874
|
}
|
|
2854
2875
|
return lines.join("\n");
|
|
2855
2876
|
}
|
|
2856
2877
|
__name(generatePlainTypes, "generatePlainTypes");
|
|
2857
|
-
function generateModel2(model, outPath, modelsWithInput, modelsWithOutput, modelMap) {
|
|
2878
|
+
function generateModel2(model, target, outPath, modelsWithInput, modelsWithOutput, modelMap) {
|
|
2858
2879
|
if (model.type) {
|
|
2859
|
-
return generateTypeAlias2(model, outPath, modelsWithInput, modelsWithOutput);
|
|
2880
|
+
return generateTypeAlias2(model, target, outPath, modelsWithInput, modelsWithOutput);
|
|
2860
2881
|
}
|
|
2861
2882
|
const needsInputSplit = model.fields.some((f) => f.visibility !== "normal") || (modelsWithInput?.has(model.name) ?? false);
|
|
2862
|
-
const lines = needsInputSplit ? generateVisibilityModel(model, outPath, modelsWithInput, modelMap) : generateSimpleModel2(model, outPath, modelMap);
|
|
2883
|
+
const lines = needsInputSplit ? generateVisibilityModel(model, target, outPath, modelsWithInput, modelMap) : generateSimpleModel2(model, target, outPath, modelMap);
|
|
2863
2884
|
if (modelsWithOutput?.has(model.name)) {
|
|
2864
2885
|
lines.push("");
|
|
2865
|
-
lines.push(...generateOutputModel(model, modelsWithOutput));
|
|
2886
|
+
lines.push(...generateOutputModel(model, target, modelsWithOutput));
|
|
2866
2887
|
}
|
|
2867
2888
|
return lines;
|
|
2868
2889
|
}
|
|
@@ -2899,15 +2920,15 @@ function generateComments2(model, outPath) {
|
|
|
2899
2920
|
return lines;
|
|
2900
2921
|
}
|
|
2901
2922
|
__name(generateComments2, "generateComments");
|
|
2902
|
-
function generateTypeAlias2(model, outPath, modelsWithInput, modelsWithOutput) {
|
|
2923
|
+
function generateTypeAlias2(model, target, outPath, modelsWithInput, modelsWithOutput) {
|
|
2903
2924
|
const lines = [];
|
|
2904
2925
|
lines.push(...generateComments2(model, outPath));
|
|
2905
|
-
lines.push(`export type ${model.name} = ${renderTsType(model.type)};`);
|
|
2926
|
+
lines.push(`export type ${model.name} = ${renderTsType(model.type, target)};`);
|
|
2906
2927
|
if (modelsWithInput?.has(model.name)) {
|
|
2907
|
-
lines.push(`export type ${model.name}Input = ${renderInputTsType(model.type, modelsWithInput)};`);
|
|
2928
|
+
lines.push(`export type ${model.name}Input = ${renderInputTsType(model.type, modelsWithInput, target)};`);
|
|
2908
2929
|
}
|
|
2909
2930
|
if (modelsWithOutput?.has(model.name)) {
|
|
2910
|
-
lines.push(`export type ${model.name}Output = ${renderOutputTsType(model.type, modelsWithOutput)};`);
|
|
2931
|
+
lines.push(`export type ${model.name}Output = ${renderOutputTsType(model.type, modelsWithOutput, target)};`);
|
|
2911
2932
|
}
|
|
2912
2933
|
return lines;
|
|
2913
2934
|
}
|
|
@@ -2920,20 +2941,20 @@ function buildExtendsClause(bases, overrideNames, baseNameResolver) {
|
|
|
2920
2941
|
return ` extends ${wrapped.join(", ")}`;
|
|
2921
2942
|
}
|
|
2922
2943
|
__name(buildExtendsClause, "buildExtendsClause");
|
|
2923
|
-
function generateSimpleModel2(model, outPath, modelMap) {
|
|
2944
|
+
function generateSimpleModel2(model, target, outPath, modelMap) {
|
|
2924
2945
|
const lines = [];
|
|
2925
2946
|
lines.push(...generateComments2(model, outPath));
|
|
2926
2947
|
const bases = model.bases ?? [];
|
|
2927
2948
|
const overrideNames = computeOverrideNames(model, modelMap);
|
|
2928
2949
|
lines.push(`export interface ${model.name}${buildExtendsClause(bases, overrideNames, (b) => b)} {`);
|
|
2929
2950
|
for (const field of model.fields) {
|
|
2930
|
-
lines.push(` ${renderField2(field)}`);
|
|
2951
|
+
lines.push(` ${renderField2(field, target)}`);
|
|
2931
2952
|
}
|
|
2932
2953
|
lines.push("}");
|
|
2933
2954
|
return lines;
|
|
2934
2955
|
}
|
|
2935
2956
|
__name(generateSimpleModel2, "generateSimpleModel");
|
|
2936
|
-
function generateVisibilityModel(model, outPath, modelsWithInput, modelMap) {
|
|
2957
|
+
function generateVisibilityModel(model, target, outPath, modelsWithInput, modelMap) {
|
|
2937
2958
|
const lines = [];
|
|
2938
2959
|
lines.push(...generateComments2(model, outPath));
|
|
2939
2960
|
const bases = model.bases ?? [];
|
|
@@ -2941,7 +2962,7 @@ function generateVisibilityModel(model, outPath, modelsWithInput, modelMap) {
|
|
|
2941
2962
|
const readFields = model.fields.filter((f) => f.visibility !== "writeonly");
|
|
2942
2963
|
lines.push(`export interface ${model.name}${buildExtendsClause(bases, overrideNames, (b) => b)} {`);
|
|
2943
2964
|
for (const field of readFields) {
|
|
2944
|
-
lines.push(` ${renderField2(field)}`);
|
|
2965
|
+
lines.push(` ${renderField2(field, target)}`);
|
|
2945
2966
|
}
|
|
2946
2967
|
lines.push("}");
|
|
2947
2968
|
lines.push("");
|
|
@@ -2949,7 +2970,7 @@ function generateVisibilityModel(model, outPath, modelsWithInput, modelMap) {
|
|
|
2949
2970
|
const inputResolver = /* @__PURE__ */ __name((b) => modelsWithInput?.has(b) ? `${b}Input` : b, "inputResolver");
|
|
2950
2971
|
lines.push(`export interface ${model.name}Input${buildExtendsClause(bases, overrideNames, inputResolver)} {`);
|
|
2951
2972
|
for (const field of writeFields) {
|
|
2952
|
-
lines.push(` ${modelsWithInput ? renderInputField2(field, modelsWithInput) : renderField2(field)}`);
|
|
2973
|
+
lines.push(` ${modelsWithInput ? renderInputField2(field, modelsWithInput, target) : renderField2(field, target)}`);
|
|
2953
2974
|
}
|
|
2954
2975
|
lines.push("}");
|
|
2955
2976
|
return lines;
|
|
@@ -2969,9 +2990,9 @@ ${body}
|
|
|
2969
2990
|
${line}`;
|
|
2970
2991
|
}
|
|
2971
2992
|
__name(withFieldJsDoc, "withFieldJsDoc");
|
|
2972
|
-
function renderField2(field) {
|
|
2993
|
+
function renderField2(field, target) {
|
|
2973
2994
|
const opt = field.optional || field.default !== void 0 ? "?" : "";
|
|
2974
|
-
let typeStr = renderTsType(field.type);
|
|
2995
|
+
let typeStr = renderTsType(field.type, target);
|
|
2975
2996
|
if (field.nullable) typeStr += " | null";
|
|
2976
2997
|
const line = `${quoteKey(field.name)}${opt}: ${typeStr};`;
|
|
2977
2998
|
const jsdocParts = [];
|
|
@@ -2980,9 +3001,9 @@ function renderField2(field) {
|
|
|
2980
3001
|
return withFieldJsDoc(jsdocParts, line);
|
|
2981
3002
|
}
|
|
2982
3003
|
__name(renderField2, "renderField");
|
|
2983
|
-
function renderInputField2(field, modelsWithInput) {
|
|
3004
|
+
function renderInputField2(field, modelsWithInput, target) {
|
|
2984
3005
|
const opt = field.optional || field.default !== void 0 ? "?" : "";
|
|
2985
|
-
let typeStr = renderInputTsType(field.type, modelsWithInput);
|
|
3006
|
+
let typeStr = renderInputTsType(field.type, modelsWithInput, target);
|
|
2986
3007
|
if (field.nullable) typeStr += " | null";
|
|
2987
3008
|
const line = `${quoteKey(field.name)}${opt}: ${typeStr};`;
|
|
2988
3009
|
const jsdocParts = [];
|
|
@@ -3005,7 +3026,7 @@ function applyOutputCase(name, c) {
|
|
|
3005
3026
|
return camelToPascal2(name);
|
|
3006
3027
|
}
|
|
3007
3028
|
__name(applyOutputCase, "applyOutputCase");
|
|
3008
|
-
function generateOutputModel(model, modelsWithOutput) {
|
|
3029
|
+
function generateOutputModel(model, target, modelsWithOutput) {
|
|
3009
3030
|
const lines = [];
|
|
3010
3031
|
const outputCase = model.outputCase && model.outputCase !== "camel" ? model.outputCase : void 0;
|
|
3011
3032
|
const readFields = model.fields.filter((f) => f.visibility !== "writeonly");
|
|
@@ -3013,23 +3034,23 @@ function generateOutputModel(model, modelsWithOutput) {
|
|
|
3013
3034
|
const baseExt = model.bases?.[0] && modelsWithOutput.has(model.bases?.[0]) ? ` extends ${model.bases?.[0]}Output` : model.bases?.[0] ? ` extends ${model.bases?.[0]}` : "";
|
|
3014
3035
|
lines.push(`export interface ${model.name}Output${baseExt} {`);
|
|
3015
3036
|
for (const field of readFields) {
|
|
3016
|
-
lines.push(` ${renderOutputField(field, model.outputCase, modelsWithOutput)}`);
|
|
3037
|
+
lines.push(` ${renderOutputField(field, model.outputCase, modelsWithOutput, target)}`);
|
|
3017
3038
|
}
|
|
3018
3039
|
lines.push("}");
|
|
3019
3040
|
return lines;
|
|
3020
3041
|
}
|
|
3021
3042
|
lines.push(`export interface ${model.name}Output {`);
|
|
3022
3043
|
for (const field of readFields) {
|
|
3023
|
-
lines.push(` ${renderOutputField(field, outputCase, modelsWithOutput)}`);
|
|
3044
|
+
lines.push(` ${renderOutputField(field, outputCase, modelsWithOutput, target)}`);
|
|
3024
3045
|
}
|
|
3025
3046
|
lines.push("}");
|
|
3026
3047
|
return lines;
|
|
3027
3048
|
}
|
|
3028
3049
|
__name(generateOutputModel, "generateOutputModel");
|
|
3029
|
-
function renderOutputField(field, outputCase, modelsWithOutput) {
|
|
3050
|
+
function renderOutputField(field, outputCase, modelsWithOutput, target) {
|
|
3030
3051
|
const opt = field.optional || field.default !== void 0 ? "?" : "";
|
|
3031
3052
|
const key = applyOutputCase(field.name, outputCase);
|
|
3032
|
-
let typeStr = renderOutputTsType(field.type, modelsWithOutput);
|
|
3053
|
+
let typeStr = renderOutputTsType(field.type, modelsWithOutput, target);
|
|
3033
3054
|
if (field.nullable) typeStr += " | null";
|
|
3034
3055
|
const line = `${quoteKey(key)}${opt}: ${typeStr};`;
|
|
3035
3056
|
const jsdocParts = [];
|
|
@@ -3856,7 +3877,9 @@ function collectServerOutput(config, rootDir, inputs, units) {
|
|
|
3856
3877
|
modelOutPaths: serverModelOutPaths,
|
|
3857
3878
|
currentOutPath: typeOutPath,
|
|
3858
3879
|
modelsWithInput,
|
|
3859
|
-
modelsWithOutput
|
|
3880
|
+
modelsWithOutput,
|
|
3881
|
+
// These types are consumed by Koa handlers, so `binary` is a Buffer, not a Blob.
|
|
3882
|
+
target: "server"
|
|
3860
3883
|
};
|
|
3861
3884
|
const content = config.zod ? generateContract(ast, renderCtx) : generatePlainTypes(ast, renderCtx);
|
|
3862
3885
|
return [
|
|
@@ -4368,7 +4391,8 @@ function collectTypesOutput(config, rootDir, inputs, units) {
|
|
|
4368
4391
|
modelOutPaths,
|
|
4369
4392
|
currentOutPath: outPath,
|
|
4370
4393
|
modelsWithInput,
|
|
4371
|
-
modelsWithOutput
|
|
4394
|
+
modelsWithOutput,
|
|
4395
|
+
target: config.target
|
|
4372
4396
|
})
|
|
4373
4397
|
}
|
|
4374
4398
|
], "render")
|