@sdk-it/typescript 0.17.0 → 0.19.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
@@ -1,12 +1,138 @@
1
1
  // packages/typescript/src/lib/generate.ts
2
- import { join as join2 } from "node:path";
2
+ import { template as template2 } from "lodash-es";
3
+ import { join as join3 } from "node:path";
3
4
  import { npmRunPathEnv } from "npm-run-path";
4
5
  import { spinalcase as spinalcase3 } from "stringcase";
5
- import { getFolderExports, methods, writeFiles } from "@sdk-it/core";
6
+
7
+ // packages/core/dist/index.js
8
+ import {
9
+ pascalcase as _pascalcase,
10
+ snakecase as _snakecase,
11
+ spinalcase as _spinalcase
12
+ } from "stringcase";
13
+ import ts, { TypeFlags, symbolName } from "typescript";
14
+ import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
15
+ import { dirname, extname, isAbsolute, join } from "node:path";
16
+ import debug from "debug";
17
+ import ts2 from "typescript";
18
+ import { get } from "lodash-es";
19
+ var deriveSymbol = Symbol.for("serialize");
20
+ var $types = Symbol.for("types");
21
+ async function exist(file) {
22
+ return stat(file).then(() => true).catch(() => false);
23
+ }
24
+ async function writeFiles(dir, contents) {
25
+ await Promise.all(
26
+ Object.entries(contents).map(async ([file, content]) => {
27
+ if (content === null) {
28
+ return;
29
+ }
30
+ const filePath = isAbsolute(file) ? file : join(dir, file);
31
+ await mkdir(dirname(filePath), { recursive: true });
32
+ if (typeof content === "string") {
33
+ await writeFile(filePath, content, "utf-8");
34
+ } else {
35
+ if (content.ignoreIfExists) {
36
+ if (!await exist(filePath)) {
37
+ await writeFile(filePath, content.content, "utf-8");
38
+ }
39
+ } else {
40
+ await writeFile(filePath, content.content, "utf-8");
41
+ }
42
+ }
43
+ })
44
+ );
45
+ }
46
+ async function getFolderExports(folder, includeExtension = true, extensions = ["ts"], ignore = () => false) {
47
+ const files = await readdir(folder, { withFileTypes: true });
48
+ const exports = [];
49
+ for (const file of files) {
50
+ if (ignore(file)) {
51
+ continue;
52
+ }
53
+ if (file.isDirectory()) {
54
+ if (await exist(`${file.parentPath}/${file.name}/index.ts`)) {
55
+ exports.push(
56
+ `export * from './${file.name}/index${includeExtension ? ".ts" : ""}';`
57
+ );
58
+ }
59
+ } else if (file.name !== "index.ts" && extensions.includes(getExt(file.name))) {
60
+ exports.push(
61
+ `export * from './${includeExtension ? file.name : file.name.replace(extname(file.name), "")}';`
62
+ );
63
+ }
64
+ }
65
+ return exports.join("\n");
66
+ }
67
+ var getExt = (fileName) => {
68
+ if (!fileName) {
69
+ return "";
70
+ }
71
+ const lastDot = fileName.lastIndexOf(".");
72
+ if (lastDot === -1) {
73
+ return "";
74
+ }
75
+ const ext = fileName.slice(lastDot + 1).split("/").filter(Boolean).join("");
76
+ if (ext === fileName) {
77
+ return "";
78
+ }
79
+ return ext || "txt";
80
+ };
81
+ var methods = [
82
+ "get",
83
+ "post",
84
+ "put",
85
+ "patch",
86
+ "delete",
87
+ "trace",
88
+ "head"
89
+ ];
90
+ var logger = debug("january:client");
91
+ function isRef(obj) {
92
+ return obj && "$ref" in obj;
93
+ }
94
+ function cleanRef(ref) {
95
+ return ref.replace(/^#\//, "");
96
+ }
97
+ function parseRef(ref) {
98
+ const parts = ref.split("/");
99
+ const [model] = parts.splice(-1);
100
+ const [namespace] = parts.splice(-1);
101
+ return {
102
+ model,
103
+ namespace,
104
+ path: cleanRef(parts.join("/"))
105
+ };
106
+ }
107
+ function followRef(spec, ref) {
108
+ const pathParts = cleanRef(ref).split("/");
109
+ const entry = get(spec, pathParts);
110
+ if (entry && "$ref" in entry) {
111
+ return followRef(spec, entry.$ref);
112
+ }
113
+ return entry;
114
+ }
115
+ function removeDuplicates(data, accessor = (item) => item) {
116
+ return [...new Map(data.map((x) => [accessor(x), x])).values()];
117
+ }
118
+ function toLitObject(obj, accessor = (value) => value) {
119
+ return `{${Object.keys(obj).map((key) => `${key}: ${accessor(obj[key])}`).join(", ")}}`;
120
+ }
121
+ function isEmpty(value) {
122
+ if (value === null || value === void 0 || value === "") {
123
+ return true;
124
+ }
125
+ if (Array.isArray(value) && value.length === 0) {
126
+ return true;
127
+ }
128
+ if (typeof value === "object" && Object.keys(value).length === 0) {
129
+ return true;
130
+ }
131
+ return false;
132
+ }
6
133
 
7
134
  // packages/typescript/src/lib/client.ts
8
- import { toLitObject } from "@sdk-it/core";
9
- var client_default = (spec) => {
135
+ var client_default = (spec, style) => {
10
136
  const optionsEntries = Object.entries(spec.options).map(
11
137
  ([key, value]) => [`'${key}'`, value]
12
138
  );
@@ -28,8 +154,8 @@ var client_default = (spec) => {
28
154
  }
29
155
  };
30
156
  return `
31
- import type { RequestConfig } from './http/${spec.makeImport("request")}';
32
- import { fetchType, sendRequest, parse } from './http/${spec.makeImport("send-request")}';
157
+ import type { HeadersInit, RequestConfig } from './http/${spec.makeImport("request")}';
158
+ import { fetchType, dispatch, parse } from './http/${spec.makeImport("send-request")}';
33
159
  import z from 'zod';
34
160
  import type { Endpoints } from './api/${spec.makeImport("endpoints")}';
35
161
  import schemas from './api/${spec.makeImport("schemas")}';
@@ -56,9 +182,9 @@ export class ${spec.name} {
56
182
  endpoint: E,
57
183
  input: Endpoints[E]['input'],
58
184
  options?: { signal?: AbortSignal, headers?: HeadersInit },
59
- ): Promise<readonly [Endpoints[E]['output'], Endpoints[E]['error'] | null]> {
185
+ ) ${style.errorAsValue ? `: Promise<readonly [Endpoints[E]['output'], Endpoints[E]['error'] | null]>` : `: Promise<Endpoints[E]['output']>`} {
60
186
  const route = schemas[endpoint];
61
- return sendRequest(Object.assign(this.#defaultInputs, input), route, {
187
+ const result = await dispatch(Object.assign(this.#defaultInputs, input), route, {
62
188
  fetch: this.options.fetch,
63
189
  interceptors: [
64
190
  createHeadersInterceptor(() => this.defaultHeaders, options?.headers ?? {}),
@@ -66,20 +192,23 @@ export class ${spec.name} {
66
192
  ],
67
193
  signal: options?.signal,
68
194
  });
195
+ return ${style.errorAsValue ? `result as [Endpoints[E]['output'], Endpoints[E]['error'] | null]` : `result as Endpoints[E]['output']`};
69
196
  }
70
197
 
71
198
  async prepare<E extends keyof Endpoints>(
72
199
  endpoint: E,
73
200
  input: Endpoints[E]['input'],
74
201
  options?: { headers?: HeadersInit },
75
- ): Promise<
202
+ ): ${style.errorAsValue ? `Promise<
76
203
  readonly [
77
204
  RequestConfig & {
78
205
  parse: (response: Response) => ReturnType<typeof parse>;
79
206
  },
80
207
  ParseError<(typeof schemas)[E]['schema']> | null,
81
208
  ]
82
- > {
209
+ >` : `Promise<RequestConfig & {
210
+ parse: (response: Response) => ReturnType<typeof parse>;
211
+ }>`} {
83
212
  const route = schemas[endpoint];
84
213
 
85
214
  const interceptors = [
@@ -91,7 +220,7 @@ export class ${spec.name} {
91
220
  ];
92
221
  const [parsedInput, parseError] = parseInput(route.schema, input);
93
222
  if (parseError) {
94
- return [null as never, parseError as never] as const;
223
+ ${style.errorAsValue ? "return [null as never, parseError as never] as const;" : "throw parseError;"}
95
224
  }
96
225
 
97
226
  let config = route.toRequest(parsedInput as never);
@@ -100,10 +229,8 @@ export class ${spec.name} {
100
229
  config = await interceptor.before(config);
101
230
  }
102
231
  }
103
- return [
104
- { ...config, parse: (response: Response) => parse(route, response) },
105
- null as never,
106
- ] as const;
232
+ const prepared = { ...config, parse: (response: Response) => parse(route, response) };
233
+ return ${style.errorAsValue ? "[prepared, null as never] as const;" : "prepared"}
107
234
  }
108
235
 
109
236
  get defaultHeaders() {
@@ -127,10 +254,9 @@ export class ${spec.name} {
127
254
  };
128
255
 
129
256
  // packages/typescript/src/lib/generator.ts
130
- import { merge } from "lodash-es";
131
- import { join } from "node:path";
257
+ import { merge, template } from "lodash-es";
258
+ import { join as join2 } from "node:path";
132
259
  import { camelcase as camelcase3, pascalcase as pascalcase2, spinalcase as spinalcase2 } from "stringcase";
133
- import { followRef as followRef4, isEmpty, isRef as isRef5 } from "@sdk-it/core";
134
260
 
135
261
  // packages/spec/dist/lib/operation.js
136
262
  import { camelcase } from "stringcase";
@@ -395,7 +521,6 @@ function determineGenericTag(pathString, operation) {
395
521
  }
396
522
 
397
523
  // packages/typescript/src/lib/emitters/zod.ts
398
- import { cleanRef, followRef, isRef, parseRef } from "@sdk-it/core";
399
524
  var ZodDeserialzer = class {
400
525
  circularRefTracker = /* @__PURE__ */ new Set();
401
526
  #spec;
@@ -657,12 +782,10 @@ function appendDefault(defaultValue) {
657
782
  }
658
783
 
659
784
  // packages/typescript/src/lib/sdk.ts
660
- import { get } from "lodash-es";
785
+ import { get as get2 } from "lodash-es";
661
786
  import { camelcase as camelcase2, pascalcase, spinalcase } from "stringcase";
662
- import { followRef as followRef3, isRef as isRef4, toLitObject as toLitObject2 } from "@sdk-it/core";
663
787
 
664
788
  // packages/typescript/src/lib/emitters/interface.ts
665
- import { cleanRef as cleanRef2, followRef as followRef2, isRef as isRef2, parseRef as parseRef2 } from "@sdk-it/core";
666
789
  var TypeScriptDeserialzer = class {
667
790
  circularRefTracker = /* @__PURE__ */ new Set();
668
791
  #spec;
@@ -671,70 +794,12 @@ var TypeScriptDeserialzer = class {
671
794
  this.#spec = spec;
672
795
  this.#onRef = onRef;
673
796
  }
674
- #stringifyKey = (key) => {
675
- const reservedWords = [
676
- "constructor",
677
- "prototype",
678
- "break",
679
- "case",
680
- "catch",
681
- "class",
682
- "const",
683
- "continue",
684
- "debugger",
685
- "default",
686
- "delete",
687
- "do",
688
- "else",
689
- "export",
690
- "extends",
691
- "false",
692
- "finally",
693
- "for",
694
- "function",
695
- "if",
696
- "import",
697
- "in",
698
- "instanceof",
699
- "new",
700
- "null",
701
- "return",
702
- "super",
703
- "switch",
704
- "this",
705
- "throw",
706
- "true",
707
- "try",
708
- "typeof",
709
- "var",
710
- "void",
711
- "while",
712
- "with",
713
- "yield"
714
- ];
715
- if (reservedWords.includes(key)) {
716
- return `'${key}'`;
717
- }
718
- if (key.trim() === "") {
719
- return `'${key}'`;
720
- }
721
- const firstChar = key.charAt(0);
722
- const validFirstChar = firstChar >= "a" && firstChar <= "z" || firstChar >= "A" && firstChar <= "Z" || firstChar === "_" || firstChar === "$";
723
- if (!validFirstChar) {
724
- return `'${key.replace(/'/g, "\\'")}'`;
725
- }
726
- for (let i = 1; i < key.length; i++) {
727
- const char = key.charAt(i);
728
- const validChar = char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char >= "0" && char <= "9" || char === "_" || char === "$";
729
- if (!validChar) {
730
- return `'${key.replace(/'/g, "\\'")}'`;
731
- }
732
- }
733
- return key;
734
- };
735
- #stringifyKeyV2 = (value) => {
797
+ #stringifyKey = (value) => {
736
798
  return `'${value}'`;
