@sdk-it/typescript 0.18.0 → 0.19.1

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,5 +1,5 @@
1
1
  // packages/typescript/src/lib/generate.ts
2
- import { template } from "lodash-es";
2
+ import { template as template2 } from "lodash-es";
3
3
  import { join as join2 } from "node:path";
4
4
  import { npmRunPathEnv } from "npm-run-path";
5
5
  import { spinalcase as spinalcase3 } from "stringcase";
@@ -7,7 +7,7 @@ import { getFolderExports, methods, writeFiles } from "@sdk-it/core";
7
7
 
8
8
  // packages/typescript/src/lib/client.ts
9
9
  import { toLitObject } from "@sdk-it/core";
10
- var client_default = (spec, throwError) => {
10
+ var client_default = (spec, style) => {
11
11
  const optionsEntries = Object.entries(spec.options).map(
12
12
  ([key, value]) => [`'${key}'`, value]
13
13
  );
@@ -29,8 +29,8 @@ var client_default = (spec, throwError) => {
29
29
  }
30
30
  };
31
31
  return `
32
- import type { RequestConfig } from './http/${spec.makeImport("request")}';
33
- import { fetchType, sendRequest, parse } from './http/${spec.makeImport("send-request")}';
32
+ import type { HeadersInit, RequestConfig } from './http/${spec.makeImport("request")}';
33
+ import { fetchType, dispatch, parse } from './http/${spec.makeImport("send-request")}';
34
34
  import z from 'zod';
35
35
  import type { Endpoints } from './api/${spec.makeImport("endpoints")}';
36
36
  import schemas from './api/${spec.makeImport("schemas")}';
@@ -57,11 +57,9 @@ export class ${spec.name} {
57
57
  endpoint: E,
58
58
  input: Endpoints[E]['input'],
59
59
  options?: { signal?: AbortSignal, headers?: HeadersInit },
60
- )
61
- ${throwError ? `: Endpoints[E]['output']` : `: Promise<readonly [Endpoints[E]['output'], Endpoints[E]['error'] | null]>`}
62
- {
60
+ ) ${style.errorAsValue ? `: Promise<readonly [Endpoints[E]['output'], Endpoints[E]['error'] | null]>` : `: Promise<Endpoints[E]['output']>`} {
63
61
  const route = schemas[endpoint];
64
- return sendRequest(Object.assign(this.#defaultInputs, input), route, {
62
+ const result = await dispatch(Object.assign(this.#defaultInputs, input), route, {
65
63
  fetch: this.options.fetch,
66
64
  interceptors: [
67
65
  createHeadersInterceptor(() => this.defaultHeaders, options?.headers ?? {}),
@@ -69,20 +67,23 @@ export class ${spec.name} {
69
67
  ],
70
68
  signal: options?.signal,
71
69
  });
70
+ return ${style.errorAsValue ? `result as [Endpoints[E]['output'], Endpoints[E]['error'] | null]` : `result as Endpoints[E]['output']`};
72
71
  }
73
72
 
74
73
  async prepare<E extends keyof Endpoints>(
75
74
  endpoint: E,
76
75
  input: Endpoints[E]['input'],
77
76
  options?: { headers?: HeadersInit },
78
- ): Promise<
77
+ ): ${style.errorAsValue ? `Promise<
79
78
  readonly [
80
79
  RequestConfig & {
81
80
  parse: (response: Response) => ReturnType<typeof parse>;
82
81
  },
83
82
  ParseError<(typeof schemas)[E]['schema']> | null,
84
83
  ]
85
- > {
84
+ >` : `Promise<RequestConfig & {
85
+ parse: (response: Response) => ReturnType<typeof parse>;
86
+ }>`} {
86
87
  const route = schemas[endpoint];
87
88
 
88
89
  const interceptors = [
@@ -94,7 +95,7 @@ export class ${spec.name} {
94
95
  ];
95
96
  const [parsedInput, parseError] = parseInput(route.schema, input);
96
97
  if (parseError) {
97
- return [null as never, parseError as never] as const;
98
+ ${style.errorAsValue ? "return [null as never, parseError as never] as const;" : "throw parseError;"}
98
99
  }
99
100
 
100
101
  let config = route.toRequest(parsedInput as never);
@@ -103,10 +104,8 @@ export class ${spec.name} {
103
104
  config = await interceptor.before(config);
104
105
  }
105
106
  }
106
- return [
107
- { ...config, parse: (response: Response) => parse(route, response) },
108
- null as never,
109
- ] as const;
107
+ const prepared = { ...config, parse: (response: Response) => parse(route, response) };
108
+ return ${style.errorAsValue ? "[prepared, null as never] as const;" : "prepared"}
110
109
  }
111
110
 
112
111
  get defaultHeaders() {
@@ -130,7 +129,7 @@ export class ${spec.name} {
130
129
  };
131
130
 
132
131
  // packages/typescript/src/lib/generator.ts
133
- import { merge } from "lodash-es";
132
+ import { merge, template } from "lodash-es";
134
133
  import { join } from "node:path";
135
134
  import { camelcase as camelcase3, pascalcase as pascalcase2, spinalcase as spinalcase2 } from "stringcase";
136
135
  import { followRef as followRef4, isEmpty, isRef as isRef5 } from "@sdk-it/core";
@@ -674,70 +673,12 @@ var TypeScriptDeserialzer = class {
674
673
  this.#spec = spec;
675
674
  this.#onRef = onRef;
676
675
  }
677
- #stringifyKey = (key) => {
678
- const reservedWords = [
679
- "constructor",
680
- "prototype",
681
- "break",
682
- "case",
683
- "catch",
684
- "class",
685
- "const",
686
- "continue",
687
- "debugger",
688
- "default",
689
- "delete",
690
- "do",
691
- "else",
692
- "export",
693
- "extends",
694
- "false",
695
- "finally",
696
- "for",
697
- "function",
698
- "if",
699
- "import",
700
- "in",
701
- "instanceof",
702
- "new",
703
- "null",
704
- "return",
705
- "super",
706
- "switch",
707
- "this",
708
- "throw",
709
- "true",
710
- "try",
711
- "typeof",
712
- "var",
713
- "void",
714
- "while",
715
- "with",
716
- "yield"
717
- ];
718
- if (reservedWords.includes(key)) {
719
- return `'${key}'`;
720
- }
721
- if (key.trim() === "") {
722
- return `'${key}'`;
723
- }
724
- const firstChar = key.charAt(0);
725
- const validFirstChar = firstChar >= "a" && firstChar <= "z" || firstChar >= "A" && firstChar <= "Z" || firstChar === "_" || firstChar === "$";
726
- if (!validFirstChar) {
727
- return `'${key.replace(/'/g, "\\'")}'`;
728
- }
729
- for (let i = 1; i < key.length; i++) {
730
- const char = key.charAt(i);
731
- const validChar = char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char >= "0" && char <= "9" || char === "_" || char === "$";
732
- if (!validChar) {
733
- return `'${key.replace(/'/g, "\\'")}'`;
734
- }
735
- }
736
- return key;
737
- };
738
- #stringifyKeyV2 = (value) => {
676
+ #stringifyKey = (value) => {
739
677
  return `'${value}'`;
