@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.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 camelcase4, spinalcase as spinalcase3 } from "stringcase";
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 camelcase2, spinalcase } from "stringcase";
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 { forEachOperation } from "@sdk-it/spec";
590
-
591
- // packages/typescript/src/lib/import-utilities.ts
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 = camelcase(`${operation.name} schema`);
668
- const schemaRef = `${camelcase(groupName)}.${schemaName}`;
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
- for (const status in specOperation.responses) {
697
- const handled = handleResponse(
680
+ const outputs = Object.keys(specOperation.responses).flatMap(
681
+ (status) => toHttpOutput(
698
682
  spec,
699
- operation.name,
683
+ specOperation.operationId,
700
684
  status,
701
- specOperation.responses[status],
702
- utils
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 { responses, schemas };
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 handleResponse(spec, operationName, status, response, utils) {
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
- let parser = "buffered";
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 responseSchema = contentTypeResult.responseSchema;
856
- responses.push({
857
- name: interfaceName,
858
- schema: responseSchema,
859
- description: response.description
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.push(
876
- parser !== "buffered" ? `{type: ${statusName}<outputs.${interfaceName}>, parser: ${parser}}` : `${statusName}<outputs.${interfaceName}>`
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 { schemas, imports, endpointImports, responses, outputs };
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
- forEachOperation(config.spec, (entry, operation) => {
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
- let objectSchema = resolveRef(
962
+ const { objectSchema, xProperties, xRequired } = coearceRequestInput(
951
963
  config.spec,
952
- operation.requestBody.content[type].schema
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
- if (operation.requestBody.content["application/json"]) {
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
- inputs
995
+ outgoingContentType: details.outgoingContentType,
996
+ inputs: details.inputs
1013
997
  },
1014
- { makeImport: config.makeImport, style: config.style }
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 ${camelcase2(it)} from './${config.makeImport(spinalcase(it))}';`,
1028
- use: ` ...${camelcase2(it)}`
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", `${spinalcase(name)}.ts`),
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 ${camelcase2(name)} from '../inputs/${config.makeImport(spinalcase(name))}';`,
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 forEachOperation2 } from "@sdk-it/spec";
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((l) => !l.startsWith("**Default:**")).map((l) => ` ${l}`);
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(`##### Request Body`);
1855
- if (requestBody.description) {
1856
- lines.push(requestBody.description);
1857
- }
1858
- if (requestBody.content) {
1859
- const contentEntries = Object.entries(requestBody.content);
1860
- if (contentEntries.length === 1) {
1861
- const [contentType, mediaType] = contentEntries[0];
1862
- lines.push(`**Content Type:** \`${contentType}\``);
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
- forEachOperation2(spec, (entry, operation) => {
1917
+ forEachOperation3(spec, (entry, operation) => {
1916
1918
  const { method, path } = entry;
1917
1919
  markdown.push(
1918
- `#### ${operation["x-fn-name"]} | ${`_${method.toUpperCase()} ${path}_`}`
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(`##### Example usage`);
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(`##### Responses`);
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
- for (const [contentType, mediaType] of Object.entries(
1937
- response.content
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.map((l) => `
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 camelcase3, spinalcase as spinalcase2 } from "stringcase";
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 forEachOperation3,
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 (/* @__PURE__ */ new Date()).toISOString();
2045
+ return `2025-07-17T09:08:00.097Z`;
2032
2046
  case "date":
2033
- return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2047
+ return `2025-07-17`;
2034
2048
  case "time":
2035
- return (/* @__PURE__ */ new Date()).toISOString().split("T")[1];
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 TypeScriptGenerator = class {
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 ? `@${spinalcase2(this.#clientName.toLowerCase())}/sdk` : "sdk";
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 ${camelcase3(this.#clientName)}.request('${entry.method.toUpperCase()} ${entry.path}', ${payload});`;
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 ${camelcase3(this.#clientName)} = new ${this.#clientName}({
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
- forEachOperation3(spec, (entry, operation) => {
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 ? `@${spinalcase3(settings.name.trim().toLowerCase())}/sdk` : "sdk";
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, generator)
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, `${spinalcase3(responseGroup)}.ts`) : join2(folder, `${spinalcase3(name)}.ts`);
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 = camelcase4(`${operation.name} schema`);
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(spinalcase3(it))}';`
3221
+ `import { ${it} } from './schemas/${makeImport(spinalcase4(it))}';`
3202
3222
  );
3203
3223
  }
3204
3224
  }
3205
3225
  output.push(schema);
3206
3226
  }
3207
- inputs[`inputs/${spinalcase3(name)}.ts`] = [...imports, ...output].join("\n") + "\n";
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(spinalcase3(schema2))}';`
3236
+ `import { ${schema2} } from './${makeImport(spinalcase4(schema2))}';`
3217
3237
  );
3218
3238
  }
3219
3239
  }
3220
3240
  output.push(content);
3221
3241
  return [
3222
- [`inputs/schemas/${spinalcase3(name)}.ts`, output.join("\n")],
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
- TypeScriptGenerator,
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