@rexeus/typeweaver-clients 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3 -15
- package/dist/index.mjs +3 -15
- package/dist/index.mjs.map +1 -1
- package/dist/lib/ApiClient.ts +0 -23
- package/dist/lib/RequestCommand.ts +8 -27
- package/dist/templates/Client.ejs +8 -10
- package/dist/templates/RequestCommand.ejs +10 -32
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -78,23 +78,15 @@ var ClientGenerator = class {
|
|
|
78
78
|
}
|
|
79
79
|
static writeRequestCommand(templateFilePath, operationResource, context) {
|
|
80
80
|
const { definition, sourceDir, sourceFile, outputDir, outputResponseFileName, outputResponseValidationFileName, outputRequestFileName } = operationResource;
|
|
81
|
-
const { operationId, method, request
|
|
81
|
+
const { operationId, method, request } = definition;
|
|
82
82
|
const pascalCaseOperationId = case$1.default.pascal(operationId);
|
|
83
|
-
const allResponses = responses;
|
|
84
|
-
const ownSuccessResponses = allResponses.filter((r) => r.statusCode >= 200 && r.statusCode < 300 && !r.isReference);
|
|
85
|
-
const ownErrorResponses = allResponses.filter((r) => (r.statusCode < 200 || r.statusCode >= 300) && !r.isReference);
|
|
86
|
-
const sharedSuccessResponses = allResponses.filter((r) => r.statusCode >= 200 && r.statusCode < 300 && r.isReference);
|
|
87
|
-
const sharedErrorResponses = allResponses.filter((r) => (r.statusCode < 200 || r.statusCode >= 300) && r.isReference);
|
|
88
83
|
const headerTsType = request.header ? _rexeus_typeweaver_zod_to_ts.TsTypePrinter.print(_rexeus_typeweaver_zod_to_ts.TsTypeNode.fromZod(request.header)) : void 0;
|
|
89
84
|
const paramTsType = request.param ? _rexeus_typeweaver_zod_to_ts.TsTypePrinter.print(_rexeus_typeweaver_zod_to_ts.TsTypeNode.fromZod(request.param)) : void 0;
|
|
90
85
|
const queryTsType = request.query ? _rexeus_typeweaver_zod_to_ts.TsTypePrinter.print(_rexeus_typeweaver_zod_to_ts.TsTypeNode.fromZod(request.query)) : void 0;
|
|
91
86
|
const bodyTsType = request.body ? _rexeus_typeweaver_zod_to_ts.TsTypePrinter.print(_rexeus_typeweaver_zod_to_ts.TsTypeNode.fromZod(request.body)) : void 0;
|
|
92
|
-
const successResponseImportPath = (response) => {
|
|
93
|
-
if (response.isReference && response.path) return response.path;
|
|
94
|
-
return `./${node_path.default.basename(outputResponseFileName, ".ts")}`;
|
|
95
|
-
};
|
|
96
87
|
const requestFile = `./${node_path.default.basename(outputRequestFileName, ".ts")}`;
|
|
97
88
|
const responseValidatorFile = `./${node_path.default.basename(outputResponseValidationFileName, ".ts")}`;
|
|
89
|
+
const responseFile = `./${node_path.default.basename(outputResponseFileName, ".ts")}`;
|
|
98
90
|
const relativeSourceFile = node_path.default.relative(sourceDir, sourceFile);
|
|
99
91
|
const sourcePath = node_path.default.join(sourceDir, relativeSourceFile.replace(/\.ts$/, ""));
|
|
100
92
|
const relativeSourcePath = node_path.default.relative(outputDir, sourcePath);
|
|
@@ -107,13 +99,9 @@ var ClientGenerator = class {
|
|
|
107
99
|
paramTsType,
|
|
108
100
|
queryTsType,
|
|
109
101
|
bodyTsType,
|
|
110
|
-
ownSuccessResponses,
|
|
111
|
-
ownErrorResponses,
|
|
112
|
-
sharedSuccessResponses,
|
|
113
|
-
sharedErrorResponses,
|
|
114
102
|
requestFile,
|
|
115
103
|
responseValidatorFile,
|
|
116
|
-
|
|
104
|
+
responseFile
|
|
117
105
|
});
|
|
118
106
|
const outputCommandFile = node_path.default.join(outputDir, `${pascalCaseOperationId}RequestCommand.ts`);
|
|
119
107
|
const relativePath = node_path.default.relative(context.outputDir, outputCommandFile);
|
package/dist/index.mjs
CHANGED
|
@@ -49,23 +49,15 @@ var ClientGenerator = class {
|
|
|
49
49
|
}
|
|
50
50
|
static writeRequestCommand(templateFilePath, operationResource, context) {
|
|
51
51
|
const { definition, sourceDir, sourceFile, outputDir, outputResponseFileName, outputResponseValidationFileName, outputRequestFileName } = operationResource;
|
|
52
|
-
const { operationId, method, request
|
|
52
|
+
const { operationId, method, request } = definition;
|
|
53
53
|
const pascalCaseOperationId = Case.pascal(operationId);
|
|
54
|
-
const allResponses = responses;
|
|
55
|
-
const ownSuccessResponses = allResponses.filter((r) => r.statusCode >= 200 && r.statusCode < 300 && !r.isReference);
|
|
56
|
-
const ownErrorResponses = allResponses.filter((r) => (r.statusCode < 200 || r.statusCode >= 300) && !r.isReference);
|
|
57
|
-
const sharedSuccessResponses = allResponses.filter((r) => r.statusCode >= 200 && r.statusCode < 300 && r.isReference);
|
|
58
|
-
const sharedErrorResponses = allResponses.filter((r) => (r.statusCode < 200 || r.statusCode >= 300) && r.isReference);
|
|
59
54
|
const headerTsType = request.header ? TsTypePrinter.print(TsTypeNode.fromZod(request.header)) : void 0;
|
|
60
55
|
const paramTsType = request.param ? TsTypePrinter.print(TsTypeNode.fromZod(request.param)) : void 0;
|
|
61
56
|
const queryTsType = request.query ? TsTypePrinter.print(TsTypeNode.fromZod(request.query)) : void 0;
|
|
62
57
|
const bodyTsType = request.body ? TsTypePrinter.print(TsTypeNode.fromZod(request.body)) : void 0;
|
|
63
|
-
const successResponseImportPath = (response) => {
|
|
64
|
-
if (response.isReference && response.path) return response.path;
|
|
65
|
-
return `./${path.basename(outputResponseFileName, ".ts")}`;
|
|
66
|
-
};
|
|
67
58
|
const requestFile = `./${path.basename(outputRequestFileName, ".ts")}`;
|
|
68
59
|
const responseValidatorFile = `./${path.basename(outputResponseValidationFileName, ".ts")}`;
|
|
60
|
+
const responseFile = `./${path.basename(outputResponseFileName, ".ts")}`;
|
|
69
61
|
const relativeSourceFile = path.relative(sourceDir, sourceFile);
|
|
70
62
|
const sourcePath = path.join(sourceDir, relativeSourceFile.replace(/\.ts$/, ""));
|
|
71
63
|
const relativeSourcePath = path.relative(outputDir, sourcePath);
|
|
@@ -78,13 +70,9 @@ var ClientGenerator = class {
|
|
|
78
70
|
paramTsType,
|
|
79
71
|
queryTsType,
|
|
80
72
|
bodyTsType,
|
|
81
|
-
ownSuccessResponses,
|
|
82
|
-
ownErrorResponses,
|
|
83
|
-
sharedSuccessResponses,
|
|
84
|
-
sharedErrorResponses,
|
|
85
73
|
requestFile,
|
|
86
74
|
responseValidatorFile,
|
|
87
|
-
|
|
75
|
+
responseFile
|
|
88
76
|
});
|
|
89
77
|
const outputCommandFile = path.join(outputDir, `${pascalCaseOperationId}RequestCommand.ts`);
|
|
90
78
|
const relativePath = path.relative(context.outputDir, outputCommandFile);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["moduleDir"],"sources":["../src/ClientGenerator.ts","../src/index.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type {\n GeneratorContext,\n OperationResource,\n} from \"@rexeus/typeweaver-gen\";\nimport { TsTypeNode, TsTypePrinter } from \"@rexeus/typeweaver-zod-to-ts\";\nimport Case from \"case\";\n\nconst moduleDir = path.dirname(fileURLToPath(import.meta.url));\n\nexport class ClientGenerator {\n public static generate(context: GeneratorContext): void {\n const clientTemplatePath = path.join(moduleDir, \"templates\", \"Client.ejs\");\n const commandTemplatePath = path.join(\n moduleDir,\n \"templates\",\n \"RequestCommand.ejs\"\n );\n\n for (const [, entityResource] of Object.entries(\n context.resources.entityResources\n )) {\n this.writeClient(clientTemplatePath, entityResource.operations, context);\n this.writeRequestCommands(\n commandTemplatePath,\n entityResource.operations,\n context\n );\n }\n }\n\n private static writeClient(\n templateFilePath: string,\n operationResources: OperationResource[],\n context: GeneratorContext\n ): void {\n const entityName = operationResources[0]!.entityName;\n const pascalCaseEntityName = Case.pascal(entityName);\n const outputDir = operationResources[0]!.outputDir;\n\n const operations: {\n operationId: string;\n pascalCaseOperationId: string;\n requestFile: string;\n responseValidatorFile: string;\n responseFile: string;\n }[] = [];\n for (const operationResource of operationResources) {\n const {\n definition,\n outputResponseFileName,\n outputResponseValidationFileName,\n outputRequestFileName,\n } = operationResource;\n const { operationId } = definition;\n\n const pascalCaseOperationId = Case.pascal(operationId);\n const requestFile = `./${path.basename(outputRequestFileName, \".ts\")}`;\n const responseValidatorFile = `./${path.basename(outputResponseValidationFileName, \".ts\")}`;\n const responseFile = `./${path.basename(outputResponseFileName, \".ts\")}`;\n\n operations.push({\n operationId,\n pascalCaseOperationId,\n requestFile,\n responseValidatorFile,\n responseFile,\n });\n }\n\n const content = context.renderTemplate(templateFilePath, {\n coreDir: context.coreDir,\n pascalCaseEntityName,\n operations,\n });\n\n const outputClientFile = path.join(\n outputDir,\n `${pascalCaseEntityName}Client.ts`\n );\n const relativePath = path.relative(context.outputDir, outputClientFile);\n context.writeFile(relativePath, content);\n }\n\n private static writeRequestCommands(\n templateFilePath: string,\n operationResources: OperationResource[],\n context: GeneratorContext\n ): void {\n for (const operationResource of operationResources) {\n this.writeRequestCommand(templateFilePath, operationResource, context);\n }\n }\n\n private static writeRequestCommand(\n templateFilePath: string,\n operationResource: OperationResource,\n context: GeneratorContext\n ): void {\n const {\n definition,\n sourceDir,\n sourceFile,\n outputDir,\n outputResponseFileName,\n outputResponseValidationFileName,\n outputRequestFileName,\n } = operationResource;\n\n const { operationId, method, request
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["moduleDir"],"sources":["../src/ClientGenerator.ts","../src/index.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type {\n GeneratorContext,\n OperationResource,\n} from \"@rexeus/typeweaver-gen\";\nimport { TsTypeNode, TsTypePrinter } from \"@rexeus/typeweaver-zod-to-ts\";\nimport Case from \"case\";\n\nconst moduleDir = path.dirname(fileURLToPath(import.meta.url));\n\nexport class ClientGenerator {\n public static generate(context: GeneratorContext): void {\n const clientTemplatePath = path.join(moduleDir, \"templates\", \"Client.ejs\");\n const commandTemplatePath = path.join(\n moduleDir,\n \"templates\",\n \"RequestCommand.ejs\"\n );\n\n for (const [, entityResource] of Object.entries(\n context.resources.entityResources\n )) {\n this.writeClient(clientTemplatePath, entityResource.operations, context);\n this.writeRequestCommands(\n commandTemplatePath,\n entityResource.operations,\n context\n );\n }\n }\n\n private static writeClient(\n templateFilePath: string,\n operationResources: OperationResource[],\n context: GeneratorContext\n ): void {\n const entityName = operationResources[0]!.entityName;\n const pascalCaseEntityName = Case.pascal(entityName);\n const outputDir = operationResources[0]!.outputDir;\n\n const operations: {\n operationId: string;\n pascalCaseOperationId: string;\n requestFile: string;\n responseValidatorFile: string;\n responseFile: string;\n }[] = [];\n for (const operationResource of operationResources) {\n const {\n definition,\n outputResponseFileName,\n outputResponseValidationFileName,\n outputRequestFileName,\n } = operationResource;\n const { operationId } = definition;\n\n const pascalCaseOperationId = Case.pascal(operationId);\n const requestFile = `./${path.basename(outputRequestFileName, \".ts\")}`;\n const responseValidatorFile = `./${path.basename(outputResponseValidationFileName, \".ts\")}`;\n const responseFile = `./${path.basename(outputResponseFileName, \".ts\")}`;\n\n operations.push({\n operationId,\n pascalCaseOperationId,\n requestFile,\n responseValidatorFile,\n responseFile,\n });\n }\n\n const content = context.renderTemplate(templateFilePath, {\n coreDir: context.coreDir,\n pascalCaseEntityName,\n operations,\n });\n\n const outputClientFile = path.join(\n outputDir,\n `${pascalCaseEntityName}Client.ts`\n );\n const relativePath = path.relative(context.outputDir, outputClientFile);\n context.writeFile(relativePath, content);\n }\n\n private static writeRequestCommands(\n templateFilePath: string,\n operationResources: OperationResource[],\n context: GeneratorContext\n ): void {\n for (const operationResource of operationResources) {\n this.writeRequestCommand(templateFilePath, operationResource, context);\n }\n }\n\n private static writeRequestCommand(\n templateFilePath: string,\n operationResource: OperationResource,\n context: GeneratorContext\n ): void {\n const {\n definition,\n sourceDir,\n sourceFile,\n outputDir,\n outputResponseFileName,\n outputResponseValidationFileName,\n outputRequestFileName,\n } = operationResource;\n\n const { operationId, method, request } = definition;\n const pascalCaseOperationId = Case.pascal(operationId);\n\n // Build request type information\n const headerTsType = request.header\n ? TsTypePrinter.print(TsTypeNode.fromZod(request.header))\n : undefined;\n const paramTsType = request.param\n ? TsTypePrinter.print(TsTypeNode.fromZod(request.param))\n : undefined;\n const queryTsType = request.query\n ? TsTypePrinter.print(TsTypeNode.fromZod(request.query))\n : undefined;\n const bodyTsType = request.body\n ? TsTypePrinter.print(TsTypeNode.fromZod(request.body))\n : undefined;\n\n // Build relative paths\n const requestFile = `./${path.basename(outputRequestFileName, \".ts\")}`;\n const responseValidatorFile = `./${path.basename(outputResponseValidationFileName, \".ts\")}`;\n const responseFile = `./${path.basename(outputResponseFileName, \".ts\")}`;\n const relativeSourceFile = path.relative(sourceDir, sourceFile);\n const sourcePath = path.join(\n sourceDir,\n relativeSourceFile.replace(/\\.ts$/, \"\")\n );\n const relativeSourcePath = path.relative(outputDir, sourcePath);\n\n const content = context.renderTemplate(templateFilePath, {\n sourcePath: relativeSourcePath,\n operationId,\n pascalCaseOperationId,\n method,\n headerTsType,\n paramTsType,\n queryTsType,\n bodyTsType,\n requestFile,\n responseValidatorFile,\n responseFile,\n });\n\n const outputCommandFile = path.join(\n outputDir,\n `${pascalCaseOperationId}RequestCommand.ts`\n );\n const relativePath = path.relative(context.outputDir, outputCommandFile);\n context.writeFile(relativePath, content);\n }\n}\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { BasePlugin } from \"@rexeus/typeweaver-gen\";\nimport type { GeneratorContext } from \"@rexeus/typeweaver-gen\";\nimport { ClientGenerator } from \"./ClientGenerator\";\n\nconst moduleDir = path.dirname(fileURLToPath(import.meta.url));\n\nexport default class ClientsPlugin extends BasePlugin {\n public name = \"clients\";\n public override generate(context: GeneratorContext): Promise<void> | void {\n // Copy lib files to lib/clients/ from dist folder\n const libDir = path.join(moduleDir, \"lib\");\n this.copyLibFiles(context, libDir, \"clients\");\n\n ClientGenerator.generate(context);\n }\n}\n"],"mappings":"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"}
|
package/dist/lib/ApiClient.ts
CHANGED
|
@@ -16,12 +16,6 @@ import { PathParameterError } from "./PathParameterError";
|
|
|
16
16
|
import { RequestCommand } from "./RequestCommand";
|
|
17
17
|
import { ResponseParseError } from "./ResponseParseError";
|
|
18
18
|
import type { NetworkErrorCode } from "./NetworkError";
|
|
19
|
-
import type { ProcessResponseOptions } from "./RequestCommand";
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Configuration options for handling unknown responses.
|
|
23
|
-
*/
|
|
24
|
-
export type UnknownResponseHandling = "throw" | "passthrough";
|
|
25
19
|
|
|
26
20
|
/**
|
|
27
21
|
* Configuration options for ApiClient initialization.
|
|
@@ -31,10 +25,6 @@ export type ApiClientProps = {
|
|
|
31
25
|
readonly fetchFn?: typeof globalThis.fetch;
|
|
32
26
|
/** Base URL for API requests */
|
|
33
27
|
readonly baseUrl: string;
|
|
34
|
-
/** How to handle unknown responses. Defaults to "throw" */
|
|
35
|
-
readonly unknownResponseHandling?: UnknownResponseHandling;
|
|
36
|
-
/** Predicate to determine if a status code represents success. Defaults to 2xx status codes */
|
|
37
|
-
readonly isSuccessStatusCode?: (statusCode: number) => boolean;
|
|
38
28
|
/** Request timeout in milliseconds. When set, requests will be aborted after this duration */
|
|
39
29
|
readonly timeoutMs?: number;
|
|
40
30
|
};
|
|
@@ -60,8 +50,6 @@ const NETWORK_ERROR_MESSAGES: Readonly<
|
|
|
60
50
|
export abstract class ApiClient {
|
|
61
51
|
private readonly fetchFn: typeof globalThis.fetch;
|
|
62
52
|
public readonly baseUrl: string;
|
|
63
|
-
public readonly unknownResponseHandling: UnknownResponseHandling;
|
|
64
|
-
public readonly isSuccessStatusCode: (statusCode: number) => boolean;
|
|
65
53
|
private readonly timeoutMs: number | undefined;
|
|
66
54
|
|
|
67
55
|
protected constructor(props: ApiClientProps) {
|
|
@@ -79,20 +67,9 @@ export abstract class ApiClient {
|
|
|
79
67
|
throw new Error("timeoutMs must be a positive finite number");
|
|
80
68
|
}
|
|
81
69
|
|
|
82
|
-
this.unknownResponseHandling = props.unknownResponseHandling ?? "throw";
|
|
83
|
-
this.isSuccessStatusCode =
|
|
84
|
-
props.isSuccessStatusCode ??
|
|
85
|
-
((statusCode: number) => statusCode >= 200 && statusCode < 300);
|
|
86
70
|
this.timeoutMs = props.timeoutMs;
|
|
87
71
|
}
|
|
88
72
|
|
|
89
|
-
protected get processResponseOptions(): ProcessResponseOptions {
|
|
90
|
-
return {
|
|
91
|
-
unknownResponseHandling: this.unknownResponseHandling,
|
|
92
|
-
isSuccessStatusCode: this.isSuccessStatusCode,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
73
|
protected async execute(request: RequestCommand): Promise<IHttpResponse> {
|
|
97
74
|
const { method, path, header, query, param, body } = request;
|
|
98
75
|
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* @generated by @rexeus/typeweaver
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { HttpResponse } from "@rexeus/typeweaver-core";
|
|
9
8
|
import type {
|
|
10
9
|
HttpMethod,
|
|
11
10
|
IHttpBody,
|
|
@@ -15,28 +14,15 @@ import type {
|
|
|
15
14
|
IHttpRequest,
|
|
16
15
|
IHttpResponse,
|
|
17
16
|
} from "@rexeus/typeweaver-core";
|
|
18
|
-
import type { UnknownResponseHandling } from "./ApiClient";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Configuration options for processing HTTP responses.
|
|
22
|
-
*/
|
|
23
|
-
export type ProcessResponseOptions = {
|
|
24
|
-
readonly unknownResponseHandling: UnknownResponseHandling;
|
|
25
|
-
readonly isSuccessStatusCode: (statusCode: number) => boolean;
|
|
26
|
-
};
|
|
27
17
|
|
|
28
18
|
/**
|
|
29
19
|
* Abstract base class for type-safe API request commands.
|
|
30
20
|
*
|
|
31
|
-
*
|
|
21
|
+
* Represents a command pattern for HTTP requests, providing:
|
|
32
22
|
* - Type-safe request parameters (headers, path params, query, body)
|
|
33
|
-
* - Response processing
|
|
23
|
+
* - Response processing with validation and type narrowing
|
|
34
24
|
* - Integration with ApiClient for execution
|
|
35
25
|
*
|
|
36
|
-
* Implementations should:
|
|
37
|
-
* 1. Set all readonly properties in the constructor
|
|
38
|
-
* 2. Implement processResponse to handle response transformation
|
|
39
|
-
*
|
|
40
26
|
* @template Header - The HTTP header type
|
|
41
27
|
* @template Param - The path parameter type
|
|
42
28
|
* @template Query - The query string parameter type
|
|
@@ -64,19 +50,14 @@ export abstract class RequestCommand<
|
|
|
64
50
|
public readonly body!: Body;
|
|
65
51
|
|
|
66
52
|
/**
|
|
67
|
-
* Processes the raw HTTP response into a typed response object.
|
|
53
|
+
* Processes the raw HTTP response into a typed, validated response object.
|
|
68
54
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* - Data transformation
|
|
72
|
-
* - Error response handling
|
|
55
|
+
* Returns the full response union (success and error responses).
|
|
56
|
+
* Throws UnknownResponseError if the response doesn't match any defined schema.
|
|
73
57
|
*
|
|
74
58
|
* @param response - The raw HTTP response from the server
|
|
75
|
-
* @
|
|
76
|
-
* @
|
|
59
|
+
* @returns The validated, type-safe response object
|
|
60
|
+
* @throws {UnknownResponseError} If the response doesn't match any defined schema
|
|
77
61
|
*/
|
|
78
|
-
public abstract processResponse(
|
|
79
|
-
response: IHttpResponse,
|
|
80
|
-
options: ProcessResponseOptions
|
|
81
|
-
): HttpResponse;
|
|
62
|
+
public abstract processResponse(response: IHttpResponse): IHttpResponse;
|
|
82
63
|
}
|
|
@@ -2,16 +2,14 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file was automatically generated by typeweaver.
|
|
4
4
|
* DO NOT EDIT. Instead, modify the source definition file and generate again.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* @generated by @rexeus/typeweaver
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { ApiClient, type ApiClientProps } from "../lib/clients";
|
|
10
10
|
<% for (const operation of operations) { %>
|
|
11
11
|
import { <%= operation.operationId %>RequestCommand } from "./<%= operation.operationId %>RequestCommand";
|
|
12
|
-
import type {
|
|
13
|
-
Successful<%= operation.operationId %>Response
|
|
14
|
-
} from "<%= operation.requestFile %>";
|
|
12
|
+
import type { <%= operation.operationId %>Response } from "<%= operation.responseFile %>";
|
|
15
13
|
<% } %>
|
|
16
14
|
|
|
17
15
|
export type <%= pascalCaseEntityName %>RequestCommands =
|
|
@@ -19,9 +17,9 @@ export type <%= pascalCaseEntityName %>RequestCommands =
|
|
|
19
17
|
| <%= operation.operationId %>RequestCommand
|
|
20
18
|
<% } %>;
|
|
21
19
|
|
|
22
|
-
export type
|
|
20
|
+
export type <%= pascalCaseEntityName %>Responses =
|
|
23
21
|
<% for (const operation of operations) { %>
|
|
24
|
-
|
|
|
22
|
+
| <%= operation.operationId %>Response
|
|
25
23
|
<% } %>;
|
|
26
24
|
|
|
27
25
|
|
|
@@ -31,10 +29,10 @@ export class <%= pascalCaseEntityName %>Client extends ApiClient {
|
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
<% for (const operation of operations) { %>
|
|
34
|
-
public async send(command: <%= operation.operationId %>RequestCommand): Promise
|
|
32
|
+
public async send(command: <%= operation.operationId %>RequestCommand): Promise<<%= operation.operationId %>Response>;
|
|
35
33
|
<% } %>
|
|
36
|
-
public async send(command: <%= pascalCaseEntityName %>RequestCommands): Promise
|
|
34
|
+
public async send(command: <%= pascalCaseEntityName %>RequestCommands): Promise<<%= pascalCaseEntityName %>Responses> {
|
|
37
35
|
const response = await this.execute(command);
|
|
38
|
-
return command.processResponse(response
|
|
36
|
+
return command.processResponse(response);
|
|
39
37
|
}
|
|
40
|
-
}
|
|
38
|
+
}
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file was automatically generated by typeweaver.
|
|
4
4
|
* DO NOT EDIT. Instead, modify the source definition file and generate again.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* @generated by @rexeus/typeweaver
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import definition from "<%= sourcePath %>";
|
|
10
|
-
import { HttpMethod, type IHttpResponse, ResponseValidationError,
|
|
11
|
-
import { RequestCommand
|
|
10
|
+
import { HttpMethod, type IHttpResponse, ResponseValidationError, UnknownResponseError } from "@rexeus/typeweaver-core";
|
|
11
|
+
import { RequestCommand } from "../lib/clients";
|
|
12
12
|
import { <%= pascalCaseOperationId %>ResponseValidator } from "<%= responseValidatorFile %>";
|
|
13
13
|
import type {
|
|
14
14
|
I<%= pascalCaseOperationId %>Request,
|
|
@@ -16,11 +16,10 @@ import type {
|
|
|
16
16
|
<%= paramTsType ? `I${pascalCaseOperationId}RequestParam,` : "" %>
|
|
17
17
|
<%= queryTsType ? `I${pascalCaseOperationId}RequestQuery,` : "" %>
|
|
18
18
|
<%= bodyTsType ? `I${pascalCaseOperationId}RequestBody,` : "" %>
|
|
19
|
-
Successful<%= pascalCaseOperationId %>Response,
|
|
20
19
|
} from "<%= requestFile %>";
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
import type { <%= pascalCaseOperationId %>Response } from "<%= responseFile %>";
|
|
21
|
+
|
|
22
|
+
const responseValidator = new <%= pascalCaseOperationId %>ResponseValidator();
|
|
24
23
|
|
|
25
24
|
export class <%= pascalCaseOperationId %>RequestCommand extends RequestCommand implements I<%= pascalCaseOperationId %>Request {
|
|
26
25
|
public override readonly operationId = definition.operationId;
|
|
@@ -32,8 +31,6 @@ export class <%= pascalCaseOperationId %>RequestCommand extends RequestCommand i
|
|
|
32
31
|
public <%= queryTsType ? `override` : `declare` %> readonly query: <%= queryTsType ? `I${pascalCaseOperationId}RequestQuery` : `undefined` %>;
|
|
33
32
|
public <%= bodyTsType ? `override` : `declare` %> readonly body: <%= bodyTsType ? `I${pascalCaseOperationId}RequestBody` : `undefined` %>;
|
|
34
33
|
|
|
35
|
-
private readonly responseValidator: <%= pascalCaseOperationId %>ResponseValidator;
|
|
36
|
-
|
|
37
34
|
public constructor(input: Omit<I<%= pascalCaseOperationId %>Request, "method" | "path">) {
|
|
38
35
|
super();
|
|
39
36
|
|
|
@@ -52,40 +49,21 @@ export class <%= pascalCaseOperationId %>RequestCommand extends RequestCommand i
|
|
|
52
49
|
<% if (bodyTsType) { %>
|
|
53
50
|
this.body = input.body;
|
|
54
51
|
<% } %>
|
|
55
|
-
|
|
56
|
-
this.responseValidator = new <%= pascalCaseOperationId %>ResponseValidator();
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
public processResponse(
|
|
60
|
-
response: IHttpResponse,
|
|
61
|
-
options: ProcessResponseOptions
|
|
62
|
-
): Successful<%= pascalCaseOperationId %>Response {
|
|
54
|
+
public processResponse(response: IHttpResponse): <%= pascalCaseOperationId %>Response {
|
|
63
55
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<% for (const successResponse of [...ownSuccessResponses, ...sharedSuccessResponses]) { %>
|
|
67
|
-
if (result instanceof <%= successResponse.name %>Response) {
|
|
68
|
-
return result;
|
|
69
|
-
}
|
|
70
|
-
<% } %>
|
|
71
|
-
|
|
72
|
-
throw result;
|
|
56
|
+
return responseValidator.validate(response);
|
|
73
57
|
} catch (error) {
|
|
74
58
|
if (error instanceof ResponseValidationError) {
|
|
75
|
-
|
|
59
|
+
throw new UnknownResponseError(
|
|
76
60
|
response.statusCode,
|
|
77
61
|
response.header,
|
|
78
62
|
response.body,
|
|
79
63
|
error
|
|
80
64
|
);
|
|
81
|
-
|
|
82
|
-
if (options.unknownResponseHandling === "passthrough" && options.isSuccessStatusCode(response.statusCode)) {
|
|
83
|
-
return unknownResponse as any;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
throw unknownResponse;
|
|
87
65
|
}
|
|
88
66
|
throw error;
|
|
89
67
|
}
|
|
90
68
|
}
|
|
91
|
-
}
|
|
69
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rexeus/typeweaver-clients",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Generates HTTP clients directly from your API definitions. Powered by Typeweaver 🧵✨",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -48,22 +48,22 @@
|
|
|
48
48
|
"homepage": "https://github.com/rexeus/typeweaver#readme",
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"zod": "^4.3.0",
|
|
51
|
-
"@rexeus/typeweaver-core": "^0.
|
|
52
|
-
"@rexeus/typeweaver-gen": "^0.
|
|
51
|
+
"@rexeus/typeweaver-core": "^0.8.0",
|
|
52
|
+
"@rexeus/typeweaver-gen": "^0.8.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@hono/node-server": "^1.19.7",
|
|
56
56
|
"test-utils": "file:../test-utils",
|
|
57
57
|
"zod": "^4.3.6",
|
|
58
|
-
"@rexeus/typeweaver-core": "^0.
|
|
59
|
-
"@rexeus/typeweaver-gen": "^0.
|
|
58
|
+
"@rexeus/typeweaver-core": "^0.8.0",
|
|
59
|
+
"@rexeus/typeweaver-gen": "^0.8.0"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"case": "^1.6.3",
|
|
63
|
-
"@rexeus/typeweaver-zod-to-ts": "^0.
|
|
63
|
+
"@rexeus/typeweaver-zod-to-ts": "^0.8.0"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
|
-
"typecheck": "tsc --noEmit",
|
|
66
|
+
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json",
|
|
67
67
|
"format": "oxfmt",
|
|
68
68
|
"build": "tsdown && mkdir -p ./dist/templates ./dist/lib && cp -r ./src/templates/* ./dist/templates/ && cp -r ./src/lib/* ./dist/lib/ && cp ../../LICENSE ../../NOTICE ./dist/",
|
|
69
69
|
"test": "vitest --run",
|