@rexeus/typeweaver-clients 0.8.0 โ 0.9.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/README.md +16 -44
- package/dist/index.cjs +74 -88
- package/dist/index.mjs +69 -77
- package/dist/index.mjs.map +1 -1
- package/dist/templates/RequestCommand.ejs +3 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ npm install @rexeus/typeweaver-core
|
|
|
28
28
|
## ๐ก How to use
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
npx typeweaver generate --input ./api/
|
|
31
|
+
npx typeweaver generate --input ./api/spec/index.ts --output ./api/generated --plugins clients
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
More on the CLI in
|
|
@@ -51,16 +51,11 @@ Resource-specific HTTP clients are generated as `<ResourceName>Client.ts` files,
|
|
|
51
51
|
checking
|
|
52
52
|
- **fetch based** - Zero dependencies, uses the native fetch API. Supports custom fetch functions
|
|
53
53
|
for middleware and testing
|
|
54
|
-
- **Response type mapping** - Each response is automatically mapped to the associated response
|
|
55
|
-
|
|
56
|
-
format and it is type-safe.
|
|
54
|
+
- **Response type mapping** - Each response is automatically mapped to the associated typed response
|
|
55
|
+
object. This ensures that all responses are in the defined format and it is type-safe.
|
|
57
56
|
- **Unknown response handling**
|
|
58
57
|
- Unknown properties are automatically removed from the response. If a response exceeds the
|
|
59
58
|
definition, it is not rejected directly.
|
|
60
|
-
- If a response does not match any known format, it will be rejected by default as an
|
|
61
|
-
`UnknownResponse` instance.
|
|
62
|
-
- This unknown response handling can be configured. It is also possible for an `UnknownResponse`
|
|
63
|
-
instance to be created without being thrown.
|
|
64
59
|
|
|
65
60
|
**Using generated clients**
|
|
66
61
|
|
|
@@ -70,9 +65,7 @@ import { TodoClient } from "path/to/generated/output";
|
|
|
70
65
|
const client = new TodoClient({
|
|
71
66
|
fetchFn: customFetch, // Custom fetch function (optional, defaults to globalThis.fetch)
|
|
72
67
|
baseUrl: "https://api.example.com", // Base URL for all requests (required)
|
|
73
|
-
|
|
74
|
-
// -> In "passthrough" mode, the received status code determines if the response is thrown
|
|
75
|
-
isSuccessStatusCode: code => code < 400, // Custom success status code predicate, determines whether the response is successful or should be thrown
|
|
68
|
+
timeoutMs: 30_000, // Request timeout in milliseconds (optional)
|
|
76
69
|
});
|
|
77
70
|
```
|
|
78
71
|
|
|
@@ -83,20 +76,12 @@ Request commands are generated as `<OperationId>RequestCommand.ts` files, e.g.
|
|
|
83
76
|
|
|
84
77
|
- **Type-safe construction** - Constructor enforces correct request structure
|
|
85
78
|
- **Complete request encapsulation** - Contains method, path, headers, query parameters, and body
|
|
86
|
-
- **Response processing** - Transform raw HTTP responses into typed response objects
|
|
87
|
-
response class
|
|
79
|
+
- **Response processing** - Transform raw HTTP responses into typed response objects
|
|
88
80
|
|
|
89
81
|
### Basic Usage
|
|
90
82
|
|
|
91
83
|
```typescript
|
|
92
|
-
import {
|
|
93
|
-
TodoClient,
|
|
94
|
-
CreateTodoRequestCommand,
|
|
95
|
-
CreateTodoSuccessResponse,
|
|
96
|
-
OtherSuccessResponse,
|
|
97
|
-
ValidationErrorResponse,
|
|
98
|
-
InternalServerErrorResponse,
|
|
99
|
-
} from "path/to/generated/output";
|
|
84
|
+
import { TodoClient, CreateTodoRequestCommand } from "path/to/generated/output";
|
|
100
85
|
|
|
101
86
|
const client = new TodoClient({
|
|
102
87
|
baseUrl: "https://api.example.com",
|
|
@@ -107,30 +92,17 @@ const command = new CreateTodoRequestCommand({
|
|
|
107
92
|
body: { title: "New Todo", status: "PENDING" },
|
|
108
93
|
});
|
|
109
94
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
console.log("Todo created successfully:", response.body);
|
|
120
|
-
}
|
|
121
|
-
if (response instanceof OtherSuccessResponse) {
|
|
122
|
-
// ... Handle "OtherSuccessResponse"
|
|
123
|
-
}
|
|
124
|
-
// ...
|
|
125
|
-
} catch (error) {
|
|
126
|
-
if (error instanceof ValidationErrorResponse) {
|
|
127
|
-
// Handle validation errors
|
|
128
|
-
}
|
|
129
|
-
if (error instanceof InternalServerErrorResponse) {
|
|
130
|
-
// Handle internal server errors
|
|
131
|
-
}
|
|
132
|
-
// ... Handle other errors
|
|
95
|
+
const response = await client.send(command);
|
|
96
|
+
|
|
97
|
+
// Use the type discriminator to narrow the response type
|
|
98
|
+
if (response.type === "CreateTodoSuccess") {
|
|
99
|
+
console.log("Todo created successfully:", response.body);
|
|
100
|
+
} else if (response.type === "ValidationError") {
|
|
101
|
+
// Handle validation errors
|
|
102
|
+
} else if (response.type === "InternalServerError") {
|
|
103
|
+
// Handle internal server errors
|
|
133
104
|
}
|
|
105
|
+
// ... Handle other response types
|
|
134
106
|
```
|
|
135
107
|
|
|
136
108
|
## ๐ License
|
package/dist/index.cjs
CHANGED
|
@@ -6,16 +6,12 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
8
|
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
}
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
19
15
|
}
|
|
20
16
|
return to;
|
|
21
17
|
};
|
|
@@ -23,7 +19,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
19
|
value: mod,
|
|
24
20
|
enumerable: true
|
|
25
21
|
}) : target, mod));
|
|
26
|
-
|
|
27
22
|
//#endregion
|
|
28
23
|
let node_path = require("node:path");
|
|
29
24
|
node_path = __toESM(node_path);
|
|
@@ -32,83 +27,75 @@ let _rexeus_typeweaver_gen = require("@rexeus/typeweaver-gen");
|
|
|
32
27
|
let _rexeus_typeweaver_zod_to_ts = require("@rexeus/typeweaver-zod-to-ts");
|
|
33
28
|
let case$1 = require("case");
|
|
34
29
|
case$1 = __toESM(case$1);
|
|
35
|
-
|
|
36
|
-
//#region src/ClientGenerator.ts
|
|
30
|
+
//#region src/clientGenerator.ts
|
|
37
31
|
const moduleDir$1 = node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
this.writeRequestCommands(commandTemplatePath, entityResource.operations, context);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
static writeClient(templateFilePath, operationResources, context) {
|
|
48
|
-
const entityName = operationResources[0].entityName;
|
|
49
|
-
const pascalCaseEntityName = case$1.default.pascal(entityName);
|
|
50
|
-
const outputDir = operationResources[0].outputDir;
|
|
51
|
-
const operations = [];
|
|
52
|
-
for (const operationResource of operationResources) {
|
|
53
|
-
const { definition, outputResponseFileName, outputResponseValidationFileName, outputRequestFileName } = operationResource;
|
|
54
|
-
const { operationId } = definition;
|
|
55
|
-
const pascalCaseOperationId = case$1.default.pascal(operationId);
|
|
56
|
-
const requestFile = `./${node_path.default.basename(outputRequestFileName, ".ts")}`;
|
|
57
|
-
const responseValidatorFile = `./${node_path.default.basename(outputResponseValidationFileName, ".ts")}`;
|
|
58
|
-
const responseFile = `./${node_path.default.basename(outputResponseFileName, ".ts")}`;
|
|
59
|
-
operations.push({
|
|
60
|
-
operationId,
|
|
61
|
-
pascalCaseOperationId,
|
|
62
|
-
requestFile,
|
|
63
|
-
responseValidatorFile,
|
|
64
|
-
responseFile
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
const content = context.renderTemplate(templateFilePath, {
|
|
68
|
-
coreDir: context.coreDir,
|
|
69
|
-
pascalCaseEntityName,
|
|
70
|
-
operations
|
|
71
|
-
});
|
|
72
|
-
const outputClientFile = node_path.default.join(outputDir, `${pascalCaseEntityName}Client.ts`);
|
|
73
|
-
const relativePath = node_path.default.relative(context.outputDir, outputClientFile);
|
|
74
|
-
context.writeFile(relativePath, content);
|
|
32
|
+
function generate(context) {
|
|
33
|
+
const clientTemplatePath = node_path.default.join(moduleDir$1, "templates", "Client.ejs");
|
|
34
|
+
const commandTemplatePath = node_path.default.join(moduleDir$1, "templates", "RequestCommand.ejs");
|
|
35
|
+
for (const resource of context.normalizedSpec.resources) {
|
|
36
|
+
writeClient(clientTemplatePath, resource, context);
|
|
37
|
+
writeRequestCommands(commandTemplatePath, resource, context);
|
|
75
38
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const paramTsType = request.param ? _rexeus_typeweaver_zod_to_ts.TsTypePrinter.print(_rexeus_typeweaver_zod_to_ts.TsTypeNode.fromZod(request.param)) : void 0;
|
|
85
|
-
const queryTsType = request.query ? _rexeus_typeweaver_zod_to_ts.TsTypePrinter.print(_rexeus_typeweaver_zod_to_ts.TsTypeNode.fromZod(request.query)) : void 0;
|
|
86
|
-
const bodyTsType = request.body ? _rexeus_typeweaver_zod_to_ts.TsTypePrinter.print(_rexeus_typeweaver_zod_to_ts.TsTypeNode.fromZod(request.body)) : void 0;
|
|
87
|
-
const requestFile = `./${node_path.default.basename(outputRequestFileName, ".ts")}`;
|
|
88
|
-
const responseValidatorFile = `./${node_path.default.basename(outputResponseValidationFileName, ".ts")}`;
|
|
89
|
-
const responseFile = `./${node_path.default.basename(outputResponseFileName, ".ts")}`;
|
|
90
|
-
const relativeSourceFile = node_path.default.relative(sourceDir, sourceFile);
|
|
91
|
-
const sourcePath = node_path.default.join(sourceDir, relativeSourceFile.replace(/\.ts$/, ""));
|
|
92
|
-
const relativeSourcePath = node_path.default.relative(outputDir, sourcePath);
|
|
93
|
-
const content = context.renderTemplate(templateFilePath, {
|
|
94
|
-
sourcePath: relativeSourcePath,
|
|
95
|
-
operationId,
|
|
96
|
-
pascalCaseOperationId,
|
|
97
|
-
method,
|
|
98
|
-
headerTsType,
|
|
99
|
-
paramTsType,
|
|
100
|
-
queryTsType,
|
|
101
|
-
bodyTsType,
|
|
102
|
-
requestFile,
|
|
103
|
-
responseValidatorFile,
|
|
104
|
-
responseFile
|
|
39
|
+
}
|
|
40
|
+
function writeClient(templateFilePath, resource, context) {
|
|
41
|
+
const pascalCaseEntityName = case$1.default.pascal(resource.name);
|
|
42
|
+
const outputDir = context.getResourceOutputDir(resource.name);
|
|
43
|
+
const operations = resource.operations.map((operation) => {
|
|
44
|
+
const outputPaths = context.getOperationOutputPaths({
|
|
45
|
+
resourceName: resource.name,
|
|
46
|
+
operationId: operation.operationId
|
|
105
47
|
});
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
48
|
+
return {
|
|
49
|
+
operationId: operation.operationId,
|
|
50
|
+
pascalCaseOperationId: case$1.default.pascal(operation.operationId),
|
|
51
|
+
requestFile: `./${node_path.default.basename(outputPaths.requestFileName, ".ts")}`,
|
|
52
|
+
responseValidatorFile: `./${node_path.default.basename(outputPaths.responseValidationFileName, ".ts")}`,
|
|
53
|
+
responseFile: `./${node_path.default.basename(outputPaths.responseFileName, ".ts")}`
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
const content = context.renderTemplate(templateFilePath, {
|
|
57
|
+
coreDir: context.coreDir,
|
|
58
|
+
pascalCaseEntityName,
|
|
59
|
+
operations
|
|
60
|
+
});
|
|
61
|
+
const outputClientFile = node_path.default.join(outputDir, `${pascalCaseEntityName}Client.ts`);
|
|
62
|
+
const relativePath = node_path.default.relative(context.outputDir, outputClientFile);
|
|
63
|
+
context.writeFile(relativePath, content);
|
|
64
|
+
}
|
|
65
|
+
function writeRequestCommands(templateFilePath, resource, context) {
|
|
66
|
+
resource.operations.forEach((operation) => {
|
|
67
|
+
writeRequestCommand(templateFilePath, resource.name, operation, context);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function writeRequestCommand(templateFilePath, resourceName, operation, context) {
|
|
71
|
+
const outputPaths = context.getOperationOutputPaths({
|
|
72
|
+
resourceName,
|
|
73
|
+
operationId: operation.operationId
|
|
74
|
+
});
|
|
75
|
+
const request = operation.request ?? {};
|
|
76
|
+
const pascalCaseOperationId = case$1.default.pascal(operation.operationId);
|
|
77
|
+
const headerTsType = request.header ? (0, _rexeus_typeweaver_zod_to_ts.print)((0, _rexeus_typeweaver_zod_to_ts.fromZod)(request.header)) : void 0;
|
|
78
|
+
const paramTsType = request.param ? (0, _rexeus_typeweaver_zod_to_ts.print)((0, _rexeus_typeweaver_zod_to_ts.fromZod)(request.param)) : void 0;
|
|
79
|
+
const queryTsType = request.query ? (0, _rexeus_typeweaver_zod_to_ts.print)((0, _rexeus_typeweaver_zod_to_ts.fromZod)(request.query)) : void 0;
|
|
80
|
+
const bodyTsType = request.body ? (0, _rexeus_typeweaver_zod_to_ts.print)((0, _rexeus_typeweaver_zod_to_ts.fromZod)(request.body)) : void 0;
|
|
81
|
+
const content = context.renderTemplate(templateFilePath, {
|
|
82
|
+
resourceName,
|
|
83
|
+
specPath: context.getSpecImportPath({ importerDir: outputPaths.outputDir }),
|
|
84
|
+
operationId: operation.operationId,
|
|
85
|
+
pascalCaseOperationId,
|
|
86
|
+
method: operation.method,
|
|
87
|
+
headerTsType,
|
|
88
|
+
paramTsType,
|
|
89
|
+
queryTsType,
|
|
90
|
+
bodyTsType,
|
|
91
|
+
requestFile: `./${node_path.default.basename(outputPaths.requestFileName, ".ts")}`,
|
|
92
|
+
responseValidatorFile: `./${node_path.default.basename(outputPaths.responseValidationFileName, ".ts")}`,
|
|
93
|
+
responseFile: `./${node_path.default.basename(outputPaths.responseFileName, ".ts")}`
|
|
94
|
+
});
|
|
95
|
+
const outputCommandFile = node_path.default.join(outputPaths.outputDir, `${pascalCaseOperationId}RequestCommand.ts`);
|
|
96
|
+
const relativePath = node_path.default.relative(context.outputDir, outputCommandFile);
|
|
97
|
+
context.writeFile(relativePath, content);
|
|
98
|
+
}
|
|
112
99
|
//#endregion
|
|
113
100
|
//#region src/index.ts
|
|
114
101
|
const moduleDir = node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
@@ -117,9 +104,8 @@ var ClientsPlugin = class extends _rexeus_typeweaver_gen.BasePlugin {
|
|
|
117
104
|
generate(context) {
|
|
118
105
|
const libDir = node_path.default.join(moduleDir, "lib");
|
|
119
106
|
this.copyLibFiles(context, libDir, "clients");
|
|
120
|
-
|
|
107
|
+
generate(context);
|
|
121
108
|
}
|
|
122
109
|
};
|
|
123
|
-
|
|
124
110
|
//#endregion
|
|
125
|
-
module.exports = ClientsPlugin;
|
|
111
|
+
module.exports = ClientsPlugin;
|
package/dist/index.mjs
CHANGED
|
@@ -1,85 +1,77 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { BasePlugin } from "@rexeus/typeweaver-gen";
|
|
4
|
-
import {
|
|
4
|
+
import { fromZod, print } from "@rexeus/typeweaver-zod-to-ts";
|
|
5
5
|
import Case from "case";
|
|
6
|
-
|
|
7
|
-
//#region src/ClientGenerator.ts
|
|
6
|
+
//#region src/clientGenerator.ts
|
|
8
7
|
const moduleDir$1 = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.writeRequestCommands(commandTemplatePath, entityResource.operations, context);
|
|
16
|
-
}
|
|
8
|
+
function generate(context) {
|
|
9
|
+
const clientTemplatePath = path.join(moduleDir$1, "templates", "Client.ejs");
|
|
10
|
+
const commandTemplatePath = path.join(moduleDir$1, "templates", "RequestCommand.ejs");
|
|
11
|
+
for (const resource of context.normalizedSpec.resources) {
|
|
12
|
+
writeClient(clientTemplatePath, resource, context);
|
|
13
|
+
writeRequestCommands(commandTemplatePath, resource, context);
|
|
17
14
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const pascalCaseOperationId = Case.pascal(operationId);
|
|
27
|
-
const requestFile = `./${path.basename(outputRequestFileName, ".ts")}`;
|
|
28
|
-
const responseValidatorFile = `./${path.basename(outputResponseValidationFileName, ".ts")}`;
|
|
29
|
-
const responseFile = `./${path.basename(outputResponseFileName, ".ts")}`;
|
|
30
|
-
operations.push({
|
|
31
|
-
operationId,
|
|
32
|
-
pascalCaseOperationId,
|
|
33
|
-
requestFile,
|
|
34
|
-
responseValidatorFile,
|
|
35
|
-
responseFile
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
const content = context.renderTemplate(templateFilePath, {
|
|
39
|
-
coreDir: context.coreDir,
|
|
40
|
-
pascalCaseEntityName,
|
|
41
|
-
operations
|
|
15
|
+
}
|
|
16
|
+
function writeClient(templateFilePath, resource, context) {
|
|
17
|
+
const pascalCaseEntityName = Case.pascal(resource.name);
|
|
18
|
+
const outputDir = context.getResourceOutputDir(resource.name);
|
|
19
|
+
const operations = resource.operations.map((operation) => {
|
|
20
|
+
const outputPaths = context.getOperationOutputPaths({
|
|
21
|
+
resourceName: resource.name,
|
|
22
|
+
operationId: operation.operationId
|
|
42
23
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
24
|
+
return {
|
|
25
|
+
operationId: operation.operationId,
|
|
26
|
+
pascalCaseOperationId: Case.pascal(operation.operationId),
|
|
27
|
+
requestFile: `./${path.basename(outputPaths.requestFileName, ".ts")}`,
|
|
28
|
+
responseValidatorFile: `./${path.basename(outputPaths.responseValidationFileName, ".ts")}`,
|
|
29
|
+
responseFile: `./${path.basename(outputPaths.responseFileName, ".ts")}`
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
const content = context.renderTemplate(templateFilePath, {
|
|
33
|
+
coreDir: context.coreDir,
|
|
34
|
+
pascalCaseEntityName,
|
|
35
|
+
operations
|
|
36
|
+
});
|
|
37
|
+
const outputClientFile = path.join(outputDir, `${pascalCaseEntityName}Client.ts`);
|
|
38
|
+
const relativePath = path.relative(context.outputDir, outputClientFile);
|
|
39
|
+
context.writeFile(relativePath, content);
|
|
40
|
+
}
|
|
41
|
+
function writeRequestCommands(templateFilePath, resource, context) {
|
|
42
|
+
resource.operations.forEach((operation) => {
|
|
43
|
+
writeRequestCommand(templateFilePath, resource.name, operation, context);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function writeRequestCommand(templateFilePath, resourceName, operation, context) {
|
|
47
|
+
const outputPaths = context.getOperationOutputPaths({
|
|
48
|
+
resourceName,
|
|
49
|
+
operationId: operation.operationId
|
|
50
|
+
});
|
|
51
|
+
const request = operation.request ?? {};
|
|
52
|
+
const pascalCaseOperationId = Case.pascal(operation.operationId);
|
|
53
|
+
const headerTsType = request.header ? print(fromZod(request.header)) : void 0;
|
|
54
|
+
const paramTsType = request.param ? print(fromZod(request.param)) : void 0;
|
|
55
|
+
const queryTsType = request.query ? print(fromZod(request.query)) : void 0;
|
|
56
|
+
const bodyTsType = request.body ? print(fromZod(request.body)) : void 0;
|
|
57
|
+
const content = context.renderTemplate(templateFilePath, {
|
|
58
|
+
resourceName,
|
|
59
|
+
specPath: context.getSpecImportPath({ importerDir: outputPaths.outputDir }),
|
|
60
|
+
operationId: operation.operationId,
|
|
61
|
+
pascalCaseOperationId,
|
|
62
|
+
method: operation.method,
|
|
63
|
+
headerTsType,
|
|
64
|
+
paramTsType,
|
|
65
|
+
queryTsType,
|
|
66
|
+
bodyTsType,
|
|
67
|
+
requestFile: `./${path.basename(outputPaths.requestFileName, ".ts")}`,
|
|
68
|
+
responseValidatorFile: `./${path.basename(outputPaths.responseValidationFileName, ".ts")}`,
|
|
69
|
+
responseFile: `./${path.basename(outputPaths.responseFileName, ".ts")}`
|
|
70
|
+
});
|
|
71
|
+
const outputCommandFile = path.join(outputPaths.outputDir, `${pascalCaseOperationId}RequestCommand.ts`);
|
|
72
|
+
const relativePath = path.relative(context.outputDir, outputCommandFile);
|
|
73
|
+
context.writeFile(relativePath, content);
|
|
74
|
+
}
|
|
83
75
|
//#endregion
|
|
84
76
|
//#region src/index.ts
|
|
85
77
|
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -88,10 +80,10 @@ var ClientsPlugin = class extends BasePlugin {
|
|
|
88
80
|
generate(context) {
|
|
89
81
|
const libDir = path.join(moduleDir, "lib");
|
|
90
82
|
this.copyLibFiles(context, libDir, "clients");
|
|
91
|
-
|
|
83
|
+
generate(context);
|
|
92
84
|
}
|
|
93
85
|
};
|
|
94
|
-
|
|
95
86
|
//#endregion
|
|
96
87
|
export { ClientsPlugin as default };
|
|
88
|
+
|
|
97
89
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["moduleDir"],"sources":["../src/
|
|
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 NormalizedOperation,\n NormalizedResource,\n} from \"@rexeus/typeweaver-gen\";\nimport { fromZod, print } from \"@rexeus/typeweaver-zod-to-ts\";\nimport Case from \"case\";\n\nconst moduleDir = path.dirname(fileURLToPath(import.meta.url));\n\nexport function 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 resource of context.normalizedSpec.resources) {\n writeClient(clientTemplatePath, resource, context);\n writeRequestCommands(commandTemplatePath, resource, context);\n }\n}\n\nfunction writeClient(\n templateFilePath: string,\n resource: NormalizedResource,\n context: GeneratorContext\n): void {\n const pascalCaseEntityName = Case.pascal(resource.name);\n const outputDir = context.getResourceOutputDir(resource.name);\n\n const operations = resource.operations.map(operation => {\n const outputPaths = context.getOperationOutputPaths({\n resourceName: resource.name,\n operationId: operation.operationId,\n });\n\n return {\n operationId: operation.operationId,\n pascalCaseOperationId: Case.pascal(operation.operationId),\n requestFile: `./${path.basename(outputPaths.requestFileName, \".ts\")}`,\n responseValidatorFile: `./${path.basename(outputPaths.responseValidationFileName, \".ts\")}`,\n responseFile: `./${path.basename(outputPaths.responseFileName, \".ts\")}`,\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\nfunction writeRequestCommands(\n templateFilePath: string,\n resource: NormalizedResource,\n context: GeneratorContext\n): void {\n resource.operations.forEach(operation => {\n writeRequestCommand(templateFilePath, resource.name, operation, context);\n });\n}\n\nfunction writeRequestCommand(\n templateFilePath: string,\n resourceName: string,\n operation: NormalizedOperation,\n context: GeneratorContext\n): void {\n const outputPaths = context.getOperationOutputPaths({\n resourceName,\n operationId: operation.operationId,\n });\n const request = operation.request ?? {};\n const pascalCaseOperationId = Case.pascal(operation.operationId);\n\n const headerTsType = request.header\n ? print(fromZod(request.header))\n : undefined;\n const paramTsType = request.param ? print(fromZod(request.param)) : undefined;\n const queryTsType = request.query ? print(fromZod(request.query)) : undefined;\n const bodyTsType = request.body ? print(fromZod(request.body)) : undefined;\n\n const content = context.renderTemplate(templateFilePath, {\n resourceName,\n specPath: context.getSpecImportPath({\n importerDir: outputPaths.outputDir,\n }),\n operationId: operation.operationId,\n pascalCaseOperationId,\n method: operation.method,\n headerTsType,\n paramTsType,\n queryTsType,\n bodyTsType,\n requestFile: `./${path.basename(outputPaths.requestFileName, \".ts\")}`,\n responseValidatorFile: `./${path.basename(outputPaths.responseValidationFileName, \".ts\")}`,\n responseFile: `./${path.basename(outputPaths.responseFileName, \".ts\")}`,\n });\n\n const outputCommandFile = path.join(\n outputPaths.outputDir,\n `${pascalCaseOperationId}RequestCommand.ts`\n );\n const relativePath = path.relative(context.outputDir, outputCommandFile);\n context.writeFile(relativePath, content);\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 { generate as generateClients } 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 generateClients(context);\n }\n}\n"],"mappings":";;;;;;AAUA,MAAMA,cAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE9D,SAAgB,SAAS,SAAiC;CACxD,MAAM,qBAAqB,KAAK,KAAKA,aAAW,aAAa,aAAa;CAC1E,MAAM,sBAAsB,KAAK,KAC/BA,aACA,aACA,qBACD;AAED,MAAK,MAAM,YAAY,QAAQ,eAAe,WAAW;AACvD,cAAY,oBAAoB,UAAU,QAAQ;AAClD,uBAAqB,qBAAqB,UAAU,QAAQ;;;AAIhE,SAAS,YACP,kBACA,UACA,SACM;CACN,MAAM,uBAAuB,KAAK,OAAO,SAAS,KAAK;CACvD,MAAM,YAAY,QAAQ,qBAAqB,SAAS,KAAK;CAE7D,MAAM,aAAa,SAAS,WAAW,KAAI,cAAa;EACtD,MAAM,cAAc,QAAQ,wBAAwB;GAClD,cAAc,SAAS;GACvB,aAAa,UAAU;GACxB,CAAC;AAEF,SAAO;GACL,aAAa,UAAU;GACvB,uBAAuB,KAAK,OAAO,UAAU,YAAY;GACzD,aAAa,KAAK,KAAK,SAAS,YAAY,iBAAiB,MAAM;GACnE,uBAAuB,KAAK,KAAK,SAAS,YAAY,4BAA4B,MAAM;GACxF,cAAc,KAAK,KAAK,SAAS,YAAY,kBAAkB,MAAM;GACtE;GACD;CAEF,MAAM,UAAU,QAAQ,eAAe,kBAAkB;EACvD,SAAS,QAAQ;EACjB;EACA;EACD,CAAC;CAEF,MAAM,mBAAmB,KAAK,KAC5B,WACA,GAAG,qBAAqB,WACzB;CACD,MAAM,eAAe,KAAK,SAAS,QAAQ,WAAW,iBAAiB;AACvE,SAAQ,UAAU,cAAc,QAAQ;;AAG1C,SAAS,qBACP,kBACA,UACA,SACM;AACN,UAAS,WAAW,SAAQ,cAAa;AACvC,sBAAoB,kBAAkB,SAAS,MAAM,WAAW,QAAQ;GACxE;;AAGJ,SAAS,oBACP,kBACA,cACA,WACA,SACM;CACN,MAAM,cAAc,QAAQ,wBAAwB;EAClD;EACA,aAAa,UAAU;EACxB,CAAC;CACF,MAAM,UAAU,UAAU,WAAW,EAAE;CACvC,MAAM,wBAAwB,KAAK,OAAO,UAAU,YAAY;CAEhE,MAAM,eAAe,QAAQ,SACzB,MAAM,QAAQ,QAAQ,OAAO,CAAC,GAC9B,KAAA;CACJ,MAAM,cAAc,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,MAAM,CAAC,GAAG,KAAA;CACpE,MAAM,cAAc,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,MAAM,CAAC,GAAG,KAAA;CACpE,MAAM,aAAa,QAAQ,OAAO,MAAM,QAAQ,QAAQ,KAAK,CAAC,GAAG,KAAA;CAEjE,MAAM,UAAU,QAAQ,eAAe,kBAAkB;EACvD;EACA,UAAU,QAAQ,kBAAkB,EAClC,aAAa,YAAY,WAC1B,CAAC;EACF,aAAa,UAAU;EACvB;EACA,QAAQ,UAAU;EAClB;EACA;EACA;EACA;EACA,aAAa,KAAK,KAAK,SAAS,YAAY,iBAAiB,MAAM;EACnE,uBAAuB,KAAK,KAAK,SAAS,YAAY,4BAA4B,MAAM;EACxF,cAAc,KAAK,KAAK,SAAS,YAAY,kBAAkB,MAAM;EACtE,CAAC;CAEF,MAAM,oBAAoB,KAAK,KAC7B,YAAY,WACZ,GAAG,sBAAsB,mBAC1B;CACD,MAAM,eAAe,KAAK,SAAS,QAAQ,WAAW,kBAAkB;AACxE,SAAQ,UAAU,cAAc,QAAQ;;;;AC7G1C,MAAM,YAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE9D,IAAqB,gBAArB,cAA2C,WAAW;CACpD,OAAc;CACd,SAAyB,SAAiD;EAExE,MAAM,SAAS,KAAK,KAAK,WAAW,MAAM;AAC1C,OAAK,aAAa,SAAS,QAAQ,UAAU;AAE7C,WAAgB,QAAQ"}
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
* @generated by @rexeus/typeweaver
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import spec from "<%= specPath %>";
|
|
10
10
|
import { HttpMethod, type IHttpResponse, ResponseValidationError, UnknownResponseError } from "@rexeus/typeweaver-core";
|
|
11
11
|
import { RequestCommand } from "../lib/clients";
|
|
12
|
+
import { getOperationDefinition } from "../lib/types";
|
|
12
13
|
import { <%= pascalCaseOperationId %>ResponseValidator } from "<%= responseValidatorFile %>";
|
|
13
14
|
import type {
|
|
14
15
|
I<%= pascalCaseOperationId %>Request,
|
|
@@ -19,6 +20,7 @@ import type {
|
|
|
19
20
|
} from "<%= requestFile %>";
|
|
20
21
|
import type { <%= pascalCaseOperationId %>Response } from "<%= responseFile %>";
|
|
21
22
|
|
|
23
|
+
const definition = getOperationDefinition(spec, "<%= resourceName %>", "<%= operationId %>");
|
|
22
24
|
const responseValidator = new <%= pascalCaseOperationId %>ResponseValidator();
|
|
23
25
|
|
|
24
26
|
export class <%= pascalCaseOperationId %>RequestCommand extends RequestCommand implements I<%= pascalCaseOperationId %>Request {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rexeus/typeweaver-clients",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Generates HTTP clients directly from your API definitions. Powered by Typeweaver ๐งตโจ",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -48,19 +48,19 @@
|
|
|
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.9.0",
|
|
52
|
+
"@rexeus/typeweaver-gen": "^0.9.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@hono/node-server": "^1.19.
|
|
55
|
+
"@hono/node-server": "^1.19.11",
|
|
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.9.0",
|
|
59
|
+
"@rexeus/typeweaver-gen": "^0.9.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.9.0"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
66
|
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json",
|