@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.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,
@@ -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 camelcase2, spinalcase } from "stringcase";
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 { forEachOperation } from "@sdk-it/spec";
594
-
595
- // packages/typescript/src/lib/import-utilities.ts
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 = camelcase(`${operation.name} schema`);
672
- const schemaRef = `${camelcase(groupName)}.${schemaName}`;
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
- for (const status in specOperation.responses) {
701
- const handled = handleResponse(
679
+ const outputs = Object.keys(specOperation.responses).flatMap(
680
+ (status) => toHttpOutput(
702
681
  spec,
703
- operation.name,
682
+ specOperation.operationId,
704
683
  status,
705
- specOperation.responses[status],
706
- utils
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 { responses, schemas };
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 handleResponse(spec, operationName, status, response, utils) {
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
- let parser = "buffered";
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 responseSchema = contentTypeResult.responseSchema;
860
- responses.push({
861
- name: interfaceName,
862
- schema: responseSchema,
863
- description: response.description
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.push(
880
- parser !== "buffered" ? `{type: ${statusName}<outputs.${interfaceName}>, parser: ${parser}}` : `${statusName}<outputs.${interfaceName}>`
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 { schemas, imports, endpointImports, responses, outputs };
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
- forEachOperation(config.spec, (entry, operation) => {
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
- let objectSchema = resolveRef(
955
- config.spec,
956
- operation.requestBody.content[type].schema
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
- inputs
976
+ outgoingContentType: details.outgoingContentType,
977
+ inputs: details.inputs
1017
978
  },
1018
- { makeImport: config.makeImport, style: config.style }
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 ${camelcase2(it)} from './${config.makeImport(spinalcase(it))}';`,
1032
- use: ` ...${camelcase2(it)}`
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", `${spinalcase(name)}.ts`),
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 ${camelcase2(name)} from '../inputs/${config.makeImport(spinalcase(name))}';`,
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 forEachOperation2 } from "@sdk-it/spec";
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
- forEachOperation2(spec, (entry, operation) => {
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 camelcase3, spinalcase as spinalcase2 } from "stringcase";
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 forEachOperation3,
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 (/* @__PURE__ */ new Date()).toISOString();
2053
+ return `2025-07-17T09:08:00.097Z`;
2042
2054
  case "date":
2043
- return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2055
+ return `2025-07-17`;
2044
2056
  case "time":
2045
- return (/* @__PURE__ */ new Date()).toISOString().split("T")[1];
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 TypeScriptGenerator = class {
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 ? `@${spinalcase2(this.#clientName.toLowerCase())}/sdk` : "sdk";
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 ${camelcase3(this.#clientName)}.request('${entry.method.toUpperCase()} ${entry.path}', ${payload});`;
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 ${camelcase3(this.#clientName)} = new ${this.#clientName}({
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
- forEachOperation3(spec, (entry, operation) => {
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 = augmentSpec(
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 ? `@${spinalcase3(settings.name.trim().toLowerCase())}/sdk` : "sdk";
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": `${generateAgentTools(spec)}
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, generator)
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, `${spinalcase3(responseGroup)}.ts`) : join2(folder, `${spinalcase3(name)}.ts`);
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 = camelcase4(`${operation.name} schema`);
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(spinalcase3(it))}';`
3227
+ `import { ${it} } from './schemas/${makeImport(spinalcase4(it))}';`
3218
3228
  );
3219
3229
  }
3220
3230
  }
3221
3231
  output.push(schema);
3222
3232
  }
3223
- inputs[`inputs/${spinalcase3(name)}.ts`] = [...imports, ...output].join("\n") + "\n";
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(spinalcase3(schema2))}';`
3242
+ `import { ${schema2} } from './${makeImport(spinalcase4(schema2))}';`
3233
3243
  );
3234
3244
  }
3235
3245
  }
3236
3246
  output.push(content);
3237
3247
  return [
3238
- [`inputs/schemas/${spinalcase3(name)}.ts`, output.join("\n")],
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
- TypeScriptGenerator,
3258
+ TypeScriptSnippet,
3259
+ buildInput,
3283
3260
  generate,
3284
- generateAgentTools,
3261
+ generateCode,
3262
+ inputToPath,
3263
+ operationSchema,
3264
+ toEndpoint,
3265
+ toHttpOutput,
3285
3266
  toInputs
3286
3267
  };
3287
3268
  //# sourceMappingURL=index.js.map