@apollo-deploy/tesseract 0.9.0 → 1.0.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/adapters/csharp/index.d.ts +12 -0
- package/dist/adapters/csharp/index.d.ts.map +1 -0
- package/dist/adapters/csharp/index.js +99 -0
- package/dist/adapters/csharp/index.js.map +1 -0
- package/dist/adapters/go/index.d.ts +12 -0
- package/dist/adapters/go/index.d.ts.map +1 -0
- package/dist/adapters/go/index.js +135 -0
- package/dist/adapters/go/index.js.map +1 -0
- package/dist/adapters/kotlin/index.d.ts +13 -0
- package/dist/adapters/kotlin/index.d.ts.map +1 -0
- package/dist/adapters/kotlin/index.js +186 -0
- package/dist/adapters/kotlin/index.js.map +1 -0
- package/dist/adapters/php/index.d.ts +20 -0
- package/dist/adapters/php/index.d.ts.map +1 -0
- package/dist/adapters/php/index.js +123 -0
- package/dist/adapters/php/index.js.map +1 -0
- package/dist/adapters/python/index.d.ts +12 -0
- package/dist/adapters/python/index.d.ts.map +1 -0
- package/dist/adapters/python/index.js +145 -0
- package/dist/adapters/python/index.js.map +1 -0
- package/dist/adapters/ruby/index.d.ts +12 -0
- package/dist/adapters/ruby/index.d.ts.map +1 -0
- package/dist/adapters/ruby/index.js +86 -0
- package/dist/adapters/ruby/index.js.map +1 -0
- package/dist/adapters/rust/index.d.ts +13 -0
- package/dist/adapters/rust/index.d.ts.map +1 -0
- package/dist/adapters/rust/index.js +213 -0
- package/dist/adapters/rust/index.js.map +1 -0
- package/dist/adapters/typescript/emitter/emitSchema.d.ts +1 -1
- package/dist/adapters/typescript/emitter/emitSchema.d.ts.map +1 -1
- package/dist/adapters/typescript/emitter/emitSchema.js +1 -1
- package/dist/adapters/typescript/emitter/emitSchema.js.map +1 -1
- package/dist/adapters/typescript/emitter/emitTypeScriptModels.d.ts +2 -2
- package/dist/adapters/typescript/emitter/emitTypeScriptModels.d.ts.map +1 -1
- package/dist/adapters/typescript/emitter/emitTypeScriptModels.js +2 -2
- package/dist/adapters/typescript/emitter/emitTypeScriptModels.js.map +1 -1
- package/dist/adapters/typescript/emitter/importCollector.d.ts +1 -1
- package/dist/adapters/typescript/emitter/importCollector.d.ts.map +1 -1
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/intake.d.ts +3 -3
- package/dist/pipeline/intake.d.ts.map +1 -1
- package/dist/pipeline/intake.js +194 -121
- package/dist/pipeline/intake.js.map +1 -1
- package/dist/types/ir.d.ts +1 -1
- package/dist/types/ir.d.ts.map +1 -1
- package/dist/types/sdk-module.d.ts +22 -2
- package/dist/types/sdk-module.d.ts.map +1 -1
- package/dist/types/sdk-module.js +1 -1
- package/package.json +1 -1
- package/templates/csharp/client.hbs +95 -0
- package/templates/csharp/csproj.hbs +18 -0
- package/templates/csharp/domain.hbs +183 -0
- package/templates/csharp/errors.hbs +45 -0
- package/templates/csharp/readme.hbs +76 -0
- package/templates/csharp/transport.hbs +291 -0
- package/templates/csharp/types.hbs +104 -0
- package/templates/go/client.hbs +158 -0
- package/templates/go/domain.hbs +89 -0
- package/templates/go/errors.hbs +38 -0
- package/templates/go/go-mod.hbs +6 -0
- package/templates/go/readme.hbs +73 -0
- package/templates/go/transport.hbs +379 -0
- package/templates/go/types.hbs +80 -0
- package/templates/go/utils.hbs +45 -0
- package/templates/kotlin/build-gradle.hbs +41 -0
- package/templates/kotlin/client.hbs +57 -0
- package/templates/kotlin/domain.hbs +73 -0
- package/templates/kotlin/errors.hbs +42 -0
- package/templates/kotlin/gradle-properties.hbs +2 -0
- package/templates/kotlin/readme.hbs +105 -0
- package/templates/kotlin/settings-gradle.hbs +1 -0
- package/templates/kotlin/transport.hbs +246 -0
- package/templates/kotlin/types.hbs +100 -0
- package/templates/php/client.hbs +74 -0
- package/templates/php/composer.hbs +19 -0
- package/templates/php/domain.hbs +173 -0
- package/templates/php/errors.hbs +131 -0
- package/templates/php/readme.hbs +89 -0
- package/templates/php/transport.hbs +285 -0
- package/templates/php/types.hbs +95 -0
- package/templates/python/client.hbs +102 -0
- package/templates/python/domain.hbs +139 -0
- package/templates/python/index.hbs +20 -0
- package/templates/python/pyproject.hbs +26 -0
- package/templates/python/readme.hbs +64 -0
- package/templates/python/resources-init.hbs +11 -0
- package/templates/python/transport.hbs +191 -0
- package/templates/python/types-errors.hbs +75 -0
- package/templates/python/types.hbs +79 -0
- package/templates/ruby/Gemfile.hbs +5 -0
- package/templates/ruby/client.hbs +86 -0
- package/templates/ruby/domain.hbs +110 -0
- package/templates/ruby/errors.hbs +150 -0
- package/templates/ruby/gemspec.hbs +30 -0
- package/templates/ruby/index.hbs +54 -0
- package/templates/ruby/readme.hbs +113 -0
- package/templates/ruby/transport.hbs +177 -0
- package/templates/ruby/types.hbs +126 -0
- package/templates/ruby/version.hbs +5 -0
- package/templates/rust/api-mod.hbs +3 -0
- package/templates/rust/cargo.hbs +22 -0
- package/templates/rust/client.hbs +114 -0
- package/templates/rust/domain.hbs +94 -0
- package/templates/rust/error.hbs +74 -0
- package/templates/rust/lib.hbs +85 -0
- package/templates/rust/readme.hbs +77 -0
- package/templates/rust/transport.hbs +287 -0
- package/templates/rust/types.hbs +142 -0
package/dist/pipeline/intake.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This is the only intake path in Tesseract. No OpenAPI parsing.
|
|
5
5
|
*/
|
|
6
|
-
import { readFileSync } from
|
|
7
|
-
import { camelCase, pascalCase, kebabCase } from
|
|
8
|
-
import { deriveCleanMethodName } from
|
|
9
|
-
import { validateManifest } from
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import { camelCase, pascalCase, kebabCase } from "change-case";
|
|
8
|
+
import { deriveCleanMethodName } from "../utils/naming.js";
|
|
9
|
+
import { validateManifest } from "../types/manifest.js";
|
|
10
10
|
// ── Entry point ──────────────────────────────────────────────────────────────
|
|
11
11
|
export function intake(config) {
|
|
12
12
|
let manifest;
|
|
@@ -15,7 +15,7 @@ export function intake(config) {
|
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
17
|
try {
|
|
18
|
-
const raw = readFileSync(config.input,
|
|
18
|
+
const raw = readFileSync(config.input, "utf-8");
|
|
19
19
|
manifest = JSON.parse(raw);
|
|
20
20
|
}
|
|
21
21
|
catch (err) {
|
|
@@ -23,7 +23,7 @@ export function intake(config) {
|
|
|
23
23
|
}
|
|
24
24
|
const validation = validateManifest(manifest);
|
|
25
25
|
if (!validation.valid) {
|
|
26
|
-
throw new Error(`Invalid manifest:\n ${validation.errors.join(
|
|
26
|
+
throw new Error(`Invalid manifest:\n ${validation.errors.join("\n ")}`);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
const schemaPackage = manifest.schemaPackage;
|
|
@@ -38,7 +38,7 @@ export function intake(config) {
|
|
|
38
38
|
isEnum: false,
|
|
39
39
|
properties: [],
|
|
40
40
|
ownership: {
|
|
41
|
-
kind:
|
|
41
|
+
kind: "external",
|
|
42
42
|
externalImport: {
|
|
43
43
|
packageName: schemaPackage.name,
|
|
44
44
|
importPath: importPath,
|
|
@@ -49,7 +49,10 @@ export function intake(config) {
|
|
|
49
49
|
: [];
|
|
50
50
|
// Local definitions — skip types already covered by the external package
|
|
51
51
|
const localDefinitions = Object.fromEntries(Object.entries(manifest.definitions ?? {}).filter(([k]) => !externalRefNames.has(pascalCase(k))));
|
|
52
|
-
const schemas = [
|
|
52
|
+
const schemas = [
|
|
53
|
+
...externalSchemas,
|
|
54
|
+
...buildSchemas(localDefinitions),
|
|
55
|
+
];
|
|
53
56
|
const groups = [];
|
|
54
57
|
const allInlineSchemas = [];
|
|
55
58
|
for (const domain of manifest.domains) {
|
|
@@ -65,7 +68,7 @@ export function intake(config) {
|
|
|
65
68
|
// When undefined the TypeScript adapter queries npm and bumps the patch.
|
|
66
69
|
packageVersion: config.packageVersion,
|
|
67
70
|
description: manifest.info.description,
|
|
68
|
-
baseUrl: config.baseUrl ?? manifest.info.baseUrl ??
|
|
71
|
+
baseUrl: config.baseUrl ?? manifest.info.baseUrl ?? "http://localhost",
|
|
69
72
|
packageName: config.packageName ?? kebabCase(manifest.info.title).toLowerCase(),
|
|
70
73
|
clientName: config.clientName ?? pascalCase(manifest.info.title),
|
|
71
74
|
schemaPackage: schemaPackage
|
|
@@ -83,7 +86,13 @@ function collectExternalRefNames(manifest) {
|
|
|
83
86
|
const s = route.schema;
|
|
84
87
|
if (!s)
|
|
85
88
|
continue;
|
|
86
|
-
for (const field of [
|
|
89
|
+
for (const field of [
|
|
90
|
+
s.params,
|
|
91
|
+
s.querystring,
|
|
92
|
+
s.query,
|
|
93
|
+
s.body,
|
|
94
|
+
s.headers,
|
|
95
|
+
]) {
|
|
87
96
|
if (field)
|
|
88
97
|
scanSchemaForExternalRefs(field, names);
|
|
89
98
|
}
|
|
@@ -102,9 +111,9 @@ function collectExternalRefNames(manifest) {
|
|
|
102
111
|
}
|
|
103
112
|
function scanSchemaForExternalRefs(schema, out) {
|
|
104
113
|
const s = schema;
|
|
105
|
-
if (typeof s.$ref ===
|
|
106
|
-
if (!s.$ref.startsWith(
|
|
107
|
-
out.add(pascalCase(s.$ref.split(
|
|
114
|
+
if (typeof s.$ref === "string") {
|
|
115
|
+
if (!s.$ref.startsWith("#")) {
|
|
116
|
+
out.add(pascalCase(s.$ref.split("/").pop()));
|
|
108
117
|
}
|
|
109
118
|
return;
|
|
110
119
|
}
|
|
@@ -113,12 +122,12 @@ function scanSchemaForExternalRefs(schema, out) {
|
|
|
113
122
|
for (const v of Object.values(props))
|
|
114
123
|
scanSchemaForExternalRefs(v, out);
|
|
115
124
|
}
|
|
116
|
-
if (s.additionalProperties && typeof s.additionalProperties ===
|
|
125
|
+
if (s.additionalProperties && typeof s.additionalProperties === "object") {
|
|
117
126
|
scanSchemaForExternalRefs(s.additionalProperties, out);
|
|
118
127
|
}
|
|
119
128
|
if (s.items)
|
|
120
129
|
scanSchemaForExternalRefs(s.items, out);
|
|
121
|
-
for (const key of [
|
|
130
|
+
for (const key of ["allOf", "oneOf", "anyOf"]) {
|
|
122
131
|
const arr = s[key];
|
|
123
132
|
if (Array.isArray(arr)) {
|
|
124
133
|
for (const m of arr)
|
|
@@ -132,7 +141,7 @@ function buildSchemas(definitions) {
|
|
|
132
141
|
}
|
|
133
142
|
function jsonSchemaToDefinition(name, schema) {
|
|
134
143
|
const s = schema;
|
|
135
|
-
if (
|
|
144
|
+
if ("enum" in s && Array.isArray(s.enum)) {
|
|
136
145
|
return {
|
|
137
146
|
name,
|
|
138
147
|
description: s.description,
|
|
@@ -142,7 +151,7 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
142
151
|
isTypeAlias: true,
|
|
143
152
|
};
|
|
144
153
|
}
|
|
145
|
-
if (
|
|
154
|
+
if ("oneOf" in s && Array.isArray(s.oneOf)) {
|
|
146
155
|
return {
|
|
147
156
|
name,
|
|
148
157
|
description: s.description,
|
|
@@ -153,7 +162,7 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
153
162
|
isTypeAlias: true,
|
|
154
163
|
};
|
|
155
164
|
}
|
|
156
|
-
if (
|
|
165
|
+
if ("anyOf" in s && Array.isArray(s.anyOf)) {
|
|
157
166
|
return {
|
|
158
167
|
name,
|
|
159
168
|
description: s.description,
|
|
@@ -164,11 +173,11 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
164
173
|
isTypeAlias: true,
|
|
165
174
|
};
|
|
166
175
|
}
|
|
167
|
-
if (
|
|
176
|
+
if ("allOf" in s && Array.isArray(s.allOf)) {
|
|
168
177
|
const members = s.allOf;
|
|
169
178
|
if (members.length === 2) {
|
|
170
|
-
const refMember = members.find((m) =>
|
|
171
|
-
const objectMember = members.find((m) => !(
|
|
179
|
+
const refMember = members.find((m) => "$ref" in m);
|
|
180
|
+
const objectMember = members.find((m) => !("$ref" in m) && m.type === "object");
|
|
172
181
|
if (refMember && objectMember) {
|
|
173
182
|
const baseDef = jsonSchemaToDefinition(name, objectMember);
|
|
174
183
|
return { ...baseDef, extends: jsonSchemaToType(refMember) };
|
|
@@ -184,7 +193,7 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
184
193
|
isTypeAlias: true,
|
|
185
194
|
};
|
|
186
195
|
}
|
|
187
|
-
if (s.type ===
|
|
196
|
+
if (s.type === "object") {
|
|
188
197
|
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
189
198
|
const rawProps = s.properties ?? {};
|
|
190
199
|
const properties = Object.entries(rawProps).map(([propName, propSchema]) => {
|
|
@@ -201,7 +210,9 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
201
210
|
};
|
|
202
211
|
});
|
|
203
212
|
const ap = s.additionalProperties;
|
|
204
|
-
const additionalPropertiesType = ap && typeof ap ===
|
|
213
|
+
const additionalPropertiesType = ap && typeof ap === "object"
|
|
214
|
+
? jsonSchemaToType(ap)
|
|
215
|
+
: undefined;
|
|
205
216
|
return {
|
|
206
217
|
name,
|
|
207
218
|
description: s.description,
|
|
@@ -221,72 +232,72 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
221
232
|
// ── Type string conversion ───────────────────────────────────────────────────
|
|
222
233
|
export function jsonSchemaToType(schema) {
|
|
223
234
|
const s = schema;
|
|
224
|
-
if (
|
|
225
|
-
return pascalCase(s.$ref.split(
|
|
235
|
+
if ("$ref" in s && typeof s.$ref === "string") {
|
|
236
|
+
return pascalCase(s.$ref.split("/").pop());
|
|
226
237
|
}
|
|
227
|
-
if (
|
|
228
|
-
return s.enum.map((v) => JSON.stringify(v)).join(
|
|
238
|
+
if ("enum" in s && Array.isArray(s.enum)) {
|
|
239
|
+
return s.enum.map((v) => JSON.stringify(v)).join(" | ");
|
|
229
240
|
}
|
|
230
241
|
if (Array.isArray(s.allOf))
|
|
231
|
-
return s.allOf.map(jsonSchemaToType).join(
|
|
242
|
+
return s.allOf.map(jsonSchemaToType).join(" & ");
|
|
232
243
|
if (Array.isArray(s.oneOf))
|
|
233
|
-
return s.oneOf.map(jsonSchemaToType).join(
|
|
244
|
+
return s.oneOf.map(jsonSchemaToType).join(" | ");
|
|
234
245
|
if (Array.isArray(s.anyOf))
|
|
235
|
-
return s.anyOf.map(jsonSchemaToType).join(
|
|
246
|
+
return s.anyOf.map(jsonSchemaToType).join(" | ");
|
|
236
247
|
const type = s.type;
|
|
237
|
-
if (type ===
|
|
248
|
+
if (type === "object") {
|
|
238
249
|
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
239
250
|
const rawProps = s.properties;
|
|
240
251
|
if (rawProps) {
|
|
241
252
|
const entries = Object.entries(rawProps)
|
|
242
|
-
.map(([k, v]) => `${k}${required.has(k) ?
|
|
243
|
-
.join(
|
|
253
|
+
.map(([k, v]) => `${k}${required.has(k) ? "" : "?"}: ${jsonSchemaToType(v)}`)
|
|
254
|
+
.join("; ");
|
|
244
255
|
const ap = s.additionalProperties;
|
|
245
|
-
if (ap && typeof ap ===
|
|
256
|
+
if (ap && typeof ap === "object") {
|
|
246
257
|
return `{ ${entries}; [key: string]: ${jsonSchemaToType(ap)} }`;
|
|
247
258
|
}
|
|
248
|
-
return entries ? `{ ${entries} }` :
|
|
259
|
+
return entries ? `{ ${entries} }` : "Record<string, unknown>";
|
|
249
260
|
}
|
|
250
|
-
if (s.additionalProperties && typeof s.additionalProperties ===
|
|
261
|
+
if (s.additionalProperties && typeof s.additionalProperties === "object") {
|
|
251
262
|
return `Record<string, ${jsonSchemaToType(s.additionalProperties)}>`;
|
|
252
263
|
}
|
|
253
|
-
return
|
|
264
|
+
return "Record<string, unknown>";
|
|
254
265
|
}
|
|
255
|
-
if (type ===
|
|
266
|
+
if (type === "array") {
|
|
256
267
|
const items = s.items;
|
|
257
|
-
return `Array<${items ? jsonSchemaToType(items) :
|
|
268
|
+
return `Array<${items ? jsonSchemaToType(items) : "unknown"}>`;
|
|
258
269
|
}
|
|
259
|
-
if (type ===
|
|
260
|
-
return
|
|
261
|
-
if (type ===
|
|
262
|
-
return
|
|
263
|
-
if (type ===
|
|
264
|
-
return
|
|
265
|
-
if (type ===
|
|
266
|
-
return
|
|
267
|
-
if (type ===
|
|
268
|
-
return
|
|
269
|
-
if (type ===
|
|
270
|
+
if (type === "string" || type === "date")
|
|
271
|
+
return "string";
|
|
272
|
+
if (type === "number" || type === "integer")
|
|
273
|
+
return "number";
|
|
274
|
+
if (type === "boolean")
|
|
275
|
+
return "boolean";
|
|
276
|
+
if (type === "null")
|
|
277
|
+
return "null";
|
|
278
|
+
if (type === "never")
|
|
279
|
+
return "never";
|
|
280
|
+
if (type === "nullable") {
|
|
270
281
|
const def = s.def;
|
|
271
282
|
if (def?.innerType)
|
|
272
|
-
return jsonSchemaToType(def.innerType) +
|
|
273
|
-
return
|
|
283
|
+
return jsonSchemaToType(def.innerType) + " | null";
|
|
284
|
+
return "unknown | null";
|
|
274
285
|
}
|
|
275
|
-
if (type ===
|
|
286
|
+
if (type === "union") {
|
|
276
287
|
const options = s.options;
|
|
277
288
|
if (options)
|
|
278
|
-
return options.map(jsonSchemaToType).join(
|
|
289
|
+
return options.map(jsonSchemaToType).join(" | ");
|
|
279
290
|
}
|
|
280
|
-
return
|
|
291
|
+
return "unknown";
|
|
281
292
|
}
|
|
282
293
|
// ── Group / operation building ───────────────────────────────────────────────
|
|
283
294
|
function isComplexInlineSchema(s) {
|
|
284
|
-
if (s.type ===
|
|
295
|
+
if (s.type === "object")
|
|
285
296
|
return true;
|
|
286
297
|
if (Array.isArray(s.allOf))
|
|
287
298
|
return true;
|
|
288
299
|
// anyOf / oneOf that is just `SomeRef | null` is not complex — emit inline
|
|
289
|
-
for (const key of [
|
|
300
|
+
for (const key of ["anyOf", "oneOf"]) {
|
|
290
301
|
const arr = s[key];
|
|
291
302
|
if (!Array.isArray(arr))
|
|
292
303
|
continue;
|
|
@@ -300,8 +311,8 @@ function isComplexInlineSchema(s) {
|
|
|
300
311
|
function isNullableRefUnion(members) {
|
|
301
312
|
if (members.length !== 2)
|
|
302
313
|
return false;
|
|
303
|
-
const refMember = members.find((m) =>
|
|
304
|
-
const nullMember = members.find((m) => m.type ===
|
|
314
|
+
const refMember = members.find((m) => "$ref" in m);
|
|
315
|
+
const nullMember = members.find((m) => m.type === "null");
|
|
305
316
|
return !!refMember && !!nullMember;
|
|
306
317
|
}
|
|
307
318
|
function buildOperationGroup(domain, _schemas) {
|
|
@@ -314,11 +325,14 @@ function buildOperationGroup(domain, _schemas) {
|
|
|
314
325
|
if (route.sdk?.exclude)
|
|
315
326
|
continue;
|
|
316
327
|
const { operation, inlineSchemas } = buildOperation(route, name, domain.prefix);
|
|
317
|
-
operations.push({
|
|
328
|
+
operations.push({
|
|
329
|
+
...operation,
|
|
330
|
+
visibility: route.sdk?.internal ? "internal" : "public",
|
|
331
|
+
});
|
|
318
332
|
for (const s of inlineSchemas) {
|
|
319
333
|
allInlineSchemas.push({
|
|
320
334
|
...s,
|
|
321
|
-
ownership: { kind:
|
|
335
|
+
ownership: { kind: "generated", domainFile: fileName },
|
|
322
336
|
});
|
|
323
337
|
}
|
|
324
338
|
}
|
|
@@ -326,18 +340,20 @@ function buildOperationGroup(domain, _schemas) {
|
|
|
326
340
|
group: {
|
|
327
341
|
name,
|
|
328
342
|
fileName,
|
|
329
|
-
interfaceName: pascalCase(domainName) +
|
|
330
|
-
factoryName:
|
|
343
|
+
interfaceName: pascalCase(domainName) + "API",
|
|
344
|
+
factoryName: "create" + pascalCase(domainName) + "API",
|
|
331
345
|
description: domain.description,
|
|
332
|
-
visibility: domain.stability ===
|
|
346
|
+
visibility: domain.stability === "internal" ? "internal" : "public",
|
|
333
347
|
operations,
|
|
334
348
|
},
|
|
335
349
|
inlineSchemas: allInlineSchemas,
|
|
336
350
|
};
|
|
337
351
|
}
|
|
338
352
|
function buildOperation(route, groupName, domainPrefix) {
|
|
339
|
-
const prefix = (route.sdk?.prefix ?? domainPrefix).replace(/\/$/,
|
|
340
|
-
|
|
353
|
+
const prefix = (route.sdk?.prefix ?? domainPrefix).replace(/\/$/, "");
|
|
354
|
+
// When route.url is '/' it means the route URL exactly matched the domain
|
|
355
|
+
// prefix — avoid doubling the slash (e.g. /signal/projects + / = /signal/projects/).
|
|
356
|
+
const fullUrl = route.url === "/" ? prefix || "/" : prefix + route.url;
|
|
341
357
|
const httpMethod = (Array.isArray(route.method) ? route.method[0] : route.method).toUpperCase();
|
|
342
358
|
const pathParams = extractPathParams(fullUrl, route.schema?.params);
|
|
343
359
|
const querySchema = route.schema?.querystring ?? route.schema?.query;
|
|
@@ -348,28 +364,36 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
348
364
|
deriveCleanMethodName({
|
|
349
365
|
operationId: route.schema?.operationId ?? route.sdk?.operationId,
|
|
350
366
|
summary: route.schema?.summary,
|
|
351
|
-
path: route.url.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,
|
|
367
|
+
path: route.url.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "{$1}"),
|
|
352
368
|
httpMethod: httpMethod.toLowerCase(),
|
|
353
369
|
tagName: groupName,
|
|
354
370
|
});
|
|
355
371
|
// Response type
|
|
356
|
-
let responseType =
|
|
372
|
+
let responseType = "void";
|
|
357
373
|
let statusCode = 200;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
374
|
+
// SDK config override: explicit returnType takes priority over schema derivation.
|
|
375
|
+
// Useful for paginated list endpoints that return a wrapper but should expose the inner array type.
|
|
376
|
+
if (route.sdk?.returnType) {
|
|
377
|
+
responseType = route.sdk.returnType;
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
const successResponse = pickSuccessResponse(route.schema?.response);
|
|
381
|
+
if (successResponse) {
|
|
382
|
+
const { schema: successSchema, statusCode: sc } = successResponse;
|
|
383
|
+
statusCode = sc;
|
|
384
|
+
const rs = successSchema;
|
|
385
|
+
if (rs.type === "null" ||
|
|
386
|
+
(rs.type === "object" && !rs.properties && !rs.$ref)) {
|
|
387
|
+
responseType = "void";
|
|
388
|
+
}
|
|
389
|
+
else if (typeof rs.$ref !== "string" && isComplexInlineSchema(rs)) {
|
|
390
|
+
const typeName = pascalCase(name) + "Response";
|
|
391
|
+
inlineSchemas.push(jsonSchemaToDefinition(typeName, successSchema));
|
|
392
|
+
responseType = typeName;
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
responseType = jsonSchemaToType(successSchema);
|
|
396
|
+
}
|
|
373
397
|
}
|
|
374
398
|
}
|
|
375
399
|
// Request body
|
|
@@ -378,8 +402,8 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
378
402
|
const bodySchema = route.schema.body;
|
|
379
403
|
const bs = bodySchema;
|
|
380
404
|
let bodyType;
|
|
381
|
-
if (typeof bs.$ref !==
|
|
382
|
-
const typeName = pascalCase(name) +
|
|
405
|
+
if (typeof bs.$ref !== "string" && isComplexInlineSchema(bs)) {
|
|
406
|
+
const typeName = pascalCase(name) + "Input";
|
|
383
407
|
inlineSchemas.push(jsonSchemaToDefinition(typeName, bodySchema));
|
|
384
408
|
bodyType = typeName;
|
|
385
409
|
}
|
|
@@ -389,18 +413,18 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
389
413
|
requestBody = {
|
|
390
414
|
type: bodyType,
|
|
391
415
|
required: true,
|
|
392
|
-
contentType: route.sdk?.transport ===
|
|
393
|
-
?
|
|
394
|
-
: route.sdk?.transport ===
|
|
395
|
-
?
|
|
396
|
-
:
|
|
416
|
+
contentType: route.sdk?.transport === "multipart"
|
|
417
|
+
? "multipart/form-data"
|
|
418
|
+
: route.sdk?.transport === "binary"
|
|
419
|
+
? "application/octet-stream"
|
|
420
|
+
: "application/json",
|
|
397
421
|
};
|
|
398
422
|
}
|
|
399
423
|
// Query type
|
|
400
424
|
let queryType;
|
|
401
425
|
if (querySchema) {
|
|
402
426
|
const qs = querySchema;
|
|
403
|
-
if (typeof qs.$ref ===
|
|
427
|
+
if (typeof qs.$ref === "string") {
|
|
404
428
|
queryType = jsonSchemaToType(querySchema);
|
|
405
429
|
}
|
|
406
430
|
else if (queryParams.length > 0) {
|
|
@@ -413,7 +437,7 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
413
437
|
headerParams.push({
|
|
414
438
|
name: camelCase(rh.paramName),
|
|
415
439
|
originalName: rh.name,
|
|
416
|
-
type:
|
|
440
|
+
type: "string",
|
|
417
441
|
required: true,
|
|
418
442
|
description: rh.description,
|
|
419
443
|
});
|
|
@@ -423,10 +447,10 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
423
447
|
let headerType;
|
|
424
448
|
if (headerParams.length > 0) {
|
|
425
449
|
const props = headerParams.map((p) => {
|
|
426
|
-
const opt = p.required ?
|
|
450
|
+
const opt = p.required ? "" : "?";
|
|
427
451
|
return `${p.name}${opt}: ${p.type}`;
|
|
428
452
|
});
|
|
429
|
-
headerType = `{ ${props.join(
|
|
453
|
+
headerType = `{ ${props.join("; ")} }`;
|
|
430
454
|
}
|
|
431
455
|
return {
|
|
432
456
|
operation: {
|
|
@@ -447,10 +471,12 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
447
471
|
responseType,
|
|
448
472
|
statusCode,
|
|
449
473
|
deprecated: route.sdk?.deprecated ? true : false,
|
|
450
|
-
deprecationMessage: typeof route.sdk?.deprecated ===
|
|
474
|
+
deprecationMessage: typeof route.sdk?.deprecated === "string"
|
|
475
|
+
? route.sdk.deprecated
|
|
476
|
+
: undefined,
|
|
451
477
|
timeoutMs: route.sdk?.timeout,
|
|
452
|
-
visibility:
|
|
453
|
-
isEventStream: route.sse ?? route.sdk?.transport ===
|
|
478
|
+
visibility: "public",
|
|
479
|
+
isEventStream: route.sse ?? route.sdk?.transport === "stream",
|
|
454
480
|
sseReturnType: route.sdk?.sseReturnType,
|
|
455
481
|
},
|
|
456
482
|
inlineSchemas,
|
|
@@ -458,15 +484,62 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
458
484
|
}
|
|
459
485
|
// ── Parameter extraction ─────────────────────────────────────────────────────
|
|
460
486
|
const TS_RESERVED_WORDS = new Set([
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
487
|
+
"break",
|
|
488
|
+
"case",
|
|
489
|
+
"catch",
|
|
490
|
+
"class",
|
|
491
|
+
"const",
|
|
492
|
+
"continue",
|
|
493
|
+
"debugger",
|
|
494
|
+
"default",
|
|
495
|
+
"delete",
|
|
496
|
+
"do",
|
|
497
|
+
"else",
|
|
498
|
+
"enum",
|
|
499
|
+
"export",
|
|
500
|
+
"extends",
|
|
501
|
+
"false",
|
|
502
|
+
"finally",
|
|
503
|
+
"for",
|
|
504
|
+
"function",
|
|
505
|
+
"if",
|
|
506
|
+
"import",
|
|
507
|
+
"in",
|
|
508
|
+
"instanceof",
|
|
509
|
+
"new",
|
|
510
|
+
"null",
|
|
511
|
+
"return",
|
|
512
|
+
"super",
|
|
513
|
+
"switch",
|
|
514
|
+
"this",
|
|
515
|
+
"throw",
|
|
516
|
+
"true",
|
|
517
|
+
"try",
|
|
518
|
+
"typeof",
|
|
519
|
+
"var",
|
|
520
|
+
"void",
|
|
521
|
+
"while",
|
|
522
|
+
"with",
|
|
523
|
+
"as",
|
|
524
|
+
"implements",
|
|
525
|
+
"interface",
|
|
526
|
+
"let",
|
|
527
|
+
"package",
|
|
528
|
+
"private",
|
|
529
|
+
"protected",
|
|
530
|
+
"public",
|
|
531
|
+
"static",
|
|
532
|
+
"yield",
|
|
533
|
+
"type",
|
|
467
534
|
]);
|
|
468
535
|
/** Parameter names reserved by the generated method signatures. */
|
|
469
|
-
const FIXED_PARAM_NAMES = new Set([
|
|
536
|
+
const FIXED_PARAM_NAMES = new Set([
|
|
537
|
+
"input",
|
|
538
|
+
"query",
|
|
539
|
+
"headerOptions",
|
|
540
|
+
"cookies",
|
|
541
|
+
"options",
|
|
542
|
+
]);
|
|
470
543
|
function safeParamName(name) {
|
|
471
544
|
if (TS_RESERVED_WORDS.has(name) || FIXED_PARAM_NAMES.has(name)) {
|
|
472
545
|
return `${name}Param`;
|
|
@@ -478,7 +551,7 @@ function extractPathParams(url, paramsSchema) {
|
|
|
478
551
|
if (urlParams.length === 0)
|
|
479
552
|
return [];
|
|
480
553
|
const s = paramsSchema;
|
|
481
|
-
const props = typeof s?.properties ===
|
|
554
|
+
const props = typeof s?.properties === "object" && !Array.isArray(s?.properties)
|
|
482
555
|
? s.properties
|
|
483
556
|
: undefined;
|
|
484
557
|
const required = new Set(Array.isArray(s?.required) ? s.required : []);
|
|
@@ -487,7 +560,7 @@ function extractPathParams(url, paramsSchema) {
|
|
|
487
560
|
return {
|
|
488
561
|
name: safeParamName(camelCase(paramName)),
|
|
489
562
|
originalName: paramName,
|
|
490
|
-
type: propSchema ? jsonSchemaToType(propSchema) :
|
|
563
|
+
type: propSchema ? jsonSchemaToType(propSchema) : "string",
|
|
491
564
|
required: required.size === 0 || required.has(paramName),
|
|
492
565
|
description: propSchema
|
|
493
566
|
? propSchema.description
|
|
@@ -499,7 +572,7 @@ function extractQueryParams(querySchema) {
|
|
|
499
572
|
if (!querySchema)
|
|
500
573
|
return [];
|
|
501
574
|
const s = querySchema;
|
|
502
|
-
if (s.type !==
|
|
575
|
+
if (s.type !== "object" || !s.properties)
|
|
503
576
|
return [];
|
|
504
577
|
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
505
578
|
const props = s.properties;
|
|
@@ -513,18 +586,18 @@ function extractQueryParams(querySchema) {
|
|
|
513
586
|
}));
|
|
514
587
|
}
|
|
515
588
|
const SYSTEM_HEADERS = new Set([
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
589
|
+
"content-type",
|
|
590
|
+
"content-length",
|
|
591
|
+
"transfer-encoding",
|
|
592
|
+
"host",
|
|
593
|
+
"accept",
|
|
594
|
+
"accept-encoding",
|
|
522
595
|
]);
|
|
523
596
|
function extractHeaderParams(headersSchema) {
|
|
524
597
|
if (!headersSchema)
|
|
525
598
|
return [];
|
|
526
599
|
const s = headersSchema;
|
|
527
|
-
if (s.type !==
|
|
600
|
+
if (s.type !== "object" || !s.properties)
|
|
528
601
|
return [];
|
|
529
602
|
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
530
603
|
const props = s.properties;
|
|
@@ -543,19 +616,19 @@ function fastifyPathToIR(url, pathParams) {
|
|
|
543
616
|
const nameMap = new Map(pathParams.map((p) => [p.originalName, p.name]));
|
|
544
617
|
return url.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, raw) => {
|
|
545
618
|
const name = nameMap.get(raw) ?? raw;
|
|
546
|
-
return
|
|
619
|
+
return "${" + name + "}";
|
|
547
620
|
});
|
|
548
621
|
}
|
|
549
622
|
function pickSuccessResponse(response) {
|
|
550
623
|
if (!response)
|
|
551
624
|
return undefined;
|
|
552
625
|
for (const [code, schema] of [
|
|
553
|
-
[200, response[
|
|
554
|
-
[201, response[
|
|
555
|
-
[202, response[
|
|
556
|
-
[204, response[
|
|
557
|
-
[200, response[
|
|
558
|
-
[200, response[
|
|
626
|
+
[200, response["200"] ?? response[200]],
|
|
627
|
+
[201, response["201"] ?? response[201]],
|
|
628
|
+
[202, response["202"] ?? response[202]],
|
|
629
|
+
[204, response["204"] ?? response[204]],
|
|
630
|
+
[200, response["2xx"]],
|
|
631
|
+
[200, response["default"]],
|
|
559
632
|
]) {
|
|
560
633
|
if (schema)
|
|
561
634
|
return { schema, statusCode: code };
|