@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 +486 -116
- package/dist/index.js.map +3 -3
- package/dist/lib/client.d.ts +2 -1
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/emitters/interface.d.ts +0 -4
- package/dist/lib/emitters/interface.d.ts.map +1 -1
- package/dist/lib/generate.d.ts +2 -2
- package/dist/lib/generate.d.ts.map +1 -1
- package/dist/lib/generator.d.ts +4 -1
- package/dist/lib/generator.d.ts.map +1 -1
- package/dist/lib/sdk.d.ts +1 -0
- package/dist/lib/sdk.d.ts.map +1 -1
- package/dist/lib/style.d.ts +6 -0
- package/dist/lib/style.d.ts.map +1 -0
- package/package.json +3 -3
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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 = (
|
|
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.#
|
|
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
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
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 = "
|
|
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 = [
|
|
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(
|
|
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.
|
|
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 =
|
|
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
|
|
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
|
|
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 {
|
|
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
|
-
${
|
|
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
|
-
|
|
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) =>
|
|
2024
|
+
(dirent) => !["response.ts", "parser.ts"].includes(dirent.name)
|
|
1655
2025
|
)
|
|
1656
2026
|
];
|
|
1657
2027
|
if (modelsImports.length) {
|