@bagelink/sdk 1.5.17 → 1.5.22
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/bin/index.ts +1 -1
- package/bin/splitClientGen.ts +40 -40
- package/dist/index.cjs +110 -52
- package/dist/index.mjs +110 -52
- package/package.json +1 -1
- package/src/index.ts +9 -9
- package/src/openAPITools/functionGenerator.ts +21 -21
- package/src/openAPITools/index.ts +2 -2
- package/src/openAPITools/typeGenerator.ts +6 -6
- package/src/openAPITools/types/utils.ts +3 -3
- package/src/openAPITools/utils.ts +11 -11
- package/src/utils.ts +1 -1
package/bin/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ export async function loadTypes() {
|
|
|
14
14
|
'import.meta.env.VITE_BAGEL_BASE_URL'
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
-
if (!fs.existsSync(bagelinkDir)) fs.mkdirSync(bagelinkDir)
|
|
17
|
+
if (!fs.existsSync(bagelinkDir)) {fs.mkdirSync(bagelinkDir)}
|
|
18
18
|
|
|
19
19
|
// Generate monolithic files first
|
|
20
20
|
const typesPath = join(bagelinkDir, 'types.d.ts')
|
package/bin/splitClientGen.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { formatAndWriteCode } from './utils'
|
|
|
10
10
|
* @returns The camelCase version of the string
|
|
11
11
|
*/
|
|
12
12
|
function toCamelCase(str: string): string {
|
|
13
|
-
if (!str) return ''
|
|
13
|
+
if (!str) {return ''}
|
|
14
14
|
return str.charAt(0).toLowerCase() + str.slice(1)
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -67,7 +67,7 @@ export function createDefaultGroupingStrategy(groupingPattern = /^([A-Z][a-z]+)/
|
|
|
67
67
|
|
|
68
68
|
getFileName(group: string, kind: 'types' | 'api'): string {
|
|
69
69
|
// Use the utility function for consistent camelCase conversion
|
|
70
|
-
const suffix =
|
|
70
|
+
const suffix = 'types' === kind ? 'Types' : 'Api'
|
|
71
71
|
return `${toCamelCase(group)}${suffix}.ts`
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -108,8 +108,8 @@ export async function splitClientCode(options: SplitClientOptions): Promise<void
|
|
|
108
108
|
const apisDir = useDirectories ? path.join(bagelinkDir, 'api') : bagelinkDir
|
|
109
109
|
|
|
110
110
|
if (useDirectories) {
|
|
111
|
-
if (!fs.existsSync(typesDir)) fs.mkdirSync(typesDir, { recursive: true })
|
|
112
|
-
if (!fs.existsSync(apisDir)) fs.mkdirSync(apisDir, { recursive: true })
|
|
111
|
+
if (!fs.existsSync(typesDir)) {fs.mkdirSync(typesDir, { recursive: true })}
|
|
112
|
+
if (!fs.existsSync(apisDir)) {fs.mkdirSync(apisDir, { recursive: true })}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// Read the generated files
|
|
@@ -152,7 +152,7 @@ export async function splitClientCode(options: SplitClientOptions): Promise<void
|
|
|
152
152
|
// Generate and write files for each group
|
|
153
153
|
for (const [group, info] of Object.entries(groups)) {
|
|
154
154
|
// Write types file
|
|
155
|
-
if (info.types.size
|
|
155
|
+
if (0 < info.types.size) {
|
|
156
156
|
const dependencies = getDependencyImports(info.dependencies, groups, 'types')
|
|
157
157
|
const typesOutput = generateTypesFile(group, info.types, typesContent, dependencies)
|
|
158
158
|
const typesFileName = useDirectories
|
|
@@ -163,7 +163,7 @@ export async function splitClientCode(options: SplitClientOptions): Promise<void
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
// Write API file
|
|
166
|
-
if (info.apis.size
|
|
166
|
+
if (0 < info.apis.size) {
|
|
167
167
|
const dependencies = getDependencyImports(info.dependencies, groups, 'apis')
|
|
168
168
|
const apisOutput = generateApiFile(
|
|
169
169
|
group,
|
|
@@ -186,7 +186,7 @@ export async function splitClientCode(options: SplitClientOptions): Promise<void
|
|
|
186
186
|
if (useDirectories) {
|
|
187
187
|
// Types index
|
|
188
188
|
const typesIndexContent = Object.keys(groups)
|
|
189
|
-
.filter(group => groups[group].types.size
|
|
189
|
+
.filter(group => 0 < groups[group].types.size)
|
|
190
190
|
.map(group => `export * from './${toCamelCase(group)}Types';`)
|
|
191
191
|
.join('\n')
|
|
192
192
|
|
|
@@ -199,7 +199,7 @@ export async function splitClientCode(options: SplitClientOptions): Promise<void
|
|
|
199
199
|
const apiIndexContent
|
|
200
200
|
= `export { axios } from './axios';\n${
|
|
201
201
|
Object.keys(groups)
|
|
202
|
-
.filter(group => groups[group].apis.size
|
|
202
|
+
.filter(group => 0 < groups[group].apis.size)
|
|
203
203
|
.map(group => `export * from './${toCamelCase(group)}Api';`)
|
|
204
204
|
.join('\n')}`
|
|
205
205
|
|
|
@@ -218,10 +218,10 @@ export async function splitClientCode(options: SplitClientOptions): Promise<void
|
|
|
218
218
|
const indexContent = Object.keys(groups)
|
|
219
219
|
.map((group) => {
|
|
220
220
|
const exports = []
|
|
221
|
-
if (groups[group].types.size
|
|
221
|
+
if (0 < groups[group].types.size) {
|
|
222
222
|
exports.push(`export * from './${toCamelCase(group)}Types';`)
|
|
223
223
|
}
|
|
224
|
-
if (groups[group].apis.size
|
|
224
|
+
if (0 < groups[group].apis.size) {
|
|
225
225
|
exports.push(`export * from './${toCamelCase(group)}Api';`)
|
|
226
226
|
}
|
|
227
227
|
return exports.join('\n')
|
|
@@ -256,20 +256,20 @@ function extractCommonCode(apiContent: string): { commonCode: string, axiosInsta
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
// Get imports
|
|
259
|
-
if (node.type
|
|
259
|
+
if ('ImportDeclaration' === node.type) {
|
|
260
260
|
imports.push(apiContent.substring(node.range[0], node.range[1]))
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
// Get axios setup
|
|
264
|
-
if (node.type
|
|
265
|
-
&& node.declaration?.type
|
|
266
|
-
&& node.declaration.declarations.some(d => d.id.type
|
|
264
|
+
if ('ExportNamedDeclaration' === node.type
|
|
265
|
+
&& 'VariableDeclaration' === node.declaration?.type
|
|
266
|
+
&& node.declaration.declarations.some(d => 'Identifier' === d.id.type && 'axios' === d.id.name)) {
|
|
267
267
|
axiosDefinition.push(apiContent.substring(node.range[0], node.range[1]))
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
// Get utility interfaces like UploadOptions
|
|
271
|
-
if (node.type
|
|
272
|
-
&& node.declaration?.type
|
|
271
|
+
if ('ExportNamedDeclaration' === node.type
|
|
272
|
+
&& 'TSInterfaceDeclaration' === node.declaration?.type) {
|
|
273
273
|
utilityTypes.push(apiContent.substring(node.range[0], node.range[1]))
|
|
274
274
|
}
|
|
275
275
|
}
|
|
@@ -296,11 +296,11 @@ function organizeTypes(typesContent: string, groupingStrategy: GroupingStrategy)
|
|
|
296
296
|
|
|
297
297
|
for (const node of ast.body) {
|
|
298
298
|
// Skip nodes without range info
|
|
299
|
-
if (!node.range) continue
|
|
299
|
+
if (!node.range) {continue}
|
|
300
300
|
|
|
301
|
-
if (node.type
|
|
302
|
-
&& (node.declaration?.type
|
|
303
|
-
|| node.declaration?.type
|
|
301
|
+
if ('ExportNamedDeclaration' === node.type
|
|
302
|
+
&& ('TSTypeAliasDeclaration' === node.declaration?.type
|
|
303
|
+
|| 'TSInterfaceDeclaration' === node.declaration?.type)) {
|
|
304
304
|
const typeName = node.declaration.id.name
|
|
305
305
|
const group = groupingStrategy.getGroupName(typeName)
|
|
306
306
|
|
|
@@ -334,12 +334,12 @@ function organizeApiFunctions(apiContent: string, groupingStrategy: GroupingStra
|
|
|
334
334
|
|
|
335
335
|
// First pass: identify top-level exports
|
|
336
336
|
for (const node of ast.body) {
|
|
337
|
-
if (!node.range) continue
|
|
337
|
+
if (!node.range) {continue}
|
|
338
338
|
|
|
339
339
|
// Handle direct function exports
|
|
340
|
-
if (node.type
|
|
340
|
+
if ('ExportNamedDeclaration' === node.type && 'VariableDeclaration' === node.declaration?.type) {
|
|
341
341
|
for (const declarator of node.declaration.declarations) {
|
|
342
|
-
if (declarator.id.type
|
|
342
|
+
if ('Identifier' === declarator.id.type) {
|
|
343
343
|
const functionName = declarator.id.name
|
|
344
344
|
|
|
345
345
|
// Skip utility exports
|
|
@@ -348,7 +348,7 @@ function organizeApiFunctions(apiContent: string, groupingStrategy: GroupingStra
|
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
// Check if this is a nested API object (like `auth: {...}` or `dataExports: {...}`)
|
|
351
|
-
if (declarator.init?.type
|
|
351
|
+
if ('ObjectExpression' === declarator.init?.type) {
|
|
352
352
|
// For object groups, use the object name as the group name directly
|
|
353
353
|
// rather than applying the grouping strategy
|
|
354
354
|
const groupName = functionName
|
|
@@ -523,11 +523,11 @@ function findTypeDependencies(node: ProgramStatement, _typesContent: string): st
|
|
|
523
523
|
|
|
524
524
|
// Type guard functions for type safety
|
|
525
525
|
function isTypeReference(node: unknown): node is TSESTree.TSTypeReference {
|
|
526
|
-
return
|
|
526
|
+
return null !== node && 'object' === typeof node && 'type' in node && node.type === AST_NODE_TYPES.TSTypeReference
|
|
527
527
|
}
|
|
528
528
|
|
|
529
529
|
function isIdentifier(node: unknown): node is TSESTree.Identifier {
|
|
530
|
-
return
|
|
530
|
+
return null !== node && 'object' === typeof node && 'type' in node && node.type === AST_NODE_TYPES.Identifier
|
|
531
531
|
}
|
|
532
532
|
|
|
533
533
|
// Simple approach - extract all identifiers from type references
|
|
@@ -540,7 +540,7 @@ function findTypeDependencies(node: ProgramStatement, _typesContent: string): st
|
|
|
540
540
|
}
|
|
541
541
|
|
|
542
542
|
// Recursively visit child nodes
|
|
543
|
-
if (node && typeof node
|
|
543
|
+
if (node && 'object' === typeof node) {
|
|
544
544
|
// Handle arrays
|
|
545
545
|
if (Array.isArray(node)) {
|
|
546
546
|
node.forEach((item) => { visit(item) })
|
|
@@ -552,7 +552,7 @@ function findTypeDependencies(node: ProgramStatement, _typesContent: string): st
|
|
|
552
552
|
|
|
553
553
|
// Visit each property
|
|
554
554
|
Object.entries(node).forEach(([key, value]) => {
|
|
555
|
-
if (!skipProps.includes(key) && value && typeof value
|
|
555
|
+
if (!skipProps.includes(key) && value && 'object' === typeof value) {
|
|
556
556
|
visit(value)
|
|
557
557
|
}
|
|
558
558
|
})
|
|
@@ -610,11 +610,11 @@ function generateTypesFile(
|
|
|
610
610
|
const declarations: string[] = []
|
|
611
611
|
for (const node of ast.body) {
|
|
612
612
|
// Skip nodes without range info
|
|
613
|
-
if (!node.range) continue
|
|
613
|
+
if (!node.range) {continue}
|
|
614
614
|
|
|
615
|
-
if (node.type
|
|
616
|
-
&& (node.declaration?.type
|
|
617
|
-
|| node.declaration?.type
|
|
615
|
+
if ('ExportNamedDeclaration' === node.type
|
|
616
|
+
&& ('TSTypeAliasDeclaration' === node.declaration?.type
|
|
617
|
+
|| 'TSInterfaceDeclaration' === node.declaration?.type)) {
|
|
618
618
|
const typeName = node.declaration.id.name
|
|
619
619
|
if (typeNames.has(typeName)) {
|
|
620
620
|
declarations.push(typesContent.substring(node.range[0], node.range[1]))
|
|
@@ -658,16 +658,16 @@ function generateApiFile(
|
|
|
658
658
|
if (useDirectories) {
|
|
659
659
|
Object.entries(dependencies).forEach(([depGroup, types]) => {
|
|
660
660
|
// Filter out AxiosResponse as we're importing it directly
|
|
661
|
-
const filteredTypes = types.filter(t =>
|
|
662
|
-
if (filteredTypes.length
|
|
661
|
+
const filteredTypes = types.filter(t => 'AxiosResponse' !== t)
|
|
662
|
+
if (0 < filteredTypes.length) {
|
|
663
663
|
imports.push(`import type { ${filteredTypes.sort().join(', ')} } from '../types/${toCamelCase(depGroup)}Types';`)
|
|
664
664
|
}
|
|
665
665
|
})
|
|
666
666
|
} else {
|
|
667
667
|
Object.entries(dependencies).forEach(([depGroup, types]) => {
|
|
668
668
|
// Filter out AxiosResponse as we're importing it directly
|
|
669
|
-
const filteredTypes = types.filter(t =>
|
|
670
|
-
if (filteredTypes.length
|
|
669
|
+
const filteredTypes = types.filter(t => 'AxiosResponse' !== t)
|
|
670
|
+
if (0 < filteredTypes.length) {
|
|
671
671
|
imports.push(`import type { ${filteredTypes.sort().join(', ')} } from './${toCamelCase(depGroup)}Types';`)
|
|
672
672
|
}
|
|
673
673
|
})
|
|
@@ -677,12 +677,12 @@ function generateApiFile(
|
|
|
677
677
|
const declarations: string[] = []
|
|
678
678
|
|
|
679
679
|
for (const node of ast.body) {
|
|
680
|
-
if (!node.range) continue
|
|
680
|
+
if (!node.range) {continue}
|
|
681
681
|
|
|
682
682
|
// Handle export declarations
|
|
683
|
-
if (node.type
|
|
683
|
+
if ('ExportNamedDeclaration' === node.type && 'VariableDeclaration' === node.declaration?.type) {
|
|
684
684
|
for (const declarator of node.declaration.declarations) {
|
|
685
|
-
if (declarator.id.type
|
|
685
|
+
if ('Identifier' === declarator.id.type) {
|
|
686
686
|
const functionName = declarator.id.name
|
|
687
687
|
|
|
688
688
|
// Check if this function should be included
|
|
@@ -698,7 +698,7 @@ function generateApiFile(
|
|
|
698
698
|
&& !comments.some(c => comment.range[1] < c.range[1] && c.range[1] <= node.range[0])
|
|
699
699
|
)
|
|
700
700
|
|
|
701
|
-
if (nodeLeadingComments.length
|
|
701
|
+
if (0 < nodeLeadingComments.length) {
|
|
702
702
|
startPos = nodeLeadingComments[0].range[0]
|
|
703
703
|
}
|
|
704
704
|
|
package/dist/index.cjs
CHANGED
|
@@ -10,11 +10,13 @@ function getPath(pathsObject, path) {
|
|
|
10
10
|
return pathsObject ? pathsObject[path] : void 0;
|
|
11
11
|
}
|
|
12
12
|
function isReferenceObject(obj) {
|
|
13
|
-
if (!obj)
|
|
14
|
-
|
|
13
|
+
if (!obj) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return Object.hasOwn(obj, "$ref");
|
|
15
17
|
}
|
|
16
18
|
function isSchemaObject(schema) {
|
|
17
|
-
return !Object.
|
|
19
|
+
return !Object.hasOwn(schema, "$ref");
|
|
18
20
|
}
|
|
19
21
|
function dereference(property) {
|
|
20
22
|
return property;
|
|
@@ -32,7 +34,9 @@ function formatType(typeName) {
|
|
|
32
34
|
}
|
|
33
35
|
function resolveReference(ref) {
|
|
34
36
|
const t = ref.split("/").pop();
|
|
35
|
-
if (!t)
|
|
37
|
+
if (!t) {
|
|
38
|
+
return "any";
|
|
39
|
+
}
|
|
36
40
|
return formatType(t);
|
|
37
41
|
}
|
|
38
42
|
function handleArrayItems(schema) {
|
|
@@ -51,21 +55,27 @@ function handleArrayItems(schema) {
|
|
|
51
55
|
return res;
|
|
52
56
|
}
|
|
53
57
|
function schemaToType(schema) {
|
|
54
|
-
if (!schema)
|
|
58
|
+
if (!schema) {
|
|
59
|
+
return "any";
|
|
60
|
+
}
|
|
55
61
|
if (isReferenceObject(schema)) {
|
|
56
|
-
if (schema.$ref)
|
|
62
|
+
if (schema.$ref) {
|
|
63
|
+
return resolveReference(schema.$ref);
|
|
64
|
+
}
|
|
57
65
|
}
|
|
58
66
|
if (!isSchemaObject(schema)) {
|
|
59
67
|
console.warn("Schema is not a SchemaObject:", schema);
|
|
60
68
|
return "any";
|
|
61
69
|
}
|
|
62
70
|
if (schema.anyOf) {
|
|
63
|
-
let _t = schema.anyOf.map((s) => schemaToType(s)).filter((p) =>
|
|
64
|
-
if (
|
|
71
|
+
let _t = schema.anyOf.map((s) => schemaToType(s)).filter((p) => "any" !== p).join(" | ");
|
|
72
|
+
if ("" === _t || "null" === _t) {
|
|
73
|
+
_t = "any";
|
|
74
|
+
}
|
|
65
75
|
return _t;
|
|
66
76
|
}
|
|
67
77
|
if (schema.allOf) {
|
|
68
|
-
return schema.allOf.map((s) => schemaToType(s)).filter((p) =>
|
|
78
|
+
return schema.allOf.map((s) => schemaToType(s)).filter((p) => "any" !== p).join(" & ");
|
|
69
79
|
}
|
|
70
80
|
switch (schema.type) {
|
|
71
81
|
case "object":
|
|
@@ -102,7 +112,7 @@ function isOptional(schema) {
|
|
|
102
112
|
return includesNull || includesUndefined || schema?.default !== void 0;
|
|
103
113
|
}
|
|
104
114
|
function cleanOptionals(str) {
|
|
105
|
-
return str.split(" | ").filter((t) =>
|
|
115
|
+
return str.split(" | ").filter((t) => "null" !== t && "undefined" !== t).join(" | ");
|
|
106
116
|
}
|
|
107
117
|
function formatVarType({
|
|
108
118
|
varName,
|
|
@@ -114,16 +124,18 @@ function formatVarType({
|
|
|
114
124
|
type = cleanOptionals(type);
|
|
115
125
|
let defaultStr = "";
|
|
116
126
|
if (defaultValue) {
|
|
117
|
-
if (typeof defaultValue
|
|
127
|
+
if ("string" === typeof defaultValue) {
|
|
118
128
|
defaultStr = ` = '${defaultValue}'`;
|
|
119
|
-
} else if (typeof defaultValue
|
|
129
|
+
} else if ("object" === typeof defaultValue) {
|
|
120
130
|
defaultStr = ` = ${JSON.stringify(defaultValue)}`;
|
|
121
131
|
} else {
|
|
122
132
|
defaultStr = ` = ${defaultValue}`;
|
|
123
133
|
}
|
|
124
134
|
}
|
|
125
135
|
let optionalStr = !required && isOptional(schema) ? "?" : "";
|
|
126
|
-
if (defaultStr)
|
|
136
|
+
if (defaultStr) {
|
|
137
|
+
optionalStr = "";
|
|
138
|
+
}
|
|
127
139
|
return `${varName}${optionalStr}: ${type}${defaultStr}`;
|
|
128
140
|
}
|
|
129
141
|
const RESERVED_KEYWORDS = /* @__PURE__ */ new Set([
|
|
@@ -236,7 +248,9 @@ const RESERVED_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
236
248
|
"Reflect"
|
|
237
249
|
]);
|
|
238
250
|
function sanitizeVarName(varName) {
|
|
239
|
-
if (!varName)
|
|
251
|
+
if (!varName) {
|
|
252
|
+
return varName;
|
|
253
|
+
}
|
|
240
254
|
if (RESERVED_KEYWORDS.has(varName.toLowerCase())) {
|
|
241
255
|
return `${varName}_`;
|
|
242
256
|
}
|
|
@@ -273,8 +287,12 @@ function collectTypeForImportStatement(typeName) {
|
|
|
273
287
|
}
|
|
274
288
|
const isPrimitive = primitiveTypes.includes(typeName);
|
|
275
289
|
typeName = formatType(typeName);
|
|
276
|
-
if (!typeName || isPrimitive)
|
|
277
|
-
|
|
290
|
+
if (!typeName || isPrimitive) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (!allTypes.includes(typeName)) {
|
|
294
|
+
allTypes.push(typeName);
|
|
295
|
+
}
|
|
278
296
|
}
|
|
279
297
|
function schemaToTypeWithCollection(schema) {
|
|
280
298
|
const type = schemaToType(schema);
|
|
@@ -286,16 +304,20 @@ function getResponseType(response) {
|
|
|
286
304
|
return void 0;
|
|
287
305
|
}
|
|
288
306
|
const mediaTypeObject = response.content?.["application/json"];
|
|
289
|
-
if (!mediaTypeObject?.schema)
|
|
307
|
+
if (!mediaTypeObject?.schema) {
|
|
308
|
+
return void 0;
|
|
309
|
+
}
|
|
290
310
|
return schemaToTypeWithCollection(mediaTypeObject.schema);
|
|
291
311
|
}
|
|
292
312
|
function generateResponseType(responses) {
|
|
293
|
-
if (!responses)
|
|
313
|
+
if (!responses) {
|
|
314
|
+
return "";
|
|
315
|
+
}
|
|
294
316
|
const types = [];
|
|
295
317
|
for (const [statusCode, response] of Object.entries(responses)) {
|
|
296
318
|
if (statusCode.startsWith("2")) {
|
|
297
319
|
const responseType = getResponseType(response);
|
|
298
|
-
if (responseType &&
|
|
320
|
+
if (responseType && "any" !== responseType) {
|
|
299
321
|
types.push(responseType);
|
|
300
322
|
}
|
|
301
323
|
}
|
|
@@ -308,12 +330,14 @@ function getParamsFromPath(path) {
|
|
|
308
330
|
}
|
|
309
331
|
function formatPathWithParams(path) {
|
|
310
332
|
const params = getParamsFromPath(path);
|
|
311
|
-
if (!params)
|
|
333
|
+
if (!params) {
|
|
334
|
+
return `'${path}'`;
|
|
335
|
+
}
|
|
312
336
|
return `\`${path.replace(/\{([^}]+)\}/g, (_, paramName) => `\${${toCamelCase(paramName)}}`)}\``;
|
|
313
337
|
}
|
|
314
338
|
function generateRequestBody(requestBody) {
|
|
315
339
|
const content = dereference(requestBody)?.content;
|
|
316
|
-
if (!content || Object.keys(content).length
|
|
340
|
+
if (!content || 0 === Object.keys(content).length) {
|
|
317
341
|
return { requestBodyParam: "", requestBodyPayload: "" };
|
|
318
342
|
}
|
|
319
343
|
if (content["multipart/form-data"]) {
|
|
@@ -345,7 +369,9 @@ function generateRequestBody(requestBody) {
|
|
|
345
369
|
return { requestBodyParam, requestBodyPayload };
|
|
346
370
|
}
|
|
347
371
|
function generateFunctionParameters(params, isFileUpload = false) {
|
|
348
|
-
if (!params?.length)
|
|
372
|
+
if (!params?.length) {
|
|
373
|
+
return {};
|
|
374
|
+
}
|
|
349
375
|
if (isFileUpload) {
|
|
350
376
|
return {
|
|
351
377
|
config: {
|
|
@@ -364,7 +390,7 @@ function generateFunctionParameters(params, isFileUpload = false) {
|
|
|
364
390
|
collectTypeForImportStatement(paramType);
|
|
365
391
|
const paramName = param.name;
|
|
366
392
|
const varName = toCamelCaseSafe(param.name);
|
|
367
|
-
if (
|
|
393
|
+
if ("path" === param.in || "query" === param.in || "header" === param.in) {
|
|
368
394
|
functionParams.push(
|
|
369
395
|
formatVarType({
|
|
370
396
|
varName,
|
|
@@ -374,7 +400,7 @@ function generateFunctionParameters(params, isFileUpload = false) {
|
|
|
374
400
|
})
|
|
375
401
|
);
|
|
376
402
|
}
|
|
377
|
-
if (
|
|
403
|
+
if ("query" === param.in || "header" === param.in) {
|
|
378
404
|
queryParams.push(
|
|
379
405
|
paramName === varName ? paramName : `'${paramName}': ${varName}`
|
|
380
406
|
);
|
|
@@ -392,7 +418,7 @@ function parseParameter(paramStr) {
|
|
|
392
418
|
const name = nameMatch ? nameMatch[1] : "";
|
|
393
419
|
const typeMatch = trimmed.match(/:\s*([^=]+)/);
|
|
394
420
|
const type = typeMatch ? typeMatch[1].trim() : "any";
|
|
395
|
-
const defaultMatch = trimmed.match(
|
|
421
|
+
const defaultMatch = trimmed.match(/[=](.+)$/);
|
|
396
422
|
const defaultValue = defaultMatch ? defaultMatch[1].trim() : void 0;
|
|
397
423
|
return { name, type, defaultValue, isOptional };
|
|
398
424
|
}
|
|
@@ -404,7 +430,7 @@ function combineAllParams(parameters, requestBodyParam) {
|
|
|
404
430
|
if (requestBodyParam) {
|
|
405
431
|
allParamsArray.push(requestBodyParam.trim());
|
|
406
432
|
}
|
|
407
|
-
if (allParamsArray.length
|
|
433
|
+
if (0 === allParamsArray.length) {
|
|
408
434
|
return "";
|
|
409
435
|
}
|
|
410
436
|
const parsedParams = allParamsArray.filter(Boolean).map(parseParameter);
|
|
@@ -415,10 +441,12 @@ function combineAllParams(parameters, requestBodyParam) {
|
|
|
415
441
|
return `{ ${destructuredNames} }: { ${typeDefinition} } = {}`;
|
|
416
442
|
}
|
|
417
443
|
function generateAxiosFunction(method, formattedPath, allParams, responseTypeStr, parameters, requestBodyPayload) {
|
|
418
|
-
if (
|
|
444
|
+
if ("undefined" === allParams) {
|
|
445
|
+
allParams = "";
|
|
446
|
+
}
|
|
419
447
|
let axiosFunction = `async (${allParams})${responseTypeStr} => {`;
|
|
420
448
|
const paramStr = parameters.config?.params ? `params: {${parameters.config.params}}` : "";
|
|
421
|
-
if (
|
|
449
|
+
if ("formData" === requestBodyPayload) {
|
|
422
450
|
if (allParams.includes("file: File")) {
|
|
423
451
|
axiosFunction += `
|
|
424
452
|
const formData = new FormData()
|
|
@@ -448,8 +476,10 @@ function buildJSDocComment(operation, method, path) {
|
|
|
448
476
|
`;
|
|
449
477
|
}
|
|
450
478
|
if (operation.description) {
|
|
451
|
-
if (operation.summary)
|
|
479
|
+
if (operation.summary) {
|
|
480
|
+
functionComment += ` *
|
|
452
481
|
`;
|
|
482
|
+
}
|
|
453
483
|
const descriptionLines = operation.description.split("\n");
|
|
454
484
|
descriptionLines.forEach((line) => {
|
|
455
485
|
functionComment += ` * ${line}
|
|
@@ -463,7 +493,9 @@ function buildJSDocComment(operation, method, path) {
|
|
|
463
493
|
return functionComment;
|
|
464
494
|
}
|
|
465
495
|
function generateFunctionForOperation(method, path, operation) {
|
|
466
|
-
if (!operation)
|
|
496
|
+
if (!operation) {
|
|
497
|
+
return "";
|
|
498
|
+
}
|
|
467
499
|
const multiPartSchema = dereference(operation.requestBody)?.content?.["multipart/form-data"]?.schema;
|
|
468
500
|
const isFileUpload = isReferenceObject(multiPartSchema) && multiPartSchema?.$ref?.includes("Body_upload_files");
|
|
469
501
|
const parameters = generateFunctionParameters(
|
|
@@ -492,7 +524,7 @@ function hasConflict(path, method) {
|
|
|
492
524
|
(p) => p.path === cleanPathName && p.method === method
|
|
493
525
|
);
|
|
494
526
|
pathOperations.push({ path: cleanPathName, method });
|
|
495
|
-
return matchingPaths.length
|
|
527
|
+
return 0 < matchingPaths.length;
|
|
496
528
|
}
|
|
497
529
|
function generateRandomString() {
|
|
498
530
|
return Math.random().toString(36).slice(7);
|
|
@@ -534,15 +566,17 @@ function generateFunctions(paths, baseUrl) {
|
|
|
534
566
|
const splitPath = path.split("/").filter((p) => p && !/\{|\}/.test(p));
|
|
535
567
|
splitPath.reduce((acc, key, index, array) => {
|
|
536
568
|
const objFuncKey = toCamelCaseSafe(key);
|
|
537
|
-
if (!objFuncKey)
|
|
569
|
+
if (!objFuncKey) {
|
|
570
|
+
return acc;
|
|
571
|
+
}
|
|
538
572
|
const methods = Object.keys(operation);
|
|
539
|
-
if (index === array.length - 1 && methods.length
|
|
573
|
+
if (index === array.length - 1 && 1 === methods.length && 1 === allPathsClean.filter((p) => p === cleanPath(path)).length) {
|
|
540
574
|
const method = methods[0];
|
|
541
575
|
const opp = operation[method];
|
|
542
576
|
acc[objFuncKey] = createFunctionPlaceholder(path, method, opp);
|
|
543
577
|
} else if (index === array.length - 1) {
|
|
544
578
|
acc[objFuncKey] = handlePathSegment(path, operation, acc[objFuncKey]);
|
|
545
|
-
} else if (!acc[objFuncKey] || typeof acc[objFuncKey]
|
|
579
|
+
} else if (!acc[objFuncKey] || "object" !== typeof acc[objFuncKey]) {
|
|
546
580
|
acc[objFuncKey] = {};
|
|
547
581
|
}
|
|
548
582
|
return acc[objFuncKey];
|
|
@@ -591,27 +625,31 @@ ${tsString}`;
|
|
|
591
625
|
}
|
|
592
626
|
|
|
593
627
|
function generateTypes(schemas) {
|
|
594
|
-
if (!schemas)
|
|
628
|
+
if (!schemas) {
|
|
629
|
+
return "";
|
|
630
|
+
}
|
|
595
631
|
const schemaEntries = Object.entries(schemas);
|
|
596
632
|
return schemaEntries.map(([_typeName, schema]) => {
|
|
597
633
|
const typeName = formatType(_typeName);
|
|
598
|
-
if (!isSchemaObject(schema))
|
|
634
|
+
if (!isSchemaObject(schema)) {
|
|
635
|
+
return "";
|
|
636
|
+
}
|
|
599
637
|
if (schema?.enum) {
|
|
600
638
|
return `export type ${typeName} = ${schema.enum.map((item) => `'${item}'`).join(" | ")};
|
|
601
639
|
`;
|
|
602
640
|
}
|
|
603
|
-
if (schema?.type
|
|
641
|
+
if ("array" === schema?.type && schema.items) {
|
|
604
642
|
return `export type ${typeName} = (${schemaToType(schema.items)})[];
|
|
605
643
|
`;
|
|
606
644
|
}
|
|
607
645
|
if (!schema.properties) {
|
|
608
|
-
if (schema.additionalProperties
|
|
646
|
+
if (true === schema.additionalProperties) {
|
|
609
647
|
return `export type ${typeName} = { [key: string]: any };
|
|
610
648
|
`;
|
|
611
649
|
}
|
|
612
650
|
return "";
|
|
613
651
|
}
|
|
614
|
-
if (Object.keys(schema.properties).length
|
|
652
|
+
if (0 === Object.keys(schema.properties).length && true === schema.additionalProperties) {
|
|
615
653
|
return `export type ${typeName} = { [key: string]: any };
|
|
616
654
|
`;
|
|
617
655
|
}
|
|
@@ -619,7 +657,7 @@ function generateTypes(schemas) {
|
|
|
619
657
|
const varType = formatVarType({ varName: key, schema: value });
|
|
620
658
|
return ` ${varType}`;
|
|
621
659
|
}).join(";\n ");
|
|
622
|
-
const indexSignature = schema.additionalProperties
|
|
660
|
+
const indexSignature = true === schema.additionalProperties ? "\n [key: string]: any;" : "";
|
|
623
661
|
return `export type ${typeName} = {
|
|
624
662
|
${properties};${indexSignature}
|
|
625
663
|
};
|
|
@@ -632,10 +670,14 @@ const index = async (openApiUrl, baseUrl) => {
|
|
|
632
670
|
try {
|
|
633
671
|
const { data: openApi } = await axios__default.get(openApiUrl, { headers: basicAuthHeader });
|
|
634
672
|
const schemas = openApi.components?.schemas;
|
|
635
|
-
if (!schemas)
|
|
673
|
+
if (!schemas) {
|
|
674
|
+
throw new Error("No schemas found in OpenAPI document");
|
|
675
|
+
}
|
|
636
676
|
const types = generateTypes(schemas);
|
|
637
677
|
const { paths } = openApi;
|
|
638
|
-
if (!paths)
|
|
678
|
+
if (!paths) {
|
|
679
|
+
throw new Error("No paths found in OpenAPI document");
|
|
680
|
+
}
|
|
639
681
|
const code = generateFunctions(paths, baseUrl);
|
|
640
682
|
return { types, code };
|
|
641
683
|
} catch (error) {
|
|
@@ -669,7 +711,7 @@ function formatAPIErrorMessage(err) {
|
|
|
669
711
|
case 500:
|
|
670
712
|
return "An internal server error occurred. Please try again later.";
|
|
671
713
|
}
|
|
672
|
-
if (data.detail && typeof data.detail
|
|
714
|
+
if (data.detail && "string" === typeof data.detail) {
|
|
673
715
|
return data.detail;
|
|
674
716
|
}
|
|
675
717
|
if (data.message) {
|
|
@@ -692,7 +734,9 @@ class DataRequest {
|
|
|
692
734
|
this.itemID = "";
|
|
693
735
|
}
|
|
694
736
|
async post(item) {
|
|
695
|
-
if (!this.data_table)
|
|
737
|
+
if (!this.data_table) {
|
|
738
|
+
throw new Error("Data table not set");
|
|
739
|
+
}
|
|
696
740
|
const { data } = await axios.post(`/data/${this.data_table}`, item);
|
|
697
741
|
return data;
|
|
698
742
|
}
|
|
@@ -701,8 +745,10 @@ class DataRequest {
|
|
|
701
745
|
return this;
|
|
702
746
|
}
|
|
703
747
|
async get() {
|
|
704
|
-
if (!this.data_table)
|
|
705
|
-
|
|
748
|
+
if (!this.data_table) {
|
|
749
|
+
throw new Error("Data table not set");
|
|
750
|
+
}
|
|
751
|
+
const filterStr = 0 < Object.keys(this._filter).length ? `?filter={${Object.entries(this._filter).map(([k, v]) => `${k}:${v}`).join(",")}}` : "";
|
|
706
752
|
const url = `/data/${this.data_table}${this.itemID ? `/${this.itemID}` : ""}${filterStr}`;
|
|
707
753
|
try {
|
|
708
754
|
const { data } = await axios.get(url);
|
|
@@ -718,7 +764,9 @@ class DataRequest {
|
|
|
718
764
|
return this;
|
|
719
765
|
}
|
|
720
766
|
async delete() {
|
|
721
|
-
if (!this.data_table)
|
|
767
|
+
if (!this.data_table) {
|
|
768
|
+
throw new Error("Data table not set");
|
|
769
|
+
}
|
|
722
770
|
const { data } = await axios.delete(
|
|
723
771
|
`/data/${this.data_table}/${this.itemID}`
|
|
724
772
|
);
|
|
@@ -726,8 +774,12 @@ class DataRequest {
|
|
|
726
774
|
}
|
|
727
775
|
async put(updatedItem) {
|
|
728
776
|
const { data_table, itemID } = this;
|
|
729
|
-
if (!data_table)
|
|
730
|
-
|
|
777
|
+
if (!data_table) {
|
|
778
|
+
throw new Error("Data table not set");
|
|
779
|
+
}
|
|
780
|
+
if (!itemID) {
|
|
781
|
+
throw new Error("Item ID not set");
|
|
782
|
+
}
|
|
731
783
|
const { data } = await axios.put(
|
|
732
784
|
`/data/${data_table}/${itemID}`,
|
|
733
785
|
updatedItem
|
|
@@ -845,10 +897,14 @@ class Bagel {
|
|
|
845
897
|
async get(endpoint, query) {
|
|
846
898
|
this._setAuthorization();
|
|
847
899
|
endpoint = this._endpointCleaner(endpoint);
|
|
848
|
-
if (/undefined|null/.test(endpoint))
|
|
900
|
+
if (/undefined|null/.test(endpoint)) {
|
|
901
|
+
throw new Error(`Invalid endpoint: ${endpoint}`);
|
|
902
|
+
}
|
|
849
903
|
if (query) {
|
|
850
904
|
const queryParams = Object.entries(query).filter(([_, value]) => !!value).map(([key, value]) => `${key}=${value}`).join("&");
|
|
851
|
-
if (queryParams)
|
|
905
|
+
if (queryParams) {
|
|
906
|
+
endpoint = `${endpoint}?${queryParams}`;
|
|
907
|
+
}
|
|
852
908
|
}
|
|
853
909
|
const url = `/${endpoint}`;
|
|
854
910
|
return axios.get(url).then(({ data }) => data).catch((err) => {
|
|
@@ -898,7 +954,9 @@ class Bagel {
|
|
|
898
954
|
const formData = new FormData();
|
|
899
955
|
formData.append("file", file);
|
|
900
956
|
let url = "/static_files/upload";
|
|
901
|
-
if (options?.topic)
|
|
957
|
+
if (options?.topic) {
|
|
958
|
+
url = `/static_files/upload?topic=${options.topic}`;
|
|
959
|
+
}
|
|
902
960
|
const { data } = await axios.post(url, formData, {
|
|
903
961
|
headers: {
|
|
904
962
|
"Content-Type": "multipart/form-data"
|