@apollo-deploy/tesseract 1.9.0 → 2.1.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/README.md +218 -110
- package/dist/adapters/csharp/index.d.ts +5 -8
- package/dist/adapters/csharp/index.d.ts.map +1 -1
- package/dist/adapters/csharp/index.js +314 -74
- package/dist/adapters/csharp/index.js.map +1 -1
- package/dist/adapters/documentation.d.ts +17 -0
- package/dist/adapters/documentation.d.ts.map +1 -0
- package/dist/adapters/documentation.js +268 -0
- package/dist/adapters/documentation.js.map +1 -0
- package/dist/adapters/go/index.d.ts +5 -8
- package/dist/adapters/go/index.d.ts.map +1 -1
- package/dist/adapters/go/index.js +197 -101
- package/dist/adapters/go/index.js.map +1 -1
- package/dist/adapters/kotlin/index.d.ts +5 -9
- package/dist/adapters/kotlin/index.d.ts.map +1 -1
- package/dist/adapters/kotlin/index.js +280 -212
- package/dist/adapters/kotlin/index.js.map +1 -1
- package/dist/adapters/php/index.d.ts +5 -15
- package/dist/adapters/php/index.d.ts.map +1 -1
- package/dist/adapters/php/index.js +362 -90
- package/dist/adapters/php/index.js.map +1 -1
- package/dist/adapters/python/index.d.ts +5 -8
- package/dist/adapters/python/index.d.ts.map +1 -1
- package/dist/adapters/python/index.js +240 -109
- package/dist/adapters/python/index.js.map +1 -1
- package/dist/adapters/ruby/index.d.ts +3 -3
- package/dist/adapters/ruby/index.d.ts.map +1 -1
- package/dist/adapters/ruby/index.js +235 -46
- package/dist/adapters/ruby/index.js.map +1 -1
- package/dist/adapters/rust/index.d.ts +5 -9
- package/dist/adapters/rust/index.d.ts.map +1 -1
- package/dist/adapters/rust/index.js +237 -169
- package/dist/adapters/rust/index.js.map +1 -1
- package/dist/adapters/shared.d.ts +7 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/adapters/shared.js +68 -0
- package/dist/adapters/shared.js.map +1 -0
- package/dist/adapters/type-utils.d.ts +13 -0
- package/dist/adapters/type-utils.d.ts.map +1 -0
- package/dist/adapters/type-utils.js +105 -0
- package/dist/adapters/type-utils.js.map +1 -0
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/typescript/emitter/emitSchema.d.ts +2 -2
- package/dist/adapters/typescript/emitter/emitSchema.js +39 -27
- 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.js +15 -15
- package/dist/adapters/typescript/emitter/importCollector.d.ts +2 -4
- package/dist/adapters/typescript/emitter/importCollector.d.ts.map +1 -1
- package/dist/adapters/typescript/emitter/importCollector.js +40 -50
- package/dist/adapters/typescript/emitter/importCollector.js.map +1 -1
- package/dist/adapters/typescript/emitter/parsers.d.ts +1 -1
- package/dist/adapters/typescript/emitter/parsers.js +13 -13
- package/dist/adapters/typescript/emitter/typeHoister.d.ts +1 -1
- package/dist/adapters/typescript/emitter/typeHoister.d.ts.map +1 -1
- package/dist/adapters/typescript/emitter/typeHoister.js +12 -20
- package/dist/adapters/typescript/emitter/typeHoister.js.map +1 -1
- package/dist/adapters/typescript/index.d.ts +3 -3
- package/dist/adapters/typescript/index.d.ts.map +1 -1
- package/dist/adapters/typescript/index.js +148 -166
- package/dist/adapters/typescript/index.js.map +1 -1
- package/dist/adapters/typescript/shared.d.ts +2 -2
- package/dist/adapters/typescript/shared.js +12 -12
- package/dist/cli.js +50 -37
- package/dist/cli.js.map +1 -1
- package/dist/collector.d.ts +32 -7
- package/dist/collector.d.ts.map +1 -1
- package/dist/collector.js +149 -49
- package/dist/collector.js.map +1 -1
- package/dist/elysia.js +2 -2
- package/dist/elysia.js.map +1 -1
- package/dist/express.d.ts.map +1 -1
- package/dist/express.js.map +1 -1
- package/dist/fastify.d.ts +9 -2
- package/dist/fastify.d.ts.map +1 -1
- package/dist/fastify.js +40 -21
- package/dist/fastify.js.map +1 -1
- package/dist/helpers/handlebars.d.ts +7 -16
- package/dist/helpers/handlebars.d.ts.map +1 -1
- package/dist/helpers/handlebars.js +127 -177
- package/dist/helpers/handlebars.js.map +1 -1
- package/dist/hono.d.ts.map +1 -1
- package/dist/hono.js.map +1 -1
- package/dist/index.d.ts +14 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -30
- package/dist/index.js.map +1 -1
- package/dist/koa.d.ts.map +1 -1
- package/dist/koa.js.map +1 -1
- package/dist/nestjs.d.ts.map +1 -1
- package/dist/nestjs.js +41 -14
- package/dist/nestjs.js.map +1 -1
- package/dist/pipeline/enrich.d.ts +2 -13
- package/dist/pipeline/enrich.d.ts.map +1 -1
- package/dist/pipeline/enrich.js +157 -212
- package/dist/pipeline/enrich.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 +342 -238
- package/dist/pipeline/intake.js.map +1 -1
- package/dist/pipeline/write.d.ts +1 -14
- package/dist/pipeline/write.d.ts.map +1 -1
- package/dist/pipeline/write.js +73 -113
- package/dist/pipeline/write.js.map +1 -1
- package/dist/types/config.d.ts +14 -26
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +16 -12
- package/dist/types/config.js.map +1 -1
- package/dist/types/ir.d.ts +12 -8
- package/dist/types/ir.d.ts.map +1 -1
- package/dist/types/manifest.d.ts +10 -11
- package/dist/types/manifest.d.ts.map +1 -1
- package/dist/types/manifest.js +54 -14
- package/dist/types/manifest.js.map +1 -1
- package/dist/types/sdk-module.d.ts +2 -2
- package/dist/types/sdk-module.js +1 -1
- package/dist/utils/format.d.ts +1 -3
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +3 -10
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/naming.d.ts.map +1 -1
- package/dist/utils/naming.js +174 -184
- package/dist/utils/naming.js.map +1 -1
- package/dist/utils/npm-token.d.ts +1 -17
- package/dist/utils/npm-token.d.ts.map +1 -1
- package/dist/utils/npm-token.js +27 -76
- package/dist/utils/npm-token.js.map +1 -1
- package/dist/utils/version.d.ts +14 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +60 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +4 -3
- package/templates/csharp/client.hbs +26 -49
- package/templates/csharp/csproj.hbs +7 -0
- package/templates/csharp/domain.hbs +48 -130
- package/templates/csharp/errors.hbs +17 -25
- package/templates/csharp/readme.hbs +31 -44
- package/templates/csharp/transport.hbs +238 -136
- package/templates/csharp/types.hbs +30 -76
- package/templates/go/client.hbs +44 -109
- package/templates/go/domain.hbs +83 -25
- package/templates/go/go-mod.hbs +1 -4
- package/templates/go/readme.hbs +32 -29
- package/templates/go/transport.hbs +174 -173
- package/templates/go/types.hbs +17 -53
- package/templates/go/utils.hbs +3 -2
- package/templates/kotlin/build-gradle.hbs +1 -1
- package/templates/kotlin/client.hbs +18 -15
- package/templates/kotlin/domain.hbs +54 -51
- package/templates/kotlin/errors.hbs +1 -1
- package/templates/kotlin/readme.hbs +35 -137
- package/templates/kotlin/transport.hbs +140 -123
- package/templates/kotlin/types.hbs +30 -81
- package/templates/php/client.hbs +8 -46
- package/templates/php/composer.hbs +2 -1
- package/templates/php/domain.hbs +86 -117
- package/templates/php/errors.hbs +1 -1
- package/templates/php/readme.hbs +42 -49
- package/templates/php/transport.hbs +192 -145
- package/templates/php/types.hbs +63 -60
- package/templates/python/client.hbs +15 -2
- package/templates/python/domain.hbs +50 -108
- package/templates/python/pyproject.hbs +5 -0
- package/templates/python/readme.hbs +47 -30
- package/templates/python/resources-init.hbs +1 -1
- package/templates/python/transport.hbs +167 -53
- package/templates/python/types.hbs +22 -58
- package/templates/ruby/client.hbs +10 -6
- package/templates/ruby/domain.hbs +48 -67
- package/templates/ruby/errors.hbs +3 -3
- package/templates/ruby/gemspec.hbs +3 -10
- package/templates/ruby/index.hbs +3 -3
- package/templates/ruby/readme.hbs +33 -70
- package/templates/ruby/transport.hbs +129 -103
- package/templates/ruby/types.hbs +11 -16
- package/templates/ruby/version.hbs +1 -1
- package/templates/rust/cargo.hbs +2 -0
- package/templates/rust/client.hbs +10 -1
- package/templates/rust/domain.hbs +69 -52
- package/templates/rust/error.hbs +3 -1
- package/templates/rust/lib.hbs +4 -5
- package/templates/rust/readme.hbs +33 -46
- package/templates/rust/transport.hbs +170 -159
- package/templates/rust/types.hbs +34 -111
- package/templates/typescript/domain-class.hbs +14 -6
- package/templates/typescript/domain.hbs +13 -5
- package/templates/typescript/package-json.hbs +3 -1
- package/templates/typescript/readme.hbs +42 -42
- package/templates/typescript/transport.hbs +46 -8
- package/templates/typescript/types-errors.hbs +1 -1
- package/dist/adapters/typescript/model-emitter.d.ts +0 -13
- package/dist/adapters/typescript/model-emitter.d.ts.map +0 -1
- package/dist/adapters/typescript/model-emitter.js +0 -415
- package/dist/adapters/typescript/model-emitter.js.map +0 -1
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,17 +15,16 @@ export function intake(config) {
|
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
17
|
try {
|
|
18
|
-
|
|
19
|
-
manifest = JSON.parse(raw);
|
|
18
|
+
manifest = JSON.parse(readFileSync(config.input, 'utf8'));
|
|
20
19
|
}
|
|
21
|
-
catch (
|
|
22
|
-
throw new Error(`Failed to read manifest at "${config.input}": ${
|
|
23
|
-
}
|
|
24
|
-
const validation = validateManifest(manifest);
|
|
25
|
-
if (!validation.valid) {
|
|
26
|
-
throw new Error(`Invalid manifest:\n ${validation.errors.join("\n ")}`);
|
|
20
|
+
catch (error) {
|
|
21
|
+
throw new Error(`Failed to read manifest at "${config.input}": ${error instanceof Error ? error.message : String(error)}`);
|
|
27
22
|
}
|
|
28
23
|
}
|
|
24
|
+
const validation = validateManifest(manifest);
|
|
25
|
+
if (!validation.valid) {
|
|
26
|
+
throw new Error(`Invalid manifest:\n ${validation.errors.join('\n ')}`);
|
|
27
|
+
}
|
|
29
28
|
const schemaPackage = manifest.schemaPackage;
|
|
30
29
|
const importPath = schemaPackage?.importPath ?? schemaPackage?.name;
|
|
31
30
|
const externalRefNames = schemaPackage
|
|
@@ -38,11 +37,12 @@ export function intake(config) {
|
|
|
38
37
|
isEnum: false,
|
|
39
38
|
properties: [],
|
|
40
39
|
ownership: {
|
|
41
|
-
kind:
|
|
40
|
+
kind: 'external',
|
|
42
41
|
externalImport: {
|
|
43
42
|
packageName: schemaPackage.name,
|
|
44
43
|
importPath: importPath,
|
|
45
44
|
exportName: name,
|
|
45
|
+
version: schemaPackage.version,
|
|
46
46
|
},
|
|
47
47
|
},
|
|
48
48
|
}))
|
|
@@ -56,7 +56,7 @@ export function intake(config) {
|
|
|
56
56
|
const groups = [];
|
|
57
57
|
const allInlineSchemas = [];
|
|
58
58
|
for (const domain of manifest.domains) {
|
|
59
|
-
const { group, inlineSchemas } = buildOperationGroup(domain
|
|
59
|
+
const { group, inlineSchemas } = buildOperationGroup(domain);
|
|
60
60
|
groups.push(group);
|
|
61
61
|
allInlineSchemas.push(...inlineSchemas);
|
|
62
62
|
}
|
|
@@ -67,8 +67,9 @@ export function intake(config) {
|
|
|
67
67
|
// Only set packageVersion when the caller explicitly supplied it.
|
|
68
68
|
// When undefined the TypeScript adapter queries npm and bumps the patch.
|
|
69
69
|
packageVersion: config.packageVersion,
|
|
70
|
+
versionBump: config.versionBump ?? manifest.info.versionBump ?? 'patch',
|
|
70
71
|
description: manifest.info.description,
|
|
71
|
-
baseUrl: config.baseUrl ?? manifest.info.baseUrl ??
|
|
72
|
+
baseUrl: config.baseUrl ?? manifest.info.baseUrl ?? 'http://localhost',
|
|
72
73
|
packageName: config.packageName ?? kebabCase(manifest.info.title).toLowerCase(),
|
|
73
74
|
clientName: config.clientName ?? pascalCase(manifest.info.title),
|
|
74
75
|
schemaPackage: schemaPackage
|
|
@@ -101,19 +102,68 @@ function collectExternalRefNames(manifest) {
|
|
|
101
102
|
scanSchemaForExternalRefs(resp, names);
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
collectNamedTypes(route.sdk?.sseReturnType, names);
|
|
106
|
+
collectNamedTypes(route.sdk?.returnType, names);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Transitively pull nested refs from matching definitions (e.g. Pet → Tag)
|
|
110
|
+
// so dependent models are also imported from schemaPackage instead of
|
|
111
|
+
// regenerated locally.
|
|
112
|
+
const definitions = manifest.definitions ?? {};
|
|
113
|
+
const defByName = new Map(Object.entries(definitions).map(([k, v]) => [
|
|
114
|
+
pascalCase(k),
|
|
115
|
+
v,
|
|
116
|
+
]));
|
|
117
|
+
let growing = true;
|
|
118
|
+
while (growing) {
|
|
119
|
+
growing = false;
|
|
120
|
+
for (const name of [...names]) {
|
|
121
|
+
const def = defByName.get(name);
|
|
122
|
+
if (!def)
|
|
123
|
+
continue;
|
|
124
|
+
const before = names.size;
|
|
125
|
+
scanSchemaForExternalRefs(def, names);
|
|
126
|
+
if (names.size > before)
|
|
127
|
+
growing = true;
|
|
108
128
|
}
|
|
109
129
|
}
|
|
110
130
|
return names;
|
|
111
131
|
}
|
|
132
|
+
const BUILT_IN_TYPES = new Set([
|
|
133
|
+
'Array',
|
|
134
|
+
'Record',
|
|
135
|
+
'Promise',
|
|
136
|
+
'AsyncIterable',
|
|
137
|
+
'SSEEvent',
|
|
138
|
+
'Date',
|
|
139
|
+
'ReadonlyArray',
|
|
140
|
+
'Map',
|
|
141
|
+
'Set',
|
|
142
|
+
'Partial',
|
|
143
|
+
'Pick',
|
|
144
|
+
'Omit',
|
|
145
|
+
'Required',
|
|
146
|
+
'Readonly',
|
|
147
|
+
]);
|
|
148
|
+
function collectNamedTypes(type, out) {
|
|
149
|
+
if (!type)
|
|
150
|
+
return;
|
|
151
|
+
const unquoted = type.replace(/(["'`])(?:\\.|(?!\1).)*\1/g, '');
|
|
152
|
+
for (const name of unquoted.match(/\b[A-Z][A-Za-z0-9_]*\b/g) ?? []) {
|
|
153
|
+
if (!BUILT_IN_TYPES.has(name))
|
|
154
|
+
out.add(name);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
112
157
|
function scanSchemaForExternalRefs(schema, out) {
|
|
113
158
|
const s = schema;
|
|
114
|
-
if (typeof s.$ref ===
|
|
115
|
-
|
|
116
|
-
|
|
159
|
+
if (typeof s.$ref === 'string') {
|
|
160
|
+
// Bare refs (`User`) and local definition refs (`#/definitions/User`,
|
|
161
|
+
// `#/$defs/User`) all resolve to a named type. When schemaPackage is set,
|
|
162
|
+
// intake treats those names as external imports instead of regenerating.
|
|
163
|
+
const ref = s.$ref;
|
|
164
|
+
const isLocalDef = ref.startsWith('#/definitions/') || ref.startsWith('#/$defs/');
|
|
165
|
+
if (!ref.startsWith('#') || isLocalDef) {
|
|
166
|
+
out.add(pascalCase(ref.split('/').pop()));
|
|
117
167
|
}
|
|
118
168
|
return;
|
|
119
169
|
}
|
|
@@ -122,12 +172,12 @@ function scanSchemaForExternalRefs(schema, out) {
|
|
|
122
172
|
for (const v of Object.values(props))
|
|
123
173
|
scanSchemaForExternalRefs(v, out);
|
|
124
174
|
}
|
|
125
|
-
if (s.additionalProperties && typeof s.additionalProperties ===
|
|
175
|
+
if (s.additionalProperties && typeof s.additionalProperties === 'object') {
|
|
126
176
|
scanSchemaForExternalRefs(s.additionalProperties, out);
|
|
127
177
|
}
|
|
128
178
|
if (s.items)
|
|
129
179
|
scanSchemaForExternalRefs(s.items, out);
|
|
130
|
-
for (const key of [
|
|
180
|
+
for (const key of ['allOf', 'oneOf', 'anyOf']) {
|
|
131
181
|
const arr = s[key];
|
|
132
182
|
if (Array.isArray(arr)) {
|
|
133
183
|
for (const m of arr)
|
|
@@ -141,17 +191,28 @@ function buildSchemas(definitions) {
|
|
|
141
191
|
}
|
|
142
192
|
function jsonSchemaToDefinition(name, schema) {
|
|
143
193
|
const s = schema;
|
|
144
|
-
if (
|
|
194
|
+
if ('enum' in s && Array.isArray(s.enum)) {
|
|
195
|
+
const values = s.enum;
|
|
196
|
+
if (values.every((value) => typeof value === 'string' || typeof value === 'number')) {
|
|
197
|
+
return {
|
|
198
|
+
name,
|
|
199
|
+
description: s.description,
|
|
200
|
+
isEnum: true,
|
|
201
|
+
enumValues: values,
|
|
202
|
+
properties: [],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
145
205
|
return {
|
|
146
206
|
name,
|
|
147
207
|
description: s.description,
|
|
148
|
-
isEnum:
|
|
149
|
-
|
|
208
|
+
isEnum: false,
|
|
209
|
+
isUnionType: true,
|
|
210
|
+
unionMembers: [...new Set(values.map((value) => JSON.stringify(value)))],
|
|
150
211
|
properties: [],
|
|
151
212
|
isTypeAlias: true,
|
|
152
213
|
};
|
|
153
214
|
}
|
|
154
|
-
if (
|
|
215
|
+
if ('oneOf' in s && Array.isArray(s.oneOf)) {
|
|
155
216
|
return {
|
|
156
217
|
name,
|
|
157
218
|
description: s.description,
|
|
@@ -162,7 +223,7 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
162
223
|
isTypeAlias: true,
|
|
163
224
|
};
|
|
164
225
|
}
|
|
165
|
-
if (
|
|
226
|
+
if ('anyOf' in s && Array.isArray(s.anyOf)) {
|
|
166
227
|
return {
|
|
167
228
|
name,
|
|
168
229
|
description: s.description,
|
|
@@ -173,11 +234,11 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
173
234
|
isTypeAlias: true,
|
|
174
235
|
};
|
|
175
236
|
}
|
|
176
|
-
if (
|
|
237
|
+
if ('allOf' in s && Array.isArray(s.allOf)) {
|
|
177
238
|
const members = s.allOf;
|
|
178
239
|
if (members.length === 2) {
|
|
179
|
-
const refMember = members.find((m) =>
|
|
180
|
-
const objectMember = members.find((m) => !(
|
|
240
|
+
const refMember = members.find((m) => '$ref' in m);
|
|
241
|
+
const objectMember = members.find((m) => !('$ref' in m) && m.type === 'object');
|
|
181
242
|
if (refMember && objectMember) {
|
|
182
243
|
const baseDef = jsonSchemaToDefinition(name, objectMember);
|
|
183
244
|
return { ...baseDef, extends: jsonSchemaToType(refMember) };
|
|
@@ -193,7 +254,7 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
193
254
|
isTypeAlias: true,
|
|
194
255
|
};
|
|
195
256
|
}
|
|
196
|
-
if (s.type ===
|
|
257
|
+
if (s.type === 'object') {
|
|
197
258
|
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
198
259
|
const rawProps = s.properties ?? {};
|
|
199
260
|
const properties = Object.entries(rawProps).map(([propName, propSchema]) => {
|
|
@@ -207,12 +268,16 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
207
268
|
format: ps.format,
|
|
208
269
|
readOnly: ps.readOnly,
|
|
209
270
|
writeOnly: ps.writeOnly,
|
|
271
|
+
deprecated: ps.deprecated,
|
|
272
|
+
defaultValue: ps.default,
|
|
210
273
|
};
|
|
211
274
|
});
|
|
212
275
|
const ap = s.additionalProperties;
|
|
213
|
-
const additionalPropertiesType = ap
|
|
214
|
-
?
|
|
215
|
-
:
|
|
276
|
+
const additionalPropertiesType = ap === true
|
|
277
|
+
? 'unknown'
|
|
278
|
+
: ap && typeof ap === 'object'
|
|
279
|
+
? jsonSchemaToType(ap)
|
|
280
|
+
: undefined;
|
|
216
281
|
return {
|
|
217
282
|
name,
|
|
218
283
|
description: s.description,
|
|
@@ -226,77 +291,102 @@ function jsonSchemaToDefinition(name, schema) {
|
|
|
226
291
|
description: s.description,
|
|
227
292
|
isEnum: false,
|
|
228
293
|
isTypeAlias: true,
|
|
294
|
+
aliasType: jsonSchemaToType(schema),
|
|
229
295
|
properties: [],
|
|
230
296
|
};
|
|
231
297
|
}
|
|
232
298
|
// ── Type string conversion ───────────────────────────────────────────────────
|
|
233
299
|
export function jsonSchemaToType(schema) {
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
300
|
+
const value = schema;
|
|
301
|
+
let type;
|
|
302
|
+
if (typeof value.$ref === 'string') {
|
|
303
|
+
type = pascalCase(value.$ref.split('/').pop());
|
|
237
304
|
}
|
|
238
|
-
if (
|
|
239
|
-
|
|
305
|
+
else if (Array.isArray(value.enum) && value.enum.length > 0) {
|
|
306
|
+
type = [...new Set(value.enum.map((item) => JSON.stringify(item)))].join(' | ');
|
|
240
307
|
}
|
|
241
|
-
if (Array.isArray(
|
|
242
|
-
|
|
243
|
-
if (Array.isArray(s.oneOf)) {
|
|
244
|
-
const members = [...new Set(s.oneOf.map(jsonSchemaToType).filter(Boolean))];
|
|
245
|
-
return members.join(" | ");
|
|
308
|
+
else if (Array.isArray(value.allOf)) {
|
|
309
|
+
type = uniqueTypes(value.allOf).join(' & ');
|
|
246
310
|
}
|
|
247
|
-
if (Array.isArray(
|
|
248
|
-
|
|
249
|
-
return members.join(" | ");
|
|
311
|
+
else if (Array.isArray(value.oneOf)) {
|
|
312
|
+
type = uniqueTypes(value.oneOf).join(' | ');
|
|
250
313
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
314
|
+
else if (Array.isArray(value.anyOf)) {
|
|
315
|
+
type = uniqueTypes(value.anyOf).join(' | ');
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
type = schemaType(value);
|
|
319
|
+
}
|
|
320
|
+
if (value.nullable === true &&
|
|
321
|
+
!type.split('|').some((part) => part.trim() === 'null')) {
|
|
322
|
+
return `${type.includes('|') || type.includes('&') ? `(${type})` : type} | null`;
|
|
323
|
+
}
|
|
324
|
+
return type;
|
|
325
|
+
}
|
|
326
|
+
function uniqueTypes(schemas) {
|
|
327
|
+
return [...new Set(schemas.map(jsonSchemaToType))];
|
|
328
|
+
}
|
|
329
|
+
function schemaType(schema) {
|
|
330
|
+
switch (schema.type) {
|
|
331
|
+
case 'object':
|
|
332
|
+
return objectType(schema);
|
|
333
|
+
case 'array': {
|
|
334
|
+
const items = schema.items;
|
|
335
|
+
return `Array<${items ? jsonSchemaToType(items) : 'unknown'}>`;
|
|
336
|
+
}
|
|
337
|
+
case 'string':
|
|
338
|
+
case 'date':
|
|
339
|
+
return 'string';
|
|
340
|
+
case 'number':
|
|
341
|
+
return 'number';
|
|
342
|
+
case 'integer':
|
|
343
|
+
return 'integer';
|
|
344
|
+
case 'boolean':
|
|
345
|
+
return 'boolean';
|
|
346
|
+
case 'null':
|
|
347
|
+
return 'null';
|
|
348
|
+
case 'never':
|
|
349
|
+
return 'never';
|
|
350
|
+
case 'nullable': {
|
|
351
|
+
const definition = schema.def;
|
|
352
|
+
return definition?.innerType
|
|
353
|
+
? `${jsonSchemaToType(definition.innerType)} | null`
|
|
354
|
+
: 'unknown | null';
|
|
264
355
|
}
|
|
265
|
-
|
|
266
|
-
|
|
356
|
+
case 'union': {
|
|
357
|
+
const options = schema.options;
|
|
358
|
+
return options ? uniqueTypes(options).join(' | ') : 'unknown';
|
|
267
359
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
return "unknown";
|
|
360
|
+
default:
|
|
361
|
+
return 'unknown';
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function objectType(schema) {
|
|
365
|
+
const properties = schema.properties;
|
|
366
|
+
const additionalProperties = schema.additionalProperties;
|
|
367
|
+
if (!properties) {
|
|
368
|
+
return additionalProperties && typeof additionalProperties === 'object'
|
|
369
|
+
? `Record<string, ${jsonSchemaToType(additionalProperties)}>`
|
|
370
|
+
: 'Record<string, unknown>';
|
|
371
|
+
}
|
|
372
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
373
|
+
const entries = Object.entries(properties).map(([name, property]) => `${typePropertyName(name)}${required.has(name) ? '' : '?'}: ${jsonSchemaToType(property)}`);
|
|
374
|
+
if (additionalProperties === true) {
|
|
375
|
+
entries.push('[key: string]: unknown');
|
|
376
|
+
}
|
|
377
|
+
else if (additionalProperties && typeof additionalProperties === 'object') {
|
|
378
|
+
entries.push(`[key: string]: ${jsonSchemaToType(additionalProperties)}`);
|
|
379
|
+
}
|
|
380
|
+
return entries.length
|
|
381
|
+
? `{ ${entries.join('; ')} }`
|
|
382
|
+
: 'Record<string, unknown>';
|
|
383
|
+
}
|
|
384
|
+
function typePropertyName(name) {
|
|
385
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
296
386
|
}
|
|
297
387
|
// ── Group / operation building ───────────────────────────────────────────────
|
|
298
388
|
function isComplexInlineSchema(s) {
|
|
299
|
-
if (s.type ===
|
|
389
|
+
if (s.type === 'object') {
|
|
300
390
|
// An object with only additionalProperties (no explicit `properties`) is
|
|
301
391
|
// representable as an inline `Record<string, V>` type — no need to hoist it
|
|
302
392
|
// into a named inline schema, which would lose the response type entirely.
|
|
@@ -307,7 +397,7 @@ function isComplexInlineSchema(s) {
|
|
|
307
397
|
if (Array.isArray(s.allOf))
|
|
308
398
|
return true;
|
|
309
399
|
// anyOf / oneOf that is just `SomeRef | null` is not complex — emit inline
|
|
310
|
-
for (const key of [
|
|
400
|
+
for (const key of ['anyOf', 'oneOf']) {
|
|
311
401
|
const arr = s[key];
|
|
312
402
|
if (!Array.isArray(arr))
|
|
313
403
|
continue;
|
|
@@ -321,12 +411,12 @@ function isComplexInlineSchema(s) {
|
|
|
321
411
|
function isNullableRefUnion(members) {
|
|
322
412
|
if (members.length !== 2)
|
|
323
413
|
return false;
|
|
324
|
-
const refMember = members.find((m) =>
|
|
325
|
-
const nullMember = members.find((m) => m.type ===
|
|
414
|
+
const refMember = members.find((m) => '$ref' in m);
|
|
415
|
+
const nullMember = members.find((m) => m.type === 'null');
|
|
326
416
|
return !!refMember && !!nullMember;
|
|
327
417
|
}
|
|
328
|
-
function buildOperationGroup(domain
|
|
329
|
-
const domainName = domain.domain ?? domain.prefix;
|
|
418
|
+
function buildOperationGroup(domain) {
|
|
419
|
+
const domainName = domain.domain ?? domain.prefix.split('/').filter(Boolean).pop() ?? 'root';
|
|
330
420
|
const name = camelCase(domainName);
|
|
331
421
|
const fileName = domain.fileName ?? kebabCase(domainName);
|
|
332
422
|
const allInlineSchemas = [];
|
|
@@ -337,12 +427,12 @@ function buildOperationGroup(domain, _schemas) {
|
|
|
337
427
|
const { operation, inlineSchemas } = buildOperation(route, name, domain.prefix);
|
|
338
428
|
operations.push({
|
|
339
429
|
...operation,
|
|
340
|
-
visibility: route.sdk?.internal ?
|
|
430
|
+
visibility: route.sdk?.internal ? 'internal' : 'public',
|
|
341
431
|
});
|
|
342
432
|
for (const s of inlineSchemas) {
|
|
343
433
|
allInlineSchemas.push({
|
|
344
434
|
...s,
|
|
345
|
-
ownership: { kind:
|
|
435
|
+
ownership: { kind: 'generated', domainFile: fileName },
|
|
346
436
|
});
|
|
347
437
|
}
|
|
348
438
|
}
|
|
@@ -350,20 +440,17 @@ function buildOperationGroup(domain, _schemas) {
|
|
|
350
440
|
group: {
|
|
351
441
|
name,
|
|
352
442
|
fileName,
|
|
353
|
-
interfaceName: pascalCase(domainName) +
|
|
354
|
-
factoryName:
|
|
443
|
+
interfaceName: pascalCase(domainName) + 'API',
|
|
444
|
+
factoryName: 'create' + pascalCase(domainName) + 'API',
|
|
355
445
|
description: domain.description,
|
|
356
|
-
visibility: domain.stability ===
|
|
446
|
+
visibility: domain.stability === 'internal' ? 'internal' : 'public',
|
|
357
447
|
operations,
|
|
358
448
|
},
|
|
359
449
|
inlineSchemas: allInlineSchemas,
|
|
360
450
|
};
|
|
361
451
|
}
|
|
362
452
|
function buildOperation(route, groupName, domainPrefix) {
|
|
363
|
-
const
|
|
364
|
-
// When route.url is '/' it means the route URL exactly matched the domain
|
|
365
|
-
// prefix — avoid doubling the slash (e.g. /signal/projects + / = /signal/projects/).
|
|
366
|
-
const fullUrl = route.url === "/" ? prefix || "/" : prefix + route.url;
|
|
453
|
+
const fullUrl = joinRoutePath(route.sdk?.prefix ?? domainPrefix, route.url);
|
|
367
454
|
const httpMethod = (Array.isArray(route.method) ? route.method[0] : route.method).toUpperCase();
|
|
368
455
|
const pathParams = extractPathParams(fullUrl, route.schema?.params);
|
|
369
456
|
const querySchema = route.schema?.querystring ?? route.schema?.query;
|
|
@@ -372,32 +459,26 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
372
459
|
const inlineSchemas = [];
|
|
373
460
|
const name = route.sdk?.methodName ??
|
|
374
461
|
deriveCleanMethodName({
|
|
375
|
-
operationId: route.
|
|
462
|
+
operationId: route.sdk?.operationId ?? route.schema?.operationId,
|
|
376
463
|
summary: route.schema?.summary,
|
|
377
|
-
path: route.url.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,
|
|
464
|
+
path: route.url.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '{$1}'),
|
|
378
465
|
httpMethod: httpMethod.toLowerCase(),
|
|
379
466
|
tagName: groupName,
|
|
380
467
|
});
|
|
381
468
|
// Response type
|
|
382
|
-
|
|
383
|
-
let statusCode = 200;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
if (rs.type === "null" ||
|
|
396
|
-
(rs.type === "object" && !rs.properties && !rs.$ref && !rs.additionalProperties)) {
|
|
397
|
-
responseType = "void";
|
|
398
|
-
}
|
|
399
|
-
else if (typeof rs.$ref !== "string" && isComplexInlineSchema(rs)) {
|
|
400
|
-
const typeName = pascalCase(name) + "Response";
|
|
469
|
+
const successResponse = pickSuccessResponse(route.schema?.response);
|
|
470
|
+
let statusCode = successResponse?.statusCode ?? 200;
|
|
471
|
+
let responseType = route.sdk?.returnType ?? 'void';
|
|
472
|
+
if (!route.sdk?.returnType && successResponse) {
|
|
473
|
+
const successSchema = successResponse.schema;
|
|
474
|
+
const schema = successSchema;
|
|
475
|
+
if (schema.type !== 'null' &&
|
|
476
|
+
!(schema.type === 'object' &&
|
|
477
|
+
!schema.properties &&
|
|
478
|
+
!schema.$ref &&
|
|
479
|
+
!schema.additionalProperties)) {
|
|
480
|
+
if (typeof schema.$ref !== 'string' && isComplexInlineSchema(schema)) {
|
|
481
|
+
const typeName = `${pascalCase(name)}Response`;
|
|
401
482
|
inlineSchemas.push(jsonSchemaToDefinition(typeName, successSchema));
|
|
402
483
|
responseType = typeName;
|
|
403
484
|
}
|
|
@@ -406,35 +487,47 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
406
487
|
}
|
|
407
488
|
}
|
|
408
489
|
}
|
|
409
|
-
// Request body
|
|
490
|
+
// Request body — empty object schemas (no properties / $ref / additionalProperties)
|
|
491
|
+
// are treated as "no body planned", matching void response handling. Domain
|
|
492
|
+
// templates still emit an empty JSON body (`{}`) for body-capable methods.
|
|
410
493
|
let requestBody;
|
|
411
494
|
if (route.schema?.body) {
|
|
412
495
|
const bodySchema = route.schema.body;
|
|
413
496
|
const bs = bodySchema;
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
497
|
+
const isEmptyBodySchema = bs.type === 'null' ||
|
|
498
|
+
(bs.type === 'object' &&
|
|
499
|
+
!bs.properties &&
|
|
500
|
+
!bs.$ref &&
|
|
501
|
+
!bs.additionalProperties &&
|
|
502
|
+
!bs.allOf &&
|
|
503
|
+
!bs.anyOf &&
|
|
504
|
+
!bs.oneOf);
|
|
505
|
+
if (!isEmptyBodySchema) {
|
|
506
|
+
let bodyType;
|
|
507
|
+
if (typeof bs.$ref !== 'string' && isComplexInlineSchema(bs)) {
|
|
508
|
+
const typeName = pascalCase(name) + 'Input';
|
|
509
|
+
inlineSchemas.push(jsonSchemaToDefinition(typeName, bodySchema));
|
|
510
|
+
bodyType = typeName;
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
bodyType = jsonSchemaToType(bodySchema);
|
|
514
|
+
}
|
|
515
|
+
requestBody = {
|
|
516
|
+
type: bodyType,
|
|
517
|
+
required: true,
|
|
518
|
+
contentType: route.sdk?.transport === 'multipart'
|
|
519
|
+
? 'multipart/form-data'
|
|
520
|
+
: route.sdk?.transport === 'binary'
|
|
521
|
+
? 'application/octet-stream'
|
|
522
|
+
: (route.schema?.consumes?.[0] ?? 'application/json'),
|
|
523
|
+
};
|
|
422
524
|
}
|
|
423
|
-
requestBody = {
|
|
424
|
-
type: bodyType,
|
|
425
|
-
required: true,
|
|
426
|
-
contentType: route.sdk?.transport === "multipart"
|
|
427
|
-
? "multipart/form-data"
|
|
428
|
-
: route.sdk?.transport === "binary"
|
|
429
|
-
? "application/octet-stream"
|
|
430
|
-
: "application/json",
|
|
431
|
-
};
|
|
432
525
|
}
|
|
433
526
|
// Query type
|
|
434
527
|
let queryType;
|
|
435
528
|
if (querySchema) {
|
|
436
529
|
const qs = querySchema;
|
|
437
|
-
if (typeof qs.$ref ===
|
|
530
|
+
if (typeof qs.$ref === 'string') {
|
|
438
531
|
queryType = jsonSchemaToType(querySchema);
|
|
439
532
|
}
|
|
440
533
|
else if (queryParams.length > 0) {
|
|
@@ -443,31 +536,37 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
443
536
|
}
|
|
444
537
|
// Required headers from sdk config
|
|
445
538
|
if (route.sdk?.requiredHeaders) {
|
|
446
|
-
for (const
|
|
447
|
-
headerParams.
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
539
|
+
for (const header of route.sdk.requiredHeaders) {
|
|
540
|
+
const existingIndex = headerParams.findIndex(({ originalName }) => originalName.toLowerCase() === header.name.toLowerCase());
|
|
541
|
+
const parameter = {
|
|
542
|
+
name: safeParamName(camelCase(header.paramName)),
|
|
543
|
+
originalName: header.name,
|
|
544
|
+
type: 'string',
|
|
451
545
|
required: true,
|
|
452
|
-
description:
|
|
453
|
-
}
|
|
546
|
+
description: header.description,
|
|
547
|
+
};
|
|
548
|
+
if (existingIndex >= 0)
|
|
549
|
+
headerParams[existingIndex] = parameter;
|
|
550
|
+
else
|
|
551
|
+
headerParams.push(parameter);
|
|
454
552
|
}
|
|
455
553
|
}
|
|
456
554
|
// Header type (inline object type from headerParams)
|
|
457
555
|
let headerType;
|
|
458
556
|
if (headerParams.length > 0) {
|
|
459
557
|
const props = headerParams.map((p) => {
|
|
460
|
-
const opt = p.required ?
|
|
558
|
+
const opt = p.required ? '' : '?';
|
|
461
559
|
return `${p.name}${opt}: ${p.type}`;
|
|
462
560
|
});
|
|
463
|
-
headerType = `{ ${props.join(
|
|
561
|
+
headerType = `{ ${props.join('; ')} }`;
|
|
464
562
|
}
|
|
563
|
+
const isEventStream = route.sse ?? route.sdk?.transport === 'stream';
|
|
465
564
|
return {
|
|
466
565
|
operation: {
|
|
467
566
|
name,
|
|
468
|
-
operationId: route.
|
|
567
|
+
operationId: route.sdk?.operationId ?? route.schema?.operationId,
|
|
469
568
|
summary: route.schema?.summary,
|
|
470
|
-
description: route.
|
|
569
|
+
description: route.sdk?.description ?? route.schema?.description,
|
|
471
570
|
httpMethod,
|
|
472
571
|
path: fastifyPathToIR(fullUrl, pathParams),
|
|
473
572
|
pathParams,
|
|
@@ -481,12 +580,13 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
481
580
|
responseType,
|
|
482
581
|
statusCode,
|
|
483
582
|
deprecated: route.sdk?.deprecated ? true : false,
|
|
484
|
-
deprecationMessage: typeof route.sdk?.deprecated ===
|
|
583
|
+
deprecationMessage: typeof route.sdk?.deprecated === 'string'
|
|
485
584
|
? route.sdk.deprecated
|
|
486
585
|
: undefined,
|
|
487
586
|
timeoutMs: route.sdk?.timeout,
|
|
488
|
-
visibility:
|
|
489
|
-
isEventStream
|
|
587
|
+
visibility: 'public',
|
|
588
|
+
isEventStream,
|
|
589
|
+
eventSchema: isEventStream ? responseType : undefined,
|
|
490
590
|
sseReturnType: route.sdk?.sseReturnType,
|
|
491
591
|
},
|
|
492
592
|
inlineSchemas,
|
|
@@ -494,61 +594,61 @@ function buildOperation(route, groupName, domainPrefix) {
|
|
|
494
594
|
}
|
|
495
595
|
// ── Parameter extraction ─────────────────────────────────────────────────────
|
|
496
596
|
const TS_RESERVED_WORDS = new Set([
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
597
|
+
'break',
|
|
598
|
+
'case',
|
|
599
|
+
'catch',
|
|
600
|
+
'class',
|
|
601
|
+
'const',
|
|
602
|
+
'continue',
|
|
603
|
+
'debugger',
|
|
604
|
+
'default',
|
|
605
|
+
'delete',
|
|
606
|
+
'do',
|
|
607
|
+
'else',
|
|
608
|
+
'enum',
|
|
609
|
+
'export',
|
|
610
|
+
'extends',
|
|
611
|
+
'false',
|
|
612
|
+
'finally',
|
|
613
|
+
'for',
|
|
614
|
+
'function',
|
|
615
|
+
'if',
|
|
616
|
+
'import',
|
|
617
|
+
'in',
|
|
618
|
+
'instanceof',
|
|
619
|
+
'new',
|
|
620
|
+
'null',
|
|
621
|
+
'return',
|
|
622
|
+
'super',
|
|
623
|
+
'switch',
|
|
624
|
+
'this',
|
|
625
|
+
'throw',
|
|
626
|
+
'true',
|
|
627
|
+
'try',
|
|
628
|
+
'typeof',
|
|
629
|
+
'var',
|
|
630
|
+
'void',
|
|
631
|
+
'while',
|
|
632
|
+
'with',
|
|
633
|
+
'as',
|
|
634
|
+
'implements',
|
|
635
|
+
'interface',
|
|
636
|
+
'let',
|
|
637
|
+
'package',
|
|
638
|
+
'private',
|
|
639
|
+
'protected',
|
|
640
|
+
'public',
|
|
641
|
+
'static',
|
|
642
|
+
'yield',
|
|
643
|
+
'type',
|
|
544
644
|
]);
|
|
545
645
|
/** Parameter names reserved by the generated method signatures. */
|
|
546
646
|
const FIXED_PARAM_NAMES = new Set([
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
647
|
+
'input',
|
|
648
|
+
'query',
|
|
649
|
+
'headerOptions',
|
|
650
|
+
'cookies',
|
|
651
|
+
'options',
|
|
552
652
|
]);
|
|
553
653
|
function safeParamName(name) {
|
|
554
654
|
if (TS_RESERVED_WORDS.has(name) || FIXED_PARAM_NAMES.has(name)) {
|
|
@@ -561,7 +661,7 @@ function extractPathParams(url, paramsSchema) {
|
|
|
561
661
|
if (urlParams.length === 0)
|
|
562
662
|
return [];
|
|
563
663
|
const s = paramsSchema;
|
|
564
|
-
const props = typeof s?.properties ===
|
|
664
|
+
const props = typeof s?.properties === 'object' && !Array.isArray(s?.properties)
|
|
565
665
|
? s.properties
|
|
566
666
|
: undefined;
|
|
567
667
|
const required = new Set(Array.isArray(s?.required) ? s.required : []);
|
|
@@ -570,8 +670,8 @@ function extractPathParams(url, paramsSchema) {
|
|
|
570
670
|
return {
|
|
571
671
|
name: safeParamName(camelCase(paramName)),
|
|
572
672
|
originalName: paramName,
|
|
573
|
-
type: propSchema ? jsonSchemaToType(propSchema) :
|
|
574
|
-
required:
|
|
673
|
+
type: propSchema ? jsonSchemaToType(propSchema) : 'string',
|
|
674
|
+
required: true,
|
|
575
675
|
description: propSchema
|
|
576
676
|
? propSchema.description
|
|
577
677
|
: undefined,
|
|
@@ -582,12 +682,12 @@ function extractQueryParams(querySchema) {
|
|
|
582
682
|
if (!querySchema)
|
|
583
683
|
return [];
|
|
584
684
|
const s = querySchema;
|
|
585
|
-
if (s.type !==
|
|
685
|
+
if (s.type !== 'object' || !s.properties)
|
|
586
686
|
return [];
|
|
587
687
|
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
588
688
|
const props = s.properties;
|
|
589
689
|
return Object.entries(props).map(([name, schema]) => ({
|
|
590
|
-
name: camelCase(name),
|
|
690
|
+
name: safeParamName(camelCase(name)),
|
|
591
691
|
originalName: name,
|
|
592
692
|
type: jsonSchemaToType(schema),
|
|
593
693
|
required: required.has(name),
|
|
@@ -596,25 +696,25 @@ function extractQueryParams(querySchema) {
|
|
|
596
696
|
}));
|
|
597
697
|
}
|
|
598
698
|
const SYSTEM_HEADERS = new Set([
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
699
|
+
'content-type',
|
|
700
|
+
'content-length',
|
|
701
|
+
'transfer-encoding',
|
|
702
|
+
'host',
|
|
703
|
+
'accept',
|
|
704
|
+
'accept-encoding',
|
|
605
705
|
]);
|
|
606
706
|
function extractHeaderParams(headersSchema) {
|
|
607
707
|
if (!headersSchema)
|
|
608
708
|
return [];
|
|
609
709
|
const s = headersSchema;
|
|
610
|
-
if (s.type !==
|
|
710
|
+
if (s.type !== 'object' || !s.properties)
|
|
611
711
|
return [];
|
|
612
712
|
const required = new Set(Array.isArray(s.required) ? s.required : []);
|
|
613
713
|
const props = s.properties;
|
|
614
714
|
return Object.entries(props)
|
|
615
715
|
.filter(([name]) => !SYSTEM_HEADERS.has(name.toLowerCase()))
|
|
616
716
|
.map(([name, schema]) => ({
|
|
617
|
-
name: camelCase(name),
|
|
717
|
+
name: safeParamName(camelCase(name)),
|
|
618
718
|
originalName: name,
|
|
619
719
|
type: jsonSchemaToType(schema),
|
|
620
720
|
required: required.has(name),
|
|
@@ -622,27 +722,31 @@ function extractHeaderParams(headersSchema) {
|
|
|
622
722
|
}));
|
|
623
723
|
}
|
|
624
724
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
725
|
+
function joinRoutePath(prefix, routePath) {
|
|
726
|
+
const left = prefix === '/' ? '' : prefix.replace(/\/$/, '');
|
|
727
|
+
const right = routePath === '/' ? '' : `/${routePath.replace(/^\//, '')}`;
|
|
728
|
+
return `${left}${right}` || '/';
|
|
729
|
+
}
|
|
625
730
|
function fastifyPathToIR(url, pathParams) {
|
|
626
731
|
const nameMap = new Map(pathParams.map((p) => [p.originalName, p.name]));
|
|
627
732
|
return url.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, raw) => {
|
|
628
733
|
const name = nameMap.get(raw) ?? raw;
|
|
629
|
-
return
|
|
734
|
+
return '${' + name + '}';
|
|
630
735
|
});
|
|
631
736
|
}
|
|
632
737
|
function pickSuccessResponse(response) {
|
|
633
738
|
if (!response)
|
|
634
739
|
return undefined;
|
|
635
|
-
|
|
636
|
-
[
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
}
|
|
740
|
+
const numeric = Object.entries(response)
|
|
741
|
+
.map(([code, schema]) => ({ statusCode: Number(code), schema }))
|
|
742
|
+
.filter(({ statusCode }) => statusCode >= 200 && statusCode < 300)
|
|
743
|
+
.sort((left, right) => left.statusCode - right.statusCode)[0];
|
|
744
|
+
if (numeric)
|
|
745
|
+
return numeric;
|
|
746
|
+
if (response['2xx'])
|
|
747
|
+
return { schema: response['2xx'], statusCode: 200 };
|
|
748
|
+
if (response.default)
|
|
749
|
+
return { schema: response.default, statusCode: 200 };
|
|
646
750
|
return undefined;
|
|
647
751
|
}
|
|
648
752
|
//# sourceMappingURL=intake.js.map
|