@tuyau/core 1.0.0 → 1.1.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/build/backend/generate_registry.d.ts +14 -4
- package/build/backend/generate_registry.js +252 -157
- package/build/client/index.d.ts +27 -29
- package/build/client/index.js +293 -56
- package/build/client/types/index.d.ts +3 -265
- package/build/index-CzGwWv_7.d.ts +438 -0
- package/package.json +11 -11
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AllHooks } from '@adonisjs/assembler/types';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Extending the HTTP response interface to include status and response
|
|
3
5
|
* in the return type.
|
|
@@ -180,7 +182,6 @@ declare module '@adonisjs/core/http' {
|
|
|
180
182
|
};
|
|
181
183
|
}
|
|
182
184
|
}
|
|
183
|
-
|
|
184
185
|
interface GenerateRegistryConfig {
|
|
185
186
|
/**
|
|
186
187
|
* Path to write the generated registry directory
|
|
@@ -202,9 +203,18 @@ interface GenerateRegistryConfig {
|
|
|
202
203
|
*/
|
|
203
204
|
except?: Array<string | RegExp | ((routeName: string) => boolean)>;
|
|
204
205
|
};
|
|
206
|
+
/**
|
|
207
|
+
* Custom TypeScript type string for 422 validation error responses.
|
|
208
|
+
* When not provided, uses the default VineJS SimpleErrorReporter format.
|
|
209
|
+
* Set to `false` to disable auto-adding 422 errors.
|
|
210
|
+
*/
|
|
211
|
+
validationErrorType?: string | false;
|
|
205
212
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* AdonisJS assembler hook that scans routes and generates
|
|
216
|
+
* the tuyau typed client registry files (runtime, schema, tree).
|
|
217
|
+
*/
|
|
218
|
+
declare function generateRegistry(options?: GenerateRegistryConfig): AllHooks['init'][number];
|
|
209
219
|
|
|
210
220
|
export { generateRegistry };
|
|
@@ -1,144 +1,195 @@
|
|
|
1
1
|
// src/backend/types.ts
|
|
2
2
|
import "@adonisjs/core/http";
|
|
3
3
|
|
|
4
|
-
// src/backend/
|
|
4
|
+
// src/backend/registry_generator.ts
|
|
5
5
|
import { dirname } from "path";
|
|
6
6
|
import { writeFile, mkdir } from "fs/promises";
|
|
7
7
|
import stringHelpers from "@adonisjs/core/helpers/string";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
8
|
+
var DEFAULT_VALIDATION_ERROR_TYPE = "{ errors: SimpleError[] }";
|
|
9
|
+
var RegistryGenerator = class {
|
|
10
|
+
#validationErrorType;
|
|
11
|
+
#routesFilter;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.#validationErrorType = options?.validationErrorType === void 0 ? DEFAULT_VALIDATION_ERROR_TYPE : options.validationErrorType;
|
|
14
|
+
this.#routesFilter = options?.routes;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Test a route name against a string, regex, or function pattern.
|
|
18
|
+
*/
|
|
19
|
+
#matchesPattern(routeName, pattern) {
|
|
20
|
+
if (typeof pattern === "string") return routeName.includes(pattern);
|
|
21
|
+
if (pattern instanceof RegExp) return pattern.test(routeName);
|
|
22
|
+
if (typeof pattern === "function") return pattern(routeName);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if a route passes the configured only/except filters.
|
|
27
|
+
* Returns true when no filters are set.
|
|
28
|
+
*/
|
|
29
|
+
filterRoute(route) {
|
|
30
|
+
if (!this.#routesFilter) return true;
|
|
31
|
+
const { only, except } = this.#routesFilter;
|
|
32
|
+
if (only && except)
|
|
33
|
+
throw new Error('Cannot use both "only" and "except" filters at the same time');
|
|
34
|
+
if (only) return only.some((pattern) => this.#matchesPattern(route.name, pattern));
|
|
35
|
+
if (except) return !except.some((pattern) => this.#matchesPattern(route.name, pattern));
|
|
21
36
|
return true;
|
|
22
37
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Convert `import('app/...')` paths to `import('#app/...')` subpath
|
|
40
|
+
* imports and strip `.ts` extensions.
|
|
41
|
+
*/
|
|
42
|
+
#normalizeImportPaths(typeString) {
|
|
43
|
+
return typeString.replace(/import\('app\//g, "import('#app/").replace(/\.ts'\)/g, "')");
|
|
28
44
|
}
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Strip server-only properties (matcher, etc.) from route tokens.
|
|
47
|
+
*/
|
|
48
|
+
#sanitizeTokens(tokens) {
|
|
49
|
+
return tokens.map(({ old, type, val, end }) => ({ old, type, val, end }));
|
|
31
50
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Extract dynamic params from route tokens and return
|
|
53
|
+
* the TS type string and tuple representation.
|
|
54
|
+
*/
|
|
55
|
+
#generateRouteParams(route) {
|
|
56
|
+
const dynamicParams = route.tokens.filter((token) => token.type === 1);
|
|
57
|
+
const paramsType = dynamicParams.map((token) => `${token.val}: ParamValue`).join("; ");
|
|
58
|
+
const paramsTuple = dynamicParams.map(() => "ParamValue").join(", ");
|
|
59
|
+
return { paramsType, paramsTuple };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Wrap a response type with `ExtractResponse` (and `Awaited`
|
|
63
|
+
* for `ReturnType<>`) to extract the `__response` property.
|
|
64
|
+
*/
|
|
65
|
+
#wrapResponseType(responseType) {
|
|
66
|
+
if (responseType === "unknown" || responseType === "{}") return responseType;
|
|
67
|
+
if (responseType.startsWith("ReturnType<")) return `ExtractResponse<Awaited<${responseType}>>`;
|
|
68
|
+
return `ExtractResponse<${responseType}>`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Wrap a response type with `ExtractErrorResponse` to
|
|
72
|
+
* extract non-2xx error types from the response union.
|
|
73
|
+
*/
|
|
74
|
+
#wrapErrorResponseType(responseType) {
|
|
75
|
+
if (responseType === "unknown" || responseType === "{}") return "unknown";
|
|
76
|
+
if (responseType.startsWith("ReturnType<"))
|
|
77
|
+
return `ExtractErrorResponse<Awaited<${responseType}>>`;
|
|
78
|
+
return `ExtractErrorResponse<${responseType}>`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Resolve body and query type strings based on the HTTP method.
|
|
82
|
+
* GET/HEAD routes use `ExtractQueryForGet`, others use
|
|
83
|
+
* `ExtractBody`/`ExtractQuery`.
|
|
84
|
+
*/
|
|
85
|
+
#determineBodyAndQueryTypes(options) {
|
|
86
|
+
const { methods, requestType } = options;
|
|
87
|
+
const primaryMethod = methods[0];
|
|
88
|
+
const isGetLike = primaryMethod === "GET" || primaryMethod === "HEAD";
|
|
89
|
+
const hasValidator = requestType !== "{}";
|
|
90
|
+
if (!hasValidator) return { bodyType: "{}", queryType: "{}" };
|
|
91
|
+
if (isGetLike) return { bodyType: "{}", queryType: `ExtractQueryForGet<${requestType}>` };
|
|
92
|
+
return {
|
|
93
|
+
bodyType: `ExtractBody<${requestType}>`,
|
|
94
|
+
queryType: `ExtractQuery<${requestType}>`
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Build a nested Map from dot-separated route names.
|
|
99
|
+
* Uses `$self` to handle nodes that are both a leaf and a prefix.
|
|
100
|
+
*/
|
|
101
|
+
#buildTreeStructure(routes) {
|
|
102
|
+
const tree = /* @__PURE__ */ new Map();
|
|
103
|
+
for (const route of routes) {
|
|
104
|
+
const segments = route.name.split(".");
|
|
105
|
+
let current = tree;
|
|
106
|
+
for (let i = 0; i < segments.length; i++) {
|
|
107
|
+
const segment = stringHelpers.camelCase(segments[i]);
|
|
108
|
+
const isLast = i === segments.length - 1;
|
|
109
|
+
if (isLast) {
|
|
110
|
+
if (current.has(segment) && current.get(segment) instanceof Map) {
|
|
111
|
+
current.get(segment).set("$self", { routeName: route.name, route });
|
|
112
|
+
} else {
|
|
113
|
+
current.set(segment, { routeName: route.name, route });
|
|
114
|
+
}
|
|
57
115
|
} else {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
116
|
+
if (!current.has(segment)) {
|
|
117
|
+
current.set(segment, /* @__PURE__ */ new Map());
|
|
118
|
+
} else {
|
|
119
|
+
const existing = current.get(segment);
|
|
120
|
+
if (!(existing instanceof Map)) {
|
|
121
|
+
const newMap = /* @__PURE__ */ new Map();
|
|
122
|
+
newMap.set("$self", existing);
|
|
123
|
+
current.set(segment, newMap);
|
|
124
|
+
}
|
|
63
125
|
}
|
|
126
|
+
current = current.get(segment);
|
|
64
127
|
}
|
|
65
|
-
current = current.get(segment);
|
|
66
128
|
}
|
|
67
129
|
}
|
|
130
|
+
return tree;
|
|
68
131
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Recursively emit TS interface lines from the nested tree Map.
|
|
134
|
+
* Nodes with `$self` produce intersection types.
|
|
135
|
+
*/
|
|
136
|
+
#generateTreeInterface(tree, indent = 2) {
|
|
137
|
+
const spaces = " ".repeat(indent);
|
|
138
|
+
const lines = [];
|
|
139
|
+
for (const [key, value] of tree) {
|
|
140
|
+
if (key === "$self") continue;
|
|
141
|
+
if (value instanceof Map) {
|
|
142
|
+
const selfRoute = value.get("$self");
|
|
143
|
+
if (selfRoute) {
|
|
144
|
+
lines.push(`${spaces}${key}: typeof routes['${selfRoute.routeName}'] & {`);
|
|
145
|
+
lines.push(this.#generateTreeInterface(value, indent + 2));
|
|
146
|
+
lines.push(`${spaces}}`);
|
|
147
|
+
} else {
|
|
148
|
+
lines.push(`${spaces}${key}: {`);
|
|
149
|
+
lines.push(this.#generateTreeInterface(value, indent + 2));
|
|
150
|
+
lines.push(`${spaces}}`);
|
|
151
|
+
}
|
|
82
152
|
} else {
|
|
83
|
-
lines.push(`${spaces}${key}: {`);
|
|
84
|
-
lines.push(generateTreeInterface(value, indent + 2));
|
|
85
|
-
lines.push(`${spaces}}`);
|
|
153
|
+
lines.push(`${spaces}${key}: typeof routes['${value.routeName}']`);
|
|
86
154
|
}
|
|
87
|
-
} else {
|
|
88
|
-
lines.push(`${spaces}${key}: typeof routes['${value.routeName}']`);
|
|
89
155
|
}
|
|
156
|
+
return lines.join("\n");
|
|
90
157
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
function generateRuntimeRegistryEntry(route) {
|
|
100
|
-
const routeName = route.name;
|
|
101
|
-
const sanitizedTokens = sanitizeTokens(route.tokens);
|
|
102
|
-
return ` '${routeName}': {
|
|
158
|
+
/**
|
|
159
|
+
* Generate a single runtime registry entry for a route
|
|
160
|
+
* (methods, pattern, tokens, and a typed placeholder).
|
|
161
|
+
*/
|
|
162
|
+
generateRuntimeRegistryEntry(route) {
|
|
163
|
+
const routeName = route.name;
|
|
164
|
+
const sanitizedTokens = this.#sanitizeTokens(route.tokens);
|
|
165
|
+
return ` '${routeName}': {
|
|
103
166
|
methods: ${JSON.stringify(route.methods)},
|
|
104
167
|
pattern: '${route.pattern}',
|
|
105
168
|
tokens: ${JSON.stringify(sanitizedTokens)},
|
|
106
169
|
types: placeholder as Registry['${routeName}']['types'],
|
|
107
170
|
}`;
|
|
108
|
-
}
|
|
109
|
-
function wrapResponseType(responseType) {
|
|
110
|
-
if (responseType === "unknown" || responseType === "{}") return responseType;
|
|
111
|
-
if (responseType.startsWith("ReturnType<")) {
|
|
112
|
-
return `ExtractResponse<Awaited<${responseType}>>`;
|
|
113
|
-
}
|
|
114
|
-
return `ExtractResponse<${responseType}>`;
|
|
115
|
-
}
|
|
116
|
-
function determineBodyAndQueryTypes(options) {
|
|
117
|
-
const { methods, requestType } = options;
|
|
118
|
-
const primaryMethod = methods[0];
|
|
119
|
-
const isGetLike = primaryMethod === "GET" || primaryMethod === "HEAD";
|
|
120
|
-
const hasValidator = requestType !== "{}";
|
|
121
|
-
if (!hasValidator) {
|
|
122
|
-
return { bodyType: "{}", queryType: "{}" };
|
|
123
|
-
}
|
|
124
|
-
if (isGetLike) {
|
|
125
|
-
return { bodyType: "{}", queryType: `ExtractQueryForGet<${requestType}>` };
|
|
126
171
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
172
|
+
/**
|
|
173
|
+
* Generate a single type-level registry entry for a route
|
|
174
|
+
* (body, query, params, response, and error response types).
|
|
175
|
+
*/
|
|
176
|
+
generateTypesRegistryEntry(route) {
|
|
177
|
+
const requestType = this.#normalizeImportPaths(route.request?.type || "{}");
|
|
178
|
+
const rawResponseType = route.response?.type || "unknown";
|
|
179
|
+
const responseType = this.#wrapResponseType(rawResponseType);
|
|
180
|
+
const hasValidator = requestType !== "{}";
|
|
181
|
+
let errorResponseType = this.#wrapErrorResponseType(rawResponseType);
|
|
182
|
+
if (hasValidator && this.#validationErrorType !== false) {
|
|
183
|
+
const validationError = `{ status: 422; response: ${this.#validationErrorType} }`;
|
|
184
|
+
errorResponseType = errorResponseType === "unknown" ? validationError : `${errorResponseType} | ${validationError}`;
|
|
185
|
+
}
|
|
186
|
+
const { paramsType, paramsTuple } = this.#generateRouteParams(route);
|
|
187
|
+
const routeName = route.name;
|
|
188
|
+
const { bodyType, queryType } = this.#determineBodyAndQueryTypes({
|
|
189
|
+
methods: route.methods,
|
|
190
|
+
requestType
|
|
191
|
+
});
|
|
192
|
+
return ` '${routeName}': {
|
|
142
193
|
methods: ${JSON.stringify(route.methods)}
|
|
143
194
|
pattern: '${route.pattern}'
|
|
144
195
|
types: {
|
|
@@ -147,12 +198,17 @@ function generateTypesRegistryEntry(route) {
|
|
|
147
198
|
params: ${paramsType ? `{ ${paramsType} }` : "{}"}
|
|
148
199
|
query: ${queryType}
|
|
149
200
|
response: ${responseType}
|
|
201
|
+
errorResponse: ${errorResponseType}
|
|
150
202
|
}
|
|
151
203
|
}`;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Generate the full runtime registry file (`index.ts`)
|
|
207
|
+
* with route definitions and module augmentation.
|
|
208
|
+
*/
|
|
209
|
+
generateRuntimeContent(routes) {
|
|
210
|
+
const registryEntries = routes.map((route) => this.generateRuntimeRegistryEntry(route)).join(",\n");
|
|
211
|
+
return `/* eslint-disable prettier/prettier */
|
|
156
212
|
import type { AdonisEndpoint } from '@tuyau/core/types'
|
|
157
213
|
import type { Registry } from './schema.d.ts'
|
|
158
214
|
import type { ApiDefinition } from './tree.d.ts'
|
|
@@ -177,25 +233,28 @@ declare module '@tuyau/core/types' {
|
|
|
177
233
|
}
|
|
178
234
|
}
|
|
179
235
|
`;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Generate the types-only registry file (`schema.d.ts`)
|
|
239
|
+
* with request/response type definitions for each route.
|
|
240
|
+
*/
|
|
241
|
+
generateTypesContent(routes) {
|
|
242
|
+
const registryEntries = routes.map((route) => this.generateTypesRegistryEntry(route)).join("\n");
|
|
243
|
+
const useDefaultValidationType = this.#validationErrorType === DEFAULT_VALIDATION_ERROR_TYPE;
|
|
244
|
+
const coreImports = [
|
|
245
|
+
"ExtractBody",
|
|
246
|
+
"ExtractErrorResponse",
|
|
247
|
+
"ExtractQuery",
|
|
248
|
+
"ExtractQueryForGet",
|
|
249
|
+
"ExtractResponse"
|
|
250
|
+
];
|
|
251
|
+
const vineImports = ["InferInput"];
|
|
252
|
+
if (useDefaultValidationType) vineImports.push("SimpleError");
|
|
253
|
+
return `/* eslint-disable prettier/prettier */
|
|
195
254
|
/// <reference path="../manifest.d.ts" />
|
|
196
255
|
|
|
197
|
-
import type {
|
|
198
|
-
import type {
|
|
256
|
+
import type { ${coreImports.join(", ")} } from '@tuyau/core/types'
|
|
257
|
+
import type { ${vineImports.join(", ")} } from '@vinejs/vine/types'
|
|
199
258
|
|
|
200
259
|
export type ParamValue = string | number | bigint | boolean
|
|
201
260
|
|
|
@@ -203,35 +262,71 @@ export interface Registry {
|
|
|
203
262
|
${registryEntries}
|
|
204
263
|
}
|
|
205
264
|
`;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Generate the tree types file (`tree.d.ts`) with a nested
|
|
268
|
+
* interface mirroring the dot-separated route name hierarchy.
|
|
269
|
+
*/
|
|
270
|
+
generateTreeContent(routes) {
|
|
271
|
+
const tree = this.#buildTreeStructure(routes);
|
|
272
|
+
const treeInterface = this.#generateTreeInterface(tree);
|
|
273
|
+
return `/* eslint-disable prettier/prettier */
|
|
274
|
+
import type { routes } from './index.ts'
|
|
275
|
+
|
|
276
|
+
export interface ApiDefinition {
|
|
277
|
+
${treeInterface}
|
|
206
278
|
}
|
|
279
|
+
`;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Write a file to disk, creating parent directories if needed.
|
|
283
|
+
*/
|
|
284
|
+
async #writeFile(filePath, content) {
|
|
285
|
+
const dir = dirname(filePath);
|
|
286
|
+
await mkdir(dir, { recursive: true });
|
|
287
|
+
await writeFile(filePath, content);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Generate all three registry files at once.
|
|
291
|
+
* Returns the content strings without writing to disk.
|
|
292
|
+
*/
|
|
293
|
+
generate(routes) {
|
|
294
|
+
return {
|
|
295
|
+
runtime: this.generateRuntimeContent(routes),
|
|
296
|
+
types: this.generateTypesContent(routes),
|
|
297
|
+
tree: this.generateTreeContent(routes)
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Generate and write all registry files (index.ts, schema.d.ts, tree.d.ts)
|
|
302
|
+
* to the given output directory.
|
|
303
|
+
*/
|
|
304
|
+
async writeOutput(options) {
|
|
305
|
+
const result = this.generate(options.routes);
|
|
306
|
+
const dir = options.outputDir.replace(/\/$/, "");
|
|
307
|
+
await Promise.all([
|
|
308
|
+
this.#writeFile(`${dir}/index.ts`, result.runtime),
|
|
309
|
+
this.#writeFile(`${dir}/schema.d.ts`, result.types),
|
|
310
|
+
this.#writeFile(`${dir}/tree.d.ts`, result.tree)
|
|
311
|
+
]);
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// src/backend/generate_registry.ts
|
|
207
317
|
function generateRegistry(options) {
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
...options
|
|
211
|
-
};
|
|
318
|
+
const generator = new RegistryGenerator(options);
|
|
319
|
+
const outputDir = options?.output ?? "./.adonisjs/client/registry";
|
|
212
320
|
return {
|
|
213
321
|
async run(_, hooks) {
|
|
214
322
|
hooks.add("routesScanning", (_2, routesScanner) => {
|
|
215
|
-
routesScanner.filter((route) =>
|
|
216
|
-
return filterRoute(route, config.routes);
|
|
217
|
-
});
|
|
323
|
+
routesScanner.filter((route) => generator.filterRoute(route));
|
|
218
324
|
});
|
|
219
325
|
hooks.add("routesScanned", async (devServer, routesScanner) => {
|
|
220
326
|
const startTime = process.hrtime();
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const treeContent = generateTreeContent(scannedRoutes);
|
|
225
|
-
const registryDir = config.output.replace(/\/$/, "");
|
|
226
|
-
const runtimePath = `${registryDir}/index.ts`;
|
|
227
|
-
const typesPath = `${registryDir}/schema.d.ts`;
|
|
228
|
-
const treePath = `${registryDir}/tree.d.ts`;
|
|
229
|
-
await Promise.all([
|
|
230
|
-
writeOutputFile(runtimePath, runtimeContent),
|
|
231
|
-
writeOutputFile(typesPath, typesContent),
|
|
232
|
-
writeOutputFile(treePath, treeContent)
|
|
233
|
-
]);
|
|
234
|
-
devServer.ui.logger.info(`tuyau: created api client registry (${registryDir})`, {
|
|
327
|
+
const routes = routesScanner.getScannedRoutes();
|
|
328
|
+
await generator.writeOutput({ outputDir, routes });
|
|
329
|
+
devServer.ui.logger.info(`tuyau: created api client registry (${outputDir})`, {
|
|
235
330
|
startTime
|
|
236
331
|
});
|
|
237
332
|
});
|
package/build/client/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { UrlFor } from '@adonisjs/http-server/client/url_builder';
|
|
2
|
-
import { TuyauRegistry, TuyauConfiguration, AdonisEndpoint, InferRoutes, TransformApiDefinition, InferTree, RegistryGroupedByMethod, PatternsByMethod, RequestArgs, EndpointByMethodPattern, StrKeys, Method } from '
|
|
3
|
-
|
|
2
|
+
import { T as TuyauRegistry, a as TuyauConfiguration, A as AdonisEndpoint, I as InferRoutes, b as TransformApiDefinition, c as InferTree, R as RegistryGroupedByMethod, P as PatternsByMethod, d as RequestArgs, E as EndpointByMethodPattern, e as TuyauPromise, f as ErrorResponseOf, S as StrKeys, M as Method, C as CurrentRouteOptions } from '../index-CzGwWv_7.js';
|
|
3
|
+
export { g as TuyauError, h as TuyauHTTPError, i as TuyauNetworkError } from '../index-CzGwWv_7.js';
|
|
4
|
+
import 'ky';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Main client class for making HTTP requests to AdonisJS endpoints
|
|
@@ -20,31 +21,31 @@ declare class Tuyau<Reg extends TuyauRegistry, Routes extends Record<string, Ado
|
|
|
20
21
|
/**
|
|
21
22
|
* Makes a GET request to the specified pattern
|
|
22
23
|
*/
|
|
23
|
-
get<P extends PatternsByMethod<Routes, 'GET'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'GET', P>>):
|
|
24
|
+
get<P extends PatternsByMethod<Routes, 'GET'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'GET', P>>): TuyauPromise<EndpointByMethodPattern<Routes, 'GET', P>['types']['response'], ErrorResponseOf<EndpointByMethodPattern<Routes, 'GET', P>>>;
|
|
24
25
|
/**
|
|
25
26
|
* Makes a POST request to the specified pattern
|
|
26
27
|
*/
|
|
27
|
-
post<P extends PatternsByMethod<Routes, 'POST'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'POST', P>>):
|
|
28
|
+
post<P extends PatternsByMethod<Routes, 'POST'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'POST', P>>): TuyauPromise<EndpointByMethodPattern<Routes, 'POST', P>['types']['response'], ErrorResponseOf<EndpointByMethodPattern<Routes, 'POST', P>>>;
|
|
28
29
|
/**
|
|
29
30
|
* Makes a PUT request to the specified pattern
|
|
30
31
|
*/
|
|
31
|
-
put<P extends PatternsByMethod<Routes, 'PUT'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'PUT', P>>):
|
|
32
|
+
put<P extends PatternsByMethod<Routes, 'PUT'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'PUT', P>>): TuyauPromise<EndpointByMethodPattern<Routes, 'PUT', P>['types']['response'], ErrorResponseOf<EndpointByMethodPattern<Routes, 'PUT', P>>>;
|
|
32
33
|
/**
|
|
33
34
|
* Makes a PATCH request to the specified pattern
|
|
34
35
|
*/
|
|
35
|
-
patch<P extends PatternsByMethod<Routes, 'PATCH'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'PATCH', P>>):
|
|
36
|
+
patch<P extends PatternsByMethod<Routes, 'PATCH'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'PATCH', P>>): TuyauPromise<EndpointByMethodPattern<Routes, 'PATCH', P>['types']['response'], ErrorResponseOf<EndpointByMethodPattern<Routes, 'PATCH', P>>>;
|
|
36
37
|
/**
|
|
37
38
|
* Makes a DELETE request to the specified pattern
|
|
38
39
|
*/
|
|
39
|
-
delete<P extends PatternsByMethod<Routes, 'DELETE'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'DELETE', P>>):
|
|
40
|
+
delete<P extends PatternsByMethod<Routes, 'DELETE'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'DELETE', P>>): TuyauPromise<EndpointByMethodPattern<Routes, 'DELETE', P>['types']['response'], ErrorResponseOf<EndpointByMethodPattern<Routes, 'DELETE', P>>>;
|
|
40
41
|
/**
|
|
41
42
|
* Makes a HEAD request to the specified pattern
|
|
42
43
|
*/
|
|
43
|
-
head<P extends PatternsByMethod<Routes, 'HEAD'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'HEAD', P>>):
|
|
44
|
+
head<P extends PatternsByMethod<Routes, 'HEAD'>>(pattern: P, args: RequestArgs<EndpointByMethodPattern<Routes, 'HEAD', P>>): TuyauPromise<EndpointByMethodPattern<Routes, 'HEAD', P>['types']['response'], ErrorResponseOf<EndpointByMethodPattern<Routes, 'HEAD', P>>>;
|
|
44
45
|
/**
|
|
45
46
|
* Makes a request to a named endpoint
|
|
46
47
|
*/
|
|
47
|
-
request<Name extends StrKeys<Routes>>(name: Name, args: RequestArgs<Routes[Name]>):
|
|
48
|
+
request<Name extends StrKeys<Routes>>(name: Name, args: RequestArgs<Routes[Name]>): TuyauPromise<Routes[Name]['types']['response'], ErrorResponseOf<Routes[Name]>>;
|
|
48
49
|
/**
|
|
49
50
|
* Gets route information by name including URL and HTTP method
|
|
50
51
|
*/
|
|
@@ -52,29 +53,26 @@ declare class Tuyau<Reg extends TuyauRegistry, Routes extends Record<string, Ado
|
|
|
52
53
|
url: string;
|
|
53
54
|
methods: Method[];
|
|
54
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* Checks if a route name exists in the registry.
|
|
58
|
+
*/
|
|
59
|
+
has(routeName: StrKeys<Routes>): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Determines the current route based on `window.location`.
|
|
62
|
+
*
|
|
63
|
+
* - **No arguments** — returns the current route name, or `undefined`
|
|
64
|
+
* if no route matches (or running server-side).
|
|
65
|
+
* - **With a route name** — returns `true` if the current URL matches
|
|
66
|
+
* that route. Supports `*` wildcards.
|
|
67
|
+
* - **With options** — additionally checks that the current URL params
|
|
68
|
+
* and/or query match the provided values.
|
|
69
|
+
*/
|
|
70
|
+
current(): StrKeys<Routes> | undefined;
|
|
71
|
+
current(routeName: StrKeys<Routes> | (string & {}), options?: CurrentRouteOptions): boolean;
|
|
55
72
|
}
|
|
56
73
|
/**
|
|
57
74
|
* Factory function to create a new Tuyau client instance
|
|
58
75
|
*/
|
|
59
76
|
declare function createTuyau<Reg extends TuyauRegistry>(config: TuyauConfiguration<Reg>): Tuyau<Reg, InferRoutes<Reg>>;
|
|
60
77
|
|
|
61
|
-
|
|
62
|
-
declare class TuyauHTTPError extends Error {
|
|
63
|
-
status: number | undefined;
|
|
64
|
-
rawResponse: KyResponse | undefined;
|
|
65
|
-
rawRequest: KyRequest | undefined;
|
|
66
|
-
response: any;
|
|
67
|
-
constructor(kyError: HTTPError, response: any);
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Network error that occurs when the server is unreachable or the client is offline
|
|
71
|
-
*/
|
|
72
|
-
declare class TuyauNetworkError extends Error {
|
|
73
|
-
cause: Error;
|
|
74
|
-
constructor(cause: Error, request?: {
|
|
75
|
-
url: string;
|
|
76
|
-
method: string;
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export { Tuyau, TuyauHTTPError, TuyauNetworkError, createTuyau, parseResponse };
|
|
78
|
+
export { Tuyau, TuyauPromise, createTuyau };
|