@sdk-it/typescript 0.28.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +254 -273
- package/dist/index.js.map +4 -4
- package/dist/lib/agent/ai-sdk.d.ts +3 -0
- package/dist/lib/agent/ai-sdk.d.ts.map +1 -0
- package/dist/lib/agent/openai-agents.d.ts +3 -0
- package/dist/lib/agent/openai-agents.d.ts.map +1 -0
- package/dist/lib/emitters/snippet.d.ts +2 -2
- package/dist/lib/emitters/snippet.d.ts.map +1 -1
- package/dist/lib/emitters/zod.d.ts +0 -1
- package/dist/lib/emitters/zod.d.ts.map +1 -1
- package/dist/lib/generate.d.ts +0 -2
- package/dist/lib/generate.d.ts.map +1 -1
- package/dist/lib/generator.d.ts +14 -4
- package/dist/lib/generator.d.ts.map +1 -1
- package/dist/lib/readme/readme.d.ts +2 -2
- package/dist/lib/readme/readme.d.ts.map +1 -1
- package/dist/lib/sdk.d.ts +10 -14
- package/dist/lib/sdk.d.ts.map +1 -1
- package/dist/lib/typescript-snippet.d.ts +3 -3
- package/dist/lib/typescript-snippet.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { template as template2 } from "lodash-es";
|
|
|
3
3
|
import { readdir } from "node:fs/promises";
|
|
4
4
|
import { join as join2 } from "node:path";
|
|
5
5
|
import { npmRunPathEnv } from "npm-run-path";
|
|
6
|
-
import { camelcase as
|
|
6
|
+
import { camelcase as camelcase5, spinalcase as spinalcase4 } from "stringcase";
|
|
7
7
|
import { methods, pascalcase as pascalcase5, toLitObject as toLitObject2 } from "@sdk-it/core";
|
|
8
8
|
import {
|
|
9
9
|
createWriterProxy,
|
|
@@ -11,13 +11,50 @@ import {
|
|
|
11
11
|
writeFiles
|
|
12
12
|
} from "@sdk-it/core/file-system.js";
|
|
13
13
|
import {
|
|
14
|
-
augmentSpec,
|
|
15
14
|
cleanFiles,
|
|
16
|
-
forEachOperation as forEachOperation4,
|
|
17
15
|
readWriteMetadata,
|
|
18
16
|
sanitizeTag as sanitizeTag4,
|
|
19
|
-
securityToOptions as securityToOptions2
|
|
17
|
+
securityToOptions as securityToOptions2,
|
|
18
|
+
toIR
|
|
19
|
+
} from "@sdk-it/spec";
|
|
20
|
+
|
|
21
|
+
// packages/typescript/src/lib/agent/ai-sdk.ts
|
|
22
|
+
import { camelcase, spinalcase } from "stringcase";
|
|
23
|
+
import {
|
|
24
|
+
forEachOperation
|
|
20
25
|
} from "@sdk-it/spec";
|
|
26
|
+
function generateAISDKTools(spec) {
|
|
27
|
+
const groups = {};
|
|
28
|
+
forEachOperation(spec, (entry, operation) => {
|
|
29
|
+
groups[entry.tag] ??= [];
|
|
30
|
+
groups[entry.tag].push(createTool(entry, operation));
|
|
31
|
+
});
|
|
32
|
+
const imports = [
|
|
33
|
+
`import { z } from 'zod';`,
|
|
34
|
+
`import { tool } from 'ai';`,
|
|
35
|
+
`import * as schemas from './inputs/index.ts';`
|
|
36
|
+
];
|
|
37
|
+
const tools = Object.entries(groups).map(([group, tools2]) => {
|
|
38
|
+
return `export const ${spinalcase(group)} = (context: { client: any }) => ({ ${tools2.join(", ")} });`;
|
|
39
|
+
});
|
|
40
|
+
return [...imports, ...tools].join("\n\n");
|
|
41
|
+
}
|
|
42
|
+
function createTool(entry, operation) {
|
|
43
|
+
const schemaName = camelcase(`${operation.operationId} schema`);
|
|
44
|
+
return `'${operation["x-fn-name"]}': tool({
|
|
45
|
+
description: \`${operation.description || operation.summary}\`,
|
|
46
|
+
type: 'function',
|
|
47
|
+
inputSchema: schemas.${schemaName},
|
|
48
|
+
execute: async (input) => {
|
|
49
|
+
console.log('Executing ${operation.operationId} tool with input:', input);
|
|
50
|
+
const response = await context.client.request(
|
|
51
|
+
'${entry.method.toUpperCase()} ${entry.path}' ,
|
|
52
|
+
input as any,
|
|
53
|
+
);
|
|
54
|
+
return JSON.stringify(response);
|
|
55
|
+
},
|
|
56
|
+
})`;
|
|
57
|
+
}
|
|
21
58
|
|
|
22
59
|
// packages/typescript/src/lib/agent/utils.txt
|
|
23
60
|
var utils_default = "function coerceContext(context?: any) {\n if (!context) {\n throw new Error('Context is required');\n }\n return context as {\n client: any\n };\n}\n/**\n * Takes a Zod object schema and makes all optional properties nullable as well.\n * This is useful for APIs where optional fields can be explicitly set to null.\n *\n * @param schema - The Zod object schema to transform\n * @returns A new Zod schema with optional properties made nullable\n */\nfunction makeOptionalPropsNullable<T extends z.ZodRawShape>(\n schema: z.ZodObject<T>,\n) {\n const shape = schema.shape;\n const newShape = {} as Record<string, z.ZodTypeAny>;\n\n for (const [key, value] of Object.entries(shape)) {\n if (value instanceof z.ZodOptional) {\n // Make optional properties also nullable\n newShape[key] = value._def.innerType.nullable().optional();\n } else {\n // Keep non-optional properties as they are\n newShape[key] = value;\n }\n }\n\n return z.object(newShape);\n}";
|
|
@@ -588,58 +625,25 @@ function appendOptional2(type, isRequired) {
|
|
|
588
625
|
// packages/typescript/src/lib/generator.ts
|
|
589
626
|
import { merge, template } from "lodash-es";
|
|
590
627
|
import { join } from "node:path";
|
|
591
|
-
import { camelcase as
|
|
628
|
+
import { camelcase as camelcase3, spinalcase as spinalcase2 } from "stringcase";
|
|
592
629
|
import { followRef as followRef3, isEmpty as isEmpty2, isRef as isRef3, resolveRef } from "@sdk-it/core";
|
|
593
|
-
import {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
import { removeDuplicates } from "@sdk-it/core";
|
|
597
|
-
function mergeImports(...imports) {
|
|
598
|
-
const merged = {};
|
|
599
|
-
for (const it of imports) {
|
|
600
|
-
merged[it.moduleSpecifier] = merged[it.moduleSpecifier] ?? {
|
|
601
|
-
moduleSpecifier: it.moduleSpecifier,
|
|
602
|
-
defaultImport: it.defaultImport,
|
|
603
|
-
namespaceImport: it.namespaceImport,
|
|
604
|
-
namedImports: []
|
|
605
|
-
};
|
|
606
|
-
for (const named of it.namedImports) {
|
|
607
|
-
if (!merged[it.moduleSpecifier].namedImports.some(
|
|
608
|
-
(x) => x.name === named.name
|
|
609
|
-
)) {
|
|
610
|
-
merged[it.moduleSpecifier].namedImports.push(named);
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
return Object.values(merged);
|
|
615
|
-
}
|
|
616
|
-
function importsToString(...imports) {
|
|
617
|
-
return imports.map((it) => {
|
|
618
|
-
if (it.defaultImport) {
|
|
619
|
-
return `import ${it.defaultImport} from '${it.moduleSpecifier}'`;
|
|
620
|
-
}
|
|
621
|
-
if (it.namespaceImport) {
|
|
622
|
-
return `import * as ${it.namespaceImport} from '${it.moduleSpecifier}'`;
|
|
623
|
-
}
|
|
624
|
-
if (it.namedImports) {
|
|
625
|
-
return `import {${removeDuplicates(it.namedImports, (it2) => it2.name).map((n) => `${n.isTypeOnly ? "type" : ""} ${n.name}`).join(", ")}} from '${it.moduleSpecifier}'`;
|
|
626
|
-
}
|
|
627
|
-
throw new Error(`Invalid import ${JSON.stringify(it)}`);
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
+
import {
|
|
631
|
+
forEachOperation as forEachOperation2
|
|
632
|
+
} from "@sdk-it/spec";
|
|
630
633
|
|
|
631
634
|
// packages/typescript/src/lib/sdk.ts
|
|
632
|
-
import { camelcase } from "stringcase";
|
|
635
|
+
import { camelcase as camelcase2 } from "stringcase";
|
|
633
636
|
import { isEmpty, pascalcase as pascalcase3 } from "@sdk-it/core";
|
|
634
637
|
import {
|
|
635
|
-
isErrorStatusCode,
|
|
636
638
|
isStreamingContentType,
|
|
637
|
-
isSuccessStatusCode,
|
|
638
639
|
isTextContentType,
|
|
639
640
|
parseJsonContentType,
|
|
640
641
|
sanitizeTag as sanitizeTag3
|
|
641
642
|
} from "@sdk-it/spec";
|
|
642
643
|
|
|
644
|
+
// packages/typescript/src/lib/import-utilities.ts
|
|
645
|
+
import { removeDuplicates } from "@sdk-it/core";
|
|
646
|
+
|
|
643
647
|
// packages/typescript/src/lib/status-map.ts
|
|
644
648
|
var status_map_default = {
|
|
645
649
|
"200": "Ok",
|
|
@@ -668,52 +672,25 @@ var status_map_default = {
|
|
|
668
672
|
|
|
669
673
|
// packages/typescript/src/lib/sdk.ts
|
|
670
674
|
function toEndpoint(groupName, spec, specOperation, operation, utils) {
|
|
671
|
-
const schemaName =
|
|
672
|
-
const schemaRef = `${
|
|
673
|
-
const inputHeaders = [];
|
|
674
|
-
const inputQuery = [];
|
|
675
|
-
const inputBody = [];
|
|
676
|
-
const inputParams = [];
|
|
675
|
+
const schemaName = camelcase2(`${specOperation.operationId} schema`);
|
|
676
|
+
const schemaRef = `${camelcase2(groupName)}.${schemaName}`;
|
|
677
677
|
const schemas = [];
|
|
678
|
-
const responses = [];
|
|
679
|
-
for (const [name, prop] of Object.entries(operation.inputs)) {
|
|
680
|
-
if (prop.in === "headers" || prop.in === "header") {
|
|
681
|
-
inputHeaders.push(`"${name}"`);
|
|
682
|
-
} else if (prop.in === "query") {
|
|
683
|
-
inputQuery.push(`"${name}"`);
|
|
684
|
-
} else if (prop.in === "body") {
|
|
685
|
-
inputBody.push(`"${name}"`);
|
|
686
|
-
} else if (prop.in === "path") {
|
|
687
|
-
inputParams.push(`"${name}"`);
|
|
688
|
-
} else if (prop.in === "internal") {
|
|
689
|
-
continue;
|
|
690
|
-
} else {
|
|
691
|
-
throw new Error(
|
|
692
|
-
`Unknown source ${prop.in} in ${name} ${JSON.stringify(
|
|
693
|
-
prop
|
|
694
|
-
)} in ${operation.name}`
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
678
|
specOperation.responses ??= {};
|
|
699
|
-
const outputs =
|
|
700
|
-
|
|
701
|
-
const handled = handleResponse(
|
|
679
|
+
const outputs = Object.keys(specOperation.responses).flatMap(
|
|
680
|
+
(status) => toHttpOutput(
|
|
702
681
|
spec,
|
|
703
|
-
|
|
682
|
+
specOperation.operationId,
|
|
704
683
|
status,
|
|
705
|
-
specOperation.responses[status]
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
responses.push(handled);
|
|
709
|
-
outputs.push(...handled.outputs);
|
|
710
|
-
}
|
|
684
|
+
specOperation.responses[status]
|
|
685
|
+
)
|
|
686
|
+
);
|
|
711
687
|
const addTypeParser = Object.keys(operation.schemas).length > 1;
|
|
712
688
|
for (const type in operation.schemas ?? {}) {
|
|
713
689
|
let typePrefix = "";
|
|
714
690
|
if (addTypeParser && type !== "json") {
|
|
715
691
|
typePrefix = `${type} `;
|
|
716
692
|
}
|
|
693
|
+
const paths = inputToPath(specOperation, operation.inputs);
|
|
717
694
|
const endpoint = `${typePrefix}${operation.method.toUpperCase()} ${operation.path}`;
|
|
718
695
|
schemas.push(
|
|
719
696
|
`"${endpoint}": {
|
|
@@ -721,10 +698,10 @@ function toEndpoint(groupName, spec, specOperation, operation, utils) {
|
|
|
721
698
|
output:[${outputs.join(",")}],
|
|
722
699
|
toRequest(input: z.infer<typeof ${schemaRef}${addTypeParser ? `.${type}` : ""}>) {
|
|
723
700
|
return toRequest('${endpoint}', ${operation.outgoingContentType || "empty"}(input, {
|
|
724
|
-
inputHeaders: [${inputHeaders}],
|
|
725
|
-
inputQuery: [${inputQuery}],
|
|
726
|
-
inputBody: [${inputBody}],
|
|
727
|
-
inputParams: [${inputParams}],
|
|
701
|
+
inputHeaders: [${paths.inputHeaders}],
|
|
702
|
+
inputQuery: [${paths.inputQuery}],
|
|
703
|
+
inputBody: [${paths.inputBody}],
|
|
704
|
+
inputParams: [${paths.inputParams}],
|
|
728
705
|
}));},
|
|
729
706
|
async dispatch(input: z.infer<typeof ${schemaRef}${addTypeParser ? `.${type}` : ""}>,options: {
|
|
730
707
|
signal?: AbortSignal;
|
|
@@ -733,7 +710,7 @@ function toEndpoint(groupName, spec, specOperation, operation, utils) {
|
|
|
733
710
|
})${specOperation["x-pagination"] ? paginationOperation(specOperation, utils.style) : normalOperation(utils.style)}`
|
|
734
711
|
);
|
|
735
712
|
}
|
|
736
|
-
return {
|
|
713
|
+
return { schemas };
|
|
737
714
|
}
|
|
738
715
|
function normalOperation(style) {
|
|
739
716
|
return `{
|
|
@@ -819,32 +796,10 @@ function paginationOperation(operation, style) {
|
|
|
819
796
|
}
|
|
820
797
|
return normalOperation(style);
|
|
821
798
|
}
|
|
822
|
-
function
|
|
823
|
-
const schemas = {};
|
|
824
|
-
const imports = {};
|
|
825
|
-
const endpointImports = {
|
|
826
|
-
ParseError: {
|
|
827
|
-
defaultImport: void 0,
|
|
828
|
-
isTypeOnly: false,
|
|
829
|
-
moduleSpecifier: utils.makeImport(`../http/parser`),
|
|
830
|
-
namedImports: [{ isTypeOnly: false, name: "ParseError" }],
|
|
831
|
-
namespaceImport: void 0
|
|
832
|
-
}
|
|
833
|
-
};
|
|
834
|
-
const responses = [];
|
|
835
|
-
const outputs = [];
|
|
799
|
+
function toHttpOutput(spec, operationName, status, response, withGenerics = true) {
|
|
836
800
|
const typeScriptDeserialzer = new TypeScriptEmitter(spec);
|
|
837
|
-
const statusCode = +status;
|
|
838
|
-
const statusName = `http.${status_map_default[status] || "APIResponse"}`;
|
|
839
801
|
const interfaceName = pascalcase3(sanitizeTag3(response["x-response-name"]));
|
|
840
|
-
|
|
841
|
-
if (isEmpty(response.content)) {
|
|
842
|
-
responses.push({
|
|
843
|
-
name: interfaceName,
|
|
844
|
-
schema: "void",
|
|
845
|
-
description: response.description
|
|
846
|
-
});
|
|
847
|
-
} else {
|
|
802
|
+
if (!isEmpty(response.content)) {
|
|
848
803
|
const contentTypeResult = fromContentType(
|
|
849
804
|
spec,
|
|
850
805
|
typeScriptDeserialzer,
|
|
@@ -855,33 +810,27 @@ function handleResponse(spec, operationName, status, response, utils) {
|
|
|
855
810
|
`No recognizable content type for response ${status} in operation ${operationName}`
|
|
856
811
|
);
|
|
857
812
|
}
|
|
858
|
-
parser = contentTypeResult.parser;
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
});
|
|
865
|
-
if (isErrorStatusCode(statusCode)) {
|
|
866
|
-
endpointImports[status_map_default[status] ?? "APIError"] = {
|
|
867
|
-
moduleSpecifier: utils.makeImport("../http/response"),
|
|
868
|
-
namedImports: [{ name: status_map_default[status] ?? "APIError" }]
|
|
869
|
-
};
|
|
870
|
-
} else if (isSuccessStatusCode(statusCode)) {
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
if (statusCode === 204) {
|
|
874
|
-
outputs.push(statusName);
|
|
875
|
-
} else {
|
|
876
|
-
if (status.endsWith("XX")) {
|
|
877
|
-
outputs.push(`http.APIError<outputs.${interfaceName}>`);
|
|
813
|
+
const parser = contentTypeResult.parser || "buffered";
|
|
814
|
+
const outputs = [];
|
|
815
|
+
const statusName = `http.${status_map_default[status] || "APIResponse"}`;
|
|
816
|
+
const statusCode = +status;
|
|
817
|
+
if (statusCode === 204) {
|
|
818
|
+
outputs.push(statusName);
|
|
878
819
|
} else {
|
|
879
|
-
outputs
|
|
880
|
-
|
|
881
|
-
|
|
820
|
+
const generic = withGenerics ? `<outputs.${interfaceName}>` : "";
|
|
821
|
+
if (status.endsWith("XX")) {
|
|
822
|
+
outputs.push(`http.APIError${generic}`);
|
|
823
|
+
} else {
|
|
824
|
+
if (parser !== "buffered") {
|
|
825
|
+
outputs.push(`{type: ${statusName}${generic}, parser: ${parser}}`);
|
|
826
|
+
} else {
|
|
827
|
+
outputs.push(`${statusName}${generic}`);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
882
830
|
}
|
|
831
|
+
return outputs;
|
|
883
832
|
}
|
|
884
|
-
return
|
|
833
|
+
return [];
|
|
885
834
|
}
|
|
886
835
|
function fromContentType(spec, typeScriptDeserialzer, response) {
|
|
887
836
|
if ((response.headers ?? {})["Transfer-Encoding"]) {
|
|
@@ -912,11 +861,71 @@ function streamedOutput() {
|
|
|
912
861
|
responseSchema: "ReadableStream"
|
|
913
862
|
};
|
|
914
863
|
}
|
|
864
|
+
function inputToPath(operation, inputs) {
|
|
865
|
+
const inputHeaders = [];
|
|
866
|
+
const inputQuery = [];
|
|
867
|
+
const inputBody = [];
|
|
868
|
+
const inputParams = [];
|
|
869
|
+
for (const [name, prop] of Object.entries(inputs)) {
|
|
870
|
+
if (prop.in === "headers" || prop.in === "header") {
|
|
871
|
+
inputHeaders.push(`"${name}"`);
|
|
872
|
+
} else if (prop.in === "query") {
|
|
873
|
+
inputQuery.push(`"${name}"`);
|
|
874
|
+
} else if (prop.in === "body") {
|
|
875
|
+
inputBody.push(`"${name}"`);
|
|
876
|
+
} else if (prop.in === "path") {
|
|
877
|
+
inputParams.push(`"${name}"`);
|
|
878
|
+
} else if (prop.in === "internal") {
|
|
879
|
+
continue;
|
|
880
|
+
} else {
|
|
881
|
+
throw new Error(
|
|
882
|
+
`Unknown source ${prop.in} in ${name} ${JSON.stringify(
|
|
883
|
+
prop
|
|
884
|
+
)} in ${operation.operationId}`
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
inputHeaders,
|
|
890
|
+
inputQuery,
|
|
891
|
+
inputBody,
|
|
892
|
+
inputParams
|
|
893
|
+
};
|
|
894
|
+
}
|
|
915
895
|
|
|
916
896
|
// packages/typescript/src/lib/styles/github/endpoints.txt
|
|
917
897
|
var endpoints_default = "type EndpointOutput<K extends keyof typeof schemas> = Extract<\n Unionize<(typeof schemas)[K]['output']>,\n SuccessfulResponse\n>;\n\ntype EndpointError<K extends keyof typeof schemas> = Extract<\n Unionize<(typeof schemas)[K]['output']>,\n ProblematicResponse\n>;\n\nexport type Endpoints = {\n [K in keyof typeof schemas]: {\n input: z.infer<(typeof schemas)[K]['schema']>;\n output: <% if (outputType === 'default') { %>EndpointOutput<K>['data']<% } else { %>EndpointOutput<K><% } %>;\n error: EndpointError<K> | ParseError<(typeof schemas)[K]['schema']>;\n };\n};";
|
|
918
898
|
|
|
919
899
|
// packages/typescript/src/lib/generator.ts
|
|
900
|
+
function coearceRequestInput(spec, operation, type) {
|
|
901
|
+
let objectSchema = resolveRef(
|
|
902
|
+
spec,
|
|
903
|
+
operation.requestBody.content[type].schema
|
|
904
|
+
);
|
|
905
|
+
const xProperties = objectSchema["x-properties"] ?? {};
|
|
906
|
+
const xRequired = objectSchema["x-required"] ?? [];
|
|
907
|
+
if (type === "application/empty") {
|
|
908
|
+
objectSchema = {
|
|
909
|
+
type: "object",
|
|
910
|
+
additionalProperties: isEmpty2(xProperties)
|
|
911
|
+
};
|
|
912
|
+
} else {
|
|
913
|
+
if (objectSchema.type !== "object") {
|
|
914
|
+
objectSchema = {
|
|
915
|
+
type: "object",
|
|
916
|
+
required: [operation.requestBody.required ? "$body" : ""],
|
|
917
|
+
properties: {
|
|
918
|
+
$body: objectSchema
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return {
|
|
924
|
+
objectSchema,
|
|
925
|
+
xProperties,
|
|
926
|
+
xRequired
|
|
927
|
+
};
|
|
928
|
+
}
|
|
920
929
|
function generateCode(config) {
|
|
921
930
|
const commonZod = /* @__PURE__ */ new Map();
|
|
922
931
|
const commonZodImports = [];
|
|
@@ -932,11 +941,10 @@ function generateCode(config) {
|
|
|
932
941
|
});
|
|
933
942
|
const groups = {};
|
|
934
943
|
const endpoints = {};
|
|
935
|
-
|
|
944
|
+
forEachOperation2(config.spec, (entry, operation) => {
|
|
936
945
|
console.log(`Processing ${entry.method} ${entry.path}`);
|
|
937
946
|
groups[entry.tag] ??= [];
|
|
938
947
|
endpoints[entry.tag] ??= [];
|
|
939
|
-
const inputs = {};
|
|
940
948
|
const schemas = {};
|
|
941
949
|
const shortContenTypeMap = {
|
|
942
950
|
"application/json": "json",
|
|
@@ -949,87 +957,40 @@ function generateCode(config) {
|
|
|
949
957
|
"application/xml": "xml",
|
|
950
958
|
"text/plain": "text"
|
|
951
959
|
};
|
|
952
|
-
let outgoingContentType = "empty";
|
|
953
960
|
for (const type in operation.requestBody.content) {
|
|
954
|
-
|
|
955
|
-
config.spec,
|
|
956
|
-
|
|
961
|
+
schemas[shortContenTypeMap[type]] = zodDeserialzer.handle(
|
|
962
|
+
operationSchema(config.spec, operation, type),
|
|
963
|
+
true
|
|
957
964
|
);
|
|
958
|
-
const xProperties = objectSchema["x-properties"] ?? {};
|
|
959
|
-
const xRequired = objectSchema["x-required"] ?? [];
|
|
960
|
-
if (type === "application/empty") {
|
|
961
|
-
objectSchema = {
|
|
962
|
-
type: "object",
|
|
963
|
-
additionalProperties: isEmpty2(xProperties)
|
|
964
|
-
};
|
|
965
|
-
} else {
|
|
966
|
-
if (objectSchema.type !== "object") {
|
|
967
|
-
objectSchema = {
|
|
968
|
-
type: "object",
|
|
969
|
-
required: [operation.requestBody.required ? "$body" : ""],
|
|
970
|
-
properties: {
|
|
971
|
-
$body: objectSchema
|
|
972
|
-
}
|
|
973
|
-
};
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
const additionalProperties = {};
|
|
977
|
-
for (const [name, prop] of Object.entries(xProperties)) {
|
|
978
|
-
additionalProperties[name] = {
|
|
979
|
-
name,
|
|
980
|
-
required: xRequired?.includes(name),
|
|
981
|
-
schema: prop,
|
|
982
|
-
in: prop["x-in"]
|
|
983
|
-
};
|
|
984
|
-
inputs[name] = {
|
|
985
|
-
in: prop["x-in"],
|
|
986
|
-
schema: ""
|
|
987
|
-
};
|
|
988
|
-
}
|
|
989
|
-
const schema = merge({}, objectSchema, {
|
|
990
|
-
required: Object.values(additionalProperties).filter((p) => p.required).map((p) => p.name),
|
|
991
|
-
properties: Object.entries(additionalProperties).reduce(
|
|
992
|
-
(acc, [, p]) => ({ ...acc, [p.name]: p.schema }),
|
|
993
|
-
{}
|
|
994
|
-
)
|
|
995
|
-
});
|
|
996
|
-
Object.assign(inputs, bodyInputs(config.spec, objectSchema));
|
|
997
|
-
schemas[shortContenTypeMap[type]] = zodDeserialzer.handle(schema, true);
|
|
998
|
-
}
|
|
999
|
-
if (operation.requestBody.content["application/json"]) {
|
|
1000
|
-
outgoingContentType = "json";
|
|
1001
|
-
} else if (operation.requestBody.content["application/x-www-form-urlencoded"]) {
|
|
1002
|
-
outgoingContentType = "urlencoded";
|
|
1003
|
-
} else if (operation.requestBody.content["multipart/form-data"]) {
|
|
1004
|
-
outgoingContentType = "formdata";
|
|
1005
965
|
}
|
|
966
|
+
const details = buildInput(config.spec, operation);
|
|
1006
967
|
const endpoint = toEndpoint(
|
|
1007
968
|
entry.tag,
|
|
1008
969
|
config.spec,
|
|
1009
970
|
operation,
|
|
1010
971
|
{
|
|
1011
|
-
outgoingContentType,
|
|
1012
|
-
name: operation.operationId,
|
|
1013
972
|
method: entry.method,
|
|
1014
973
|
path: entry.path,
|
|
974
|
+
operationId: operation.operationId,
|
|
1015
975
|
schemas,
|
|
1016
|
-
|
|
976
|
+
outgoingContentType: details.outgoingContentType,
|
|
977
|
+
inputs: details.inputs
|
|
1017
978
|
},
|
|
1018
|
-
|
|
979
|
+
config
|
|
1019
980
|
);
|
|
1020
981
|
endpoints[entry.tag].push(endpoint);
|
|
1021
982
|
groups[entry.tag].push({
|
|
1022
|
-
name: operation.operationId,
|
|
1023
|
-
inputs,
|
|
1024
|
-
outgoingContentType,
|
|
1025
|
-
schemas,
|
|
1026
983
|
method: entry.method,
|
|
1027
|
-
path: entry.path
|
|
984
|
+
path: entry.path,
|
|
985
|
+
operationId: operation.operationId,
|
|
986
|
+
schemas,
|
|
987
|
+
outgoingContentType: details.outgoingContentType,
|
|
988
|
+
inputs: details.inputs
|
|
1028
989
|
});
|
|
1029
990
|
});
|
|
1030
991
|
const allSchemas = Object.keys(endpoints).map((it) => ({
|
|
1031
|
-
import: `import ${
|
|
1032
|
-
use: ` ...${
|
|
992
|
+
import: `import ${camelcase3(it)} from './${config.makeImport(spinalcase2(it))}';`,
|
|
993
|
+
use: ` ...${camelcase3(it)}`
|
|
1033
994
|
}));
|
|
1034
995
|
return {
|
|
1035
996
|
groups,
|
|
@@ -1056,26 +1017,16 @@ ${allSchemas.map((it) => it.use).join(",\n")}
|
|
|
1056
1017
|
`.trim(),
|
|
1057
1018
|
...Object.fromEntries(
|
|
1058
1019
|
Object.entries(endpoints).map(([name, endpoint]) => {
|
|
1059
|
-
const imps = importsToString(
|
|
1060
|
-
...mergeImports(
|
|
1061
|
-
...endpoint.flatMap(
|
|
1062
|
-
(it) => it.responses.flatMap(
|
|
1063
|
-
(it2) => Object.values(it2.endpointImports)
|
|
1064
|
-
)
|
|
1065
|
-
)
|
|
1066
|
-
)
|
|
1067
|
-
);
|
|
1068
1020
|
return [
|
|
1069
1021
|
[
|
|
1070
|
-
join("api", `${
|
|
1022
|
+
join("api", `${spinalcase2(name)}.ts`),
|
|
1071
1023
|
`${[
|
|
1072
|
-
...imps,
|
|
1073
1024
|
`import z from 'zod';`,
|
|
1074
1025
|
`import * as http from '${config.makeImport("../http/response")}';`,
|
|
1075
1026
|
`import * as outputs from '${config.makeImport("../outputs/index")}';`,
|
|
1076
1027
|
`import { toRequest, json, urlencoded, empty, formdata, createUrl, type HeadersInit } from '${config.makeImport("../http/request")}';`,
|
|
1077
1028
|
`import { chunked, buffered } from "${config.makeImport("../http/parse-response")}";`,
|
|
1078
|
-
`import * as ${
|
|
1029
|
+
`import * as ${camelcase3(name)} from '../inputs/${config.makeImport(spinalcase2(name))}';`,
|
|
1079
1030
|
`import { createBaseUrlInterceptor, createHeadersInterceptor, type Interceptor } from '${config.makeImport("../http/interceptors")}';`,
|
|
1080
1031
|
`import { Dispatcher, fetchType, type InstanceType } from '${config.makeImport("../http/dispatcher")}';`,
|
|
1081
1032
|
`import { Pagination, OffsetPagination, CursorPagination } from "${config.makeImport("../pagination/index")}";`
|
|
@@ -1137,6 +1088,67 @@ function bodyInputs(spec, ctSchema) {
|
|
|
1137
1088
|
{}
|
|
1138
1089
|
);
|
|
1139
1090
|
}
|
|
1091
|
+
var contentTypeSerializerMap = {
|
|
1092
|
+
"application/json": "json",
|
|
1093
|
+
"application/x-www-form-urlencoded": "urlencoded",
|
|
1094
|
+
"multipart/form-data": "formdata",
|
|
1095
|
+
"application/xml": "xml",
|
|
1096
|
+
"text/plain": "text",
|
|
1097
|
+
"application/empty": "empty"
|
|
1098
|
+
};
|
|
1099
|
+
var serializerContentTypeMap = Object.fromEntries(
|
|
1100
|
+
Object.entries(contentTypeSerializerMap).map(([k, v]) => [v, k])
|
|
1101
|
+
);
|
|
1102
|
+
function buildInput(spec, operation) {
|
|
1103
|
+
const inputs = {};
|
|
1104
|
+
let outgoingContentType = "empty";
|
|
1105
|
+
for (const [ct, value] of Object.entries(contentTypeSerializerMap)) {
|
|
1106
|
+
if (operation.requestBody.content[ct]) {
|
|
1107
|
+
outgoingContentType = value;
|
|
1108
|
+
const { objectSchema, xProperties } = coearceRequestInput(
|
|
1109
|
+
spec,
|
|
1110
|
+
operation,
|
|
1111
|
+
ct
|
|
1112
|
+
);
|
|
1113
|
+
for (const [name, prop] of Object.entries(xProperties)) {
|
|
1114
|
+
inputs[name] = {
|
|
1115
|
+
in: prop["x-in"],
|
|
1116
|
+
schema: ""
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
Object.assign(inputs, bodyInputs(spec, objectSchema));
|
|
1120
|
+
break;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
return {
|
|
1124
|
+
inputs,
|
|
1125
|
+
outgoingContentType,
|
|
1126
|
+
ct: serializerContentTypeMap[outgoingContentType]
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
function operationSchema(ir, operation, type) {
|
|
1130
|
+
const { objectSchema, xProperties, xRequired } = coearceRequestInput(
|
|
1131
|
+
ir,
|
|
1132
|
+
operation,
|
|
1133
|
+
type
|
|
1134
|
+
);
|
|
1135
|
+
const additionalProperties = {};
|
|
1136
|
+
for (const [name, prop] of Object.entries(xProperties)) {
|
|
1137
|
+
additionalProperties[name] = {
|
|
1138
|
+
name,
|
|
1139
|
+
required: xRequired?.includes(name),
|
|
1140
|
+
schema: prop,
|
|
1141
|
+
in: prop["x-in"]
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
return merge({}, objectSchema, {
|
|
1145
|
+
required: Object.values(additionalProperties).filter((p) => p.required).map((p) => p.name),
|
|
1146
|
+
properties: Object.entries(additionalProperties).reduce(
|
|
1147
|
+
(acc, [, p]) => ({ ...acc, [p.name]: p.schema }),
|
|
1148
|
+
{}
|
|
1149
|
+
)
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1140
1152
|
|
|
1141
1153
|
// packages/typescript/src/lib/http/dispatcher.txt
|
|
1142
1154
|
var dispatcher_default = "export type Unionize<T> = T extends [infer Single extends OutputType]\n ? InstanceType<Single>\n : T extends readonly [...infer Tuple extends OutputType[]]\n ? { [I in keyof Tuple]: InstanceType<Tuple[I]> }[number]\n : never;\n\nexport type InstanceType<T> =\n T extends Type<infer U>\n ? U\n : T extends { type: Type<infer U> }\n ? U\n : T extends Array<unknown>\n ? Unionize<T>\n : never;\n\nexport interface Type<T> {\n new (...args: any[]): T;\n}\nexport type Parser = (\n response: Response,\n) => Promise<unknown> | ReadableStream<any>;\nexport type OutputType =\n | Type<APIResponse>\n | { parser: Parser; type: Type<APIResponse> };\n\nexport const fetchType = z\n .function()\n .args(z.instanceof(Request))\n .returns(z.promise(z.instanceof(Response)))\n .optional();\n\nexport async function parse<T extends OutputType[]>(\n outputs: T,\n response: Response,\n) <% if(!throwError) { %>\n: Promise<\n [\n Extract<InstanceType<T>, SuccessfulResponse>['data'],\n Extract<InstanceType<T>, ProblematicResponse>['data'],\n ]\n>\n <% } %>\n\n\n\n {\n let output: typeof APIResponse | null = null;\n let parser: Parser = buffered;\n for (const outputType of outputs) {\n if ('parser' in outputType) {\n parser = outputType.parser;\n if (isTypeOf(outputType.type, APIResponse)) {\n if (response.status === outputType.type.status) {\n output = outputType.type;\n break;\n }\n }\n } else if (isTypeOf(outputType, APIResponse)) {\n if (response.status === outputType.status) {\n output = outputType;\n break;\n }\n }\n }\n\n\n if (response.ok) {\n const apiresponse = (output || APIResponse).create(\n response.status,\n await parser(response),\n );\n <% if(throwError) { %>\n return <% if (outputType === 'default') { %>apiresponse as Extract<InstanceType<T>, SuccessfulResponse><% } else { %>apiresponse as Extract<InstanceType<T>, SuccessfulResponse>;<% } %>;\n <% } else { %>\n return [<% if (outputType === 'default') { %>apiresponse.data as Extract<InstanceType<T>, SuccessfulResponse>['data']<% } else { %>apiresponse as Extract<InstanceType<T>, SuccessfulResponse><% } %>, null] as const;\n <% } %>\n }\n<% if(throwError) { %>\n throw (output || APIError).create(\n response.status,\n await parser(response),\n );\n<% } else { %>\n const data = (output || APIError).create(\n response.status,\n await parser(response),\n );\n return [null, data] as const;\n<% } %>\n}\n\nexport function isTypeOf<T extends Type<APIResponse>>(\n instance: any,\n baseType: T,\n): instance is T {\n if (instance === baseType) {\n return true;\n }\n const prototype = Object.getPrototypeOf(instance);\n if (prototype === null) {\n return false;\n }\n return isTypeOf(prototype, baseType);\n}\n\nexport class Dispatcher {\n #interceptors: Interceptor[] = [];\n #fetch: z.infer<typeof fetchType>;\n constructor(interceptors: Interceptor[], fetch?: z.infer<typeof fetchType>) {\n this.#interceptors = interceptors;\n this.#fetch = fetch;\n }\n\n async send<T extends OutputType[]>(\n config: RequestConfig,\n outputs: T,\n signal?: AbortSignal,\n ) {\n for (const interceptor of this.#interceptors) {\n if (interceptor.before) {\n config = await interceptor.before(config);\n }\n }\n\n let response = await (this.#fetch ?? fetch)(\n new Request(config.url, config.init),\n {\n ...config.init,\n signal: signal,\n },\n );\n\n for (let i = this.#interceptors.length - 1; i >= 0; i--) {\n const interceptor = this.#interceptors[i];\n if (interceptor.after) {\n response = await interceptor.after(response.clone());\n }\n }\n\n return await parse(outputs, response);\n }\n}\n";
|
|
@@ -1599,7 +1611,7 @@ var page_pagination_default = "type InferPage<T> = T extends Page<infer U> ? U :
|
|
|
1599
1611
|
|
|
1600
1612
|
// packages/typescript/src/lib/readme/readme.ts
|
|
1601
1613
|
import { isEmpty as isEmpty3 } from "@sdk-it/core";
|
|
1602
|
-
import { forEachOperation as
|
|
1614
|
+
import { forEachOperation as forEachOperation3 } from "@sdk-it/spec";
|
|
1603
1615
|
|
|
1604
1616
|
// packages/typescript/src/lib/readme/prop.emitter.ts
|
|
1605
1617
|
import { followRef as followRef4, isRef as isRef4 } from "@sdk-it/core";
|
|
@@ -1889,7 +1901,7 @@ function toReadme(spec, generator) {
|
|
|
1889
1901
|
markdown.push(`# ${spec.info.title} TypeScript SDK`);
|
|
1890
1902
|
markdown.push("");
|
|
1891
1903
|
markdown.push(
|
|
1892
|
-
"A fully-typed TypeScript SDK with comprehensive IntelliSense support, automatic request/response validation, and modern async/await patterns. Built for seamless integration with TypeScript and JavaScript projects."
|
|
1904
|
+
"A fully-typed TypeScript SDK with comprehensive IntelliSense support, automatic request/response validation, and modern async/await patterns. Built for seamless integration with TypeScript and JavaScript projects. Each endpoint includes a brief description, example usage, and details about request and response formats."
|
|
1893
1905
|
);
|
|
1894
1906
|
markdown.push("");
|
|
1895
1907
|
markdown.push(generator.clientSetupDocs());
|
|
@@ -1910,7 +1922,7 @@ function toReadme(spec, generator) {
|
|
|
1910
1922
|
markdown.push("");
|
|
1911
1923
|
markdown.push("## API Reference");
|
|
1912
1924
|
markdown.push("");
|
|
1913
|
-
|
|
1925
|
+
forEachOperation3(spec, (entry, operation) => {
|
|
1914
1926
|
const { method, path } = entry;
|
|
1915
1927
|
markdown.push(
|
|
1916
1928
|
`### ${operation["x-fn-name"]} | ${`_${method.toUpperCase()} ${path}_`}`
|
|
@@ -1983,11 +1995,11 @@ ${l}`));
|
|
|
1983
1995
|
}
|
|
1984
1996
|
|
|
1985
1997
|
// packages/typescript/src/lib/typescript-snippet.ts
|
|
1986
|
-
import { camelcase as
|
|
1998
|
+
import { camelcase as camelcase4, spinalcase as spinalcase3 } from "stringcase";
|
|
1987
1999
|
import { isEmpty as isEmpty4, pascalcase as pascalcase4, resolveRef as resolveRef3 } from "@sdk-it/core";
|
|
1988
2000
|
import "@sdk-it/readme";
|
|
1989
2001
|
import {
|
|
1990
|
-
forEachOperation as
|
|
2002
|
+
forEachOperation as forEachOperation4,
|
|
1991
2003
|
patchParameters,
|
|
1992
2004
|
securityToOptions
|
|
1993
2005
|
} from "@sdk-it/spec";
|
|
@@ -2038,11 +2050,11 @@ var SnippetEmitter = class {
|
|
|
2038
2050
|
switch (schema.format) {
|
|
2039
2051
|
case "date-time":
|
|
2040
2052
|
case "datetime":
|
|
2041
|
-
return
|
|
2053
|
+
return `2025-07-17T09:08:00.097Z`;
|
|
2042
2054
|
case "date":
|
|
2043
|
-
return
|
|
2055
|
+
return `2025-07-17`;
|
|
2044
2056
|
case "time":
|
|
2045
|
-
return
|
|
2057
|
+
return `09:08:00.097Z`;
|
|
2046
2058
|
case "email":
|
|
2047
2059
|
return "user@example.com";
|
|
2048
2060
|
case "uuid":
|
|
@@ -2182,18 +2194,16 @@ var SnippetEmitter = class {
|
|
|
2182
2194
|
};
|
|
2183
2195
|
|
|
2184
2196
|
// packages/typescript/src/lib/typescript-snippet.ts
|
|
2185
|
-
var
|
|
2197
|
+
var TypeScriptSnippet = class {
|
|
2186
2198
|
#spec;
|
|
2187
|
-
#settings;
|
|
2188
2199
|
#snippetEmitter;
|
|
2189
2200
|
#clientName;
|
|
2190
2201
|
#packageName;
|
|
2191
2202
|
constructor(spec, settings) {
|
|
2192
2203
|
this.#spec = spec;
|
|
2193
|
-
this.#settings = settings;
|
|
2194
2204
|
this.#snippetEmitter = new SnippetEmitter(spec);
|
|
2195
2205
|
this.#clientName = settings.name?.trim() ? pascalcase4(settings.name) : "Client";
|
|
2196
|
-
this.#packageName = settings.name ? `@${
|
|
2206
|
+
this.#packageName = settings.name ? `@${spinalcase3(this.#clientName.toLowerCase())}/sdk` : "sdk";
|
|
2197
2207
|
}
|
|
2198
2208
|
succinct(entry, operation, values) {
|
|
2199
2209
|
let payload = "{}";
|
|
@@ -2312,7 +2322,7 @@ var TypeScriptGenerator = class {
|
|
|
2312
2322
|
};
|
|
2313
2323
|
}
|
|
2314
2324
|
#toRequest(entry, payload) {
|
|
2315
|
-
return `await ${
|
|
2325
|
+
return `await ${camelcase4(this.#clientName)}.request('${entry.method.toUpperCase()} ${entry.path}', ${payload});`;
|
|
2316
2326
|
}
|
|
2317
2327
|
snippet(entry, operation, config = {}) {
|
|
2318
2328
|
const payload = this.succinct(entry, operation, config);
|
|
@@ -2353,7 +2363,7 @@ ${client.use}`;
|
|
|
2353
2363
|
#constructClient(options = {}) {
|
|
2354
2364
|
return {
|
|
2355
2365
|
import: `import { ${this.#clientName} } from '${this.#packageName}';`,
|
|
2356
|
-
use: `const ${
|
|
2366
|
+
use: `const ${camelcase4(this.#clientName)} = new ${this.#clientName}({
|
|
2357
2367
|
${Object.entries(
|
|
2358
2368
|
options
|
|
2359
2369
|
).map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join(",\n ")}
|
|
@@ -2888,7 +2898,7 @@ function availablePaginationTypes(spec) {
|
|
|
2888
2898
|
let offset = false;
|
|
2889
2899
|
let page = false;
|
|
2890
2900
|
let cursor = false;
|
|
2891
|
-
|
|
2901
|
+
forEachOperation4(spec, (entry, operation) => {
|
|
2892
2902
|
if (operation["x-pagination"]) {
|
|
2893
2903
|
switch (operation["x-pagination"].type) {
|
|
2894
2904
|
case "offset":
|
|
@@ -2937,7 +2947,7 @@ function security(spec) {
|
|
|
2937
2947
|
return options;
|
|
2938
2948
|
}
|
|
2939
2949
|
async function generate(openapi, settings) {
|
|
2940
|
-
const spec =
|
|
2950
|
+
const spec = toIR(
|
|
2941
2951
|
{
|
|
2942
2952
|
spec: openapi,
|
|
2943
2953
|
responses: { flattenErrorResponses: true },
|
|
@@ -2945,7 +2955,6 @@ async function generate(openapi, settings) {
|
|
|
2945
2955
|
},
|
|
2946
2956
|
false
|
|
2947
2957
|
);
|
|
2948
|
-
const generator = new TypeScriptGenerator(spec, settings);
|
|
2949
2958
|
const style = Object.assign(
|
|
2950
2959
|
{},
|
|
2951
2960
|
{
|
|
@@ -2980,7 +2989,7 @@ async function generate(openapi, settings) {
|
|
|
2980
2989
|
makeImport
|
|
2981
2990
|
});
|
|
2982
2991
|
const clientName = pascalcase5((settings.name || "client").trim());
|
|
2983
|
-
const packageName = settings.name ? `@${
|
|
2992
|
+
const packageName = settings.name ? `@${spinalcase4(settings.name.trim().toLowerCase())}/sdk` : "sdk";
|
|
2984
2993
|
const inputs = toInputs(groups, commonZod, makeImport);
|
|
2985
2994
|
const models = serializeModels(spec);
|
|
2986
2995
|
await settings.writer(output, {
|
|
@@ -3082,8 +3091,9 @@ ${template2(dispatcher_default, {})({ throwError: !style.errorAsValue, outputTyp
|
|
|
3082
3091
|
});
|
|
3083
3092
|
if (settings.agentTools) {
|
|
3084
3093
|
await settings.writer(output, {
|
|
3085
|
-
"agents.ts": `${
|
|
3094
|
+
"agents.ts": `${generateAISDKTools(spec)}
|
|
3086
3095
|
${utils_default}`
|
|
3096
|
+
// 'agents.ts': `${generateOpenAIAgentTools(spec)}\n${utilsTxt}`,
|
|
3087
3097
|
});
|
|
3088
3098
|
}
|
|
3089
3099
|
await settings.writer(output, {
|
|
@@ -3092,7 +3102,7 @@ ${utils_default}`
|
|
|
3092
3102
|
settings.readFolder,
|
|
3093
3103
|
settings.useTsExtension,
|
|
3094
3104
|
["ts"],
|
|
3095
|
-
(config) => config.fileName.endsWith("pagination")
|
|
3105
|
+
(config) => config.fileName.endsWith("pagination") || config.fileName.endsWith("agents")
|
|
3096
3106
|
)
|
|
3097
3107
|
});
|
|
3098
3108
|
if (settings.mode === "full") {
|
|
@@ -3154,7 +3164,7 @@ ${utils_default}`
|
|
|
3154
3164
|
}
|
|
3155
3165
|
if (settings.readme) {
|
|
3156
3166
|
await settings.writer(settings.mode === "full" ? settings.output : output, {
|
|
3157
|
-
"README.md": toReadme(spec,
|
|
3167
|
+
"README.md": toReadme(spec, new TypeScriptSnippet(spec, settings))
|
|
3158
3168
|
});
|
|
3159
3169
|
}
|
|
3160
3170
|
await settings.formatCode?.({
|
|
@@ -3188,7 +3198,7 @@ ${schema.description ? `
|
|
|
3188
3198
|
` : ""}`,
|
|
3189
3199
|
`export type ${pascalcase5(sanitizeTag4(name))} = ${typeContent};`
|
|
3190
3200
|
];
|
|
3191
|
-
const fileName = responseGroup ? join2(folder, `${
|
|
3201
|
+
const fileName = responseGroup ? join2(folder, `${spinalcase4(responseGroup)}.ts`) : join2(folder, `${spinalcase4(name)}.ts`);
|
|
3192
3202
|
filesMap[fileName] ??= [];
|
|
3193
3203
|
filesMap[fileName].push(fileContent.join("\n"));
|
|
3194
3204
|
}
|
|
@@ -3209,18 +3219,18 @@ function toInputs(operationsSet, commonZod, makeImport) {
|
|
|
3209
3219
|
const output = [];
|
|
3210
3220
|
const imports = /* @__PURE__ */ new Set(['import { z } from "zod";']);
|
|
3211
3221
|
for (const operation of operations) {
|
|
3212
|
-
const schemaName =
|
|
3222
|
+
const schemaName = camelcase5(`${operation.operationId} schema`);
|
|
3213
3223
|
const schema = `export const ${schemaName} = ${Object.keys(operation.schemas).length === 1 ? Object.values(operation.schemas)[0] : toLitObject2(operation.schemas)};`;
|
|
3214
3224
|
for (const it of commonImports) {
|
|
3215
3225
|
if (schema.includes(it)) {
|
|
3216
3226
|
imports.add(
|
|
3217
|
-
`import { ${it} } from './schemas/${makeImport(
|
|
3227
|
+
`import { ${it} } from './schemas/${makeImport(spinalcase4(it))}';`
|
|
3218
3228
|
);
|
|
3219
3229
|
}
|
|
3220
3230
|
}
|
|
3221
3231
|
output.push(schema);
|
|
3222
3232
|
}
|
|
3223
|
-
inputs[`inputs/${
|
|
3233
|
+
inputs[`inputs/${spinalcase4(name)}.ts`] = [...imports, ...output].join("\n") + "\n";
|
|
3224
3234
|
}
|
|
3225
3235
|
const schemas = commonZod.entries().reduce((acc, [name, schema]) => {
|
|
3226
3236
|
const output = [`import { z } from 'zod';`];
|
|
@@ -3229,13 +3239,13 @@ function toInputs(operationsSet, commonZod, makeImport) {
|
|
|
3229
3239
|
const preciseMatch = new RegExp(`\\b${schema2}\\b`);
|
|
3230
3240
|
if (preciseMatch.test(content) && schema2 !== name) {
|
|
3231
3241
|
output.push(
|
|
3232
|
-
`import { ${schema2} } from './${makeImport(
|
|
3242
|
+
`import { ${schema2} } from './${makeImport(spinalcase4(schema2))}';`
|
|
3233
3243
|
);
|
|
3234
3244
|
}
|
|
3235
3245
|
}
|
|
3236
3246
|
output.push(content);
|
|
3237
3247
|
return [
|
|
3238
|
-
[`inputs/schemas/${
|
|
3248
|
+
[`inputs/schemas/${spinalcase4(name)}.ts`, output.join("\n")],
|
|
3239
3249
|
...acc
|
|
3240
3250
|
];
|
|
3241
3251
|
}, []);
|
|
@@ -3244,44 +3254,15 @@ function toInputs(operationsSet, commonZod, makeImport) {
|
|
|
3244
3254
|
...inputs
|
|
3245
3255
|
};
|
|
3246
3256
|
}
|
|
3247
|
-
function generateAgentTools(spec) {
|
|
3248
|
-
const groups = {};
|
|
3249
|
-
forEachOperation4(spec, (entry, operation) => {
|
|
3250
|
-
groups[entry.tag] ??= [];
|
|
3251
|
-
groups[entry.tag].push(createTool(entry, operation));
|
|
3252
|
-
});
|
|
3253
|
-
const imports = [
|
|
3254
|
-
`import { z } from 'zod';`,
|
|
3255
|
-
`import { tool } from '@openai/agents';`,
|
|
3256
|
-
`import * as schemas from './inputs/index.ts';`
|
|
3257
|
-
];
|
|
3258
|
-
const tools = Object.entries(groups).map(([group, tools2]) => {
|
|
3259
|
-
return `export const ${spinalcase3(group)} = [${tools2.join(", ")}];`;
|
|
3260
|
-
});
|
|
3261
|
-
return [...imports, ...tools].join("\n\n");
|
|
3262
|
-
}
|
|
3263
|
-
function createTool(entry, operation) {
|
|
3264
|
-
const schemaName = camelcase4(`${operation.operationId} schema`);
|
|
3265
|
-
return `tool({
|
|
3266
|
-
description: \`${operation.description || operation.summary}\`,
|
|
3267
|
-
name: '${operation["x-fn-name"]}',
|
|
3268
|
-
parameters: makeOptionalPropsNullable(schemas.${schemaName}),
|
|
3269
|
-
execute: async (input, maybeContext) => {
|
|
3270
|
-
console.log('Executing ${operation.operationId} tool with input:', input);
|
|
3271
|
-
const context = coerceContext(maybeContext?.context);
|
|
3272
|
-
const client = context.client;
|
|
3273
|
-
const response = await client.request(
|
|
3274
|
-
'${entry.method.toUpperCase()} ${entry.path}' ,
|
|
3275
|
-
input as any,
|
|
3276
|
-
);
|
|
3277
|
-
return JSON.stringify(response);
|
|
3278
|
-
},
|
|
3279
|
-
})`;
|
|
3280
|
-
}
|
|
3281
3257
|
export {
|
|
3282
|
-
|
|
3258
|
+
TypeScriptSnippet,
|
|
3259
|
+
buildInput,
|
|
3283
3260
|
generate,
|
|
3284
|
-
|
|
3261
|
+
generateCode,
|
|
3262
|
+
inputToPath,
|
|
3263
|
+
operationSchema,
|
|
3264
|
+
toEndpoint,
|
|
3265
|
+
toHttpOutput,
|
|
3285
3266
|
toInputs
|
|
3286
3267
|
};
|
|
3287
3268
|
//# sourceMappingURL=index.js.map
|