@sdk-it/typescript 0.27.0 → 0.29.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 +276 -251
- 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/generate.d.ts.map +1 -1
- package/dist/lib/generator.d.ts +7 -3
- package/dist/lib/generator.d.ts.map +1 -1
- package/dist/lib/options.d.ts +1 -0
- package/dist/lib/options.d.ts.map +1 -1
- package/dist/lib/readme/prop.emitter.d.ts +1 -1
- package/dist/lib/readme/prop.emitter.d.ts.map +1 -1
- 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 +1 -1
- 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,
|
|
@@ -18,6 +18,48 @@ import {
|
|
|
18
18
|
securityToOptions as securityToOptions2
|
|
19
19
|
} from "@sdk-it/spec";
|
|
20
20
|
|
|
21
|
+
// packages/typescript/src/lib/agent/ai-sdk.ts
|
|
22
|
+
import { camelcase, spinalcase } from "stringcase";
|
|
23
|
+
import {
|
|
24
|
+
forEachOperation
|
|
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 client = context.client;
|
|
51
|
+
const response = await client.request(
|
|
52
|
+
'${entry.method.toUpperCase()} ${entry.path}' ,
|
|
53
|
+
input as any,
|
|
54
|
+
);
|
|
55
|
+
return JSON.stringify(response);
|
|
56
|
+
},
|
|
57
|
+
})`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// packages/typescript/src/lib/agent/utils.txt
|
|
61
|
+
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}";
|
|
62
|
+
|
|
21
63
|
// packages/typescript/src/lib/client.ts
|
|
22
64
|
import { toLitObject } from "@sdk-it/core";
|
|
23
65
|
|
|
@@ -584,58 +626,25 @@ function appendOptional2(type, isRequired) {
|
|
|
584
626
|
// packages/typescript/src/lib/generator.ts
|
|
585
627
|
import { merge, template } from "lodash-es";
|
|
586
628
|
import { join } from "node:path";
|
|
587
|
-
import { camelcase as
|
|
629
|
+
import { camelcase as camelcase3, spinalcase as spinalcase2 } from "stringcase";
|
|
588
630
|
import { followRef as followRef3, isEmpty as isEmpty2, isRef as isRef3, resolveRef } from "@sdk-it/core";
|
|
589
|
-
import {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
import { removeDuplicates } from "@sdk-it/core";
|
|
593
|
-
function mergeImports(...imports) {
|
|
594
|
-
const merged = {};
|
|
595
|
-
for (const it of imports) {
|
|
596
|
-
merged[it.moduleSpecifier] = merged[it.moduleSpecifier] ?? {
|
|
597
|
-
moduleSpecifier: it.moduleSpecifier,
|
|
598
|
-
defaultImport: it.defaultImport,
|
|
599
|
-
namespaceImport: it.namespaceImport,
|
|
600
|
-
namedImports: []
|
|
601
|
-
};
|
|
602
|
-
for (const named of it.namedImports) {
|
|
603
|
-
if (!merged[it.moduleSpecifier].namedImports.some(
|
|
604
|
-
(x) => x.name === named.name
|
|
605
|
-
)) {
|
|
606
|
-
merged[it.moduleSpecifier].namedImports.push(named);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
return Object.values(merged);
|
|
611
|
-
}
|
|
612
|
-
function importsToString(...imports) {
|
|
613
|
-
return imports.map((it) => {
|
|
614
|
-
if (it.defaultImport) {
|
|
615
|
-
return `import ${it.defaultImport} from '${it.moduleSpecifier}'`;
|
|
616
|
-
}
|
|
617
|
-
if (it.namespaceImport) {
|
|
618
|
-
return `import * as ${it.namespaceImport} from '${it.moduleSpecifier}'`;
|
|
619
|
-
}
|
|
620
|
-
if (it.namedImports) {
|
|
621
|
-
return `import {${removeDuplicates(it.namedImports, (it2) => it2.name).map((n) => `${n.isTypeOnly ? "type" : ""} ${n.name}`).join(", ")}} from '${it.moduleSpecifier}'`;
|
|
622
|
-
}
|
|
623
|
-
throw new Error(`Invalid import ${JSON.stringify(it)}`);
|
|
624
|
-
});
|
|
625
|
-
}
|
|
631
|
+
import {
|
|
632
|
+
forEachOperation as forEachOperation2
|
|
633
|
+
} from "@sdk-it/spec";
|
|
626
634
|
|
|
627
635
|
// packages/typescript/src/lib/sdk.ts
|
|
628
|
-
import { camelcase } from "stringcase";
|
|
636
|
+
import { camelcase as camelcase2 } from "stringcase";
|
|
629
637
|
import { isEmpty, pascalcase as pascalcase3 } from "@sdk-it/core";
|
|
630
638
|
import {
|
|
631
|
-
isErrorStatusCode,
|
|
632
639
|
isStreamingContentType,
|
|
633
|
-
isSuccessStatusCode,
|
|
634
640
|
isTextContentType,
|
|
635
641
|
parseJsonContentType,
|
|
636
642
|
sanitizeTag as sanitizeTag3
|
|
637
643
|
} from "@sdk-it/spec";
|
|
638
644
|
|
|
645
|
+
// packages/typescript/src/lib/import-utilities.ts
|
|
646
|
+
import { removeDuplicates } from "@sdk-it/core";
|
|
647
|
+
|
|
639
648
|
// packages/typescript/src/lib/status-map.ts
|
|
640
649
|
var status_map_default = {
|
|
641
650
|
"200": "Ok",
|
|
@@ -664,52 +673,25 @@ var status_map_default = {
|
|
|
664
673
|
|
|
665
674
|
// packages/typescript/src/lib/sdk.ts
|
|
666
675
|
function toEndpoint(groupName, spec, specOperation, operation, utils) {
|
|
667
|
-
const schemaName =
|
|
668
|
-
const schemaRef = `${
|
|
669
|
-
const inputHeaders = [];
|
|
670
|
-
const inputQuery = [];
|
|
671
|
-
const inputBody = [];
|
|
672
|
-
const inputParams = [];
|
|
676
|
+
const schemaName = camelcase2(`${specOperation.operationId} schema`);
|
|
677
|
+
const schemaRef = `${camelcase2(groupName)}.${schemaName}`;
|
|
673
678
|
const schemas = [];
|
|
674
|
-
const responses = [];
|
|
675
|
-
for (const [name, prop] of Object.entries(operation.inputs)) {
|
|
676
|
-
if (prop.in === "headers" || prop.in === "header") {
|
|
677
|
-
inputHeaders.push(`"${name}"`);
|
|
678
|
-
} else if (prop.in === "query") {
|
|
679
|
-
inputQuery.push(`"${name}"`);
|
|
680
|
-
} else if (prop.in === "body") {
|
|
681
|
-
inputBody.push(`"${name}"`);
|
|
682
|
-
} else if (prop.in === "path") {
|
|
683
|
-
inputParams.push(`"${name}"`);
|
|
684
|
-
} else if (prop.in === "internal") {
|
|
685
|
-
continue;
|
|
686
|
-
} else {
|
|
687
|
-
throw new Error(
|
|
688
|
-
`Unknown source ${prop.in} in ${name} ${JSON.stringify(
|
|
689
|
-
prop
|
|
690
|
-
)} in ${operation.name}`
|
|
691
|
-
);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
679
|
specOperation.responses ??= {};
|
|
695
|
-
const outputs =
|
|
696
|
-
|
|
697
|
-
const handled = handleResponse(
|
|
680
|
+
const outputs = Object.keys(specOperation.responses).flatMap(
|
|
681
|
+
(status) => toHttpOutput(
|
|
698
682
|
spec,
|
|
699
|
-
|
|
683
|
+
specOperation.operationId,
|
|
700
684
|
status,
|
|
701
|
-
specOperation.responses[status]
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
responses.push(handled);
|
|
705
|
-
outputs.push(...handled.outputs);
|
|
706
|
-
}
|
|
685
|
+
specOperation.responses[status]
|
|
686
|
+
)
|
|
687
|
+
);
|
|
707
688
|
const addTypeParser = Object.keys(operation.schemas).length > 1;
|
|
708
689
|
for (const type in operation.schemas ?? {}) {
|
|
709
690
|
let typePrefix = "";
|
|
710
691
|
if (addTypeParser && type !== "json") {
|
|
711
692
|
typePrefix = `${type} `;
|
|
712
693
|
}
|
|
694
|
+
const paths = inputToPath(specOperation, operation.inputs);
|
|
713
695
|
const endpoint = `${typePrefix}${operation.method.toUpperCase()} ${operation.path}`;
|
|
714
696
|
schemas.push(
|
|
715
697
|
`"${endpoint}": {
|
|
@@ -717,10 +699,10 @@ function toEndpoint(groupName, spec, specOperation, operation, utils) {
|
|
|
717
699
|
output:[${outputs.join(",")}],
|
|
718
700
|
toRequest(input: z.infer<typeof ${schemaRef}${addTypeParser ? `.${type}` : ""}>) {
|
|
719
701
|
return toRequest('${endpoint}', ${operation.outgoingContentType || "empty"}(input, {
|
|
720
|
-
inputHeaders: [${inputHeaders}],
|
|
721
|
-
inputQuery: [${inputQuery}],
|
|
722
|
-
inputBody: [${inputBody}],
|
|
723
|
-
inputParams: [${inputParams}],
|
|
702
|
+
inputHeaders: [${paths.inputHeaders}],
|
|
703
|
+
inputQuery: [${paths.inputQuery}],
|
|
704
|
+
inputBody: [${paths.inputBody}],
|
|
705
|
+
inputParams: [${paths.inputParams}],
|
|
724
706
|
}));},
|
|
725
707
|
async dispatch(input: z.infer<typeof ${schemaRef}${addTypeParser ? `.${type}` : ""}>,options: {
|
|
726
708
|
signal?: AbortSignal;
|
|
@@ -729,7 +711,7 @@ function toEndpoint(groupName, spec, specOperation, operation, utils) {
|
|
|
729
711
|
})${specOperation["x-pagination"] ? paginationOperation(specOperation, utils.style) : normalOperation(utils.style)}`
|
|
730
712
|
);
|
|
731
713
|
}
|
|
732
|
-
return {
|
|
714
|
+
return { schemas };
|
|
733
715
|
}
|
|
734
716
|
function normalOperation(style) {
|
|
735
717
|
return `{
|
|
@@ -815,32 +797,10 @@ function paginationOperation(operation, style) {
|
|
|
815
797
|
}
|
|
816
798
|
return normalOperation(style);
|
|
817
799
|
}
|
|
818
|
-
function
|
|
819
|
-
const schemas = {};
|
|
820
|
-
const imports = {};
|
|
821
|
-
const endpointImports = {
|
|
822
|
-
ParseError: {
|
|
823
|
-
defaultImport: void 0,
|
|
824
|
-
isTypeOnly: false,
|
|
825
|
-
moduleSpecifier: utils.makeImport(`../http/parser`),
|
|
826
|
-
namedImports: [{ isTypeOnly: false, name: "ParseError" }],
|
|
827
|
-
namespaceImport: void 0
|
|
828
|
-
}
|
|
829
|
-
};
|
|
830
|
-
const responses = [];
|
|
831
|
-
const outputs = [];
|
|
800
|
+
function toHttpOutput(spec, operationName, status, response, withGenerics = true) {
|
|
832
801
|
const typeScriptDeserialzer = new TypeScriptEmitter(spec);
|
|
833
|
-
const statusCode = +status;
|
|
834
|
-
const statusName = `http.${status_map_default[status] || "APIResponse"}`;
|
|
835
802
|
const interfaceName = pascalcase3(sanitizeTag3(response["x-response-name"]));
|
|
836
|
-
|
|
837
|
-
if (isEmpty(response.content)) {
|
|
838
|
-
responses.push({
|
|
839
|
-
name: interfaceName,
|
|
840
|
-
schema: "void",
|
|
841
|
-
description: response.description
|
|
842
|
-
});
|
|
843
|
-
} else {
|
|
803
|
+
if (!isEmpty(response.content)) {
|
|
844
804
|
const contentTypeResult = fromContentType(
|
|
845
805
|
spec,
|
|
846
806
|
typeScriptDeserialzer,
|
|
@@ -851,33 +811,27 @@ function handleResponse(spec, operationName, status, response, utils) {
|
|
|
851
811
|
`No recognizable content type for response ${status} in operation ${operationName}`
|
|
852
812
|
);
|
|
853
813
|
}
|
|
854
|
-
parser = contentTypeResult.parser;
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
});
|
|
861
|
-
if (isErrorStatusCode(statusCode)) {
|
|
862
|
-
endpointImports[status_map_default[status] ?? "APIError"] = {
|
|
863
|
-
moduleSpecifier: utils.makeImport("../http/response"),
|
|
864
|
-
namedImports: [{ name: status_map_default[status] ?? "APIError" }]
|
|
865
|
-
};
|
|
866
|
-
} else if (isSuccessStatusCode(statusCode)) {
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
if (statusCode === 204) {
|
|
870
|
-
outputs.push(statusName);
|
|
871
|
-
} else {
|
|
872
|
-
if (status.endsWith("XX")) {
|
|
873
|
-
outputs.push(`http.APIError<outputs.${interfaceName}>`);
|
|
814
|
+
const parser = contentTypeResult.parser || "buffered";
|
|
815
|
+
const outputs = [];
|
|
816
|
+
const statusName = `http.${status_map_default[status] || "APIResponse"}`;
|
|
817
|
+
const statusCode = +status;
|
|
818
|
+
if (statusCode === 204) {
|
|
819
|
+
outputs.push(statusName);
|
|
874
820
|
} else {
|
|
875
|
-
outputs
|
|
876
|
-
|
|
877
|
-
|
|
821
|
+
const generic = withGenerics ? `<outputs.${interfaceName}>` : "";
|
|
822
|
+
if (status.endsWith("XX")) {
|
|
823
|
+
outputs.push(`http.APIError${generic}`);
|
|
824
|
+
} else {
|
|
825
|
+
if (parser !== "buffered") {
|
|
826
|
+
outputs.push(`{type: ${statusName}${generic}, parser: ${parser}}`);
|
|
827
|
+
} else {
|
|
828
|
+
outputs.push(`${statusName}${generic}`);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
878
831
|
}
|
|
832
|
+
return outputs;
|
|
879
833
|
}
|
|
880
|
-
return
|
|
834
|
+
return [];
|
|
881
835
|
}
|
|
882
836
|
function fromContentType(spec, typeScriptDeserialzer, response) {
|
|
883
837
|
if ((response.headers ?? {})["Transfer-Encoding"]) {
|
|
@@ -908,11 +862,71 @@ function streamedOutput() {
|
|
|
908
862
|
responseSchema: "ReadableStream"
|
|
909
863
|
};
|
|
910
864
|
}
|
|
865
|
+
function inputToPath(operation, inputs) {
|
|
866
|
+
const inputHeaders = [];
|
|
867
|
+
const inputQuery = [];
|
|
868
|
+
const inputBody = [];
|
|
869
|
+
const inputParams = [];
|
|
870
|
+
for (const [name, prop] of Object.entries(inputs)) {
|
|
871
|
+
if (prop.in === "headers" || prop.in === "header") {
|
|
872
|
+
inputHeaders.push(`"${name}"`);
|
|
873
|
+
} else if (prop.in === "query") {
|
|
874
|
+
inputQuery.push(`"${name}"`);
|
|
875
|
+
} else if (prop.in === "body") {
|
|
876
|
+
inputBody.push(`"${name}"`);
|
|
877
|
+
} else if (prop.in === "path") {
|
|
878
|
+
inputParams.push(`"${name}"`);
|
|
879
|
+
} else if (prop.in === "internal") {
|
|
880
|
+
continue;
|
|
881
|
+
} else {
|
|
882
|
+
throw new Error(
|
|
883
|
+
`Unknown source ${prop.in} in ${name} ${JSON.stringify(
|
|
884
|
+
prop
|
|
885
|
+
)} in ${operation.operationId}`
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return {
|
|
890
|
+
inputHeaders,
|
|
891
|
+
inputQuery,
|
|
892
|
+
inputBody,
|
|
893
|
+
inputParams
|
|
894
|
+
};
|
|
895
|
+
}
|
|
911
896
|
|
|
912
897
|
// packages/typescript/src/lib/styles/github/endpoints.txt
|
|
913
898
|
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};";
|
|
914
899
|
|
|
915
900
|
// packages/typescript/src/lib/generator.ts
|
|
901
|
+
function coearceRequestInput(spec, operation, type) {
|
|
902
|
+
let objectSchema = resolveRef(
|
|
903
|
+
spec,
|
|
904
|
+
operation.requestBody.content[type].schema
|
|
905
|
+
);
|
|
906
|
+
const xProperties = objectSchema["x-properties"] ?? {};
|
|
907
|
+
const xRequired = objectSchema["x-required"] ?? [];
|
|
908
|
+
if (type === "application/empty") {
|
|
909
|
+
objectSchema = {
|
|
910
|
+
type: "object",
|
|
911
|
+
additionalProperties: isEmpty2(xProperties)
|
|
912
|
+
};
|
|
913
|
+
} else {
|
|
914
|
+
if (objectSchema.type !== "object") {
|
|
915
|
+
objectSchema = {
|
|
916
|
+
type: "object",
|
|
917
|
+
required: [operation.requestBody.required ? "$body" : ""],
|
|
918
|
+
properties: {
|
|
919
|
+
$body: objectSchema
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return {
|
|
925
|
+
objectSchema,
|
|
926
|
+
xProperties,
|
|
927
|
+
xRequired
|
|
928
|
+
};
|
|
929
|
+
}
|
|
916
930
|
function generateCode(config) {
|
|
917
931
|
const commonZod = /* @__PURE__ */ new Map();
|
|
918
932
|
const commonZodImports = [];
|
|
@@ -928,11 +942,10 @@ function generateCode(config) {
|
|
|
928
942
|
});
|
|
929
943
|
const groups = {};
|
|
930
944
|
const endpoints = {};
|
|
931
|
-
|
|
945
|
+
forEachOperation2(config.spec, (entry, operation) => {
|
|
932
946
|
console.log(`Processing ${entry.method} ${entry.path}`);
|
|
933
947
|
groups[entry.tag] ??= [];
|
|
934
948
|
endpoints[entry.tag] ??= [];
|
|
935
|
-
const inputs = {};
|
|
936
949
|
const schemas = {};
|
|
937
950
|
const shortContenTypeMap = {
|
|
938
951
|
"application/json": "json",
|
|
@@ -945,30 +958,12 @@ function generateCode(config) {
|
|
|
945
958
|
"application/xml": "xml",
|
|
946
959
|
"text/plain": "text"
|
|
947
960
|
};
|
|
948
|
-
let outgoingContentType = "empty";
|
|
949
961
|
for (const type in operation.requestBody.content) {
|
|
950
|
-
|
|
962
|
+
const { objectSchema, xProperties, xRequired } = coearceRequestInput(
|
|
951
963
|
config.spec,
|
|
952
|
-
operation
|
|
964
|
+
operation,
|
|
965
|
+
type
|
|
953
966
|
);
|
|
954
|
-
const xProperties = objectSchema["x-properties"] ?? {};
|
|
955
|
-
const xRequired = objectSchema["x-required"] ?? [];
|
|
956
|
-
if (type === "application/empty") {
|
|
957
|
-
objectSchema = {
|
|
958
|
-
type: "object",
|
|
959
|
-
additionalProperties: isEmpty2(xProperties)
|
|
960
|
-
};
|
|
961
|
-
} else {
|
|
962
|
-
if (objectSchema.type !== "object") {
|
|
963
|
-
objectSchema = {
|
|
964
|
-
type: "object",
|
|
965
|
-
required: [operation.requestBody.required ? "$body" : ""],
|
|
966
|
-
properties: {
|
|
967
|
-
$body: objectSchema
|
|
968
|
-
}
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
967
|
const additionalProperties = {};
|
|
973
968
|
for (const [name, prop] of Object.entries(xProperties)) {
|
|
974
969
|
additionalProperties[name] = {
|
|
@@ -977,10 +972,6 @@ function generateCode(config) {
|
|
|
977
972
|
schema: prop,
|
|
978
973
|
in: prop["x-in"]
|
|
979
974
|
};
|
|
980
|
-
inputs[name] = {
|
|
981
|
-
in: prop["x-in"],
|
|
982
|
-
schema: ""
|
|
983
|
-
};
|
|
984
975
|
}
|
|
985
976
|
const schema = merge({}, objectSchema, {
|
|
986
977
|
required: Object.values(additionalProperties).filter((p) => p.required).map((p) => p.name),
|
|
@@ -989,43 +980,36 @@ function generateCode(config) {
|
|
|
989
980
|
{}
|
|
990
981
|
)
|
|
991
982
|
});
|
|
992
|
-
Object.assign(inputs, bodyInputs(config.spec, objectSchema));
|
|
993
983
|
schemas[shortContenTypeMap[type]] = zodDeserialzer.handle(schema, true);
|
|
994
984
|
}
|
|
995
|
-
|
|
996
|
-
outgoingContentType = "json";
|
|
997
|
-
} else if (operation.requestBody.content["application/x-www-form-urlencoded"]) {
|
|
998
|
-
outgoingContentType = "urlencoded";
|
|
999
|
-
} else if (operation.requestBody.content["multipart/form-data"]) {
|
|
1000
|
-
outgoingContentType = "formdata";
|
|
1001
|
-
}
|
|
985
|
+
const details = buildInput(config.spec, operation);
|
|
1002
986
|
const endpoint = toEndpoint(
|
|
1003
987
|
entry.tag,
|
|
1004
988
|
config.spec,
|
|
1005
989
|
operation,
|
|
1006
990
|
{
|
|
1007
|
-
outgoingContentType,
|
|
1008
|
-
name: operation.operationId,
|
|
1009
991
|
method: entry.method,
|
|
1010
992
|
path: entry.path,
|
|
993
|
+
operationId: operation.operationId,
|
|
1011
994
|
schemas,
|
|
1012
|
-
|
|
995
|
+
outgoingContentType: details.outgoingContentType,
|
|
996
|
+
inputs: details.inputs
|
|
1013
997
|
},
|
|
1014
|
-
|
|
998
|
+
config
|
|
1015
999
|
);
|
|
1016
1000
|
endpoints[entry.tag].push(endpoint);
|
|
1017
1001
|
groups[entry.tag].push({
|
|
1018
|
-
name: operation.operationId,
|
|
1019
|
-
inputs,
|
|
1020
|
-
outgoingContentType,
|
|
1021
|
-
schemas,
|
|
1022
1002
|
method: entry.method,
|
|
1023
|
-
path: entry.path
|
|
1003
|
+
path: entry.path,
|
|
1004
|
+
operationId: operation.operationId,
|
|
1005
|
+
schemas,
|
|
1006
|
+
outgoingContentType: details.outgoingContentType,
|
|
1007
|
+
inputs: details.inputs
|
|
1024
1008
|
});
|
|
1025
1009
|
});
|
|
1026
1010
|
const allSchemas = Object.keys(endpoints).map((it) => ({
|
|
1027
|
-
import: `import ${
|
|
1028
|
-
use: ` ...${
|
|
1011
|
+
import: `import ${camelcase3(it)} from './${config.makeImport(spinalcase2(it))}';`,
|
|
1012
|
+
use: ` ...${camelcase3(it)}`
|
|
1029
1013
|
}));
|
|
1030
1014
|
return {
|
|
1031
1015
|
groups,
|
|
@@ -1052,26 +1036,16 @@ ${allSchemas.map((it) => it.use).join(",\n")}
|
|
|
1052
1036
|
`.trim(),
|
|
1053
1037
|
...Object.fromEntries(
|
|
1054
1038
|
Object.entries(endpoints).map(([name, endpoint]) => {
|
|
1055
|
-
const imps = importsToString(
|
|
1056
|
-
...mergeImports(
|
|
1057
|
-
...endpoint.flatMap(
|
|
1058
|
-
(it) => it.responses.flatMap(
|
|
1059
|
-
(it2) => Object.values(it2.endpointImports)
|
|
1060
|
-
)
|
|
1061
|
-
)
|
|
1062
|
-
)
|
|
1063
|
-
);
|
|
1064
1039
|
return [
|
|
1065
1040
|
[
|
|
1066
|
-
join("api", `${
|
|
1041
|
+
join("api", `${spinalcase2(name)}.ts`),
|
|
1067
1042
|
`${[
|
|
1068
|
-
...imps,
|
|
1069
1043
|
`import z from 'zod';`,
|
|
1070
1044
|
`import * as http from '${config.makeImport("../http/response")}';`,
|
|
1071
1045
|
`import * as outputs from '${config.makeImport("../outputs/index")}';`,
|
|
1072
1046
|
`import { toRequest, json, urlencoded, empty, formdata, createUrl, type HeadersInit } from '${config.makeImport("../http/request")}';`,
|
|
1073
1047
|
`import { chunked, buffered } from "${config.makeImport("../http/parse-response")}";`,
|
|
1074
|
-
`import * as ${
|
|
1048
|
+
`import * as ${camelcase3(name)} from '../inputs/${config.makeImport(spinalcase2(name))}';`,
|
|
1075
1049
|
`import { createBaseUrlInterceptor, createHeadersInterceptor, type Interceptor } from '${config.makeImport("../http/interceptors")}';`,
|
|
1076
1050
|
`import { Dispatcher, fetchType, type InstanceType } from '${config.makeImport("../http/dispatcher")}';`,
|
|
1077
1051
|
`import { Pagination, OffsetPagination, CursorPagination } from "${config.makeImport("../pagination/index")}";`
|
|
@@ -1133,6 +1107,40 @@ function bodyInputs(spec, ctSchema) {
|
|
|
1133
1107
|
{}
|
|
1134
1108
|
);
|
|
1135
1109
|
}
|
|
1110
|
+
var contentTypeMap = {
|
|
1111
|
+
"application/json": "json",
|
|
1112
|
+
"application/x-www-form-urlencoded": "urlencoded",
|
|
1113
|
+
"multipart/form-data": "formdata",
|
|
1114
|
+
"application/xml": "xml",
|
|
1115
|
+
"text/plain": "text",
|
|
1116
|
+
"application/empty": "empty"
|
|
1117
|
+
};
|
|
1118
|
+
function buildInput(spec, operation) {
|
|
1119
|
+
const inputs = {};
|
|
1120
|
+
let outgoingContentType = "empty";
|
|
1121
|
+
for (const [ct, value] of Object.entries(contentTypeMap)) {
|
|
1122
|
+
if (operation.requestBody.content[ct]) {
|
|
1123
|
+
outgoingContentType = value;
|
|
1124
|
+
const { objectSchema, xProperties } = coearceRequestInput(
|
|
1125
|
+
spec,
|
|
1126
|
+
operation,
|
|
1127
|
+
ct
|
|
1128
|
+
);
|
|
1129
|
+
for (const [name, prop] of Object.entries(xProperties)) {
|
|
1130
|
+
inputs[name] = {
|
|
1131
|
+
in: prop["x-in"],
|
|
1132
|
+
schema: ""
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
Object.assign(inputs, bodyInputs(spec, objectSchema));
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return {
|
|
1140
|
+
inputs,
|
|
1141
|
+
outgoingContentType
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1136
1144
|
|
|
1137
1145
|
// packages/typescript/src/lib/http/dispatcher.txt
|
|
1138
1146
|
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";
|
|
@@ -1595,7 +1603,7 @@ var page_pagination_default = "type InferPage<T> = T extends Page<infer U> ? U :
|
|
|
1595
1603
|
|
|
1596
1604
|
// packages/typescript/src/lib/readme/readme.ts
|
|
1597
1605
|
import { isEmpty as isEmpty3 } from "@sdk-it/core";
|
|
1598
|
-
import { forEachOperation as
|
|
1606
|
+
import { forEachOperation as forEachOperation3 } from "@sdk-it/spec";
|
|
1599
1607
|
|
|
1600
1608
|
// packages/typescript/src/lib/readme/prop.emitter.ts
|
|
1601
1609
|
import { followRef as followRef4, isRef as isRef4 } from "@sdk-it/core";
|
|
@@ -1639,7 +1647,7 @@ var PropEmitter = class {
|
|
|
1639
1647
|
const defaultVal = !isRef4(schema) && schema.default !== void 0 ? ` default: ${JSON.stringify(schema.default)}` : "";
|
|
1640
1648
|
const reqMark = required ? " required" : "";
|
|
1641
1649
|
const summary = `- \`${name}\` ${rawType}${reqMark}${defaultVal}:`;
|
|
1642
|
-
const detailLines = docs.slice(1).filter((
|
|
1650
|
+
const detailLines = docs.slice(1).filter((it) => !it.startsWith("**Default:**")).map((it) => ` ${it}`);
|
|
1643
1651
|
return [summary, ...detailLines];
|
|
1644
1652
|
}
|
|
1645
1653
|
/**
|
|
@@ -1849,35 +1857,29 @@ var PropEmitter = class {
|
|
|
1849
1857
|
* Process a request body and return markdown documentation
|
|
1850
1858
|
*/
|
|
1851
1859
|
requestBody(requestBody) {
|
|
1852
|
-
if (!requestBody) return [];
|
|
1853
1860
|
const lines = [];
|
|
1854
|
-
lines.push(
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
if (
|
|
1859
|
-
const
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
lines.push(
|
|
1861
|
+
lines.push(`#### Input`);
|
|
1862
|
+
lines.push(requestBody.description || "");
|
|
1863
|
+
const contentEntries = Object.entries(requestBody.content);
|
|
1864
|
+
const multipleContentTypes = contentEntries.length > 1;
|
|
1865
|
+
if (multipleContentTypes) {
|
|
1866
|
+
for (const [contentType, mediaType] of contentEntries) {
|
|
1867
|
+
lines.push(`<details>`);
|
|
1868
|
+
lines.push(`<summary>Content Type: \`${contentType}\`</summary>`);
|
|
1869
|
+
lines.push("");
|
|
1863
1870
|
if (mediaType.schema) {
|
|
1864
1871
|
const schemaDocs = this.handle(mediaType.schema);
|
|
1865
|
-
lines.push(...schemaDocs);
|
|
1866
|
-
}
|
|
1867
|
-
} else {
|
|
1868
|
-
for (const [contentType, mediaType] of contentEntries) {
|
|
1869
|
-
lines.push(`<details>`);
|
|
1870
|
-
lines.push(
|
|
1871
|
-
`<summary><b>Content Type:</b> \`${contentType}\`</summary>`
|
|
1872
|
-
);
|
|
1873
|
-
lines.push("");
|
|
1874
|
-
if (mediaType.schema) {
|
|
1875
|
-
const schemaDocs = this.handle(mediaType.schema);
|
|
1876
|
-
lines.push(...schemaDocs.map((l) => l));
|
|
1877
|
-
}
|
|
1878
|
-
lines.push("");
|
|
1879
|
-
lines.push(`</details>`);
|
|
1872
|
+
lines.push(...schemaDocs.map((l) => l));
|
|
1880
1873
|
}
|
|
1874
|
+
lines.push("");
|
|
1875
|
+
lines.push(`</details>`);
|
|
1876
|
+
}
|
|
1877
|
+
} else {
|
|
1878
|
+
const [contentType, mediaType] = contentEntries[0];
|
|
1879
|
+
lines.push(`Content Type: \`${contentType}\``);
|
|
1880
|
+
if (mediaType.schema) {
|
|
1881
|
+
const schemaDocs = this.handle(mediaType.schema);
|
|
1882
|
+
lines.push(...schemaDocs);
|
|
1881
1883
|
}
|
|
1882
1884
|
}
|
|
1883
1885
|
return lines;
|
|
@@ -1891,7 +1893,7 @@ function toReadme(spec, generator) {
|
|
|
1891
1893
|
markdown.push(`# ${spec.info.title} TypeScript SDK`);
|
|
1892
1894
|
markdown.push("");
|
|
1893
1895
|
markdown.push(
|
|
1894
|
-
"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."
|
|
1896
|
+
"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."
|
|
1895
1897
|
);
|
|
1896
1898
|
markdown.push("");
|
|
1897
1899
|
markdown.push(generator.clientSetupDocs());
|
|
@@ -1912,40 +1914,52 @@ function toReadme(spec, generator) {
|
|
|
1912
1914
|
markdown.push("");
|
|
1913
1915
|
markdown.push("## API Reference");
|
|
1914
1916
|
markdown.push("");
|
|
1915
|
-
|
|
1917
|
+
forEachOperation3(spec, (entry, operation) => {
|
|
1916
1918
|
const { method, path } = entry;
|
|
1917
1919
|
markdown.push(
|
|
1918
|
-
|
|
1920
|
+
`### ${operation["x-fn-name"]} | ${`_${method.toUpperCase()} ${path}_`}`
|
|
1919
1921
|
);
|
|
1920
1922
|
markdown.push(operation.summary || "");
|
|
1921
1923
|
const snippet = generator.snippet(entry, operation);
|
|
1922
|
-
markdown.push(
|
|
1924
|
+
markdown.push(`#### Example usage`);
|
|
1923
1925
|
markdown.push(snippet);
|
|
1924
1926
|
const requestBodyContent = propEmitter.requestBody(operation.requestBody);
|
|
1925
1927
|
if (requestBodyContent.length > 1) {
|
|
1926
1928
|
markdown.push(requestBodyContent.join("\n\n"));
|
|
1927
1929
|
}
|
|
1928
|
-
markdown.push(
|
|
1930
|
+
markdown.push(`#### Output`);
|
|
1929
1931
|
for (const status in operation.responses) {
|
|
1930
1932
|
const response = operation.responses[status];
|
|
1931
|
-
markdown.push(`<details>`);
|
|
1932
|
-
markdown.push(
|
|
1933
|
-
`<summary><b>${status}</b> <i>${response.description}</i></summary>`
|
|
1934
|
-
);
|
|
1935
1933
|
if (!isEmpty3(response.content)) {
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1934
|
+
const contentEntries = Object.entries(response.content);
|
|
1935
|
+
if (contentEntries.length === 1) {
|
|
1936
|
+
const [contentType, mediaType] = contentEntries[0];
|
|
1937
|
+
markdown.push(`**${status}** - ${response.description}`);
|
|
1939
1938
|
markdown.push(`
|
|
1940
1939
|
**Content Type:** \`${contentType}\``);
|
|
1941
1940
|
if (mediaType.schema) {
|
|
1942
1941
|
const schemaDocs = propEmitter.handle(mediaType.schema);
|
|
1943
|
-
markdown.push(...schemaDocs
|
|
1942
|
+
markdown.push(...schemaDocs);
|
|
1943
|
+
}
|
|
1944
|
+
} else {
|
|
1945
|
+
markdown.push(`<details>`);
|
|
1946
|
+
markdown.push(
|
|
1947
|
+
`<summary><b>${status}</b> <i>${response.description}</i></summary>`
|
|
1948
|
+
);
|
|
1949
|
+
for (const [contentType, mediaType] of contentEntries) {
|
|
1950
|
+
markdown.push(`
|
|
1951
|
+
**Content Type:** \`${contentType}\``);
|
|
1952
|
+
if (mediaType.schema) {
|
|
1953
|
+
const schemaDocs = propEmitter.handle(mediaType.schema);
|
|
1954
|
+
markdown.push(...schemaDocs.map((l) => `
|
|
1944
1955
|
${l}`));
|
|
1956
|
+
}
|
|
1945
1957
|
}
|
|
1958
|
+
markdown.push(`</details>`);
|
|
1946
1959
|
}
|
|
1960
|
+
} else {
|
|
1961
|
+
markdown.push(`**${status}** - ${response.description}`);
|
|
1947
1962
|
}
|
|
1948
|
-
markdown.push(`</details>`);
|
|
1949
1963
|
}
|
|
1950
1964
|
});
|
|
1951
1965
|
if (spec.components?.schemas) {
|
|
@@ -1973,11 +1987,11 @@ ${l}`));
|
|
|
1973
1987
|
}
|
|
1974
1988
|
|
|
1975
1989
|
// packages/typescript/src/lib/typescript-snippet.ts
|
|
1976
|
-
import { camelcase as
|
|
1990
|
+
import { camelcase as camelcase4, spinalcase as spinalcase3 } from "stringcase";
|
|
1977
1991
|
import { isEmpty as isEmpty4, pascalcase as pascalcase4, resolveRef as resolveRef3 } from "@sdk-it/core";
|
|
1978
1992
|
import "@sdk-it/readme";
|
|
1979
1993
|
import {
|
|
1980
|
-
forEachOperation as
|
|
1994
|
+
forEachOperation as forEachOperation4,
|
|
1981
1995
|
patchParameters,
|
|
1982
1996
|
securityToOptions
|
|
1983
1997
|
} from "@sdk-it/spec";
|
|
@@ -2028,11 +2042,11 @@ var SnippetEmitter = class {
|
|
|
2028
2042
|
switch (schema.format) {
|
|
2029
2043
|
case "date-time":
|
|
2030
2044
|
case "datetime":
|
|
2031
|
-
return
|
|
2045
|
+
return `2025-07-17T09:08:00.097Z`;
|
|
2032
2046
|
case "date":
|
|
2033
|
-
return
|
|
2047
|
+
return `2025-07-17`;
|
|
2034
2048
|
case "time":
|
|
2035
|
-
return
|
|
2049
|
+
return `09:08:00.097Z`;
|
|
2036
2050
|
case "email":
|
|
2037
2051
|
return "user@example.com";
|
|
2038
2052
|
case "uuid":
|
|
@@ -2172,7 +2186,7 @@ var SnippetEmitter = class {
|
|
|
2172
2186
|
};
|
|
2173
2187
|
|
|
2174
2188
|
// packages/typescript/src/lib/typescript-snippet.ts
|
|
2175
|
-
var
|
|
2189
|
+
var TypeScriptSnippet = class {
|
|
2176
2190
|
#spec;
|
|
2177
2191
|
#settings;
|
|
2178
2192
|
#snippetEmitter;
|
|
@@ -2183,7 +2197,7 @@ var TypeScriptGenerator = class {
|
|
|
2183
2197
|
this.#settings = settings;
|
|
2184
2198
|
this.#snippetEmitter = new SnippetEmitter(spec);
|
|
2185
2199
|
this.#clientName = settings.name?.trim() ? pascalcase4(settings.name) : "Client";
|
|
2186
|
-
this.#packageName = settings.name ? `@${
|
|
2200
|
+
this.#packageName = settings.name ? `@${spinalcase3(this.#clientName.toLowerCase())}/sdk` : "sdk";
|
|
2187
2201
|
}
|
|
2188
2202
|
succinct(entry, operation, values) {
|
|
2189
2203
|
let payload = "{}";
|
|
@@ -2302,7 +2316,7 @@ var TypeScriptGenerator = class {
|
|
|
2302
2316
|
};
|
|
2303
2317
|
}
|
|
2304
2318
|
#toRequest(entry, payload) {
|
|
2305
|
-
return `await ${
|
|
2319
|
+
return `await ${camelcase4(this.#clientName)}.request('${entry.method.toUpperCase()} ${entry.path}', ${payload});`;
|
|
2306
2320
|
}
|
|
2307
2321
|
snippet(entry, operation, config = {}) {
|
|
2308
2322
|
const payload = this.succinct(entry, operation, config);
|
|
@@ -2343,7 +2357,7 @@ ${client.use}`;
|
|
|
2343
2357
|
#constructClient(options = {}) {
|
|
2344
2358
|
return {
|
|
2345
2359
|
import: `import { ${this.#clientName} } from '${this.#packageName}';`,
|
|
2346
|
-
use: `const ${
|
|
2360
|
+
use: `const ${camelcase4(this.#clientName)} = new ${this.#clientName}({
|
|
2347
2361
|
${Object.entries(
|
|
2348
2362
|
options
|
|
2349
2363
|
).map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join(",\n ")}
|
|
@@ -2878,7 +2892,7 @@ function availablePaginationTypes(spec) {
|
|
|
2878
2892
|
let offset = false;
|
|
2879
2893
|
let page = false;
|
|
2880
2894
|
let cursor = false;
|
|
2881
|
-
|
|
2895
|
+
forEachOperation4(spec, (entry, operation) => {
|
|
2882
2896
|
if (operation["x-pagination"]) {
|
|
2883
2897
|
switch (operation["x-pagination"].type) {
|
|
2884
2898
|
case "offset":
|
|
@@ -2935,7 +2949,6 @@ async function generate(openapi, settings) {
|
|
|
2935
2949
|
},
|
|
2936
2950
|
false
|
|
2937
2951
|
);
|
|
2938
|
-
const generator = new TypeScriptGenerator(spec, settings);
|
|
2939
2952
|
const style = Object.assign(
|
|
2940
2953
|
{},
|
|
2941
2954
|
{
|
|
@@ -2970,7 +2983,7 @@ async function generate(openapi, settings) {
|
|
|
2970
2983
|
makeImport
|
|
2971
2984
|
});
|
|
2972
2985
|
const clientName = pascalcase5((settings.name || "client").trim());
|
|
2973
|
-
const packageName = settings.name ? `@${
|
|
2986
|
+
const packageName = settings.name ? `@${spinalcase4(settings.name.trim().toLowerCase())}/sdk` : "sdk";
|
|
2974
2987
|
const inputs = toInputs(groups, commonZod, makeImport);
|
|
2975
2988
|
const models = serializeModels(spec);
|
|
2976
2989
|
await settings.writer(output, {
|
|
@@ -3070,13 +3083,20 @@ ${template2(dispatcher_default, {})({ throwError: !style.errorAsValue, outputTyp
|
|
|
3070
3083
|
"models/index.ts": modelsIndex
|
|
3071
3084
|
// ...(modelsImports.length ? { 'models/index.ts': modelsIndex } : {}),
|
|
3072
3085
|
});
|
|
3086
|
+
if (settings.agentTools) {
|
|
3087
|
+
await settings.writer(output, {
|
|
3088
|
+
"agents.ts": `${generateAISDKTools(spec)}
|
|
3089
|
+
${utils_default}`
|
|
3090
|
+
// 'agents.ts': `${generateOpenAIAgentTools(spec)}\n${utilsTxt}`,
|
|
3091
|
+
});
|
|
3092
|
+
}
|
|
3073
3093
|
await settings.writer(output, {
|
|
3074
3094
|
"index.ts": await getFolderExports(
|
|
3075
3095
|
output,
|
|
3076
3096
|
settings.readFolder,
|
|
3077
3097
|
settings.useTsExtension,
|
|
3078
3098
|
["ts"],
|
|
3079
|
-
(config) => config.fileName.endsWith("pagination")
|
|
3099
|
+
(config) => config.fileName.endsWith("pagination") || config.fileName.endsWith("agents")
|
|
3080
3100
|
)
|
|
3081
3101
|
});
|
|
3082
3102
|
if (settings.mode === "full") {
|
|
@@ -3138,7 +3158,7 @@ ${template2(dispatcher_default, {})({ throwError: !style.errorAsValue, outputTyp
|
|
|
3138
3158
|
}
|
|
3139
3159
|
if (settings.readme) {
|
|
3140
3160
|
await settings.writer(settings.mode === "full" ? settings.output : output, {
|
|
3141
|
-
"README.md": toReadme(spec,
|
|
3161
|
+
"README.md": toReadme(spec, new TypeScriptSnippet(spec, settings))
|
|
3142
3162
|
});
|
|
3143
3163
|
}
|
|
3144
3164
|
await settings.formatCode?.({
|
|
@@ -3172,7 +3192,7 @@ ${schema.description ? `
|
|
|
3172
3192
|
` : ""}`,
|
|
3173
3193
|
`export type ${pascalcase5(sanitizeTag4(name))} = ${typeContent};`
|
|
3174
3194
|
];
|
|
3175
|
-
const fileName = responseGroup ? join2(folder, `${
|
|
3195
|
+
const fileName = responseGroup ? join2(folder, `${spinalcase4(responseGroup)}.ts`) : join2(folder, `${spinalcase4(name)}.ts`);
|
|
3176
3196
|
filesMap[fileName] ??= [];
|
|
3177
3197
|
filesMap[fileName].push(fileContent.join("\n"));
|
|
3178
3198
|
}
|
|
@@ -3193,18 +3213,18 @@ function toInputs(operationsSet, commonZod, makeImport) {
|
|
|
3193
3213
|
const output = [];
|
|
3194
3214
|
const imports = /* @__PURE__ */ new Set(['import { z } from "zod";']);
|
|
3195
3215
|
for (const operation of operations) {
|
|
3196
|
-
const schemaName =
|
|
3216
|
+
const schemaName = camelcase5(`${operation.operationId} schema`);
|
|
3197
3217
|
const schema = `export const ${schemaName} = ${Object.keys(operation.schemas).length === 1 ? Object.values(operation.schemas)[0] : toLitObject2(operation.schemas)};`;
|
|
3198
3218
|
for (const it of commonImports) {
|
|
3199
3219
|
if (schema.includes(it)) {
|
|
3200
3220
|
imports.add(
|
|
3201
|
-
`import { ${it} } from './schemas/${makeImport(
|
|
3221
|
+
`import { ${it} } from './schemas/${makeImport(spinalcase4(it))}';`
|
|
3202
3222
|
);
|
|
3203
3223
|
}
|
|
3204
3224
|
}
|
|
3205
3225
|
output.push(schema);
|
|
3206
3226
|
}
|
|
3207
|
-
inputs[`inputs/${
|
|
3227
|
+
inputs[`inputs/${spinalcase4(name)}.ts`] = [...imports, ...output].join("\n") + "\n";
|
|
3208
3228
|
}
|
|
3209
3229
|
const schemas = commonZod.entries().reduce((acc, [name, schema]) => {
|
|
3210
3230
|
const output = [`import { z } from 'zod';`];
|
|
@@ -3213,13 +3233,13 @@ function toInputs(operationsSet, commonZod, makeImport) {
|
|
|
3213
3233
|
const preciseMatch = new RegExp(`\\b${schema2}\\b`);
|
|
3214
3234
|
if (preciseMatch.test(content) && schema2 !== name) {
|
|
3215
3235
|
output.push(
|
|
3216
|
-
`import { ${schema2} } from './${makeImport(
|
|
3236
|
+
`import { ${schema2} } from './${makeImport(spinalcase4(schema2))}';`
|
|
3217
3237
|
);
|
|
3218
3238
|
}
|
|
3219
3239
|
}
|
|
3220
3240
|
output.push(content);
|
|
3221
3241
|
return [
|
|
3222
|
-
[`inputs/schemas/${
|
|
3242
|
+
[`inputs/schemas/${spinalcase4(name)}.ts`, output.join("\n")],
|
|
3223
3243
|
...acc
|
|
3224
3244
|
];
|
|
3225
3245
|
}, []);
|
|
@@ -3229,8 +3249,13 @@ function toInputs(operationsSet, commonZod, makeImport) {
|
|
|
3229
3249
|
};
|
|
3230
3250
|
}
|
|
3231
3251
|
export {
|
|
3232
|
-
|
|
3252
|
+
TypeScriptSnippet,
|
|
3253
|
+
buildInput,
|
|
3233
3254
|
generate,
|
|
3255
|
+
generateCode,
|
|
3256
|
+
inputToPath,
|
|
3257
|
+
toEndpoint,
|
|
3258
|
+
toHttpOutput,
|
|
3234
3259
|
toInputs
|
|
3235
3260
|
};
|
|
3236
3261
|
//# sourceMappingURL=index.js.map
|