740
678
  };
679
+ #isInternal = (schema) => {
680
+ return isRef2(schema) ? false : !!schema["x-internal"];
681
+ };
741
682
  /**
742
683
  * Handle objects (properties)
743
684
  */
@@ -746,7 +687,7 @@ var TypeScriptDeserialzer = class {
746
687
  const propEntries = Object.entries(properties).map(([key, propSchema]) => {
747
688
  const isRequired = (schema.required ?? []).includes(key);
748
689
  const tsType = this.handle(propSchema, isRequired);
749
- return `${this.#stringifyKeyV2(key)}: ${tsType}`;
690
+ return `${this.#isInternal(propSchema) ? key : this.#stringifyKey(key)}: ${tsType}`;
750
691
  });
751
692
  if (schema.additionalProperties) {
752
693
  if (typeof schema.additionalProperties === "object") {
@@ -885,6 +826,12 @@ var TypeScriptDeserialzer = class {
885
826
  if (schema.enum && Array.isArray(schema.enum)) {
886
827
  return this.enum(schema.enum, required);
887
828
  }
829
+ if (schema.const) {
830
+ if (schema["x-internal"]) {
831
+ return `${schema.const}`;
832
+ }
833
+ return this.enum([schema.const], required);
834
+ }
888
835
  const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
889
836
  if (!types.length) {
890
837
  if ("properties" in schema) {
@@ -903,13 +850,6 @@ var TypeScriptDeserialzer = class {
903
850
  }
904
851
  return this.normal(types[0], schema, required);
905
852
  }
906
- /**
907
- * Generate an interface declaration
908
- */
909
- generateInterface(name, schema) {
910
- const content = this.handle(schema, true);
911
- return `interface ${name} ${content}`;
912
- }
913
853
  };
914
854
  function appendOptional2(type, isRequired) {
915
855
  return isRequired ? type : `${type} | undefined`;
@@ -1175,7 +1115,7 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1175
1115
  );
1176
1116
  const statusCode = +status;
1177
1117
  const parser = (response.headers ?? {})["Transfer-Encoding"] ? "chunked" : "buffered";
1178
- const statusName = statusCodeToResponseMap[status] || "APIResponse";
1118
+ const statusName = `http.${statusCodeToResponseMap[status] || "APIResponse"}`;
1179
1119
  const interfaceName = pascalcase(
1180
1120
  operationName + ` output${numbered ? status : ""}`
1181
1121
  );
@@ -1183,7 +1123,7 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1183
1123
  outputs.push(statusName);
1184
1124
  } else {
1185
1125
  if (status.endsWith("XX")) {
1186
- outputs.push(`APIError<${interfaceName}>`);
1126
+ outputs.push(`http.APIError<${interfaceName}>`);
1187
1127
  } else {
1188
1128
  outputs.push(
1189
1129
  parser !== "buffered" ? `{type: ${statusName}<${interfaceName}>, parser: ${parser}}` : `${statusName}<${interfaceName}>`
@@ -1192,13 +1132,25 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1192
1132
  }
1193
1133
  const responseContent = get(response, ["content"]);
1194
1134
  const isJson = responseContent && responseContent["application/json"];
1195
- const responseSchema = isJson ? typeScriptDeserialzer.handle(
1196
- responseContent["application/json"].schema,
1197
- true
1198
- ) : "void";
1135
+ let responseSchema = parser === "chunked" ? "ReadableStream" : "void";
1136
+ if (isJson) {
1137
+ const schema = responseContent["application/json"].schema;
1138
+ const isObject = !isRef4(schema) && schema.type === "object";
1139
+ if (isObject && schema.properties) {
1140
+ schema.properties["[http.KIND]"] = {
1141
+ "x-internal": true,
1142
+ const: `typeof ${statusName}.kind`,
1143
+ type: "string"
1144
+ };
1145
+ schema.required ??= [];
1146
+ schema.required.push("[KIND]");
1147
+ }
1148
+ responseSchema = typeScriptDeserialzer.handle(schema, true);
1149
+ }
1199
1150
  responses.push({
1200
1151
  name: interfaceName,
1201
- schema: responseSchema
1152
+ schema: responseSchema,
1153
+ description: response.description
1202
1154
  });
1203
1155
  const statusGroup = +status.slice(0, 1);
1204
1156
  if (statusCode >= 400 || statusGroup >= 4) {
@@ -1212,15 +1164,6 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1212
1164
  namedImports: [{ isTypeOnly: true, name: interfaceName }]
1213
1165
  };
1214
1166
  } else if (statusCode >= 200 && statusCode < 300 || statusCode >= 2 || statusGroup <= 3) {
1215
- endpointImports[statusName] = {
1216
- moduleSpecifier: utils.makeImport("../http/response"),
1217
- namedImports: [
1218
- {
1219
- isTypeOnly: false,
1220
- name: statusName
1221
- }
1222
- ]
1223
- };
1224
1167
  endpointImports[interfaceName] = {
1225
1168
  defaultImport: void 0,
1226
1169
  isTypeOnly: true,
@@ -1233,7 +1176,7 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
1233
1176
  }
1234
1177
 
1235
1178
  // packages/typescript/src/lib/styles/github/endpoints.txt
1236
- 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};";
1179
+ 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};";
1237
1180
 
1238
1181
  // packages/typescript/src/lib/generator.ts
1239
1182
  function generateCode(config) {
@@ -1373,14 +1316,23 @@ function generateCode(config) {
1373
1316
  },
1374
1317
  { makeImport: config.makeImport }
1375
1318
  );
1376
- const output = [`import z from 'zod';`];
1319
+ const output = [
1320
+ `import z from 'zod';`,
1321
+ `import type * as http from '../http';`
1322
+ ];
1377
1323
  const responses = endpoint.responses.flatMap((it) => it.responses);
1378
1324
  const responsesImports = endpoint.responses.flatMap(
1379
1325
  (it) => Object.values(it.imports)
1380
1326
  );
1381
1327
  if (responses.length) {
1382
1328
  output.push(
1383
- ...responses.map((it) => `export type ${it.name} = ${it.schema};`)
1329
+ ...responses.map(
1330
+ (it) => `${it.description ? `
1331
+ /**
1332
+ * ${it.description}
1333
+ */
1334
+ ` : ""} export type ${it.name} = ${it.schema};`
1335
+ )
1384
1336
  );
1385
1337
  } else {
1386
1338
  output.push(
@@ -1445,9 +1397,9 @@ import type { OutputType, Parser, Type } from '${config.makeImport(
1445
1397
 
1446
1398
  import schemas from '${config.makeImport("./schemas")}';
1447
1399
 
1448
- ${endpoints_default}`,
1400
+ ${template(endpoints_default)({ outputType: config.style?.outputType })}`,
1449
1401
  [`${join("api", "schemas.ts")}`]: `${allSchemas.map((it) => it.import).join("\n")}
1450
-
1402
+ import { KIND } from "${config.makeImport("../http/index")}";
1451
1403
  export default {
1452
1404
  ${allSchemas.map((it) => it.use).join(",\n")}
1453
1405
  };
@@ -1471,6 +1423,7 @@ ${allSchemas.map((it) => it.use).join(",\n")}
1471
1423
  ...imps,
1472
1424
  // ...imports,
1473
1425
  `import z from 'zod';`,
1426
+ `import * as http from '${config.makeImport("../http/response")}';`,
1474
1427
  `import { toRequest, json, urlencoded, nobody, formdata, createUrl } from '${config.makeImport("../http/request")}';`,
1475
1428
  `import { chunked, buffered } from "${config.makeImport("../http/parse-response")}";`,
1476
1429
  `import * as ${camelcase3(name)} from '../inputs/${config.makeImport(spinalcase2(name))}';`
@@ -1534,7 +1487,7 @@ function bodyInputs(config, ctSchema) {
1534
1487
  }
1535
1488
 
1536
1489
  // packages/typescript/src/lib/http/interceptors.txt
1537
- 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";
1490
+ 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";
1538
1491
 
1539
1492
  // packages/typescript/src/lib/http/parse-response.txt
1540
1493
  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';
@@ -1543,13 +1496,421 @@ var parse_response_default = 'import { parse } from "fast-content-type-parse";\n
1543
1496
  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";
1544
1497
 
1545
1498
  // packages/typescript/src/lib/http/request.txt
1546
- 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";
1499
+ 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";
1547
1500
 
1548
1501
  // packages/typescript/src/lib/http/response.txt
1549
- 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;";
1502
+ var response_default = `export const KIND = Symbol('APIDATA');
1503
+
1504
+ export class APIResponse<Body = unknown, Status extends number = number> {
1505
+ static readonly status: number;
1506
+ static readonly kind: symbol = Symbol.for("APIResponse");
1507
+ status: Status;
1508
+ data: Body;
1509
+
1510
+ constructor(status: Status, data: Body) {
1511
+ this.status = status;
1512
+ this.data = data;
1513
+ }
1514
+
1515
+ static create<Body = unknown>(status: number, data: Body) {
1516
+ return new this(status, data);
1517
+ }
1518
+
1519
+ }
1520
+
1521
+ export class APIError<Body, Status extends number = number> extends APIResponse<
1522
+ Body,
1523
+ Status
1524
+ > {
1525
+ static override create<T>(status: number, data: T) {
1526
+ return new this(status, data);
1527
+ }
1528
+ }
1529
+
1530
+
1531
+ // 2xx Success
1532
+ export class Ok<T> extends APIResponse<T, 200> {
1533
+ static override readonly kind = Symbol.for("Ok");
1534
+ static override readonly status = 200 as const;
1535
+ constructor(data: T) {
1536
+ super(Ok.status, data);
1537
+ }
1538
+ static override create<T>(status: number, data: T) {
1539
+ Object.defineProperty(data, KIND, { value: this.kind });
1540
+ return new this(data);
1541
+ }
1542
+
1543
+ static is<T extends {[KIND]:typeof Ok['kind']}>(value: unknown): value is T {
1544
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1545
+ }
1546
+ }
1547
+
1548
+
1549
+ export class Created<T> extends APIResponse<T, 201> {
1550
+ static override readonly kind = Symbol.for("Created");
1551
+ static override status = 201 as const;
1552
+ constructor(data: T) {
1553
+ super(Created.status, data);
1554
+ }
1555
+ static override create<T>(status: number, data: T) {
1556
+ Object.defineProperty(data, KIND, { value: this.kind });
1557
+ return new this(data);
1558
+ }
1559
+
1560
+ static is<T extends {[KIND]: typeof Created['kind']}>(value: unknown): value is T {
1561
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1562
+ }
1563
+ }
1564
+ export class Accepted<T> extends APIResponse<T, 202> {
1565
+ static override readonly kind = Symbol.for("Accepted");
1566
+ static override status = 202 as const;
1567
+ constructor(data: T) {
1568
+ super(Accepted.status, data);
1569
+ }
1570
+ static override create<T>(status: number, data: T) {
1571
+ Object.defineProperty(data, KIND, { value: this.kind });
1572
+ return new this(data);
1573
+ }
1574
+
1575
+ static is<T extends {[KIND]: typeof Accepted['kind']}>(value: unknown): value is T {
1576
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1577
+ }
1578
+ }
1579
+ export class NoContent extends APIResponse<never, 204> {
1580
+ static override readonly kind = Symbol.for("NoContent");
1581
+ static override status = 204 as const;
1582
+ constructor() {
1583
+ super(NoContent.status, null as never);
1584
+ }
1585
+ static override create(status: number, data: never): NoContent {
1586
+ return new this();
1587
+ }
1588
+
1589
+ static is<T extends {[KIND]: typeof NoContent['kind']}>(value: unknown): value is T {
1590
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1591
+ }
1592
+ }
1593
+
1594
+ // 4xx Client Errors
1595
+ export class BadRequest<T> extends APIError<T, 400> {
1596
+ static override readonly kind = Symbol.for("BadRequest");
1597
+ static override status = 400 as const;
1598
+ constructor(data: T) {
1599
+ super(BadRequest.status, data);
1600
+ }
1601
+ static override create<T>(status: number, data: T) {
1602
+ Object.defineProperty(data, KIND, { value: this.kind });
1603
+ return new this(data);
1604
+ }
1605
+
1606
+ static is<T extends {[KIND]: typeof BadRequest['kind']}>(value: unknown): value is T {
1607
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1608
+ }
1609
+ }
1610
+ export class Unauthorized<T = { message: string }> extends APIError<T, 401> {
1611
+ static override readonly kind = Symbol.for("Unauthorized");
1612
+ static override status = 401 as const;
1613
+ constructor(data: T) {
1614
+ super(Unauthorized.status, data);
1615
+ }
1616
+ static override create<T>(status: number, data: T) {
1617
+ Object.defineProperty(data, KIND, { value: this.kind });
1618
+ return new this(data);
1619
+ }
1620
+
1621
+ static is<T extends {[KIND]: typeof Unauthorized['kind']}>(value: unknown): value is T {
1622
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1623
+ }
1624
+ }
1625
+ export class PaymentRequired<T = { message: string }> extends APIError<T, 402> {
1626
+ static override readonly kind = Symbol.for("PaymentRequired");
1627
+ static override status = 402 as const;
1628
+ constructor(data: T) {
1629
+ super(PaymentRequired.status, data);
1630
+ }
1631
+ static override create<T>(status: number, data: T) {
1632
+ Object.defineProperty(data, KIND, { value: this.kind });
1633
+ return new this(data);
1634
+ }
1635
+
1636
+ static is<T extends {[KIND]: typeof PaymentRequired['kind']}>(value: unknown): value is T {
1637
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1638
+ }
1639
+ }
1640
+ export class Forbidden<T = { message: string }> extends APIError<T, 403> {
1641
+ static override readonly kind = Symbol.for("Forbidden");
1642
+ static override status = 403 as const;
1643
+ constructor(data: T) {
1644
+ super(Forbidden.status, data);
1645
+ }
1646
+ static override create<T>(status: number, data: T) {
1647
+ Object.defineProperty(data, KIND, { value: this.kind });
1648
+ return new this(data);
1649
+ }
1650
+
1651
+ static is<T extends {[KIND]: typeof Forbidden['kind']}>(value: unknown): value is T {
1652
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1653
+ }
1654
+ }
1655
+ export class NotFound<T = { message: string }> extends APIError<T, 404> {
1656
+ static override readonly kind = Symbol.for("NotFound");
1657
+ static override status = 404 as const;
1658
+ constructor(data: T) {
1659
+ super(NotFound.status, data);
1660
+ }
1661
+ static override create<T>(status: number, data: T) {
1662
+ Object.defineProperty(data, KIND, { value: this.kind });
1663
+ return new this(data);
1664
+ }
1665
+
1666
+ static is<T extends {[KIND]: typeof NotFound['kind']}>(value: unknown): value is T {
1667
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1668
+ }
1669
+ }
1670
+ export class MethodNotAllowed<T = { message: string }> extends APIError<
1671
+ T,
1672
+ 405
1673
+ > {
1674
+ static override readonly kind = Symbol.for("MethodNotAllowed");
1675
+ static override status = 405 as const;
1676
+ constructor(data: T) {
1677
+ super(MethodNotAllowed.status, data);
1678
+ }
1679
+ static override create<T>(status: number, data: T) {
1680
+ Object.defineProperty(data, KIND, { value: this.kind });
1681
+ return new this(data);
1682
+ }
1683
+
1684
+ static is<T extends {[KIND]: typeof MethodNotAllowed['kind']}>(value: unknown): value is T {
1685
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1686
+ }
1687
+ }
1688
+ export class NotAcceptable<T = { message: string }> extends APIError<T, 406> {
1689
+ static override readonly kind = Symbol.for("NotAcceptable");
1690
+ static override status = 406 as const;
1691
+ constructor(data: T) {
1692
+ super(NotAcceptable.status, data);
1693
+ }
1694
+ static override create<T>(status: number, data: T) {
1695
+ Object.defineProperty(data, KIND, { value: this.kind });
1696
+ return new this(data);
1697
+ }
1698
+
1699
+ static is<T extends {[KIND]: typeof NotAcceptable['kind']}>(value: unknown): value is T {
1700
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1701
+ }
1702
+ }
1703
+ export class Conflict<T = { message: string }> extends APIError<T, 409> {
1704
+ static override readonly kind = Symbol.for("Conflict");
1705
+ static override status = 409 as const;
1706
+ constructor(data: T) {
1707
+ super(Conflict.status, data);
1708
+ }
1709
+ static override create<T>(status: number, data: T) {
1710
+ Object.defineProperty(data, KIND, { value: this.kind });
1711
+ return new this(data);
1712
+ }
1713
+
1714
+ static is<T extends {[KIND]: typeof Conflict['kind']}>(value: unknown): value is T {
1715
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1716
+ }
1717
+ }
1718
+ export class Gone<T = { message: string }> extends APIError<T, 410> {
1719
+ static override readonly kind = Symbol.for("Gone");
1720
+ static override status = 410 as const;
1721
+ constructor(data: T) {
1722
+ super(Gone.status, data);
1723
+ }
1724
+ static override create<T>(status: number, data: T) {
1725
+ Object.defineProperty(data, KIND, { value: this.kind });
1726
+ return new this(data);
1727
+ }
1728
+
1729
+ static is<T extends {[KIND]: typeof Gone['kind']}>(value: unknown): value is T {
1730
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1731
+ }
1732
+ }
1733
+ export class UnprocessableEntity<
1734
+ T = { message: string; errors?: Record<string, string[]> },
1735
+ > extends APIError<T, 422> {
1736
+ static override readonly kind = Symbol.for("UnprocessableEntity");
1737
+ static override status = 422 as const;
1738
+ constructor(data: T) {
1739
+ super(UnprocessableEntity.status, data);
1740
+ }
1741
+ static override create<T>(status: number, data: T) {
1742
+ Object.defineProperty(data, KIND, { value: this.kind });
1743
+ return new this(data);
1744
+ }
1745
+
1746
+ static is<T extends {[KIND]: typeof UnprocessableEntity['kind']}>(value: unknown): value is T {
1747
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1748
+ }
1749
+ }
1750
+ export class TooManyRequests<
1751
+ T = { message: string; retryAfter?: string },
1752
+ > extends APIError<T, 429> {
1753
+ static override readonly kind = Symbol.for("TooManyRequests");
1754
+ static override status = 429 as const;
1755
+ constructor(data: T) {
1756
+ super(TooManyRequests.status, data);
1757
+ }
1758
+ static override create<T>(status: number, data: T) {
1759
+ Object.defineProperty(data, KIND, { value: this.kind });
1760
+ return new this(data);
1761
+ }
1762
+
1763
+ static is<T extends {[KIND]: typeof TooManyRequests['kind']}>(value: unknown): value is T {
1764
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1765
+ }
1766
+ }
1767
+ export class PayloadTooLarge<T = { message: string }> extends APIError<T, 413> {
1768
+ static override readonly kind = Symbol.for("PayloadTooLarge");
1769
+ static override status = 413 as const;
1770
+ constructor(data: T) {
1771
+ super(PayloadTooLarge.status, data);
1772
+ }
1773
+ static override create<T>(status: number, data: T) {
1774
+ Object.defineProperty(data, KIND, { value: this.kind });
1775
+ return new this(data);
1776
+ }
1777
+
1778
+ static is<T extends {[KIND]: typeof PayloadTooLarge['kind']}>(value: unknown): value is T {
1779
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1780
+ }
1781
+ }
1782
+ export class UnsupportedMediaType<T = { message: string }> extends APIError<
1783
+ T,
1784
+ 415
1785
+ > {
1786
+ static override readonly kind = Symbol.for("UnsupportedMediaType");
1787
+ static override status = 415 as const;
1788
+ constructor(data: T) {
1789
+ super(UnsupportedMediaType.status, data);
1790
+ }
1791
+ static override create<T>(status: number, data: T) {
1792
+ Object.defineProperty(data, KIND, { value: this.kind });
1793
+ return new this(data);
1794
+ }
1795
+
1796
+ static is<T extends {[KIND]: typeof UnsupportedMediaType['kind']}>(value: unknown): value is T {
1797
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1798
+ }
1799
+ }
1800
+
1801
+ // 5xx Server Errors
1802
+ export class InternalServerError<T = { message: string }> extends APIError<
1803
+ T,
1804
+ 500
1805
+ > {
1806
+ static override readonly kind = Symbol.for("InternalServerError");
1807
+ static override status = 500 as const;
1808
+ constructor(data: T) {
1809
+ super(InternalServerError.status, data);
1810
+ }
1811
+ static override create<T>(status: number, data: T) {
1812
+ Object.defineProperty(data, KIND, { value: this.kind });
1813
+ return new this(data);
1814
+ }
1815
+
1816
+ static is<T extends {[KIND]: typeof InternalServerError['kind']}>(value: unknown): value is T {
1817
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1818
+ }
1819
+ }
1820
+ export class NotImplemented<T = { message: string }> extends APIError<T, 501> {
1821
+ static override readonly kind = Symbol.for("NotImplemented");
1822
+ static override status = 501 as const;
1823
+ constructor(data: T) {
1824
+ super(NotImplemented.status, data);
1825
+ }
1826
+ static override create<T>(status: number, data: T) {
1827
+ Object.defineProperty(data, KIND, { value: this.kind });
1828
+ return new this(data);
1829
+ }
1830
+
1831
+ static is<T extends {[KIND]: typeof NotImplemented['kind']}>(value: unknown): value is T {
1832
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1833
+ }
1834
+ }
1835
+ export class BadGateway<T = { message: string }> extends APIError<T, 502> {
1836
+ static override readonly kind = Symbol.for("BadGateway");
1837
+ static override status = 502 as const;
1838
+ constructor(data: T) {
1839
+ super(BadGateway.status, data);
1840
+ }
1841
+ static override create<T>(status: number, data: T) {
1842
+ Object.defineProperty(data, KIND, { value: this.kind });
1843
+ return new this(data);
1844
+ }
1845
+
1846
+ static is<T extends {[KIND]: typeof BadGateway['kind']}>(value: unknown): value is T {
1847
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1848
+ }
1849
+ }
1850
+ export class ServiceUnavailable<
1851
+ T = { message: string; retryAfter?: string },
1852
+ > extends APIError<T, 503> {
1853
+ static override readonly kind = Symbol.for("ServiceUnavailable");
1854
+ static override status = 503 as const;
1855
+ constructor(data: T) {
1856
+ super(ServiceUnavailable.status, data);
1857
+ }
1858
+ static override create<T>(status: number, data: T) {
1859
+ Object.defineProperty(data, KIND, { value: this.kind });
1860
+ return new this(data);
1861
+ }
1862
+
1863
+ static is<T extends {[KIND]: typeof ServiceUnavailable['kind']}>(value: unknown): value is T {
1864
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1865
+ }
1866
+ }
1867
+ export class GatewayTimeout<T = { message: string }> extends APIError<T, 504> {
1868
+ static override readonly kind = Symbol.for("GatewayTimeout");
1869
+ static override status = 504 as const;
1870
+ constructor(data: T) {
1871
+ super(GatewayTimeout.status, data);
1872
+ }
1873
+ static override create<T>(status: number, data: T) {
1874
+ Object.defineProperty(data, KIND, { value: this.kind });
1875
+ return new this(data);
1876
+ }
1877
+
1878
+ static is<T extends {[KIND]: typeof GatewayTimeout['kind']}>(value: unknown): value is T {
1879
+ return typeof value === 'object' && value !== null && KIND in value && value[KIND] === this.kind;
1880
+ }
1881
+ }
1882
+
1883
+ export type ClientError =
1884
+ | BadRequest<{ message: string }>
1885
+ | Unauthorized<unknown>
1886
+ | PaymentRequired<unknown>
1887
+ | Forbidden<unknown>
1888
+ | NotFound<unknown>
1889
+ | MethodNotAllowed<unknown>
1890
+ | NotAcceptable<unknown>
1891
+ | Conflict<unknown>
1892
+ | Gone<unknown>
1893
+ | UnprocessableEntity<unknown>
1894
+ | TooManyRequests<unknown>;
1895
+
1896
+ export type ServerError =
1897
+ | InternalServerError<unknown>
1898
+ | NotImplemented<unknown>
1899
+ | BadGateway<unknown>
1900
+ | ServiceUnavailable<unknown>
1901
+ | GatewayTimeout<unknown>;
1902
+
1903
+ export type ProblematicResponse = ClientError | ServerError;
1904
+
1905
+ export type SuccessfulResponse =
1906
+ | Ok<unknown>
1907
+ | Created<unknown>
1908
+ | Accepted<unknown>
1909
+ | NoContent;
1910
+ `;
1550
1911
 
1551
1912
  // packages/typescript/src/lib/http/send-request.txt
1552
- 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 <% 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 data = (output || APIResponse).create(\n response.status,\n await parser(response),\n );\n <% if(throwError) { %>\n return data;\n <% } else { %>\n return [data as never, 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";
1913
+ 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";
1553
1914
 
1554
1915
  // packages/typescript/src/lib/generate.ts
1555
1916
  function security(spec) {
@@ -1573,6 +1934,15 @@ function security(spec) {
1573
1934
  return options;
1574
1935
  }
1575
1936
  async function generate(spec, settings) {
1937
+ const style = Object.assign(
1938
+ {},
1939
+ {
1940
+ errorAsValue: true,
1941
+ name: "github",
1942
+ outputType: "default"
1943
+ },
1944
+ settings.style ?? {}
1945
+ );
1576
1946
  settings.useTsExtension ??= true;
1577
1947
  const makeImport = (moduleSpecifier) => {
1578
1948
  return settings.useTsExtension ? `${moduleSpecifier}.ts` : moduleSpecifier;
@@ -1580,7 +1950,7 @@ async function generate(spec, settings) {
1580
1950
  const { commonSchemas, endpoints, groups, outputs, commonZod } = generateCode(
1581
1951
  {
1582
1952
  spec,
1583
- style: "github",
1953
+ style,
1584
1954
  makeImport
1585
1955
  }
1586
1956
  );
@@ -1596,7 +1966,7 @@ async function generate(spec, settings) {
1596
1966
  });
1597
1967
  await writeFiles(join2(output, "http"), {
1598
1968
  "interceptors.ts": `
1599
- import { type RequestConfig } from './${makeImport("request")}';
1969
+ import type { RequestConfig, HeadersInit } from './${makeImport("request")}';
1600
1970
  ${interceptors_default}`,
1601
1971
  "parse-response.ts": parse_response_default,
1602
1972
  "send-request.ts": `import z from 'zod';
@@ -1606,7 +1976,7 @@ import { parseInput } from './${makeImport("parser")}';
1606
1976
  import type { RequestConfig } from './${makeImport("request")}';
1607
1977
  import { APIError, APIResponse } from './${makeImport("response")}';
1608
1978
 
1609
- ${template(send_request_default, {})({ throwError: settings.throwError })}`,
1979
+ ${template2(send_request_default, {})({ throwError: !style.errorAsValue, outputType: style.outputType })}`,
1610
1980
  "response.ts": response_default,
1611
1981
  "parser.ts": parser_default,
1612
1982
  "request.ts": request_default
@@ -1621,7 +1991,7 @@ ${template(send_request_default, {})({ throwError: settings.throwError })}`,
1621
1991
  options,
1622
1992
  makeImport
1623
1993
  },
1624
- settings.throwError ?? false
1994
+ style
1625
1995
  ),
1626
1996
  ...inputFiles,
1627
1997
  ...endpoints,
@@ -1651,7 +2021,7 @@ ${template(send_request_default, {})({ throwError: settings.throwError })}`,
1651
2021
  join2(output, "http"),
1652
2022
  settings.useTsExtension,
1653
2023
  ["ts"],
1654
- (dirent) => dirent.name !== "response.ts"
2024
+ (dirent) => !["response.ts", "parser.ts"].includes(dirent.name)
1655
2025
  )
1656
2026
  ];
1657
2027
  if (modelsImports.length) {