hono-takibi 0.9.78 → 0.9.80
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 +96 -61
- package/dist/cli/index.js +26 -2
- package/dist/config/index.d.ts +24 -0
- package/dist/config/index.js +161 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +6 -0
- package/dist/core/rpc/index.d.ts +35 -0
- package/dist/core/rpc/index.js +21 -233
- package/dist/core/svelte-query/index.d.ts +69 -0
- package/dist/core/svelte-query/index.js +77 -0
- package/dist/core/swr/index.d.ts +46 -0
- package/dist/core/swr/index.js +323 -0
- package/dist/core/tanstack-query/index.d.ts +69 -0
- package/dist/core/tanstack-query/index.js +74 -0
- package/dist/core/vue-query/index.d.ts +69 -0
- package/dist/core/vue-query/index.js +78 -0
- package/dist/generator/zod-openapi-hono/app/index.js +1 -1
- package/dist/generator/zod-to-openapi/z/object.js +2 -2
- package/dist/helper/hono-rpc.d.ts +394 -0
- package/dist/helper/hono-rpc.js +531 -0
- package/dist/helper/index.d.ts +3 -0
- package/dist/helper/index.js +2 -0
- package/dist/helper/openapi.js +4 -4
- package/dist/helper/query.d.ts +58 -0
- package/dist/helper/query.js +525 -0
- package/dist/utils/index.d.ts +35 -19
- package/dist/utils/index.js +47 -21
- package/dist/vite-plugin/index.js +99 -1
- package/package.json +18 -5
package/dist/core/rpc/index.js
CHANGED
|
@@ -18,240 +18,49 @@
|
|
|
18
18
|
* @link https://github.com/honojs/hono/blob/main/src/client/types.ts#L46-L76
|
|
19
19
|
*/
|
|
20
20
|
import path from 'node:path';
|
|
21
|
-
import { core } from '../../helper/index.js';
|
|
21
|
+
import { buildInferRequestType, buildOperationDocs, core, createOperationDeps, formatPath, HTTP_METHODS, isOpenAPIPaths, isOperationLike, operationHasArgs, parsePathItem, resolveSplitOutDir, } from '../../helper/index.js';
|
|
22
22
|
import { isRecord, methodPath } from '../../utils/index.js';
|
|
23
|
-
/* ───────────────────────────────
|
|
24
|
-
const
|
|
25
|
-
if (!isRecord(v))
|
|
26
|
-
return false;
|
|
27
|
-
for (const k in v) {
|
|
28
|
-
if (!isRecord(v[k]))
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
return true;
|
|
32
|
-
};
|
|
33
|
-
/* ─────────────────────────────── Formatters ─────────────────────────────── */
|
|
34
|
-
const esc = (s) => s.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
35
|
-
/** Check if a string is a valid JavaScript identifier */
|
|
36
|
-
const isValidIdent = (s) => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(s);
|
|
37
|
-
/**
|
|
38
|
-
* Format path for Hono RPC access (both type and runtime).
|
|
39
|
-
* Hono hc client uses PathToChain which splits paths by '/'.
|
|
40
|
-
*
|
|
41
|
-
* Rules:
|
|
42
|
-
* - '/' -> '.index'
|
|
43
|
-
* - Valid identifiers use dot notation: '/users' -> '.users'
|
|
44
|
-
* - Invalid identifiers use bracket notation: '/:id' -> "[':id']"
|
|
45
|
-
* - Path params converted: '/files/{fileId}' -> ".files[':fileId']"
|
|
46
|
-
*
|
|
47
|
-
* For InferRequestType, when bracket notation exists in the path,
|
|
48
|
-
* we need to wrap `typeof client.<prefix>` in parentheses and use
|
|
49
|
-
* all bracket notation after:
|
|
50
|
-
* - `/users/{id}` -> type: `(typeof client.users)[':id']['$get']`
|
|
51
|
-
* - `/users/{id}/avatar` -> type: `(typeof client.users)[':id']['avatar']['$post']`
|
|
52
|
-
*/
|
|
53
|
-
const formatPath = (p) => {
|
|
54
|
-
if (p === '/') {
|
|
55
|
-
return {
|
|
56
|
-
runtimePath: '.index',
|
|
57
|
-
typeofPrefix: '.index',
|
|
58
|
-
bracketSuffix: '',
|
|
59
|
-
hasBracket: false,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
const segs = p.replace(/^\/+/, '').split('/').filter(Boolean);
|
|
63
|
-
// Convert {param} to :param (handles both full segments like {id} and partial like {Sid}.json)
|
|
64
|
-
const honoSegs = segs.map((seg) => seg.replace(/\{([^}]+)\}/g, ':$1'));
|
|
65
|
-
// Find the first segment that needs bracket notation
|
|
66
|
-
const firstBracketIdx = honoSegs.findIndex((seg) => !isValidIdent(seg));
|
|
67
|
-
const hasBracket = firstBracketIdx !== -1;
|
|
68
|
-
// Runtime path: mixed notation (current behavior)
|
|
69
|
-
const runtimeParts = honoSegs.map((seg) => (isValidIdent(seg) ? `.${seg}` : `['${esc(seg)}']`));
|
|
70
|
-
const runtimePath = runtimeParts.join('');
|
|
71
|
-
// For type expression: split at first bracket
|
|
72
|
-
const typeofPrefix = hasBracket
|
|
73
|
-
? honoSegs
|
|
74
|
-
.slice(0, firstBracketIdx)
|
|
75
|
-
.map((seg) => `.${seg}`)
|
|
76
|
-
.join('')
|
|
77
|
-
: runtimePath;
|
|
78
|
-
const bracketSuffix = hasBracket
|
|
79
|
-
? honoSegs
|
|
80
|
-
.slice(firstBracketIdx)
|
|
81
|
-
.map((seg) => `['${esc(seg)}']`)
|
|
82
|
-
.join('')
|
|
83
|
-
: '';
|
|
84
|
-
return { runtimePath, typeofPrefix, bracketSuffix, hasBracket };
|
|
85
|
-
};
|
|
86
|
-
const isRefObject = (v) => isRecord(v) && typeof v.$ref === 'string';
|
|
87
|
-
const isParameterObject = (v) => {
|
|
88
|
-
if (!isRecord(v))
|
|
89
|
-
return false;
|
|
90
|
-
if (typeof v.name !== 'string')
|
|
91
|
-
return false;
|
|
92
|
-
const pos = v.in;
|
|
93
|
-
return pos === 'path' || pos === 'query' || pos === 'header' || pos === 'cookie';
|
|
94
|
-
};
|
|
95
|
-
const refParamName = (refLike) => {
|
|
96
|
-
const ref = typeof refLike === 'string' ? refLike : isRefObject(refLike) ? refLike.$ref : undefined;
|
|
97
|
-
const m = ref?.match(/^#\/components\/parameters\/(.+)$/);
|
|
98
|
-
return m ? m[1] : undefined;
|
|
99
|
-
};
|
|
100
|
-
const createResolveParameter = (componentsParameters) => (p) => {
|
|
101
|
-
if (isParameterObject(p))
|
|
102
|
-
return p;
|
|
103
|
-
const name = refParamName(p);
|
|
104
|
-
const cand = name ? componentsParameters[name] : undefined;
|
|
105
|
-
return isParameterObject(cand) ? cand : undefined;
|
|
106
|
-
};
|
|
107
|
-
const createToParameterLikes = (resolveParam) => (arr) => Array.isArray(arr) ? arr.map((x) => resolveParam(x)).filter((param) => param !== undefined) : [];
|
|
108
|
-
const isOperationLike = (v) => isRecord(v) && 'responses' in v;
|
|
109
|
-
const HTTP_METHODS = [
|
|
110
|
-
'get',
|
|
111
|
-
'put',
|
|
112
|
-
'post',
|
|
113
|
-
'delete',
|
|
114
|
-
'options',
|
|
115
|
-
'head',
|
|
116
|
-
'patch',
|
|
117
|
-
'trace',
|
|
118
|
-
];
|
|
119
|
-
const hasSchemaProp = (v) => isRecord(v) && 'schema' in v;
|
|
120
|
-
/** Extract requestBody name from $ref like "#/components/requestBodies/CreateProduct" */
|
|
121
|
-
const refRequestBodyName = (refLike) => {
|
|
122
|
-
const ref = typeof refLike === 'string' ? refLike : isRefObject(refLike) ? refLike.$ref : undefined;
|
|
123
|
-
const m = ref?.match(/^#\/components\/requestBodies\/(.+)$/);
|
|
124
|
-
return m ? m[1] : undefined;
|
|
125
|
-
};
|
|
126
|
-
/**
|
|
127
|
-
* Collect all body infos from content to handle multiple content-types.
|
|
128
|
-
* Groups by body key ('form' or 'json') with union types.
|
|
129
|
-
*/
|
|
130
|
-
const pickAllBodyInfoFromContent = (content) => {
|
|
131
|
-
if (!isRecord(content))
|
|
132
|
-
return undefined;
|
|
133
|
-
const formContentTypes = ['multipart/form-data', 'application/x-www-form-urlencoded'];
|
|
134
|
-
const isFormContentType = (ct) => formContentTypes.includes(ct.split(';')[0].trim());
|
|
135
|
-
const validEntries = Object.entries(content).filter(([_, mediaObj]) => isRecord(mediaObj) && hasSchemaProp(mediaObj) && isRecord(mediaObj.schema));
|
|
136
|
-
const formInfos = validEntries
|
|
137
|
-
.filter(([ct]) => isFormContentType(ct))
|
|
138
|
-
.map(([ct]) => ({ contentType: ct }));
|
|
139
|
-
const jsonInfos = validEntries
|
|
140
|
-
.filter(([ct]) => !isFormContentType(ct))
|
|
141
|
-
.map(([ct]) => ({ contentType: ct }));
|
|
142
|
-
if (formInfos.length === 0 && jsonInfos.length === 0)
|
|
143
|
-
return undefined;
|
|
144
|
-
return { form: formInfos, json: jsonInfos };
|
|
145
|
-
};
|
|
146
|
-
const createPickAllBodyInfo = (componentsRequestBodies) => (op) => {
|
|
147
|
-
const rb = op.requestBody;
|
|
148
|
-
if (!isRecord(rb))
|
|
149
|
-
return undefined;
|
|
150
|
-
// Handle $ref to components/requestBodies
|
|
151
|
-
const refName = refRequestBodyName(rb);
|
|
152
|
-
if (refName) {
|
|
153
|
-
const resolved = componentsRequestBodies[refName];
|
|
154
|
-
if (isRecord(resolved) && isRecord(resolved.content)) {
|
|
155
|
-
return pickAllBodyInfoFromContent(resolved.content);
|
|
156
|
-
}
|
|
157
|
-
// If $ref exists but can't resolve content, still consider it has a body
|
|
158
|
-
if (resolved !== undefined)
|
|
159
|
-
return { form: [], json: [{ contentType: 'application/json' }] };
|
|
160
|
-
return undefined;
|
|
161
|
-
}
|
|
162
|
-
// Direct inline requestBody
|
|
163
|
-
return pickAllBodyInfoFromContent(rb.content);
|
|
164
|
-
};
|
|
165
|
-
const generateOperationCode = (pathStr, method, item, deps) => {
|
|
23
|
+
/* ─────────────────────────────── Single-operation generator ─────────────────────────────── */
|
|
24
|
+
const makeOperationCode = (pathStr, method, item, deps) => {
|
|
166
25
|
const op = item[method];
|
|
167
26
|
if (!isOperationLike(op))
|
|
168
27
|
return null;
|
|
169
28
|
const funcName = methodPath(method, pathStr);
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
const allParams = [...pathLevelParams, ...opParams];
|
|
174
|
-
const pathParams = allParams.filter((p) => p.in === 'path');
|
|
175
|
-
const queryParams = allParams.filter((p) => p.in === 'query');
|
|
176
|
-
const headerParams = allParams.filter((p) => p.in === 'header');
|
|
177
|
-
const cookieParams = allParams.filter((p) => p.in === 'cookie');
|
|
178
|
-
const allBodyInfo = deps.pickAllBodyInfo(op);
|
|
179
|
-
const hasBody = allBodyInfo !== undefined && (allBodyInfo.form.length > 0 || allBodyInfo.json.length > 0);
|
|
180
|
-
const hasArgs = pathParams.length > 0 ||
|
|
181
|
-
queryParams.length > 0 ||
|
|
182
|
-
headerParams.length > 0 ||
|
|
183
|
-
cookieParams.length > 0 ||
|
|
184
|
-
hasBody;
|
|
185
|
-
// For runtime calls, always use dot notation for method access
|
|
186
|
-
const methodAccess = `.$${method}`;
|
|
187
|
-
// For InferRequestType, handle bracket notation properly:
|
|
188
|
-
// - No bracket: `typeof client.users.$get`
|
|
189
|
-
// - With bracket: `(typeof client.users)[':id']['avatar']['$post']`
|
|
190
|
-
const inferType = hasBracket
|
|
191
|
-
? `InferRequestType<(typeof ${deps.client}${typeofPrefix})${bracketSuffix}['$${method}']>`
|
|
192
|
-
: `InferRequestType<typeof ${deps.client}${runtimePath}.$${method}>`;
|
|
29
|
+
const pathResult = formatPath(pathStr);
|
|
30
|
+
const hasArgs = operationHasArgs(item, op, deps);
|
|
31
|
+
const inferType = buildInferRequestType(deps.client, pathResult, method);
|
|
193
32
|
const argSig = hasArgs
|
|
194
33
|
? `args:${inferType},options?:ClientRequestOptions`
|
|
195
34
|
: 'options?:ClientRequestOptions';
|
|
196
35
|
const call = hasArgs
|
|
197
|
-
? `${deps.client}${runtimePath}
|
|
198
|
-
: `${deps.client}${runtimePath}
|
|
36
|
+
? `${deps.client}${pathResult.runtimePath}.$${method}(args,options)`
|
|
37
|
+
: `${deps.client}${pathResult.runtimePath}.$${method}(undefined,options)`;
|
|
199
38
|
const summary = typeof op.summary === 'string' ? op.summary : '';
|
|
200
39
|
const description = typeof op.description === 'string' ? op.description : '';
|
|
201
|
-
|
|
202
|
-
const formatJsDocLines = (text) => text
|
|
203
|
-
.trimEnd()
|
|
204
|
-
.split('\n')
|
|
205
|
-
.map((line) => ` * ${line}`);
|
|
206
|
-
// Escape /* in path to avoid oxfmt regex parsing issue (/* looks like /regex/)
|
|
207
|
-
const safePathStr = pathStr.replace(/\/\*/g, '/[*]');
|
|
208
|
-
const docs = [
|
|
209
|
-
'/**',
|
|
210
|
-
` * ${method.toUpperCase()} ${safePathStr}`,
|
|
211
|
-
...(summary ? [' *', ...formatJsDocLines(summary)] : []),
|
|
212
|
-
...(description ? [' *', ...formatJsDocLines(description)] : []),
|
|
213
|
-
' */',
|
|
214
|
-
].join('\n');
|
|
40
|
+
const docs = buildOperationDocs(method, pathStr, summary || undefined, description || undefined);
|
|
215
41
|
const func = `export async function ${funcName}(${argSig}){return await ${call}}`;
|
|
216
42
|
return { code: `${docs}\n${func}`, hasArgs };
|
|
217
43
|
};
|
|
218
44
|
/**
|
|
219
45
|
* Builds operation codes from OpenAPI paths.
|
|
220
|
-
*
|
|
221
|
-
* Iterates through all HTTP methods for each path and generates
|
|
222
|
-
* typed RPC client wrapper functions.
|
|
223
|
-
*
|
|
224
|
-
* @param paths - OpenAPI paths object
|
|
225
|
-
* @param deps - Dependencies including client name and parameter utilities
|
|
226
|
-
* @returns Array of operation codes with function names and generated code
|
|
227
46
|
*/
|
|
228
47
|
const makeOperationCodes = (paths, deps) => Object.entries(paths)
|
|
229
48
|
.filter((entry) => isRecord(entry[1]))
|
|
230
49
|
.flatMap(([p, rawItem]) => {
|
|
231
|
-
const pathItem =
|
|
232
|
-
parameters: rawItem.parameters,
|
|
233
|
-
get: isOperationLike(rawItem.get) ? rawItem.get : undefined,
|
|
234
|
-
put: isOperationLike(rawItem.put) ? rawItem.put : undefined,
|
|
235
|
-
post: isOperationLike(rawItem.post) ? rawItem.post : undefined,
|
|
236
|
-
delete: isOperationLike(rawItem.delete) ? rawItem.delete : undefined,
|
|
237
|
-
options: isOperationLike(rawItem.options) ? rawItem.options : undefined,
|
|
238
|
-
head: isOperationLike(rawItem.head) ? rawItem.head : undefined,
|
|
239
|
-
patch: isOperationLike(rawItem.patch) ? rawItem.patch : undefined,
|
|
240
|
-
trace: isOperationLike(rawItem.trace) ? rawItem.trace : undefined,
|
|
241
|
-
};
|
|
50
|
+
const pathItem = parsePathItem(rawItem);
|
|
242
51
|
return HTTP_METHODS.map((method) => {
|
|
243
|
-
const result =
|
|
52
|
+
const result = makeOperationCode(p, method, pathItem, deps);
|
|
244
53
|
return result
|
|
245
54
|
? { funcName: methodPath(method, p), code: result.code, hasArgs: result.hasArgs }
|
|
246
55
|
: null;
|
|
247
56
|
}).filter((item) => item !== null);
|
|
248
57
|
});
|
|
249
|
-
/* ───────────────────────────────
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return {
|
|
58
|
+
/* ─────────────────────────────── Header ─────────────────────────────── */
|
|
59
|
+
const makeHeader = (importPath, needsInferRequestType, clientName) => {
|
|
60
|
+
const typeImports = needsInferRequestType
|
|
61
|
+
? 'InferRequestType,ClientRequestOptions'
|
|
62
|
+
: 'ClientRequestOptions';
|
|
63
|
+
return `import type{${typeImports}}from'hono/client'\nimport{${clientName}}from'${importPath}'\n\n`;
|
|
255
64
|
};
|
|
256
65
|
/* ─────────────────────────────── Entry ─────────────────────────────── */
|
|
257
66
|
/**
|
|
@@ -275,6 +84,7 @@ const resolveSplitOutDir = (output) => {
|
|
|
275
84
|
* @param output - Output file path or directory
|
|
276
85
|
* @param importPath - Import path for the Hono client
|
|
277
86
|
* @param split - Whether to split into multiple files (one per operation)
|
|
87
|
+
* @param clientName - Name of the client export (default: 'client')
|
|
278
88
|
* @returns Promise resolving to success message or error
|
|
279
89
|
*
|
|
280
90
|
* @example
|
|
@@ -288,42 +98,20 @@ const resolveSplitOutDir = (output) => {
|
|
|
288
98
|
* // Generates: src/rpc/getUsers.ts, src/rpc/postUsers.ts, src/rpc/index.ts
|
|
289
99
|
* ```
|
|
290
100
|
*/
|
|
291
|
-
/**
|
|
292
|
-
* Generates the import header for RPC client files.
|
|
293
|
-
*
|
|
294
|
-
* @param importPath - The import path for the Hono client
|
|
295
|
-
* @param needsInferRequestType - Whether InferRequestType import is needed
|
|
296
|
-
* @param clientName - The name of the client to import (default: 'client')
|
|
297
|
-
* @returns Import header string
|
|
298
|
-
*/
|
|
299
|
-
const makeHeader = (importPath, needsInferRequestType, clientName) => {
|
|
300
|
-
const typeImports = needsInferRequestType
|
|
301
|
-
? 'InferRequestType,ClientRequestOptions'
|
|
302
|
-
: 'ClientRequestOptions';
|
|
303
|
-
return `import type{${typeImports}}from'hono/client'\nimport{${clientName}}from'${importPath}'\n\n`;
|
|
304
|
-
};
|
|
305
101
|
export async function rpc(openAPI, output, importPath, split, clientName = 'client') {
|
|
306
|
-
const client = clientName;
|
|
307
102
|
const pathsMaybe = openAPI.paths;
|
|
308
103
|
if (!isOpenAPIPaths(pathsMaybe)) {
|
|
309
104
|
return { ok: false, error: 'Invalid OpenAPI paths' };
|
|
310
105
|
}
|
|
311
106
|
const componentsParameters = openAPI.components?.parameters ?? {};
|
|
312
107
|
const componentsRequestBodies = openAPI.components?.requestBodies ?? {};
|
|
313
|
-
const
|
|
314
|
-
const toParameterLikes = createToParameterLikes(resolveParameter);
|
|
315
|
-
const pickAllBodyInfo = createPickAllBodyInfo(componentsRequestBodies);
|
|
316
|
-
const deps = {
|
|
317
|
-
client,
|
|
318
|
-
toParameterLikes,
|
|
319
|
-
pickAllBodyInfo,
|
|
320
|
-
};
|
|
108
|
+
const deps = createOperationDeps(clientName, componentsParameters, componentsRequestBodies);
|
|
321
109
|
const operationCodes = makeOperationCodes(pathsMaybe, deps);
|
|
322
110
|
// Non-split: write single file
|
|
323
111
|
if (!split) {
|
|
324
112
|
const body = operationCodes.map(({ code }) => code).join('\n\n');
|
|
325
113
|
const needsInferRequestType = operationCodes.some(({ hasArgs }) => hasArgs);
|
|
326
|
-
const header = makeHeader(importPath, needsInferRequestType,
|
|
114
|
+
const header = makeHeader(importPath, needsInferRequestType, clientName);
|
|
327
115
|
const code = `${header}${body}${operationCodes.length ? '\n' : ''}`;
|
|
328
116
|
const coreResult = await core(code, path.dirname(output), output);
|
|
329
117
|
if (!coreResult.ok)
|
|
@@ -336,7 +124,7 @@ export async function rpc(openAPI, output, importPath, split, clientName = 'clie
|
|
|
336
124
|
const index = `${exportLines.join('\n')}\n`;
|
|
337
125
|
const allResults = await Promise.all([
|
|
338
126
|
...operationCodes.map(({ funcName, code, hasArgs }) => {
|
|
339
|
-
const header = makeHeader(importPath, hasArgs,
|
|
127
|
+
const header = makeHeader(importPath, hasArgs, clientName);
|
|
340
128
|
const fileSrc = `${header}${code}\n`;
|
|
341
129
|
const filePath = path.join(outDir, `${funcName}.ts`);
|
|
342
130
|
return core(fileSrc, path.dirname(filePath), filePath);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte Query hook generation module.
|
|
3
|
+
*
|
|
4
|
+
* Generates type-safe Svelte Query hooks from OpenAPI specifications
|
|
5
|
+
* for use with Hono's RPC client.
|
|
6
|
+
*
|
|
7
|
+
* - GET operations generate `createQuery` hooks
|
|
8
|
+
* - POST/PUT/DELETE/PATCH operations generate `createMutation` hooks
|
|
9
|
+
*
|
|
10
|
+
* ```mermaid
|
|
11
|
+
* flowchart TD
|
|
12
|
+
* A["svelteQuery(openAPI, output, importPath, split)"] --> B["Parse OpenAPI paths"]
|
|
13
|
+
* B --> C["Build hook codes"]
|
|
14
|
+
* C --> D{"split mode?"}
|
|
15
|
+
* D -->|No| E["Write single file"]
|
|
16
|
+
* D -->|Yes| F["Write per-hook files"]
|
|
17
|
+
* F --> G["Write index.ts barrel"]
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @module core/svelte-query
|
|
21
|
+
* @link https://tanstack.com/query/latest/docs/framework/svelte/overview
|
|
22
|
+
* @link https://hono.dev/docs/guides/rpc
|
|
23
|
+
*/
|
|
24
|
+
import type { OpenAPI } from '../../openapi/index.js';
|
|
25
|
+
/**
|
|
26
|
+
* Generates Svelte Query hooks from OpenAPI specification.
|
|
27
|
+
*
|
|
28
|
+
* Creates type-safe Svelte Query hooks that wrap Hono RPC client calls,
|
|
29
|
+
* with proper TypeScript types derived from OpenAPI schemas.
|
|
30
|
+
*
|
|
31
|
+
* - GET operations generate `createQuery` hooks
|
|
32
|
+
* - POST/PUT/DELETE/PATCH operations generate `createMutation` hooks
|
|
33
|
+
*
|
|
34
|
+
* ```mermaid
|
|
35
|
+
* flowchart LR
|
|
36
|
+
* subgraph "Generated Code"
|
|
37
|
+
* A["export function createGetUsers(...) { return createQuery(...) }"]
|
|
38
|
+
* end
|
|
39
|
+
* subgraph "Usage"
|
|
40
|
+
* B["const { data, error } = createGetUsers({ query: { limit: 10 } })"]
|
|
41
|
+
* end
|
|
42
|
+
* A --> B
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @param openAPI - Parsed OpenAPI specification
|
|
46
|
+
* @param output - Output file path or directory
|
|
47
|
+
* @param importPath - Import path for the Hono client
|
|
48
|
+
* @param split - Whether to split into multiple files (one per hook)
|
|
49
|
+
* @param clientName - Name of the client export (default: 'client')
|
|
50
|
+
* @returns Promise resolving to success message or error
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* // Single file output
|
|
55
|
+
* await svelteQuery(openAPI, 'src/hooks.ts', './client')
|
|
56
|
+
* // Generates: src/hooks.ts with all Svelte Query hooks
|
|
57
|
+
*
|
|
58
|
+
* // Split mode output
|
|
59
|
+
* await svelteQuery(openAPI, 'src/hooks', './client', true)
|
|
60
|
+
* // Generates: src/hooks/createGetUsers.ts, src/hooks/createPostUsers.ts, src/hooks/index.ts
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare function svelteQuery(openAPI: OpenAPI, output: string | `${string}.ts`, importPath: string, split?: boolean, clientName?: string): Promise<{
|
|
64
|
+
readonly ok: true;
|
|
65
|
+
readonly value: string;
|
|
66
|
+
} | {
|
|
67
|
+
readonly ok: false;
|
|
68
|
+
readonly error: string;
|
|
69
|
+
}>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte Query hook generation module.
|
|
3
|
+
*
|
|
4
|
+
* Generates type-safe Svelte Query hooks from OpenAPI specifications
|
|
5
|
+
* for use with Hono's RPC client.
|
|
6
|
+
*
|
|
7
|
+
* - GET operations generate `createQuery` hooks
|
|
8
|
+
* - POST/PUT/DELETE/PATCH operations generate `createMutation` hooks
|
|
9
|
+
*
|
|
10
|
+
* ```mermaid
|
|
11
|
+
* flowchart TD
|
|
12
|
+
* A["svelteQuery(openAPI, output, importPath, split)"] --> B["Parse OpenAPI paths"]
|
|
13
|
+
* B --> C["Build hook codes"]
|
|
14
|
+
* C --> D{"split mode?"}
|
|
15
|
+
* D -->|No| E["Write single file"]
|
|
16
|
+
* D -->|Yes| F["Write per-hook files"]
|
|
17
|
+
* F --> G["Write index.ts barrel"]
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @module core/svelte-query
|
|
21
|
+
* @link https://tanstack.com/query/latest/docs/framework/svelte/overview
|
|
22
|
+
* @link https://hono.dev/docs/guides/rpc
|
|
23
|
+
*/
|
|
24
|
+
import { makeQueryHooks } from '../../helper/query.js';
|
|
25
|
+
/**
|
|
26
|
+
* Generates Svelte Query hooks from OpenAPI specification.
|
|
27
|
+
*
|
|
28
|
+
* Creates type-safe Svelte Query hooks that wrap Hono RPC client calls,
|
|
29
|
+
* with proper TypeScript types derived from OpenAPI schemas.
|
|
30
|
+
*
|
|
31
|
+
* - GET operations generate `createQuery` hooks
|
|
32
|
+
* - POST/PUT/DELETE/PATCH operations generate `createMutation` hooks
|
|
33
|
+
*
|
|
34
|
+
* ```mermaid
|
|
35
|
+
* flowchart LR
|
|
36
|
+
* subgraph "Generated Code"
|
|
37
|
+
* A["export function createGetUsers(...) { return createQuery(...) }"]
|
|
38
|
+
* end
|
|
39
|
+
* subgraph "Usage"
|
|
40
|
+
* B["const { data, error } = createGetUsers({ query: { limit: 10 } })"]
|
|
41
|
+
* end
|
|
42
|
+
* A --> B
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @param openAPI - Parsed OpenAPI specification
|
|
46
|
+
* @param output - Output file path or directory
|
|
47
|
+
* @param importPath - Import path for the Hono client
|
|
48
|
+
* @param split - Whether to split into multiple files (one per hook)
|
|
49
|
+
* @param clientName - Name of the client export (default: 'client')
|
|
50
|
+
* @returns Promise resolving to success message or error
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* // Single file output
|
|
55
|
+
* await svelteQuery(openAPI, 'src/hooks.ts', './client')
|
|
56
|
+
* // Generates: src/hooks.ts with all Svelte Query hooks
|
|
57
|
+
*
|
|
58
|
+
* // Split mode output
|
|
59
|
+
* await svelteQuery(openAPI, 'src/hooks', './client', true)
|
|
60
|
+
* // Generates: src/hooks/createGetUsers.ts, src/hooks/createPostUsers.ts, src/hooks/index.ts
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export async function svelteQuery(openAPI, output, importPath, split, clientName = 'client') {
|
|
64
|
+
// Svelte Query v5+ requires thunk pattern: createQuery(() => options)
|
|
65
|
+
// @see https://tanstack.com/query/v5/docs/framework/svelte/reactivity
|
|
66
|
+
const config = {
|
|
67
|
+
packageName: '@tanstack/svelte-query',
|
|
68
|
+
frameworkName: 'Svelte Query',
|
|
69
|
+
hookPrefix: 'create',
|
|
70
|
+
queryFn: 'createQuery',
|
|
71
|
+
mutationFn: 'createMutation',
|
|
72
|
+
useThunk: true,
|
|
73
|
+
useQueryOptionsType: 'CreateQueryOptions',
|
|
74
|
+
useMutationOptionsType: 'CreateMutationOptions',
|
|
75
|
+
};
|
|
76
|
+
return makeQueryHooks(openAPI, output, importPath, config, split, clientName);
|
|
77
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { OpenAPI } from '../../openapi/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generates SWR hooks from OpenAPI specification.
|
|
4
|
+
*
|
|
5
|
+
* Creates type-safe SWR hooks that wrap Hono RPC client calls,
|
|
6
|
+
* with proper TypeScript types derived from OpenAPI schemas.
|
|
7
|
+
*
|
|
8
|
+
* - GET operations generate `useSWR` hooks
|
|
9
|
+
* - POST/PUT/DELETE/PATCH operations generate `useSWRMutation` hooks
|
|
10
|
+
*
|
|
11
|
+
* ```mermaid
|
|
12
|
+
* flowchart LR
|
|
13
|
+
* subgraph "Generated Code"
|
|
14
|
+
* A["export function useGetUsers(...) { return useSWR(...) }"]
|
|
15
|
+
* end
|
|
16
|
+
* subgraph "Usage"
|
|
17
|
+
* B["const { data, error } = useGetUsers({ query: { limit: 10 } })"]
|
|
18
|
+
* end
|
|
19
|
+
* A --> B
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @param openAPI - Parsed OpenAPI specification
|
|
23
|
+
* @param output - Output file path or directory
|
|
24
|
+
* @param importPath - Import path for the Hono client
|
|
25
|
+
* @param split - Whether to split into multiple files (one per hook)
|
|
26
|
+
* @param clientName - Name of the client export (default: 'client')
|
|
27
|
+
* @returns Promise resolving to success message or error
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* // Single file output
|
|
32
|
+
* await swr(openAPI, 'src/hooks.ts', './client')
|
|
33
|
+
* // Generates: src/hooks.ts with all SWR hooks
|
|
34
|
+
*
|
|
35
|
+
* // Split mode output
|
|
36
|
+
* await swr(openAPI, 'src/hooks', './client', true)
|
|
37
|
+
* // Generates: src/hooks/useGetUsers.ts, src/hooks/usePostUsers.ts, src/hooks/index.ts
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function swr(openAPI: OpenAPI, output: string | `${string}.ts`, importPath: string, split?: boolean, clientName?: string): Promise<{
|
|
41
|
+
readonly ok: true;
|
|
42
|
+
readonly value: string;
|
|
43
|
+
} | {
|
|
44
|
+
readonly ok: false;
|
|
45
|
+
readonly error: string;
|
|
46
|
+
}>;
|