737
799
  };
800
+ #isInternal = (schema) => {
801
+ return isRef(schema) ? false : !!schema["x-internal"];
802
+ };
738
803
  /**
739
804
  * Handle objects (properties)
740
805
  */
@@ -743,7 +808,7 @@ var TypeScriptDeserialzer = class {
743
808
  const propEntries = Object.entries(properties).map(([key, propSchema]) => {
744
809
  const isRequired = (schema.required ?? []).includes(key);
745
810
  const tsType = this.handle(propSchema, isRequired);
746
- return `${this.#stringifyKeyV2(key)}: ${tsType}`;
811
+ return `${this.#isInternal(propSchema) ? key : this.#stringifyKey(key)}: ${tsType}`;
747
812
  });
748
813
  if (schema.additionalProperties) {
749
814
  if (typeof schema.additionalProperties === "object") {
@@ -794,14 +859,14 @@ var TypeScriptDeserialzer = class {
794
859
  }
795
860
  }
796
861
  ref($ref, required) {
797
- const schemaName = cleanRef2($ref).split("/").pop();
862
+ const schemaName = cleanRef($ref).split("/").pop();
798
863
  if (this.circularRefTracker.has(schemaName)) {
799
864
  return schemaName;
800
865
  }
801
866
  this.circularRefTracker.add(schemaName);
802
867
  this.#onRef?.(
803
868
  schemaName,
804
- this.handle(followRef2(this.#spec, $ref), required)
869
+ this.handle(followRef(this.#spec, $ref), required)
805
870
  );
806
871
  this.circularRefTracker.delete(schemaName);
807
872
  return appendOptional2(schemaName, required);
@@ -819,8 +884,8 @@ var TypeScriptDeserialzer = class {
819
884
  }
820
885
  oneOf(schemas, required) {
821
886
  const oneOfTypes = schemas.map((sub) => {
822
- if (isRef2(sub)) {
823
- const { model } = parseRef2(sub.$ref);
887
+ if (isRef(sub)) {
888
+ const { model } = parseRef(sub.$ref);
824
889
  if (this.circularRefTracker.has(model)) {
825
890
  return model;
826
891
  }
@@ -867,7 +932,7 @@ var TypeScriptDeserialzer = class {
867
932
  return appendOptional2(type, required);
868
933
  }
869
934
  handle(schema, required) {
870
- if (isRef2(schema)) {
935
+ if (isRef(schema)) {
871
936
  return this.ref(schema.$ref, required);
872
937
  }
873
938
  if (schema.allOf && Array.isArray(schema.allOf)) {
@@ -882,6 +947,12 @@ var TypeScriptDeserialzer = class {
882
947
  if (schema.enum && Array.isArray(schema.enum)) {
883
948
  return this.enum(schema.enum, required);
884
949
  }
950
+ if (schema.const) {
951
+ if (schema["x-internal"]) {
952
+ return `${schema.const}`;
953
+ }
954
+ return this.enum([schema.const], required);
955
+ }
885
956
  const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
886
957
  if (!types.length) {
887
958
  if ("properties" in schema) {
@@ -900,20 +971,12 @@ var TypeScriptDeserialzer = class {
900
971
  }
901
972
  return this.normal(types[0], schema, required);
902
973
  }
903
- /**
904
- * Generate an interface declaration
905
- */
906
- generateInterface(name, schema) {
907
- const content = this.handle(schema, true);
908
- return `interface ${name} ${content}`;
909
- }
910
974
  };
911
975
  function appendOptional2(type, isRequired) {
912
976
  return isRequired ? type : `${type} | undefined`;
913
977
  }
914
978
 
915
979
  // packages/typescript/src/lib/utils.ts
916
- import { isRef as isRef3, removeDuplicates } from "@sdk-it/core";
917
980
  function securityToOptions(security2, securitySchemes, staticIn) {
918
981
  securitySchemes ??= {};
919
982
  const options = {};
@@ -923,7 +986,7 @@ function securityToOptions(security2, securitySchemes, staticIn) {
923
986
  continue;
924
987
  }
925
988
  const schema = securitySchemes[name];
926
- if (isRef3(schema)) {
989
+ if (isRef(schema)) {
927
990
  throw new Error(`Ref security schemas are not supported`);
928
991
  }
929
992
  if (schema.type === "http") {
@@ -1012,7 +1075,7 @@ function generateInputs(operationsSet, commonZod, makeImport) {
1012
1075
  const imports = /* @__PURE__ */ new Set(['import { z } from "zod";']);
1013
1076
  for (const operation of operations) {
1014
1077
  const schemaName = camelcase2(`${operation.name} schema`);
1015
- const schema = `export const ${schemaName} = ${Object.keys(operation.schemas).length === 1 ? Object.values(operation.schemas)[0] : toLitObject2(operation.schemas)};`;
1078
+ const schema = `export const ${schemaName} = ${Object.keys(operation.schemas).length === 1 ? Object.values(operation.schemas)[0] : toLitObject(operation.schemas)};`;
1016
1079
  const inputContent = schema;
1017
1080
  for (const schema2 of commonImports) {
1018
1081
  if (inputContent.includes(schema2)) {
@@ -1082,7 +1145,7 @@ function toEndpoint(groupName, spec, specOperation, operation, utils) {
1082
1145
  return statusCode >= 200 && statusCode < 300;
1083
1146
  }).length > 1;
1084
1147
  for (const status in specOperation.responses) {
1085
- const response = isRef4(specOperation.responses[status]) ? followRef3(spec, specOperation.responses[status].$ref) : specOperation.responses[status];
1148
+ const response = isRef(specOperation.responses[status]) ? followRef(spec, specOperation.responses[status].$ref) : specOperation.responses[status];
1086
1149
  const handled = handleResponse(
1087
1150
  spec,
1088
1151
  operation.name,
@@ -1172,7 +1235,7 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1172
1235
  );
1173
1236
  const statusCode = +status;
1174
1237
  const parser = (response.headers ?? {})["Transfer-Encoding"] ? "chunked" : "buffered";
1175
- const statusName = statusCodeToResponseMap[status] || "APIResponse";
1238
+ const statusName = `http.${statusCodeToResponseMap[status] || "APIResponse"}`;
1176
1239
  const interfaceName = pascalcase(
1177
1240
  operationName + ` output${numbered ? status : ""}`
1178
1241
  );
@@ -1180,22 +1243,34 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1180
1243
  outputs.push(statusName);
1181
1244
  } else {
1182
1245
  if (status.endsWith("XX")) {
1183
- outputs.push(`APIError<${interfaceName}>`);
1246
+ outputs.push(`http.APIError<${interfaceName}>`);
1184
1247
  } else {
1185
1248
  outputs.push(
1186
1249
  parser !== "buffered" ? `{type: ${statusName}<${interfaceName}>, parser: ${parser}}` : `${statusName}<${interfaceName}>`
1187
1250
  );
1188
1251
  }
1189
1252
  }
1190
- const responseContent = get(response, ["content"]);
1253
+ const responseContent = get2(response, ["content"]);
1191
1254
  const isJson = responseContent && responseContent["application/json"];
1192
- const responseSchema = isJson ? typeScriptDeserialzer.handle(
1193
- responseContent["application/json"].schema,
1194
- true
1195
- ) : "void";
1255
+ let responseSchema = parser === "chunked" ? "ReadableStream" : "void";
1256
+ if (isJson) {
1257
+ const schema = responseContent["application/json"].schema;
1258
+ const isObject = !isRef(schema) && schema.type === "object";
1259
+ if (isObject && schema.properties) {
1260
+ schema.properties["[http.KIND]"] = {
1261
+ "x-internal": true,
1262
+ const: `typeof ${statusName}.kind`,
1263
+ type: "string"
1264
+ };
1265
+ schema.required ??= [];
1266
+ schema.required.push("[KIND]");
1267
+ }
1268
+ responseSchema = typeScriptDeserialzer.handle(schema, true);
1269
+ }
1196
1270
  responses.push({
1197
1271
  name: interfaceName,
1198
- schema: responseSchema
1272
+ schema: responseSchema,
1273
+ description: response.description
1199
1274
  });
1200
1275
  const statusGroup = +status.slice(0, 1);
1201
1276
  if (statusCode >= 400 || statusGroup >= 4) {
@@ -1209,15 +1284,6 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1209
1284
  namedImports: [{ isTypeOnly: true, name: interfaceName }]
1210
1285
  };
1211
1286
  } else if (statusCode >= 200 && statusCode < 300 || statusCode >= 2 || statusGroup <= 3) {
1212
- endpointImports[statusName] = {
1213
- moduleSpecifier: utils.makeImport("../http/response"),
1214
- namedImports: [
1215
- {
1216
- isTypeOnly: false,
1217
- name: statusName
1218
- }
1219
- ]
1220
- };
1221
1287
  endpointImports[interfaceName] = {
1222
1288
  defaultImport: void 0,
1223
1289
  isTypeOnly: true,
@@ -1230,7 +1296,7 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1230
1296
  }
1231
1297
 
1232
1298
  // packages/typescript/src/lib/styles/github/endpoints.txt
1233
- var endpoints_default = "\ntype Output<T extends OutputType> = T extends {\n parser: Parser;\n type: Type<unknown>;\n}\n ? InstanceType<T['type']>\n : T extends Type<unknown>\n ? InstanceType<T>\n : never;\n\ntype Unionize<T> = T extends [infer Single extends OutputType]\n ? Output<Single>\n : T extends readonly [...infer Tuple extends OutputType[]]\n ? { [I in keyof Tuple]: Output<Tuple[I]> }[number]\n : never;\n\ntype 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: EndpointOutput<K>;\n error: EndpointError<K> | ParseError<(typeof schemas)[K]['schema']>;\n };\n};";
1299
+ var endpoints_default = "type Output<T extends OutputType> = T extends {\n parser: Parser;\n type: Type<unknown>;\n}\n ? InstanceType<T['type']>\n : T extends Type<unknown>\n ? InstanceType<T>\n : never;\n\ntype Unionize<T> = T extends [infer Single extends OutputType]\n ? Output<Single>\n : T extends readonly [...infer Tuple extends OutputType[]]\n ? { [I in keyof Tuple]: Output<Tuple[I]> }[number]\n : never;\n\ntype 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};";
1234
1300
 
1235
1301
  // packages/typescript/src/lib/generator.ts
1236
1302
  function generateCode(config) {
@@ -1256,7 +1322,7 @@ function generateCode(config) {
1256
1322
  const inputs = {};
1257
1323
  const additionalProperties = [];
1258
1324
  for (const param of operation.parameters ?? []) {
1259
- if (isRef5(param)) {
1325
+ if (isRef(param)) {
1260
1326
  throw new Error(`Found reference in parameter ${param.$ref}`);
1261
1327
  }
1262
1328
  if (!param.schema) {
@@ -1298,9 +1364,9 @@ function generateCode(config) {
1298
1364
  };
1299
1365
  let outgoingContentType;
1300
1366
  if (!isEmpty(operation.requestBody)) {
1301
- const requestBody = isRef5(operation.requestBody) ? followRef4(config.spec, operation.requestBody.$ref) : operation.requestBody;
1367
+ const requestBody = isRef(operation.requestBody) ? followRef(config.spec, operation.requestBody.$ref) : operation.requestBody;
1302
1368
  for (const type in requestBody.content) {
1303
- const ctSchema = isRef5(requestBody.content[type].schema) ? followRef4(config.spec, requestBody.content[type].schema.$ref) : requestBody.content[type].schema;
1369
+ const ctSchema = isRef(requestBody.content[type].schema) ? followRef(config.spec, requestBody.content[type].schema.$ref) : requestBody.content[type].schema;
1304
1370
  if (!ctSchema) {
1305
1371
  console.warn(
1306
1372
  `Schema not found for ${type} in ${entry.method} ${entry.path}`
@@ -1370,14 +1436,23 @@ function generateCode(config) {
1370
1436
  },
1371
1437
  { makeImport: config.makeImport }
1372
1438
  );
1373
- const output = [`import z from 'zod';`];
1439
+ const output = [
1440
+ `import z from 'zod';`,
1441
+ `import type * as http from '../http';`
1442
+ ];
1374
1443
  const responses = endpoint.responses.flatMap((it) => it.responses);
1375
1444
  const responsesImports = endpoint.responses.flatMap(
1376
1445
  (it) => Object.values(it.imports)
1377
1446
  );
1378
1447
  if (responses.length) {
1379
1448
  output.push(
1380
- ...responses.map((it) => `export type ${it.name} = ${it.schema};`)
1449
+ ...responses.map(
1450
+ (it) => `${it.description ? `
1451
+ /**
1452
+ * ${it.description}
1453
+ */
1454
+ ` : ""} export type ${it.name} = ${it.schema};`
1455
+ )
1381
1456
  );
1382
1457
  } else {
1383
1458
  output.push(
@@ -1428,7 +1503,7 @@ function generateCode(config) {
1428
1503
  commonZod,
1429
1504
  outputs,
1430
1505
  endpoints: {
1431
- [join("api", "endpoints.ts")]: `
1506
+ [join2("api", "endpoints.ts")]: `
1432
1507
 
1433
1508
 
1434
1509
  import type z from 'zod';
@@ -1442,9 +1517,9 @@ import type { OutputType, Parser, Type } from '${config.makeImport(
1442
1517
 
1443
1518
  import schemas from '${config.makeImport("./schemas")}';
1444
1519
 
1445
- ${endpoints_default}`,
1446
- [`${join("api", "schemas.ts")}`]: `${allSchemas.map((it) => it.import).join("\n")}
1447
-
1520
+ ${template(endpoints_default)({ outputType: config.style?.outputType })}`,
1521
+ [`${join2("api", "schemas.ts")}`]: `${allSchemas.map((it) => it.import).join("\n")}
1522
+ import { KIND } from "${config.makeImport("../http/index")}";
1448
1523
  export default {
1449
1524
  ${allSchemas.map((it) => it.use).join(",\n")}
1450
1525
  };
@@ -1463,11 +1538,12 @@ ${allSchemas.map((it) => it.use).join(",\n")}
1463
1538
  );
1464
1539
  return [
1465
1540
  [
1466
- join("api", `${spinalcase2(name)}.ts`),
1541
+ join2("api", `${spinalcase2(name)}.ts`),
1467
1542
  `${[
1468
1543
  ...imps,
1469
1544
  // ...imports,
1470
1545
  `import z from 'zod';`,
1546
+ `import * as http from '${config.makeImport("../http/response")}';`,
1471
1547
  `import { toRequest, json, urlencoded, nobody, formdata, createUrl } from '${config.makeImport("../http/request")}';`,
1472
1548
  `import { chunked, buffered } from "${config.makeImport("../http/parse-response")}";`,
1473
1549
  `import * as ${camelcase3(name)} from '../inputs/${config.makeImport(spinalcase2(name))}';`
@@ -1485,8 +1561,8 @@ ${endpoint.flatMap((it) => it.schemas).join(",\n")}
1485
1561
  };
1486
1562
  }
1487
1563
  function toProps(spec, schemaOrRef, aggregator = []) {
1488
- if (isRef5(schemaOrRef)) {
1489
- const schema = followRef4(spec, schemaOrRef.$ref);
1564
+ if (isRef(schemaOrRef)) {
1565
+ const schema = followRef(spec, schemaOrRef.$ref);
1490
1566
  return toProps(spec, schema, aggregator);
1491
1567
  } else if (schemaOrRef.type === "object") {
1492
1568
  for (const [name] of Object.entries(schemaOrRef.properties ?? {})) {
@@ -1531,7 +1607,7 @@ function bodyInputs(config, ctSchema) {
1531
1607
  }
1532
1608
 
1533
1609
  // packages/typescript/src/lib/http/interceptors.txt
1534
- var interceptors_default = "export interface Interceptor {\n before?: (config: RequestConfig) => Promise<RequestConfig> | RequestConfig;\n after?: (response: Response) => Promise<Response> | Response;\n}\n\nexport const createHeadersInterceptor = (\n defaultHeaders: () => Record<string, string | undefined>,\n requestHeaders: HeadersInit,\n):Interceptor => {\n return {\n before({init, url}) {\n // Priority Levels\n // 1. Headers Input\n // 2. Request Headers\n // 3. Default Headers\n const headers = defaultHeaders();\n\n for (const [key, value] of new Headers(requestHeaders)) {\n // Only set the header if it doesn't already exist and has a value\n // even though these headers are passed at operation level\n // still they are lower priority compared to the headers input\n if (value !== undefined && !init.headers.has(key)) {\n init.headers.set(key, value);\n }\n }\n\n for (const [key, value] of Object.entries(headers)) {\n // Only set the header if it doesn't already exist and has a value\n if (value !== undefined && !init.headers.has(key)) {\n init.headers.set(key, value);\n }\n }\n\n return {init, url};\n },\n };\n};\n\nexport const createBaseUrlInterceptor = (\n getBaseUrl: () => string,\n): Interceptor => {\n return {\n before({ init, url }) {\n const baseUrl = getBaseUrl();\n if (url.protocol === 'local:') {\n return {\n init,\n url: new URL(url.href.replace('local://', baseUrl))\n };\n }\n return { init, url };\n },\n };\n};\n\nexport const logInterceptor: Interceptor = {\n before({ url, init }) {\n console.dir('Request:', { url, init });\n return { url, init };\n },\n after(response) {\n console.log('Response:', response);\n return response;\n },\n};\n\n/**\n * Creates an interceptor that logs detailed information about requests and responses.\n * @param options Configuration options for the logger\n * @returns An interceptor object with before and after handlers\n */\nexport const createDetailedLogInterceptor = (options?: {\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n includeRequestBody?: boolean;\n includeResponseBody?: boolean;\n}) => {\n const logLevel = options?.logLevel || 'info';\n const includeRequestBody = options?.includeRequestBody || false;\n const includeResponseBody = options?.includeResponseBody || false;\n\n return {\n async before(request: Request) {\n const logData = {\n url: request.url,\n method: request.method,\n contentType: request.headers.get('Content-Type'),\n headers: Object.fromEntries([...request.headers.entries()]),\n };\n\n console[logLevel]('\u{1F680} Outgoing Request:', logData);\n\n if (includeRequestBody) {\n try {\n // Clone the request to avoid consuming the body stream\n const clonedRequest = request.clone();\n if (clonedRequest.headers.get('Content-Type')?.includes('application/json')) {\n const body = await clonedRequest.json().catch(() => null);\n console[logLevel]('Request Body:', body);\n } else {\n const body = await clonedRequest.text().catch(() => null);\n console[logLevel]('Request Body:', body);\n }\n } catch (error) {\n console.error('Could not log request body:', error);\n }\n }\n\n return request;\n },\n\n async after(response: Response) {\n const logData = {\n status: response.status,\n statusText: response.statusText,\n url: response.url,\n headers: Object.fromEntries([...response.headers.entries()]),\n };\n\n console[logLevel]('\u{1F4E5} Incoming Response:', logData);\n\n if (includeResponseBody && response.body) {\n try {\n // Clone the response to avoid consuming the body stream\n const clonedResponse = response.clone();\n if (clonedResponse.headers.get('Content-Type')?.includes('application/json')) {\n const body = await clonedResponse.json().catch(() => null);\n console[logLevel]('Response Body:', body);\n } else {\n const body = await clonedResponse.text().catch(() => null);\n if (body) {\n console[logLevel]('Response Body:', body.substring(0, 500) + (body.length > 500 ? '...' : ''));\n } else {\n console[logLevel]('No response body');\n }\n }\n } catch (error) {\n console.error('Could not log response body:', error);\n }\n }\n\n return response;\n },\n };\n};\n";
1610
+ var interceptors_default = "export interface Interceptor {\n before?: (config: RequestConfig) => Promise<RequestConfig> | RequestConfig;\n after?: (response: Response) => Promise<Response> | Response;\n}\n\nexport const createHeadersInterceptor = (\n defaultHeaders: () => Record<string, string | undefined>,\n requestHeaders: HeadersInit,\n):Interceptor => {\n return {\n before({init, url}) {\n // Priority Levels\n // 1. Headers Input\n // 2. Request Headers\n // 3. Default Headers\n const headers = defaultHeaders();\n\n for (const [key, value] of new Headers(requestHeaders)) {\n // Only set the header if it doesn't already exist and has a value\n // even though these headers are passed at operation level\n // still they are lower priority compared to the headers input\n if (value !== undefined && !init.headers.has(key)) {\n init.headers.set(key, value);\n }\n }\n\n for (const [key, value] of Object.entries(headers)) {\n // Only set the header if it doesn't already exist and has a value\n if (value !== undefined && !init.headers.has(key)) {\n init.headers.set(key, value);\n }\n }\n\n return {init, url};\n },\n };\n};\n\nexport const createBaseUrlInterceptor = (\n getBaseUrl: () => string,\n): Interceptor => {\n return {\n before({ init, url }) {\n const baseUrl = getBaseUrl();\n if (url.protocol === 'local:') {\n return {\n init,\n url: new URL(url.href.replace('local://', baseUrl))\n };\n }\n return { init, url };\n },\n };\n};\n\nexport const logInterceptor: Interceptor = {\n before({ url, init }) {\n console.log('Request:', { url, init });\n return { url, init };\n },\n after(response) {\n console.log('Response:', response);\n return response;\n },\n};\n\n/**\n * Creates an interceptor that logs detailed information about requests and responses.\n * @param options Configuration options for the logger\n * @returns An interceptor object with before and after handlers\n */\nexport const createDetailedLogInterceptor = (options?: {\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n includeRequestBody?: boolean;\n includeResponseBody?: boolean;\n}) => {\n const logLevel = options?.logLevel || 'info';\n const includeRequestBody = options?.includeRequestBody || false;\n const includeResponseBody = options?.includeResponseBody || false;\n\n return {\n async before(request: Request) {\n const logData = {\n url: request.url,\n method: request.method,\n contentType: request.headers.get('Content-Type'),\n headers: Object.fromEntries([...request.headers.entries()]),\n };\n\n console[logLevel]('\u{1F680} Outgoing Request:', logData);\n\n if (includeRequestBody) {\n try {\n // Clone the request to avoid consuming the body stream\n const clonedRequest = request.clone();\n if (clonedRequest.headers.get('Content-Type')?.includes('application/json')) {\n const body = await clonedRequest.json().catch(() => null);\n console[logLevel]('Request Body:', body);\n } else {\n const body = await clonedRequest.text().catch(() => null);\n console[logLevel]('Request Body:', body);\n }\n } catch (error) {\n console.error('Could not log request body:', error);\n }\n }\n\n return request;\n },\n\n async after(response: Response) {\n const logData = {\n status: response.status,\n statusText: response.statusText,\n url: response.url,\n headers: Object.fromEntries([...response.headers.entries()]),\n };\n\n console[logLevel]('\u{1F4E5} Incoming Response:', logData);\n\n if (includeResponseBody && response.body) {\n try {\n // Clone the response to avoid consuming the body stream\n const clonedResponse = response.clone();\n if (clonedResponse.headers.get('Content-Type')?.includes('application/json')) {\n const body = await clonedResponse.json().catch(() => null);\n console[logLevel]('Response Body:', body);\n } else {\n const body = await clonedResponse.text().catch(() => null);\n if (body) {\n console[logLevel]('Response Body:', body.substring(0, 500) + (body.length > 500 ? '...' : ''));\n } else {\n console[logLevel]('No response body');\n }\n }\n } catch (error) {\n console.error('Could not log response body:', error);\n }\n }\n\n return response;\n },\n };\n};\n";
1535
1611
 
1536
1612
  // packages/typescript/src/lib/http/parse-response.txt
1537
1613
  var parse_response_default = 'import { parse } from "fast-content-type-parse";\n\nasync function handleChunkedResponse(response: Response, contentType: string) {\n const { type } = parse(contentType);\n\n switch (type) {\n case "application/json": {\n let buffer = "";\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value);\n }\n return JSON.parse(buffer);\n }\n case "text/html":\n case "text/plain": {\n let buffer = "";\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value);\n }\n return buffer;\n }\n default:\n return response.body;\n }\n}\n\nexport function chunked(response: Response) {\n return response.body!;\n}\n\nexport async function buffered(response: Response) {\n const contentType = response.headers.get("Content-Type");\n if (!contentType) {\n throw new Error("Content-Type header is missing");\n }\n\n if (response.status === 204) {\n return null;\n }\n\n const { type } = parse(contentType);\n switch (type) {\n case "application/json":\n return response.json();\n case "text/plain":\n return response.text();\n case "text/html":\n return response.text();\n case "text/xml":\n case "application/xml":\n return response.text();\n case "application/x-www-form-urlencoded": {\n const text = await response.text();\n return Object.fromEntries(new URLSearchParams(text));\n }\n case "multipart/form-data":\n return response.formData();\n default:\n throw new Error(`Unsupported content type: ${contentType}`);\n }\n}\n';
@@ -1540,13 +1616,421 @@ var parse_response_default = 'import { parse } from "fast-content-type-parse";\n
1540
1616
  var parser_default = "import { z } from 'zod';\n\nexport class ParseError<T extends z.ZodType<any, any, any>> {\n public data: z.typeToFlattenedError<T, z.ZodIssue>;\n constructor(data: z.typeToFlattenedError<T, z.ZodIssue>) {\n this.data = data;\n }\n}\n\nexport function parseInput<T extends z.ZodType<any, any, any>>(\n schema: T,\n input: unknown,\n) {\n const result = schema.safeParse(input);\n if (!result.success) {\n const error = result.error.flatten((issue) => issue);\n return [null, new ParseError(error)];\n }\n return [result.data as z.infer<T>, null];\n}\n";
1541
1617
 
1542
1618
  // packages/typescript/src/lib/http/request.txt
1543
- var request_default = "type Init = Omit<RequestInit, 'headers'> & { headers: Headers; };\nexport type RequestConfig = { init: Init; url: URL };\nexport type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';\nexport type ContentType = 'xml' | 'json' | 'urlencoded' | 'multipart' | 'formdata';\nexport type Endpoint =\n | `${ContentType} ${Method} ${string}`\n | `${Method} ${string}`;\n\nexport type BodyInit =\n | ArrayBuffer\n | Blob\n | FormData\n | URLSearchParams\n | null\n | string;\n\nexport function createUrl(path: string, query: URLSearchParams) {\n const url = new URL(path, `local://`);\n url.search = query.toString();\n return url;\n}\n\nfunction template(\n templateString: string,\n templateVariables: Record<string, any>,\n): string {\n const nargs = /{([0-9a-zA-Z_]+)}/g;\n return templateString.replace(nargs, (match, key: string, index: number) => {\n // Handle escaped double braces\n if (\n templateString[index - 1] === '{' &&\n templateString[index + match.length] === '}'\n ) {\n return key;\n }\n\n const result = key in templateVariables ? templateVariables[key] : null;\n return result === null || result === undefined ? '' : String(result);\n });\n}\n\ntype Input = Record<string, any>;\ntype Props = {\n inputHeaders: string[];\n inputQuery: string[];\n inputBody: string[];\n inputParams: string[];\n};\n\nabstract class Serializer {\n protected input: Input;\n protected props: Props;\n\n constructor(\n input: Input,\n props: Props,\n ) {\n this.input = input;\n this.props = props;\n }\n\n abstract getBody(): BodyInit | null;\n abstract getHeaders(): Record<string, string>;\n serialize(): Serialized {\n const headers = new Headers({});\n for (const header of this.props.inputHeaders) {\n headers.set(header, this.input[header]);\n }\n\n const query = new URLSearchParams();\n for (const key of this.props.inputQuery) {\n const value = this.input[key];\n if (value !== undefined) {\n query.set(key, String(value));\n }\n }\n\n const params = this.props.inputParams.reduce<Record<string, any>>(\n (acc, key) => {\n acc[key] = this.input[key];\n return acc;\n },\n {},\n );\n\n return {\n body: this.getBody(),\n query,\n params,\n headers: this.getHeaders(),\n };\n }\n}\n\ninterface Serialized {\n body: BodyInit | null;\n query: URLSearchParams;\n params: Record<string, any>;\n headers: Record<string, string>;\n}\n\nclass JsonSerializer extends Serializer {\n getBody(): BodyInit | null {\n const body: Record<string, any> = {};\n if (\n this.props.inputBody.length === 1 &&\n this.props.inputBody[0] === '$body'\n ) {\n return JSON.stringify(this.input.$body);\n }\n\n for (const prop of this.props.inputBody) {\n body[prop] = this.input[prop];\n }\n return JSON.stringify(body);\n }\n getHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n }\n}\n\nclass UrlencodedSerializer extends Serializer {\n getBody(): BodyInit | null {\n const body = new URLSearchParams();\n for (const prop of this.props.inputBody) {\n body.set(prop, this.input[prop]);\n }\n return body;\n }\n getHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n };\n }\n}\n\nclass NoBodySerializer extends Serializer {\n getBody(): BodyInit | null {\n return null;\n }\n getHeaders(): Record<string, string> {\n return {};\n }\n}\n\nclass FormDataSerializer extends Serializer {\n getBody(): BodyInit | null {\n const body = new FormData();\n for (const prop of this.props.inputBody) {\n body.append(prop, this.input[prop]);\n }\n return body;\n }\n getHeaders(): Record<string, string> {\n return {\n Accept: 'application/json',\n };\n }\n}\n\nexport function json(input: Input, props: Props) {\n return new JsonSerializer(input, props).serialize();\n}\nexport function urlencoded(input: Input, props: Props) {\n return new UrlencodedSerializer(input, props).serialize();\n}\nexport function nobody(input: Input, props: Props) {\n return new NoBodySerializer(input, props).serialize();\n}\nexport function formdata(input: Input, props: Props) {\n return new FormDataSerializer(input, props).serialize();\n}\n\nexport function toRequest<T extends Endpoint>(\n endpoint: T,\n input: Serialized,\n): RequestConfig {\n const [method, path] = endpoint.split(' ');\n const pathVariable = template(path, input.params);\n\n return {\n url: createUrl(pathVariable, input.query),\n init: {\n method: method,\n headers: new Headers(input.headers),\n body: method === 'GET' ? undefined : input.body,\n },\n }\n}\n";
1619
+ var request_default = "type Init = Omit<RequestInit, 'headers'> & { headers: Headers; };\nexport type RequestConfig = { init: Init; url: URL };\nexport type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';\nexport type ContentType = 'xml' | 'json' | 'urlencoded' | 'multipart' | 'formdata';\nexport type HeadersInit = [string, string][] | Record<string, string>;\nexport type Endpoint =\n | `${ContentType} ${Method} ${string}`\n | `${Method} ${string}`;\n\nexport type BodyInit =\n | ArrayBuffer\n | Blob\n | FormData\n | URLSearchParams\n | null\n | string;\n\nexport function createUrl(path: string, query: URLSearchParams) {\n const url = new URL(path, `local://`);\n url.search = query.toString();\n return url;\n}\n\nfunction template(\n templateString: string,\n templateVariables: Record<string, any>,\n): string {\n const nargs = /{([0-9a-zA-Z_]+)}/g;\n return templateString.replace(nargs, (match, key: string, index: number) => {\n // Handle escaped double braces\n if (\n templateString[index - 1] === '{' &&\n templateString[index + match.length] === '}'\n ) {\n return key;\n }\n\n const result = key in templateVariables ? templateVariables[key] : null;\n return result === null || result === undefined ? '' : String(result);\n });\n}\n\ntype Input = Record<string, any>;\ntype Props = {\n inputHeaders: string[];\n inputQuery: string[];\n inputBody: string[];\n inputParams: string[];\n};\n\nabstract class Serializer {\n protected input: Input;\n protected props: Props;\n\n constructor(\n input: Input,\n props: Props,\n ) {\n this.input = input;\n this.props = props;\n }\n\n abstract getBody(): BodyInit | null;\n abstract getHeaders(): Record<string, string>;\n serialize(): Serialized {\n const headers = new Headers({});\n for (const header of this.props.inputHeaders) {\n headers.set(header, this.input[header]);\n }\n\n const query = new URLSearchParams();\n for (const key of this.props.inputQuery) {\n const value = this.input[key];\n if (value !== undefined) {\n query.set(key, String(value));\n }\n }\n\n const params = this.props.inputParams.reduce<Record<string, any>>(\n (acc, key) => {\n acc[key] = this.input[key];\n return acc;\n },\n {},\n );\n\n return {\n body: this.getBody(),\n query,\n params,\n headers: this.getHeaders(),\n };\n }\n}\n\ninterface Serialized {\n body: BodyInit | null;\n query: URLSearchParams;\n params: Record<string, any>;\n headers: Record<string, string>;\n}\n\nclass JsonSerializer extends Serializer {\n getBody(): BodyInit | null {\n const body: Record<string, any> = {};\n if (\n this.props.inputBody.length === 1 &&\n this.props.inputBody[0] === '$body'\n ) {\n return JSON.stringify(this.input.$body);\n }\n\n for (const prop of this.props.inputBody) {\n body[prop] = this.input[prop];\n }\n return JSON.stringify(body);\n }\n getHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n }\n}\n\nclass UrlencodedSerializer extends Serializer {\n getBody(): BodyInit | null {\n const body = new URLSearchParams();\n for (const prop of this.props.inputBody) {\n body.set(prop, this.input[prop]);\n }\n return body;\n }\n getHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n };\n }\n}\n\nclass NoBodySerializer extends Serializer {\n getBody(): BodyInit | null {\n return null;\n }\n getHeaders(): Record<string, string> {\n return {};\n }\n}\n\nclass FormDataSerializer extends Serializer {\n getBody(): BodyInit | null {\n const body = new FormData();\n for (const prop of this.props.inputBody) {\n body.append(prop, this.input[prop]);\n }\n return body;\n }\n getHeaders(): Record<string, string> {\n return {\n Accept: 'application/json',\n };\n }\n}\n\nexport function json(input: Input, props: Props) {\n return new JsonSerializer(input, props).serialize();\n}\nexport function urlencoded(input: Input, props: Props) {\n return new UrlencodedSerializer(input, props).serialize();\n}\nexport function nobody(input: Input, props: Props) {\n return new NoBodySerializer(input, props).serialize();\n}\nexport function formdata(input: Input, props: Props) {\n return new FormDataSerializer(input, props).serialize();\n}\n\nexport function toRequest<T extends Endpoint>(\n endpoint: T,\n input: Serialized,\n): RequestConfig {\n const [method, path] = endpoint.split(' ');\n const pathVariable = template(path, input.params);\n\n return {\n url: createUrl(pathVariable, input.query),\n init: {\n method: method,\n headers: new Headers(input.headers),\n body: method === 'GET' ? undefined : input.body,\n },\n }\n}\n";
1544
1620
 
1545
1621
  // packages/typescript/src/lib/http/response.txt
1546
- var response_default = "export class APIResponse<Body = unknown, Status extends number = number> {\n static status: number;\n status: Status;\n data: Body;\n\n constructor(status: Status, data: Body) {\n this.status = status;\n this.data = data;\n }\n\n static create<Body = unknown>(status: number, data: Body) {\n return new this(status, data);\n }\n}\n\nexport class APIError<Body, Status extends number = number> extends APIResponse<\n Body,\n Status\n> {\n static override create<T>(status: number, data: T) {\n return new this(status, data);\n }\n}\n\n// 2xx Success\nexport class Ok<T> extends APIResponse<T, 200> {\n static override status = 200 as const;\n constructor(data: T) {\n super(Ok.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class Created<T> extends APIResponse<T, 201> {\n static override status = 201 as const;\n constructor(data: T) {\n super(Created.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class Accepted<T> extends APIResponse<T, 202> {\n static override status = 202 as const;\n constructor(data: T) {\n super(Accepted.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class NoContent extends APIResponse<never, 204> {\n static override status = 204 as const;\n constructor() {\n super(NoContent.status, null as never);\n }\n static override create(status: number, data: never): NoContent {\n return new this();\n }\n}\n\n// 4xx Client Errors\nexport class BadRequest<T> extends APIError<T, 400> {\n static override status = 400 as const;\n constructor(data: T) {\n super(BadRequest.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class Unauthorized<T = { message: string }> extends APIError<T, 401> {\n static override status = 401 as const;\n constructor(data: T) {\n super(Unauthorized.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class PaymentRequired<T = { message: string }> extends APIError<T, 402> {\n static override status = 402 as const;\n constructor(data: T) {\n super(PaymentRequired.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class Forbidden<T = { message: string }> extends APIError<T, 403> {\n static override status = 403 as const;\n constructor(data: T) {\n super(Forbidden.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class NotFound<T = { message: string }> extends APIError<T, 404> {\n static override status = 404 as const;\n constructor(data: T) {\n super(NotFound.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class MethodNotAllowed<T = { message: string }> extends APIError<\n T,\n 405\n> {\n static override status = 405 as const;\n constructor(data: T) {\n super(MethodNotAllowed.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class NotAcceptable<T = { message: string }> extends APIError<T, 406> {\n static override status = 406 as const;\n constructor(data: T) {\n super(NotAcceptable.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class Conflict<T = { message: string }> extends APIError<T, 409> {\n static override status = 409 as const;\n constructor(data: T) {\n super(Conflict.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class Gone<T = { message: string }> extends APIError<T, 410> {\n static override status = 410 as const;\n constructor(data: T) {\n super(Gone.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class UnprocessableEntity<\n T = { message: string; errors?: Record<string, string[]> },\n> extends APIError<T, 422> {\n static override status = 422 as const;\n constructor(data: T) {\n super(UnprocessableEntity.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class TooManyRequests<\n T = { message: string; retryAfter?: string },\n> extends APIError<T, 429> {\n static override status = 429 as const;\n constructor(data: T) {\n super(TooManyRequests.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class PayloadTooLarge<T = { message: string }> extends APIError<T, 413> {\n static override status = 413 as const;\n constructor(data: T) {\n super(PayloadTooLarge.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class UnsupportedMediaType<T = { message: string }> extends APIError<\n T,\n 415\n> {\n static override status = 415 as const;\n constructor(data: T) {\n super(UnsupportedMediaType.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\n\n// 5xx Server Errors\nexport class InternalServerError<T = { message: string }> extends APIError<\n T,\n 500\n> {\n static override status = 500 as const;\n constructor(data: T) {\n super(InternalServerError.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class NotImplemented<T = { message: string }> extends APIError<T, 501> {\n static override status = 501 as const;\n constructor(data: T) {\n super(NotImplemented.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class BadGateway<T = { message: string }> extends APIError<T, 502> {\n static override status = 502 as const;\n constructor(data: T) {\n super(BadGateway.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class ServiceUnavailable<\n T = { message: string; retryAfter?: string },\n> extends APIError<T, 503> {\n static override status = 503 as const;\n constructor(data: T) {\n super(ServiceUnavailable.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\nexport class GatewayTimeout<T = { message: string }> extends APIError<T, 504> {\n static override status = 504 as const;\n constructor(data: T) {\n super(GatewayTimeout.status, data);\n }\n static override create<T>(status: number, data: T) {\n return new this(data);\n }\n}\n\nexport type ClientError =\n | BadRequest<{ message: string }>\n | Unauthorized<unknown>\n | PaymentRequired<unknown>\n | Forbidden<unknown>\n | NotFound<unknown>\n | MethodNotAllowed<unknown>\n | NotAcceptable<unknown>\n | Conflict<unknown>\n | Gone<unknown>\n | UnprocessableEntity<unknown>\n | TooManyRequests<unknown>;\n\nexport type ServerError =\n | InternalServerError<unknown>\n | NotImplemented<unknown>\n | BadGateway<unknown>\n | ServiceUnavailable<unknown>\n | GatewayTimeout<unknown>;\n\nexport type ProblematicResponse = ClientError | ServerError;\n\nexport type SuccessfulResponse = Ok<unknown> | Created<unknown> | Accepted<unknown> | NoContent;";
1622
+ var response_default = `export const KIND = Symbol('APIDATA');
1623
+
1624
+ export class APIResponse<Body = unknown, Status extends number = number> {
1625
+ static readonly status: number;
1626
+ static readonly kind: symbol = Symbol.for("APIResponse");
1627
+ status: Status;
1628
+ data: Body;
1629
+
1630
+ constructor(status: Status, data: Body) {
1631
+ this.status = status;
1632
+ this.data = data;
1633
+ }
1634
+
1635
+ static create<Body = unknown>(status: number, data: Body) {
1636
+ return new this(status, data);
1637
+ }
1638
+
1639
+ }
1640
+
1641
+ export class APIError<Body, Status extends number = number> extends APIResponse<
1642
+ Body,
1643
+ Status
1644
+ > {
1645
+ static override create<T>(status: number, data: T) {
1646
+ return new this(status, data);
1647
+ }
1648
+ }
1649
+
1650
+
1651
+ // 2xx Success
1652
+ export class Ok<T> extends APIResponse<T, 200> {
1653
+ static override readonly kind = Symbol.for("Ok");
1654
+ static override readonly status = 200 as const;
1655
+ constructor(data: T) {
1656
+ super(Ok.status, data);
1657
+ }
1658
+ static override create<T>(status: number, data: T) {
1659
+ Object.defineProperty(data, KIND, { value: this.kind });
1660
+ return new this(data);
1661
+ }
1662
+
1663
+ static is<T extends {[KIND]:typeof Ok['kind']}>(value: unknown): value is T {
1664
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1665
+ }
1666
+ }
1667
+
1668
+
1669
+ export class Created<T> extends APIResponse<T, 201> {
1670
+ static override readonly kind = Symbol.for("Created");
1671
+ static override status = 201 as const;
1672
+ constructor(data: T) {
1673
+ super(Created.status, data);
1674
+ }
1675
+ static override create<T>(status: number, data: T) {
1676
+ Object.defineProperty(data, KIND, { value: this.kind });
1677
+ return new this(data);
1678
+ }
1679
+
1680
+ static is<T extends {[KIND]: typeof Created['kind']}>(value: unknown): value is T {
1681
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1682
+ }
1683
+ }
1684
+ export class Accepted<T> extends APIResponse<T, 202> {
1685
+ static override readonly kind = Symbol.for("Accepted");
1686
+ static override status = 202 as const;
1687
+ constructor(data: T) {
1688
+ super(Accepted.status, data);
1689
+ }
1690
+ static override create<T>(status: number, data: T) {
1691
+ Object.defineProperty(data, KIND, { value: this.kind });
1692
+ return new this(data);
1693
+ }
1694
+
1695
+ static is<T extends {[KIND]: typeof Accepted['kind']}>(value: unknown): value is T {
1696
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1697
+ }
1698
+ }
1699
+ export class NoContent extends APIResponse<never, 204> {
1700
+ static override readonly kind = Symbol.for("NoContent");
1701
+ static override status = 204 as const;
1702
+ constructor() {
1703
+ super(NoContent.status, null as never);
1704
+ }
1705
+ static override create(status: number, data: never): NoContent {
1706
+ return new this();
1707
+ }
1708
+
1709
+ static is<T extends {[KIND]: typeof NoContent['kind']}>(value: unknown): value is T {
1710
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1711
+ }
1712
+ }
1713
+
1714
+ // 4xx Client Errors
1715
+ export class BadRequest<T> extends APIError<T, 400> {
1716
+ static override readonly kind = Symbol.for("BadRequest");
1717
+ static override status = 400 as const;
1718
+ constructor(data: T) {
1719
+ super(BadRequest.status, data);
1720
+ }
1721
+ static override create<T>(status: number, data: T) {
1722
+ Object.defineProperty(data, KIND, { value: this.kind });
1723
+ return new this(data);
1724
+ }
1725
+
1726
+ static is<T extends {[KIND]: typeof BadRequest['kind']}>(value: unknown): value is T {
1727
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1728
+ }
1729
+ }
1730
+ export class Unauthorized<T = { message: string }> extends APIError<T, 401> {
1731
+ static override readonly kind = Symbol.for("Unauthorized");
1732
+ static override status = 401 as const;
1733
+ constructor(data: T) {
1734
+ super(Unauthorized.status, data);
1735
+ }
1736
+ static override create<T>(status: number, data: T) {
1737
+ Object.defineProperty(data, KIND, { value: this.kind });
1738
+ return new this(data);
1739
+ }
1740
+
1741
+ static is<T extends {[KIND]: typeof Unauthorized['kind']}>(value: unknown): value is T {
1742
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1743
+ }
1744
+ }
1745
+ export class PaymentRequired<T = { message: string }> extends APIError<T, 402> {
1746
+ static override readonly kind = Symbol.for("PaymentRequired");
1747
+ static override status = 402 as const;
1748
+ constructor(data: T) {
1749
+ super(PaymentRequired.status, data);
1750
+ }
1751
+ static override create<T>(status: number, data: T) {
1752
+ Object.defineProperty(data, KIND, { value: this.kind });
1753
+ return new this(data);
1754
+ }
1755
+
1756
+ static is<T extends {[KIND]: typeof PaymentRequired['kind']}>(value: unknown): value is T {
1757
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1758
+ }
1759
+ }
1760
+ export class Forbidden<T = { message: string }> extends APIError<T, 403> {
1761
+ static override readonly kind = Symbol.for("Forbidden");
1762
+ static override status = 403 as const;
1763
+ constructor(data: T) {
1764
+ super(Forbidden.status, data);
1765
+ }
1766
+ static override create<T>(status: number, data: T) {
1767
+ Object.defineProperty(data, KIND, { value: this.kind });
1768
+ return new this(data);
1769
+ }
1770
+
1771
+ static is<T extends {[KIND]: typeof Forbidden['kind']}>(value: unknown): value is T {
1772
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1773
+ }
1774
+ }
1775
+ export class NotFound<T = { message: string }> extends APIError<T, 404> {
1776
+ static override readonly kind = Symbol.for("NotFound");
1777
+ static override status = 404 as const;
1778
+ constructor(data: T) {
1779
+ super(NotFound.status, data);
1780
+ }
1781
+ static override create<T>(status: number, data: T) {
1782
+ Object.defineProperty(data, KIND, { value: this.kind });
1783
+ return new this(data);
1784
+ }
1785
+
1786
+ static is<T extends {[KIND]: typeof NotFound['kind']}>(value: unknown): value is T {
1787
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1788
+ }
1789
+ }
1790
+ export class MethodNotAllowed<T = { message: string }> extends APIError<
1791
+ T,
1792
+ 405
1793
+ > {
1794
+ static override readonly kind = Symbol.for("MethodNotAllowed");
1795
+ static override status = 405 as const;
1796
+ constructor(data: T) {
1797
+ super(MethodNotAllowed.status, data);
1798
+ }
1799
+ static override create<T>(status: number, data: T) {
1800
+ Object.defineProperty(data, KIND, { value: this.kind });
1801
+ return new this(data);
1802
+ }
1803
+
1804
+ static is<T extends {[KIND]: typeof MethodNotAllowed['kind']}>(value: unknown): value is T {
1805
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1806
+ }
1807
+ }
1808
+ export class NotAcceptable<T = { message: string }> extends APIError<T, 406> {
1809
+ static override readonly kind = Symbol.for("NotAcceptable");
1810
+ static override status = 406 as const;
1811
+ constructor(data: T) {
1812
+ super(NotAcceptable.status, data);
1813
+ }
1814
+ static override create<T>(status: number, data: T) {
1815
+ Object.defineProperty(data, KIND, { value: this.kind });
1816
+ return new this(data);
1817
+ }
1818
+
1819
+ static is<T extends {[KIND]: typeof NotAcceptable['kind']}>(value: unknown): value is T {
1820
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1821
+ }
1822
+ }
1823
+ export class Conflict<T = { message: string }> extends APIError<T, 409> {
1824
+ static override readonly kind = Symbol.for("Conflict");
1825
+ static override status = 409 as const;
1826
+ constructor(data: T) {
1827
+ super(Conflict.status, data);
1828
+ }
1829
+ static override create<T>(status: number, data: T) {
1830
+ Object.defineProperty(data, KIND, { value: this.kind });
1831
+ return new this(data);
1832
+ }
1833
+
1834
+ static is<T extends {[KIND]: typeof Conflict['kind']}>(value: unknown): value is T {
1835
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1836
+ }
1837
+ }
1838
+ export class Gone<T = { message: string }> extends APIError<T, 410> {
1839
+ static override readonly kind = Symbol.for("Gone");
1840
+ static override status = 410 as const;
1841
+ constructor(data: T) {
1842
+ super(Gone.status, data);
1843
+ }
1844
+ static override create<T>(status: number, data: T) {
1845
+ Object.defineProperty(data, KIND, { value: this.kind });
1846
+ return new this(data);
1847
+ }
1848
+
1849
+ static is<T extends {[KIND]: typeof Gone['kind']}>(value: unknown): value is T {
1850
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1851
+ }
1852
+ }
1853
+ export class UnprocessableEntity<
1854
+ T = { message: string; errors?: Record<string, string[]> },
1855
+ > extends APIError<T, 422> {
1856
+ static override readonly kind = Symbol.for("UnprocessableEntity");
1857
+ static override status = 422 as const;
1858
+ constructor(data: T) {
1859
+ super(UnprocessableEntity.status, data);
1860
+ }
1861
+ static override create<T>(status: number, data: T) {
1862
+ Object.defineProperty(data, KIND, { value: this.kind });
1863
+ return new this(data);
1864
+ }
1865
+
1866
+ static is<T extends {[KIND]: typeof UnprocessableEntity['kind']}>(value: unknown): value is T {
1867
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1868
+ }
1869
+ }
1870
+ export class TooManyRequests<
1871
+ T = { message: string; retryAfter?: string },
1872
+ > extends APIError<T, 429> {
1873
+ static override readonly kind = Symbol.for("TooManyRequests");
1874
+ static override status = 429 as const;
1875
+ constructor(data: T) {
1876
+ super(TooManyRequests.status, data);
1877
+ }
1878
+ static override create<T>(status: number, data: T) {
1879
+ Object.defineProperty(data, KIND, { value: this.kind });
1880
+ return new this(data);
1881
+ }
1882
+
1883
+ static is<T extends {[KIND]: typeof TooManyRequests['kind']}>(value: unknown): value is T {
1884
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1885
+ }
1886
+ }
1887
+ export class PayloadTooLarge<T = { message: string }> extends APIError<T, 413> {
1888
+ static override readonly kind = Symbol.for("PayloadTooLarge");
1889
+ static override status = 413 as const;
1890
+ constructor(data: T) {
1891
+ super(PayloadTooLarge.status, data);
1892
+ }
1893
+ static override create<T>(status: number, data: T) {
1894
+ Object.defineProperty(data, KIND, { value: this.kind });
1895
+ return new this(data);
1896
+ }
1897
+
1898
+ static is<T extends {[KIND]: typeof PayloadTooLarge['kind']}>(value: unknown): value is T {
1899
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1900
+ }
1901
+ }
1902
+ export class UnsupportedMediaType<T = { message: string }> extends APIError<
1903
+ T,
1904
+ 415
1905
+ > {
1906
+ static override readonly kind = Symbol.for("UnsupportedMediaType");
1907
+ static override status = 415 as const;
1908
+ constructor(data: T) {
1909
+ super(UnsupportedMediaType.status, data);
1910
+ }
1911
+ static override create<T>(status: number, data: T) {
1912
+ Object.defineProperty(data, KIND, { value: this.kind });
1913
+ return new this(data);
1914
+ }
1915
+
1916
+ static is<T extends {[KIND]: typeof UnsupportedMediaType['kind']}>(value: unknown): value is T {
1917
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1918
+ }
1919
+ }
1920
+
1921
+ // 5xx Server Errors
1922
+ export class InternalServerError<T = { message: string }> extends APIError<
1923
+ T,
1924
+ 500
1925
+ > {
1926
+ static override readonly kind = Symbol.for("InternalServerError");
1927
+ static override status = 500 as const;
1928
+ constructor(data: T) {
1929
+ super(InternalServerError.status, data);
1930
+ }
1931
+ static override create<T>(status: number, data: T) {
1932
+ Object.defineProperty(data, KIND, { value: this.kind });
1933
+ return new this(data);
1934
+ }
1935
+
1936
+ static is<T extends {[KIND]: typeof InternalServerError['kind']}>(value: unknown): value is T {
1937
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1938
+ }
1939
+ }
1940
+ export class NotImplemented<T = { message: string }> extends APIError<T, 501> {
1941
+ static override readonly kind = Symbol.for("NotImplemented");
1942
+ static override status = 501 as const;
1943
+ constructor(data: T) {
1944
+ super(NotImplemented.status, data);
1945
+ }
1946
+ static override create<T>(status: number, data: T) {
1947
+ Object.defineProperty(data, KIND, { value: this.kind });
1948
+ return new this(data);
1949
+ }
1950
+
1951
+ static is<T extends {[KIND]: typeof NotImplemented['kind']}>(value: unknown): value is T {
1952
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1953
+ }
1954
+ }
1955
+ export class BadGateway<T = { message: string }> extends APIError<T, 502> {
1956
+ static override readonly kind = Symbol.for("BadGateway");
1957
+ static override status = 502 as const;
1958
+ constructor(data: T) {
1959
+ super(BadGateway.status, data);
1960
+ }
1961
+ static override create<T>(status: number, data: T) {
1962
+ Object.defineProperty(data, KIND, { value: this.kind });
1963
+ return new this(data);
1964
+ }
1965
+
1966
+ static is<T extends {[KIND]: typeof BadGateway['kind']}>(value: unknown): value is T {
1967
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1968
+ }
1969
+ }
1970
+ export class ServiceUnavailable<
1971
+ T = { message: string; retryAfter?: string },
1972
+ > extends APIError<T, 503> {
1973
+ static override readonly kind = Symbol.for("ServiceUnavailable");
1974
+ static override status = 503 as const;
1975
+ constructor(data: T) {
1976
+ super(ServiceUnavailable.status, data);
1977
+ }
1978
+ static override create<T>(status: number, data: T) {
1979
+ Object.defineProperty(data, KIND, { value: this.kind });
1980
+ return new this(data);
1981
+ }
1982
+
1983
+ static is<T extends {[KIND]: typeof ServiceUnavailable['kind']}>(value: unknown): value is T {
1984
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1985
+ }
1986
+ }
1987
+ export class GatewayTimeout<T = { message: string }> extends APIError<T, 504> {
1988
+ static override readonly kind = Symbol.for("GatewayTimeout");
1989
+ static override status = 504 as const;
1990
+ constructor(data: T) {
1991
+ super(GatewayTimeout.status, data);
1992
+ }
1993
+ static override create<T>(status: number, data: T) {
1994
+ Object.defineProperty(data, KIND, { value: this.kind });
1995
+ return new this(data);
1996
+ }
1997
+
1998
+ static is<T extends {[KIND]: typeof GatewayTimeout['kind']}>(value: unknown): value is T {
1999
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
2000
+ }
2001
+ }
2002
+
2003
+ export type ClientError =
2004
+ | BadRequest<{ message: string }>
2005
+ | Unauthorized<unknown>
2006
+ | PaymentRequired<unknown>
2007
+ | Forbidden<unknown>
2008
+ | NotFound<unknown>
2009
+ | MethodNotAllowed<unknown>
2010
+ | NotAcceptable<unknown>
2011
+ | Conflict<unknown>
2012
+ | Gone<unknown>
2013
+ | UnprocessableEntity<unknown>
2014
+ | TooManyRequests<unknown>;
2015
+
2016
+ export type ServerError =
2017
+ | InternalServerError<unknown>
2018
+ | NotImplemented<unknown>
2019
+ | BadGateway<unknown>
2020
+ | ServiceUnavailable<unknown>
2021
+ | GatewayTimeout<unknown>;
2022
+
2023
+ export type ProblematicResponse = ClientError | ServerError;
2024
+
2025
+ export type SuccessfulResponse =
2026
+ | Ok<unknown>
2027
+ | Created<unknown>
2028
+ | Accepted<unknown>
2029
+ | NoContent;
2030
+ `;
1547
2031
 
1548
2032
  // packages/typescript/src/lib/http/send-request.txt
1549
- var send_request_default = "export 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 interface RequestSchema {\n schema: z.ZodType;\n toRequest: (input: any) => RequestConfig;\n output: OutputType[];\n}\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 sendRequest(\n input: unknown,\n route: RequestSchema,\n options: {\n fetch?: z.infer<typeof fetchType>;\n interceptors?: Interceptor[];\n signal?: AbortSignal;\n },\n) {\n const { interceptors = [] } = options;\n const [parsedInput, parseError] = parseInput(route.schema, input);\n if (parseError) {\n return [null as never, parseError as never] as const;\n }\n\n let config = route.toRequest(parsedInput as never);\n for (const interceptor of interceptors) {\n if (interceptor.before) {\n config = await interceptor.before(config);\n }\n }\n\n let response = await (options.fetch ?? fetch)(\n new Request(config.url, config.init),\n {\n ...config.init,\n signal: options.signal,\n },\n );\n\n for (let i = interceptors.length - 1; i >= 0; i--) {\n const interceptor = interceptors[i];\n if (interceptor.after) {\n response = await interceptor.after(response.clone());\n }\n }\n return await parse(route, response);\n}\n\nexport async function parse(route: RequestSchema, response: Response) {\n let output: typeof APIResponse | null = null;\n let parser: Parser = buffered;\n for (const outputType of route.output) {\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 if (response.ok) {\n const data = (output || APIResponse).create(\n response.status,\n await parser(response),\n );\n return [data as never, null] as const;\n }\n const data = (output || APIError).create(\n response.status,\n await parser(response),\n );\n return [null as never, data as never] as const;\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";
2033
+ var send_request_default = "export 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 interface RequestSchema {\n schema: z.ZodType;\n toRequest: (input: any) => RequestConfig;\n output: OutputType[];\n}\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 dispatch(\n input: unknown,\n route: RequestSchema,\n options: {\n fetch?: z.infer<typeof fetchType>;\n interceptors?: Interceptor[];\n signal?: AbortSignal;\n },\n) {\n const { interceptors = [] } = options;\n const [parsedInput, parseError] = parseInput(route.schema, input);\n if (parseError) {\n <% if(throwError) { %>\n throw parseError;\n <% } else { %>\n return [null as never, parseError as never] as const;\n <% } %>\n }\n\n let config = route.toRequest(parsedInput as never);\n for (const interceptor of interceptors) {\n if (interceptor.before) {\n config = await interceptor.before(config);\n }\n }\n\n let response = await (options.fetch ?? fetch)(\n new Request(config.url, config.init),\n {\n ...config.init,\n signal: options.signal,\n },\n );\n\n for (let i = interceptors.length - 1; i >= 0; i--) {\n const interceptor = interceptors[i];\n if (interceptor.after) {\n response = await interceptor.after(response.clone());\n }\n }\n return await parse(route, response);\n}\n\nexport async function parse(route: RequestSchema, response: Response) {\n let output: typeof APIResponse | null = null;\n let parser: Parser = buffered;\n for (const outputType of route.output) {\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 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.data<% } else { %>apiresponse<% } %>;\n <% } else { %>\n return [<% if (outputType === 'default') { %>apiresponse.data<% } else { %>apiresponse<% } %> , 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 as never, data as never] 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";
1550
2034
 
1551
2035
  // packages/typescript/src/lib/generate.ts
1552
2036
  function security(spec) {
@@ -1570,6 +2054,15 @@ function security(spec) {
1570
2054
  return options;
1571
2055
  }
1572
2056
  async function generate(spec, settings) {
2057
+ const style = Object.assign(
2058
+ {},
2059
+ {
2060
+ errorAsValue: true,
2061
+ name: "github",
2062
+ outputType: "default"
2063
+ },
2064
+ settings.style ?? {}
2065
+ );
1573
2066
  settings.useTsExtension ??= true;
1574
2067
  const makeImport = (moduleSpecifier) => {
1575
2068
  return settings.useTsExtension ? `${moduleSpecifier}.ts` : moduleSpecifier;
@@ -1577,11 +2070,11 @@ async function generate(spec, settings) {
1577
2070
  const { commonSchemas, endpoints, groups, outputs, commonZod } = generateCode(
1578
2071
  {
1579
2072
  spec,
1580
- style: "github",
2073
+ style,
1581
2074
  makeImport
1582
2075
  }
1583
2076
  );
1584
- const output = settings.mode === "full" ? join2(settings.output, "src") : settings.output;
2077
+ const output = settings.mode === "full" ? join3(settings.output, "src") : settings.output;
1585
2078
  const options = security(spec);
1586
2079
  const clientName = settings.name || "Client";
1587
2080
  const inputFiles = generateInputs(groups, commonZod, makeImport);
@@ -1591,9 +2084,9 @@ async function generate(spec, settings) {
1591
2084
  "models/.getkeep": ""
1592
2085
  // 'README.md': readme,
1593
2086
  });
1594
- await writeFiles(join2(output, "http"), {
2087
+ await writeFiles(join3(output, "http"), {
1595
2088
  "interceptors.ts": `
1596
- import { type RequestConfig } from './${makeImport("request")}';
2089
+ import type { RequestConfig, HeadersInit } from './${makeImport("request")}';
1597
2090
  ${interceptors_default}`,
1598
2091
  "parse-response.ts": parse_response_default,
1599
2092
  "send-request.ts": `import z from 'zod';
@@ -1603,20 +2096,23 @@ import { parseInput } from './${makeImport("parser")}';
1603
2096
  import type { RequestConfig } from './${makeImport("request")}';
1604
2097
  import { APIError, APIResponse } from './${makeImport("response")}';
1605
2098
 
1606
- ${send_request_default}`,
2099
+ ${template2(send_request_default, {})({ throwError: !style.errorAsValue, outputType: style.outputType })}`,
1607
2100
  "response.ts": response_default,
1608
2101
  "parser.ts": parser_default,
1609
2102
  "request.ts": request_default
1610
2103
  });
1611
- await writeFiles(join2(output, "outputs"), outputs);
2104
+ await writeFiles(join3(output, "outputs"), outputs);
1612
2105
  const modelsImports = Object.entries(commonSchemas).map(([name]) => name);
1613
2106
  await writeFiles(output, {
1614
- "client.ts": client_default({
1615
- name: clientName,
1616
- servers: (spec.servers ?? []).map((server) => server.url) || [],
1617
- options,
1618
- makeImport
1619
- }),
2107
+ "client.ts": client_default(
2108
+ {
2109
+ name: clientName,
2110
+ servers: (spec.servers ?? []).map((server) => server.url) || [],
2111
+ options,
2112
+ makeImport
2113
+ },
2114
+ style
2115
+ ),
1620
2116
  ...inputFiles,
1621
2117
  ...endpoints,
1622
2118
  ...Object.fromEntries(
@@ -1633,24 +2129,24 @@ ${send_request_default}`,
1633
2129
  )
1634
2130
  });
1635
2131
  const folders = [
1636
- getFolderExports(join2(output, "outputs"), settings.useTsExtension),
2132
+ getFolderExports(join3(output, "outputs"), settings.useTsExtension),
1637
2133
  getFolderExports(
1638
- join2(output, "inputs"),
2134
+ join3(output, "inputs"),
1639
2135
  settings.useTsExtension,
1640
2136
  ["ts"],
1641
2137
  (dirent) => dirent.isDirectory() && ["schemas"].includes(dirent.name)
1642
2138
  ),
1643
- getFolderExports(join2(output, "api"), settings.useTsExtension),
2139
+ getFolderExports(join3(output, "api"), settings.useTsExtension),
1644
2140
  getFolderExports(
1645
- join2(output, "http"),
2141
+ join3(output, "http"),
1646
2142
  settings.useTsExtension,
1647
2143
  ["ts"],
1648
- (dirent) => dirent.name !== "response.ts"
2144
+ (dirent) => !["response.ts", "parser.ts"].includes(dirent.name)
1649
2145
  )
1650
2146
  ];
1651
2147
  if (modelsImports.length) {
1652
2148
  folders.push(
1653
- getFolderExports(join2(output, "models"), settings.useTsExtension)
2149
+ getFolderExports(join3(output, "models"), settings.useTsExtension)
1654
2150
  );
1655
2151
  }
1656
2152
  const [outputIndex, inputsIndex, apiIndex, httpIndex, modelsIndex] = await Promise.all(folders);