bytekit 1.0.20 ā 1.0.22
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/bin/{sutils.js ā bytekit.js} +2 -4
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +104 -474
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/swagger-generator.d.ts +9 -0
- package/dist/cli/swagger-generator.d.ts.map +1 -0
- package/dist/cli/swagger-generator.js +149 -0
- package/dist/cli/swagger-generator.js.map +1 -0
- package/dist/cli/type-generator.d.ts +1 -0
- package/dist/cli/type-generator.d.ts.map +1 -1
- package/dist/cli/type-generator.js +2 -1
- package/dist/cli/type-generator.js.map +1 -1
- package/dist/utils/async/allSettled.d.ts +27 -0
- package/dist/utils/async/allSettled.d.ts.map +1 -0
- package/dist/utils/async/allSettled.js +45 -0
- package/dist/utils/async/allSettled.js.map +1 -0
- package/dist/utils/async/debounce.d.ts +24 -0
- package/dist/utils/async/debounce.d.ts.map +1 -0
- package/dist/utils/async/debounce.js +138 -0
- package/dist/utils/async/debounce.js.map +1 -0
- package/dist/utils/async/errors.d.ts +22 -0
- package/dist/utils/async/errors.d.ts.map +1 -0
- package/dist/utils/async/errors.js +58 -0
- package/dist/utils/async/errors.js.map +1 -0
- package/dist/utils/async/index.d.ts +19 -0
- package/dist/utils/async/index.d.ts.map +1 -0
- package/dist/utils/async/index.js +20 -0
- package/dist/utils/async/index.js.map +1 -0
- package/dist/utils/async/parallel.d.ts +24 -0
- package/dist/utils/async/parallel.d.ts.map +1 -0
- package/dist/utils/async/parallel.js +98 -0
- package/dist/utils/async/parallel.js.map +1 -0
- package/dist/utils/async/race.d.ts +37 -0
- package/dist/utils/async/race.d.ts.map +1 -0
- package/dist/utils/async/race.js +69 -0
- package/dist/utils/async/race.js.map +1 -0
- package/dist/utils/async/retry.d.ts +34 -0
- package/dist/utils/async/retry.d.ts.map +1 -0
- package/dist/utils/async/retry.js +118 -0
- package/dist/utils/async/retry.js.map +1 -0
- package/dist/utils/async/sequential.d.ts +28 -0
- package/dist/utils/async/sequential.d.ts.map +1 -0
- package/dist/utils/async/sequential.js +66 -0
- package/dist/utils/async/sequential.js.map +1 -0
- package/dist/utils/async/sleep.d.ts +28 -0
- package/dist/utils/async/sleep.d.ts.map +1 -0
- package/dist/utils/async/sleep.js +64 -0
- package/dist/utils/async/sleep.js.map +1 -0
- package/dist/utils/async/throttle.d.ts +24 -0
- package/dist/utils/async/throttle.d.ts.map +1 -0
- package/dist/utils/async/throttle.js +118 -0
- package/dist/utils/async/throttle.js.map +1 -0
- package/dist/utils/async/timeout.d.ts +45 -0
- package/dist/utils/async/timeout.d.ts.map +1 -0
- package/dist/utils/async/timeout.js +75 -0
- package/dist/utils/async/timeout.js.map +1 -0
- package/dist/utils/async/types.d.ts +105 -0
- package/dist/utils/async/types.d.ts.map +1 -0
- package/dist/utils/async/types.js +5 -0
- package/dist/utils/async/types.js.map +1 -0
- package/dist/utils/core/ApiClient.d.ts.map +1 -1
- package/dist/utils/core/ApiClient.js +28 -10
- package/dist/utils/core/ApiClient.js.map +1 -1
- package/dist/utils/core/Logger.js.map +1 -1
- package/dist/utils/core/Profiler.d.ts +3 -1
- package/dist/utils/core/Profiler.d.ts.map +1 -1
- package/dist/utils/core/Profiler.js +17 -2
- package/dist/utils/core/Profiler.js.map +1 -1
- package/dist/utils/core/QueryClient.d.ts.map +1 -1
- package/dist/utils/core/QueryClient.js +0 -2
- package/dist/utils/core/QueryClient.js.map +1 -1
- package/dist/utils/core/QueryState.d.ts.map +1 -1
- package/dist/utils/core/QueryState.js.map +1 -1
- package/dist/utils/core/RetryPolicy.d.ts +7 -0
- package/dist/utils/core/RetryPolicy.d.ts.map +1 -1
- package/dist/utils/core/RetryPolicy.js +9 -0
- package/dist/utils/core/RetryPolicy.js.map +1 -1
- package/dist/utils/helpers/CompressionUtils.js +1 -1
- package/dist/utils/helpers/CompressionUtils.js.map +1 -1
- package/dist/utils/helpers/CryptoUtils.d.ts.map +1 -1
- package/dist/utils/helpers/CryptoUtils.js +13 -2
- package/dist/utils/helpers/CryptoUtils.js.map +1 -1
- package/dist/utils/helpers/DateUtils.d.ts.map +1 -1
- package/dist/utils/helpers/DateUtils.js +1 -1
- package/dist/utils/helpers/DateUtils.js.map +1 -1
- package/dist/utils/helpers/EnvManager.d.ts.map +1 -1
- package/dist/utils/helpers/EnvManager.js.map +1 -1
- package/dist/utils/helpers/FormUtils.d.ts +14 -0
- package/dist/utils/helpers/FormUtils.d.ts.map +1 -1
- package/dist/utils/helpers/FormUtils.js +24 -0
- package/dist/utils/helpers/FormUtils.js.map +1 -1
- package/dist/utils/helpers/ObjectUtils.d.ts +2 -2
- package/dist/utils/helpers/ObjectUtils.d.ts.map +1 -1
- package/dist/utils/helpers/ObjectUtils.js +4 -0
- package/dist/utils/helpers/ObjectUtils.js.map +1 -1
- package/dist/utils/helpers/PaginationHelper.js +6 -6
- package/dist/utils/helpers/PaginationHelper.js.map +1 -1
- package/dist/utils/helpers/Signal.d.ts.map +1 -1
- package/dist/utils/helpers/Signal.js.map +1 -1
- package/dist/utils/helpers/WebSocketHelper.d.ts.map +1 -1
- package/dist/utils/helpers/WebSocketHelper.js.map +1 -1
- package/dist/utils/helpers/index.d.ts +1 -0
- package/dist/utils/helpers/index.d.ts.map +1 -1
- package/dist/utils/helpers/index.js +1 -0
- package/dist/utils/helpers/index.js.map +1 -1
- package/package.json +10 -7
package/dist/cli/index.d.ts
CHANGED
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuE1D"}
|
package/dist/cli/index.js
CHANGED
|
@@ -2,502 +2,132 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { generateTypesFromEndpoint } from "./type-generator.js";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { generateFromSwagger } from "./swagger-generator.js";
|
|
6
|
+
/**
|
|
7
|
+
* Main CLI entry point for bytekit
|
|
8
|
+
*/
|
|
7
9
|
export async function runCli(argv) {
|
|
8
|
-
if (argv.length === 0 || argv
|
|
10
|
+
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
9
11
|
printHelp();
|
|
10
12
|
return;
|
|
11
13
|
}
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
else if (command === "types") {
|
|
17
|
-
await handleTypes(target, rest);
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
console.error(`\u001b[31mUnknown command:\u001b[0m ${command}`);
|
|
21
|
-
printHelp();
|
|
22
|
-
process.exit(1);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async function handleCreate(resource, args) {
|
|
26
|
-
if (!resource || resource.startsWith("--")) {
|
|
27
|
-
console.error("\u001b[31mMissing resource name.\u001b[0m");
|
|
28
|
-
printCreateHelp();
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
const flags = parseCreateFlags(args);
|
|
32
|
-
const resourceSegments = resource.split("/").filter(Boolean);
|
|
33
|
-
if (resourceSegments.length === 0) {
|
|
34
|
-
console.error("\u001b[31mResource name cannot be empty.\u001b[0m");
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
const config = {
|
|
38
|
-
apiDir: flags.apiDir ?? DEFAULT_API_DIR,
|
|
39
|
-
hooksDir: flags.hooksDir ?? DEFAULT_HOOKS_DIR,
|
|
40
|
-
force: Boolean(flags.force),
|
|
41
|
-
route: flags.route ?? `/${resourceSegments.join("/")}`,
|
|
42
|
-
resourceSegments,
|
|
43
|
-
queryLib: flags.queryLib ?? "react-query",
|
|
14
|
+
const options = {
|
|
15
|
+
method: "GET",
|
|
16
|
+
headers: {},
|
|
44
17
|
};
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
output: flags.output,
|
|
58
|
-
name: flags.name,
|
|
59
|
-
headers: flags.headers,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
function parseCreateFlags(args) {
|
|
63
|
-
const flags = {};
|
|
64
|
-
for (const arg of args) {
|
|
65
|
-
if (!arg.startsWith("--")) {
|
|
66
|
-
console.warn(`Ignoring unexpected argument "${arg}".`);
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const withoutDashes = arg.slice(2);
|
|
70
|
-
const [rawKey, rawValue] = withoutDashes.split("=", 2);
|
|
71
|
-
const key = rawKey.trim();
|
|
72
|
-
if (key === "force") {
|
|
73
|
-
flags.force = rawValue === undefined ? true : rawValue === "true";
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (rawValue === undefined) {
|
|
77
|
-
console.warn(`Flag "--${key}" requires a value; ignoring.`);
|
|
78
|
-
continue;
|
|
18
|
+
const VALID_METHODS = [
|
|
19
|
+
"GET",
|
|
20
|
+
"POST",
|
|
21
|
+
"PUT",
|
|
22
|
+
"PATCH",
|
|
23
|
+
"DELETE",
|
|
24
|
+
];
|
|
25
|
+
// Simple argument parser
|
|
26
|
+
for (let i = 0; i < argv.length; i++) {
|
|
27
|
+
const arg = argv[i];
|
|
28
|
+
if (arg === "--type") {
|
|
29
|
+
options.type = true;
|
|
79
30
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
flags.apiDir = rawValue;
|
|
83
|
-
break;
|
|
84
|
-
case "hooksDir":
|
|
85
|
-
flags.hooksDir = rawValue;
|
|
86
|
-
break;
|
|
87
|
-
case "route":
|
|
88
|
-
flags.route = rawValue;
|
|
89
|
-
break;
|
|
90
|
-
case "queryLib":
|
|
91
|
-
if (rawValue === "react-query" || rawValue === "rtk-query") {
|
|
92
|
-
flags.queryLib = rawValue;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
console.warn(`Invalid queryLib value "${rawValue}". Use "react-query" or "rtk-query".`);
|
|
96
|
-
}
|
|
97
|
-
break;
|
|
98
|
-
default:
|
|
99
|
-
console.warn(`Unrecognized flag "--${key}"; ignoring.`);
|
|
31
|
+
else if (arg === "--swagger") {
|
|
32
|
+
options.swagger = true;
|
|
100
33
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
for (const arg of args) {
|
|
107
|
-
if (!arg.startsWith("--")) {
|
|
108
|
-
console.warn(`Ignoring unexpected argument "${arg}".`);
|
|
109
|
-
continue;
|
|
34
|
+
else if (arg.startsWith("--method=")) {
|
|
35
|
+
const inputMethod = arg.split("=")[1].toUpperCase();
|
|
36
|
+
if (VALID_METHODS.includes(inputMethod)) {
|
|
37
|
+
options.method = inputMethod;
|
|
38
|
+
}
|
|
110
39
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const key = rawKey.trim();
|
|
114
|
-
if (rawValue === undefined) {
|
|
115
|
-
console.warn(`Flag "--${key}" requires a value; ignoring.`);
|
|
116
|
-
continue;
|
|
40
|
+
else if (arg.startsWith("--body=")) {
|
|
41
|
+
options.body = arg.split("=")[1];
|
|
117
42
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
break;
|
|
128
|
-
case "header": {
|
|
129
|
-
const [headerKey, headerValue] = rawValue.split(":", 2);
|
|
130
|
-
if (headerKey && headerValue) {
|
|
131
|
-
flags.headers[headerKey.trim()] = headerValue.trim();
|
|
132
|
-
}
|
|
133
|
-
break;
|
|
43
|
+
else if (arg.startsWith("--header=") ||
|
|
44
|
+
arg.startsWith("--headers=")) {
|
|
45
|
+
const fullValue = arg.split("=")[1];
|
|
46
|
+
// Split only on the first colon to support values like "Authorization: Bearer key:123"
|
|
47
|
+
const firstColonIndex = fullValue.indexOf(":");
|
|
48
|
+
if (firstColonIndex !== -1) {
|
|
49
|
+
const key = fullValue.slice(0, firstColonIndex).trim();
|
|
50
|
+
const value = fullValue.slice(firstColonIndex + 1).trim();
|
|
51
|
+
options.headers[key] = value;
|
|
134
52
|
}
|
|
135
|
-
|
|
136
|
-
|
|
53
|
+
}
|
|
54
|
+
else if (!arg.startsWith("-")) {
|
|
55
|
+
options.url = arg;
|
|
137
56
|
}
|
|
138
57
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const { apiDir, hooksDir, force, resourceSegments, route } = config;
|
|
143
|
-
const lastSegment = resourceSegments.at(-1);
|
|
144
|
-
const pascalPlural = toPascalCase(lastSegment);
|
|
145
|
-
const singularSegment = toSingular(lastSegment);
|
|
146
|
-
const pascalSingular = toPascalCase(singularSegment);
|
|
147
|
-
const resolvedRoute = route.startsWith("/") ? route : `/${route}`;
|
|
148
|
-
const resourceKey = resourceSegments.join("/");
|
|
149
|
-
const apiResourceDir = path.join(process.cwd(), apiDir, ...resourceSegments);
|
|
150
|
-
const hooksResourceDir = path.join(process.cwd(), hooksDir, ...resourceSegments);
|
|
151
|
-
await fs.mkdir(apiResourceDir, { recursive: true });
|
|
152
|
-
await fs.mkdir(hooksResourceDir, { recursive: true });
|
|
153
|
-
const apiFilePath = path.join(apiResourceDir, "index.ts");
|
|
154
|
-
const hooksFilePath = path.join(hooksResourceDir, `use${pascalPlural}.ts`);
|
|
155
|
-
const hooksIndexPath = path.join(hooksResourceDir, "index.ts");
|
|
156
|
-
const apiImportPath = buildImportPath(hooksResourceDir, apiResourceDir);
|
|
157
|
-
await writeFileIfAllowed(apiFilePath, buildApiTemplate({
|
|
158
|
-
pluralPascal: pascalPlural,
|
|
159
|
-
singularPascal: pascalSingular,
|
|
160
|
-
route: resolvedRoute,
|
|
161
|
-
}), force);
|
|
162
|
-
await writeFileIfAllowed(hooksFilePath, config.queryLib === "rtk-query"
|
|
163
|
-
? buildRtkQueryHooksTemplate({
|
|
164
|
-
pascalPlural,
|
|
165
|
-
pascalSingular,
|
|
166
|
-
resourceKey,
|
|
167
|
-
apiImportPath,
|
|
168
|
-
})
|
|
169
|
-
: buildHooksTemplate({
|
|
170
|
-
pascalPlural,
|
|
171
|
-
pascalSingular,
|
|
172
|
-
resourceKey,
|
|
173
|
-
apiImportPath,
|
|
174
|
-
}), force);
|
|
175
|
-
await writeFileIfAllowed(hooksIndexPath, `export * from "./use${pascalPlural}";\n`, force);
|
|
176
|
-
console.log(`Scaffolded "${resourceKey}" under "${apiDir}" and "${hooksDir}".`);
|
|
177
|
-
}
|
|
178
|
-
function buildImportPath(fromDir, toDir) {
|
|
179
|
-
const relativePath = path.relative(fromDir, toDir).replace(/\\/g, "/");
|
|
180
|
-
if (!relativePath.startsWith(".")) {
|
|
181
|
-
return `./${relativePath}`;
|
|
58
|
+
if (!options.url) {
|
|
59
|
+
console.error("\u001b[31mError: Missing URL\u001b[0m");
|
|
60
|
+
process.exit(1);
|
|
182
61
|
}
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
async function writeFileIfAllowed(filePath, content, force) {
|
|
186
|
-
try {
|
|
187
|
-
await fs.access(filePath);
|
|
188
|
-
if (!force) {
|
|
189
|
-
console.warn(`Skipping existing file: ${filePath}`);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
62
|
+
if (options.swagger) {
|
|
63
|
+
await generateFromSwagger({ url: options.url });
|
|
192
64
|
}
|
|
193
|
-
|
|
194
|
-
|
|
65
|
+
else if (options.type) {
|
|
66
|
+
await handleTypeGeneration(options);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// Simple fetch/curl behavior if --type is not present
|
|
70
|
+
await handleSimpleFetch(options);
|
|
195
71
|
}
|
|
196
|
-
await fs.writeFile(filePath, content, "utf8");
|
|
197
|
-
console.log(`Generated ${filePath}`);
|
|
198
|
-
}
|
|
199
|
-
function buildApiTemplate(options) {
|
|
200
|
-
const { pluralPascal, singularPascal, route } = options;
|
|
201
|
-
return `import type { ApiClient } from "bytekit";
|
|
202
|
-
|
|
203
|
-
const RESOURCE_PATH = "${route}";
|
|
204
|
-
|
|
205
|
-
export interface ${singularPascal}Dto {
|
|
206
|
-
id: string;
|
|
207
|
-
/** Extend with the fields returned by the API. */
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export type Create${singularPascal}Dto = Omit<${singularPascal}Dto, "id">;
|
|
211
|
-
export type Update${singularPascal}Dto = Partial<Omit<${singularPascal}Dto, "id">>;
|
|
212
|
-
|
|
213
|
-
export type ${singularPascal}Filters = Record<string, string | number | boolean | undefined>;
|
|
214
|
-
|
|
215
|
-
export const list${pluralPascal} = (
|
|
216
|
-
client: ApiClient,
|
|
217
|
-
filters?: ${singularPascal}Filters
|
|
218
|
-
) => client.get<${singularPascal}Dto[]>(RESOURCE_PATH, { searchParams: filters });
|
|
219
|
-
|
|
220
|
-
export const get${singularPascal} = (client: ApiClient, id: string) =>
|
|
221
|
-
client.get<${singularPascal}Dto>(RESOURCE_PATH + "/" + id);
|
|
222
|
-
|
|
223
|
-
export const create${singularPascal} = (client: ApiClient, payload: Create${singularPascal}Dto) =>
|
|
224
|
-
client.post<${singularPascal}Dto>(RESOURCE_PATH, { body: payload });
|
|
225
|
-
|
|
226
|
-
export const update${singularPascal} = (
|
|
227
|
-
client: ApiClient,
|
|
228
|
-
id: string,
|
|
229
|
-
payload: Update${singularPascal}Dto
|
|
230
|
-
) => client.patch<${singularPascal}Dto>(RESOURCE_PATH + "/" + id, { body: payload });
|
|
231
|
-
|
|
232
|
-
export const delete${singularPascal} = (client: ApiClient, id: string) =>
|
|
233
|
-
client.delete<void>(RESOURCE_PATH + "/" + id);
|
|
234
|
-
|
|
235
|
-
`;
|
|
236
|
-
}
|
|
237
|
-
function buildRtkQueryHooksTemplate(options) {
|
|
238
|
-
const { pascalPlural, pascalSingular, resourceKey, apiImportPath } = options;
|
|
239
|
-
return `import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
|
|
240
|
-
import type { ApiClient } from "bytekit";
|
|
241
|
-
import {
|
|
242
|
-
list${pascalPlural},
|
|
243
|
-
get${pascalSingular},
|
|
244
|
-
create${pascalSingular},
|
|
245
|
-
update${pascalSingular},
|
|
246
|
-
delete${pascalSingular},
|
|
247
|
-
type ${pascalSingular}Dto,
|
|
248
|
-
type Create${pascalSingular}Dto,
|
|
249
|
-
type Update${pascalSingular}Dto,
|
|
250
|
-
type ${pascalSingular}Filters,
|
|
251
|
-
} from "${apiImportPath}";
|
|
252
|
-
|
|
253
|
-
const resourceKey = "${resourceKey}";
|
|
254
|
-
|
|
255
|
-
export const ${pascalSingular.toLowerCase()}Api = createApi({
|
|
256
|
-
reducerPath: "${pascalSingular.toLowerCase()}Api",
|
|
257
|
-
baseQuery: fetchBaseQuery({
|
|
258
|
-
baseUrl: process.env.REACT_APP_API_URL || "",
|
|
259
|
-
}),
|
|
260
|
-
tagTypes: ["${pascalSingular}"],
|
|
261
|
-
endpoints: (builder) => ({
|
|
262
|
-
list${pascalPlural}: builder.query<${pascalSingular}Dto[], ${pascalSingular}Filters | undefined>({
|
|
263
|
-
query: (filters) => ({
|
|
264
|
-
url: resourceKey,
|
|
265
|
-
params: filters,
|
|
266
|
-
}),
|
|
267
|
-
providesTags: ["${pascalSingular}"],
|
|
268
|
-
}),
|
|
269
|
-
get${pascalSingular}: builder.query<${pascalSingular}Dto, string>({
|
|
270
|
-
query: (id) => \`\${resourceKey}/\${id}\`,
|
|
271
|
-
providesTags: (result, error, id) => [{ type: "${pascalSingular}", id }],
|
|
272
|
-
}),
|
|
273
|
-
create${pascalSingular}: builder.mutation<${pascalSingular}Dto, Create${pascalSingular}Dto>({
|
|
274
|
-
query: (payload) => ({
|
|
275
|
-
url: resourceKey,
|
|
276
|
-
method: "POST",
|
|
277
|
-
body: payload,
|
|
278
|
-
}),
|
|
279
|
-
invalidatesTags: ["${pascalSingular}"],
|
|
280
|
-
}),
|
|
281
|
-
update${pascalSingular}: builder.mutation<
|
|
282
|
-
${pascalSingular}Dto,
|
|
283
|
-
{ id: string; payload: Update${pascalSingular}Dto }
|
|
284
|
-
>({
|
|
285
|
-
query: ({ id, payload }) => ({
|
|
286
|
-
url: \`\${resourceKey}/\${id}\`,
|
|
287
|
-
method: "PATCH",
|
|
288
|
-
body: payload,
|
|
289
|
-
}),
|
|
290
|
-
invalidatesTags: (result, error, { id }) => [
|
|
291
|
-
{ type: "${pascalSingular}", id },
|
|
292
|
-
"${pascalSingular}",
|
|
293
|
-
],
|
|
294
|
-
}),
|
|
295
|
-
delete${pascalSingular}: builder.mutation<void, string>({
|
|
296
|
-
query: (id) => ({
|
|
297
|
-
url: \`\${resourceKey}/\${id}\`,
|
|
298
|
-
method: "DELETE",
|
|
299
|
-
}),
|
|
300
|
-
invalidatesTags: ["${pascalSingular}"],
|
|
301
|
-
}),
|
|
302
|
-
}),
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
export const {
|
|
306
|
-
useList${pascalPlural}Query,
|
|
307
|
-
useGet${pascalSingular}Query,
|
|
308
|
-
useCreate${pascalSingular}Mutation,
|
|
309
|
-
useUpdate${pascalSingular}Mutation,
|
|
310
|
-
useDelete${pascalSingular}Mutation,
|
|
311
|
-
} = ${pascalSingular.toLowerCase()}Api;
|
|
312
|
-
|
|
313
|
-
`;
|
|
314
72
|
}
|
|
315
|
-
function
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
delete${pascalSingular},
|
|
331
|
-
type ${pascalSingular}Dto,
|
|
332
|
-
type Create${pascalSingular}Dto,
|
|
333
|
-
type Update${pascalSingular}Dto,
|
|
334
|
-
type ${pascalSingular}Filters,
|
|
335
|
-
} from "${apiImportPath}";
|
|
336
|
-
|
|
337
|
-
const resourceKey = ["${resourceKey}"] as const;
|
|
338
|
-
|
|
339
|
-
export const ${pascalSingular.toLowerCase()}Keys = {
|
|
340
|
-
all: resourceKey,
|
|
341
|
-
detail: (id: string) => [...resourceKey, id] as const,
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
export const use${pascalPlural} = (
|
|
345
|
-
client: ApiClient,
|
|
346
|
-
filters?: ${pascalSingular}Filters,
|
|
347
|
-
options?: UseQueryOptions<${pascalSingular}Dto[], Error>
|
|
348
|
-
) =>
|
|
349
|
-
useQuery({
|
|
350
|
-
queryKey: filters ? [...resourceKey, filters] : resourceKey,
|
|
351
|
-
queryFn: () => list${pascalPlural}(client, filters),
|
|
352
|
-
...options,
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
export const use${pascalSingular} = (
|
|
356
|
-
client: ApiClient,
|
|
357
|
-
id?: string,
|
|
358
|
-
options?: UseQueryOptions<${pascalSingular}Dto, Error>
|
|
359
|
-
) =>
|
|
360
|
-
useQuery({
|
|
361
|
-
queryKey: id ? [...resourceKey, id] : [...resourceKey, "detail"],
|
|
362
|
-
queryFn: () => {
|
|
363
|
-
if (!id) {
|
|
364
|
-
throw new Error("${pascalSingular} id is required");
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return get${pascalSingular}(client, id);
|
|
368
|
-
},
|
|
369
|
-
enabled: Boolean(id),
|
|
370
|
-
...options,
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
type CreateMutationVars = Create${pascalSingular}Dto;
|
|
374
|
-
type UpdateMutationVars = {
|
|
375
|
-
id: string;
|
|
376
|
-
payload: Update${pascalSingular}Dto;
|
|
377
|
-
};
|
|
378
|
-
type DeleteMutationVars = {
|
|
379
|
-
id: string;
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
export const useCreate${pascalSingular} = (
|
|
383
|
-
client: ApiClient,
|
|
384
|
-
options?: UseMutationOptions<${pascalSingular}Dto, unknown, CreateMutationVars>
|
|
385
|
-
) => {
|
|
386
|
-
const queryClient = useQueryClient();
|
|
387
|
-
|
|
388
|
-
return useMutation({
|
|
389
|
-
mutationFn: (payload) => create${pascalSingular}(client, payload),
|
|
390
|
-
...options,
|
|
391
|
-
onSuccess(data, variables, context) {
|
|
392
|
-
queryClient.invalidateQueries(resourceKey);
|
|
393
|
-
options?.onSuccess?.(data, variables, context);
|
|
394
|
-
},
|
|
395
|
-
});
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
export const useUpdate${pascalSingular} = (
|
|
399
|
-
client: ApiClient,
|
|
400
|
-
options?: UseMutationOptions<${pascalSingular}Dto, unknown, UpdateMutationVars>
|
|
401
|
-
) => {
|
|
402
|
-
const queryClient = useQueryClient();
|
|
403
|
-
|
|
404
|
-
return useMutation({
|
|
405
|
-
mutationFn: ({ id, payload }) => update${pascalSingular}(client, id, payload),
|
|
406
|
-
...options,
|
|
407
|
-
onSuccess(data, variables, context) {
|
|
408
|
-
queryClient.invalidateQueries(resourceKey);
|
|
409
|
-
if (variables?.id) {
|
|
410
|
-
queryClient.invalidateQueries(${pascalSingular.toLowerCase()}Keys.detail(variables.id));
|
|
411
|
-
}
|
|
412
|
-
options?.onSuccess?.(data, variables, context);
|
|
413
|
-
},
|
|
414
|
-
});
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
export const useDelete${pascalSingular} = (
|
|
418
|
-
client: ApiClient,
|
|
419
|
-
options?: UseMutationOptions<void, unknown, DeleteMutationVars>
|
|
420
|
-
) => {
|
|
421
|
-
const queryClient = useQueryClient();
|
|
422
|
-
|
|
423
|
-
return useMutation({
|
|
424
|
-
mutationFn: ({ id }) => delete${pascalSingular}(client, id),
|
|
425
|
-
...options,
|
|
426
|
-
onSuccess(data, variables, context) {
|
|
427
|
-
queryClient.invalidateQueries(resourceKey);
|
|
428
|
-
if (variables?.id) {
|
|
429
|
-
queryClient.invalidateQueries(${pascalSingular.toLowerCase()}Keys.detail(variables.id));
|
|
430
|
-
}
|
|
431
|
-
options?.onSuccess?.(data, variables, context);
|
|
432
|
-
},
|
|
73
|
+
async function handleTypeGeneration(options) {
|
|
74
|
+
const url = new URL(options.url);
|
|
75
|
+
const endpointName = url.pathname.split("/").filter(Boolean).pop() || "api";
|
|
76
|
+
// Create src/types directory if it doesn't exist
|
|
77
|
+
const typesDir = path.join(process.cwd(), "src", "types");
|
|
78
|
+
await fs.mkdir(typesDir, { recursive: true });
|
|
79
|
+
const outputPath = path.join("src", "types", `${endpointName}.ts`);
|
|
80
|
+
const interfaceName = endpointName.charAt(0).toUpperCase() + endpointName.slice(1);
|
|
81
|
+
await generateTypesFromEndpoint({
|
|
82
|
+
endpoint: options.url,
|
|
83
|
+
method: options.method,
|
|
84
|
+
body: options.body,
|
|
85
|
+
headers: options.headers,
|
|
86
|
+
output: outputPath,
|
|
87
|
+
name: interfaceName,
|
|
433
88
|
});
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
`;
|
|
437
|
-
}
|
|
438
|
-
function toPascalCase(value) {
|
|
439
|
-
return value
|
|
440
|
-
.split(/[^a-zA-Z0-9]/)
|
|
441
|
-
.filter(Boolean)
|
|
442
|
-
.map((segment) => segment[0].toUpperCase() + segment.slice(1).toLowerCase())
|
|
443
|
-
.join("");
|
|
444
89
|
}
|
|
445
|
-
function
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
90
|
+
async function handleSimpleFetch(options) {
|
|
91
|
+
console.log(`\nš” Fetching ${options.method} ${options.url}...`);
|
|
92
|
+
try {
|
|
93
|
+
const response = await fetch(options.url, {
|
|
94
|
+
method: options.method,
|
|
95
|
+
headers: {
|
|
96
|
+
"Content-Type": "application/json",
|
|
97
|
+
...options.headers,
|
|
98
|
+
},
|
|
99
|
+
body: options.body,
|
|
100
|
+
});
|
|
101
|
+
const data = await response.json();
|
|
102
|
+
console.log(JSON.stringify(data, null, 2));
|
|
456
103
|
}
|
|
457
|
-
|
|
458
|
-
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error(`\u001b[31mError:\u001b[0m ${error instanceof Error ? error.message : String(error)}`);
|
|
106
|
+
process.exit(1);
|
|
459
107
|
}
|
|
460
|
-
return value;
|
|
461
108
|
}
|
|
462
109
|
function printHelp() {
|
|
463
|
-
console.log(`
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
--
|
|
477
|
-
--
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
sutils create users --queryLib=rtk-query
|
|
485
|
-
sutils create posts --apiDir=src/api --hooksDir=src/hooks --force
|
|
486
|
-
`);
|
|
487
|
-
}
|
|
488
|
-
function printTypesHelp() {
|
|
489
|
-
console.log(`sutils types <endpoint> [options]
|
|
490
|
-
|
|
491
|
-
Options
|
|
492
|
-
--method=<METHOD> HTTP method (default: GET).
|
|
493
|
-
--output=<file> Output file path (default: types.ts).
|
|
494
|
-
--name=<name> Type name (default: ApiResponse).
|
|
495
|
-
--header=<key:value> Custom header (can be used multiple times).
|
|
496
|
-
|
|
497
|
-
Examples
|
|
498
|
-
sutils types https://api.example.com/users
|
|
499
|
-
sutils types https://api.example.com/users --output=user-types.ts --name=User
|
|
500
|
-
sutils types https://api.example.com/users --method=POST --header=Authorization:Bearer token
|
|
110
|
+
console.log(`
|
|
111
|
+
\u001b[1mbytekit CLI\u001b[0m - API Inspection & Type Generation
|
|
112
|
+
|
|
113
|
+
\u001b[1mUsage:\u001b[0m
|
|
114
|
+
bytekit [options] <url>
|
|
115
|
+
|
|
116
|
+
\u001b[1mOptions:\u001b[0m
|
|
117
|
+
--type Generate TypeScript types from a single API response.
|
|
118
|
+
Saves to src/types/{endpoint}.ts
|
|
119
|
+
--swagger Generate all TypeScript DTOs from a Swagger/OpenAPI spec.
|
|
120
|
+
Saves to src/types/api-docs.ts
|
|
121
|
+
--method=<METHOD> HTTP method (GET, POST, PUT, DELETE, PATCH). Default: GET.
|
|
122
|
+
--body=<body> JSON body for the request.
|
|
123
|
+
--header=<key:val> Custom HTTP header (can be used multiple times).
|
|
124
|
+
--headers=<key:val> Alias for --header.
|
|
125
|
+
|
|
126
|
+
\u001b[1mExamples:\u001b[0m
|
|
127
|
+
bytekit https://api.example.com/users
|
|
128
|
+
bytekit --type https://api.example.com/users
|
|
129
|
+
bytekit --swagger https://api.example.com/swagger.json
|
|
130
|
+
bytekit --type --method=POST --body='{"name":"test"}' https://api.example.com/users
|
|
501
131
|
`);
|
|
502
132
|
}
|
|
503
133
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|