hono-takibi 0.9.73 → 0.9.75

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.
Files changed (68) hide show
  1. package/dist/cli/index.js +18 -268
  2. package/dist/config/index.d.ts +14 -1
  3. package/dist/config/index.js +150 -114
  4. package/dist/core/route/index.js +0 -235
  5. package/dist/core/rpc/index.js +22 -1094
  6. package/dist/core/takibi/index.js +0 -499
  7. package/dist/core/type/index.js +0 -145
  8. package/dist/format/index.d.ts +24 -0
  9. package/dist/format/index.js +24 -18
  10. package/dist/fsp/index.d.ts +1 -0
  11. package/dist/fsp/index.js +4 -87
  12. package/dist/generator/zod-openapi-hono/app/index.d.ts +7 -1
  13. package/dist/generator/zod-openapi-hono/app/index.js +12 -150
  14. package/dist/generator/zod-openapi-hono/openapi/components/callbacks.d.ts +15 -0
  15. package/dist/generator/zod-openapi-hono/openapi/components/callbacks.js +15 -61
  16. package/dist/generator/zod-openapi-hono/openapi/components/examples.js +0 -151
  17. package/dist/generator/zod-openapi-hono/openapi/components/headers.d.ts +17 -0
  18. package/dist/generator/zod-openapi-hono/openapi/components/headers.js +17 -46
  19. package/dist/generator/zod-openapi-hono/openapi/components/index.js +0 -100
  20. package/dist/generator/zod-openapi-hono/openapi/components/links.d.ts +16 -0
  21. package/dist/generator/zod-openapi-hono/openapi/components/links.js +16 -34
  22. package/dist/generator/zod-openapi-hono/openapi/components/parameters.js +0 -53
  23. package/dist/generator/zod-openapi-hono/openapi/components/request-bodies.d.ts +16 -0
  24. package/dist/generator/zod-openapi-hono/openapi/components/request-bodies.js +16 -49
  25. package/dist/generator/zod-openapi-hono/openapi/components/responses.d.ts +16 -0
  26. package/dist/generator/zod-openapi-hono/openapi/components/responses.js +16 -43
  27. package/dist/generator/zod-openapi-hono/openapi/components/schemas.d.ts +17 -0
  28. package/dist/generator/zod-openapi-hono/openapi/components/schemas.js +17 -72
  29. package/dist/generator/zod-openapi-hono/openapi/components/securitySchemes.d.ts +16 -0
  30. package/dist/generator/zod-openapi-hono/openapi/components/securitySchemes.js +16 -36
  31. package/dist/generator/zod-openapi-hono/openapi/index.js +0 -460
  32. package/dist/generator/zod-openapi-hono/openapi/routes/create-route.js +0 -36
  33. package/dist/generator/zod-openapi-hono/openapi/routes/index.d.ts +3 -0
  34. package/dist/generator/zod-openapi-hono/openapi/routes/index.js +28 -78
  35. package/dist/generator/zod-to-openapi/index.js +0 -765
  36. package/dist/generator/zod-to-openapi/z/enum.d.ts +29 -0
  37. package/dist/generator/zod-to-openapi/z/enum.js +29 -27
  38. package/dist/generator/zod-to-openapi/z/index.d.ts +12 -0
  39. package/dist/generator/zod-to-openapi/z/index.js +12 -27
  40. package/dist/generator/zod-to-openapi/z/integer.d.ts +17 -1
  41. package/dist/generator/zod-to-openapi/z/integer.js +17 -142
  42. package/dist/generator/zod-to-openapi/z/number.d.ts +17 -1
  43. package/dist/generator/zod-to-openapi/z/number.js +17 -46
  44. package/dist/generator/zod-to-openapi/z/object.d.ts +21 -2
  45. package/dist/generator/zod-to-openapi/z/object.js +21 -75
  46. package/dist/generator/zod-to-openapi/z/string.js +0 -38
  47. package/dist/helper/ast.js +0 -295
  48. package/dist/helper/barell.d.ts +20 -0
  49. package/dist/helper/barell.js +20 -32
  50. package/dist/helper/code.d.ts +17 -0
  51. package/dist/helper/code.js +17 -33
  52. package/dist/helper/core.js +0 -18
  53. package/dist/helper/exports.js +0 -79
  54. package/dist/helper/index.d.ts +1 -1
  55. package/dist/helper/index.js +1 -15
  56. package/dist/helper/openapi.d.ts +174 -27
  57. package/dist/helper/openapi.js +175 -875
  58. package/dist/helper/schema.d.ts +114 -0
  59. package/dist/helper/schema.js +114 -209
  60. package/dist/helper/type.d.ts +20 -1
  61. package/dist/helper/type.js +20 -236
  62. package/dist/helper/wrap.js +0 -241
  63. package/dist/index.js +10 -2178
  64. package/dist/openapi/index.js +0 -174
  65. package/dist/utils/index.d.ts +56 -12
  66. package/dist/utils/index.js +55 -282
  67. package/dist/vite-plugin/index.js +311 -427
  68. package/package.json +10 -10
package/dist/cli/index.js CHANGED
@@ -128,18 +128,18 @@ function parseCli(args) {
128
128
  test: args.includes('--test'),
129
129
  basePath: getFlagValue(args, '--base-path') ?? '/', // default: /
130
130
  componentsOptions: {
131
- exportSchemas: args.includes('--export-schemas') ?? false,
132
- exportSchemasTypes: args.includes('--export-schemas-types') ?? false,
133
- exportParameters: args.includes('--export-parameters') ?? false,
134
- exportParametersTypes: args.includes('--export-parameters-types') ?? false,
135
- exportSecuritySchemes: args.includes('--export-security-schemes') ?? false,
136
- exportRequestBodies: args.includes('--export-request-bodies') ?? false,
137
- exportResponses: args.includes('--export-responses') ?? false,
138
- exportHeaders: args.includes('--export-headers') ?? false,
139
- exportHeadersTypes: args.includes('--export-headers-types') ?? false,
140
- exportExamples: args.includes('--export-examples') ?? false,
141
- exportLinks: args.includes('--export-links') ?? false,
142
- exportCallbacks: args.includes('--export-callbacks') ?? false,
131
+ exportSchemas: args.includes('--export-schemas'),
132
+ exportSchemasTypes: args.includes('--export-schemas-types'),
133
+ exportParameters: args.includes('--export-parameters'),
134
+ exportParametersTypes: args.includes('--export-parameters-types'),
135
+ exportSecuritySchemes: args.includes('--export-security-schemes'),
136
+ exportRequestBodies: args.includes('--export-request-bodies'),
137
+ exportResponses: args.includes('--export-responses'),
138
+ exportHeaders: args.includes('--export-headers'),
139
+ exportHeadersTypes: args.includes('--export-headers-types'),
140
+ exportExamples: args.includes('--export-examples'),
141
+ exportLinks: args.includes('--export-links'),
142
+ exportCallbacks: args.includes('--export-callbacks'),
143
143
  },
144
144
  },
145
145
  };
@@ -154,8 +154,8 @@ export async function honoTakibi() {
154
154
  const cliResult = parseCli(args);
155
155
  if (!cliResult.ok)
156
156
  return { ok: false, error: cliResult.error };
157
- const cli = cliResult.value;
158
- const { input, output, template, test, basePath, componentsOptions } = cli;
157
+ const value = cliResult.value;
158
+ const { input, output, template, test, basePath, componentsOptions } = value;
159
159
  const openAPIResult = await parseOpenAPI(input);
160
160
  if (!openAPIResult.ok)
161
161
  return { ok: false, error: openAPIResult.error };
@@ -165,10 +165,10 @@ export async function honoTakibi() {
165
165
  return { ok: false, error: takibiResult.error };
166
166
  return { ok: true, value: takibiResult.value };
167
167
  }
168
- const loadConfigResult = await readConfig();
169
- if (!loadConfigResult.ok)
170
- return { ok: false, error: loadConfigResult.error };
171
- const config = loadConfigResult.value;
168
+ const readConfigResult = await readConfig();
169
+ if (!readConfigResult.ok)
170
+ return { ok: false, error: readConfigResult.error };
171
+ const config = readConfigResult.value;
172
172
  const openAPIResult = await parseOpenAPI(config.input);
173
173
  if (!openAPIResult.ok)
174
174
  return { ok: false, error: openAPIResult.error };
@@ -268,253 +268,3 @@ export async function honoTakibi() {
268
268
  ].filter((v) => v !== undefined);
269
269
  return { ok: true, value: results.join('\n') };
270
270
  }
271
- // Test run
272
- // pnpm vitest run ./packages/hono-takibi/src/cli/index.ts
273
- if (import.meta.vitest) {
274
- const { describe, it, expect, beforeAll, afterAll } = import.meta.vitest;
275
- const fs = await import('node:fs');
276
- const openapi = {
277
- openapi: '3.1.0',
278
- info: {
279
- title: 'HonoTakibi',
280
- version: 'v1',
281
- },
282
- tags: [{ name: 'Hono' }, { name: 'HonoX' }, { name: 'ZodOpenAPIHono' }],
283
- paths: {
284
- '/hono': {
285
- get: {
286
- tags: ['Hono'],
287
- summary: 'Hono',
288
- description: 'Hono',
289
- responses: {
290
- '200': {
291
- description: 'OK',
292
- content: {
293
- 'application/json': {
294
- schema: {
295
- type: 'object',
296
- properties: {
297
- message: {
298
- type: 'string',
299
- example: 'Hono',
300
- },
301
- },
302
- required: ['message'],
303
- },
304
- },
305
- },
306
- },
307
- },
308
- },
309
- },
310
- '/hono-x': {
311
- get: {
312
- tags: ['HonoX'],
313
- summary: 'HonoX',
314
- description: 'HonoX',
315
- responses: {
316
- '200': {
317
- description: 'OK',
318
- content: {
319
- 'application/json': {
320
- schema: {
321
- type: 'object',
322
- properties: {
323
- message: {
324
- type: 'string',
325
- example: 'HonoX',
326
- },
327
- },
328
- required: ['message'],
329
- },
330
- },
331
- },
332
- },
333
- },
334
- },
335
- },
336
- '/zod-openapi-hono': {
337
- get: {
338
- tags: ['ZodOpenAPIHono'],
339
- summary: 'ZodOpenAPIHono',
340
- description: 'ZodOpenAPIHono',
341
- responses: {
342
- '200': {
343
- description: 'OK',
344
- content: {
345
- 'application/json': {
346
- schema: {
347
- type: 'object',
348
- properties: {
349
- message: {
350
- type: 'string',
351
- example: 'ZodOpenAPIHono',
352
- },
353
- },
354
- required: ['message'],
355
- },
356
- },
357
- },
358
- },
359
- },
360
- },
361
- },
362
- },
363
- };
364
- describe('honoTakibi', () => {
365
- beforeAll(() => {
366
- process.argv = [
367
- '*/*/bin/node',
368
- '*/dist/index.js',
369
- 'openapi.json',
370
- '-o',
371
- 'zod-openapi-hono.ts',
372
- ];
373
- fs.writeFileSync('openapi.json', JSON.stringify(openapi));
374
- });
375
- afterAll(() => {
376
- fs.rmSync('openapi.json', { force: true });
377
- fs.rmSync('zod-openapi-hono.ts', { force: true });
378
- });
379
- it('honoTakibi generated', async () => {
380
- const result = await honoTakibi();
381
- expect(result).toEqual({
382
- ok: true,
383
- value: 'Generated code written to zod-openapi-hono.ts',
384
- });
385
- const generatedCode = fs.readFileSync('zod-openapi-hono.ts', 'utf-8');
386
- const expectedCode = `import { createRoute, z } from '@hono/zod-openapi'
387
-
388
- export const getHonoRoute = createRoute({
389
- method: 'get',
390
- path: '/hono',
391
- tags: ['Hono'],
392
- summary: 'Hono',
393
- description: 'Hono',
394
- responses: {
395
- 200: {
396
- description: 'OK',
397
- content: {
398
- 'application/json': {
399
- schema: z
400
- .object({ message: z.string().openapi({ example: 'Hono' }) })
401
- .openapi({ required: ['message'] }),
402
- },
403
- },
404
- },
405
- },
406
- })
407
-
408
- export const getHonoXRoute = createRoute({
409
- method: 'get',
410
- path: '/hono-x',
411
- tags: ['HonoX'],
412
- summary: 'HonoX',
413
- description: 'HonoX',
414
- responses: {
415
- 200: {
416
- description: 'OK',
417
- content: {
418
- 'application/json': {
419
- schema: z
420
- .object({ message: z.string().openapi({ example: 'HonoX' }) })
421
- .openapi({ required: ['message'] }),
422
- },
423
- },
424
- },
425
- },
426
- })
427
-
428
- export const getZodOpenapiHonoRoute = createRoute({
429
- method: 'get',
430
- path: '/zod-openapi-hono',
431
- tags: ['ZodOpenAPIHono'],
432
- summary: 'ZodOpenAPIHono',
433
- description: 'ZodOpenAPIHono',
434
- responses: {
435
- 200: {
436
- description: 'OK',
437
- content: {
438
- 'application/json': {
439
- schema: z
440
- .object({ message: z.string().openapi({ example: 'ZodOpenAPIHono' }) })
441
- .openapi({ required: ['message'] }),
442
- },
443
- },
444
- },
445
- },
446
- })
447
- `;
448
- expect(generatedCode).toBe(expectedCode);
449
- });
450
- });
451
- describe('honoTakibi --help', () => {
452
- beforeAll(() => {
453
- process.argv = ['*/*/bin/node', '*/dist/index.js', '--help'];
454
- });
455
- it('honoTakibi help requested --help', async () => {
456
- const result = await honoTakibi();
457
- expect(result).toStrictEqual({
458
- ok: true,
459
- value: `Usage: hono-takibi <input.{yaml,json,tsp}> -o <routes.ts> [options]
460
-
461
- Options:
462
- --export-schemas-types export schemas types
463
- --export-schemas export schemas
464
- --export-parameters-types export parameters types
465
- --export-parameters export parameters
466
- --export-security-schemes export securitySchemes
467
- --export-request-bodies export requestBodies
468
- --export-responses export responses
469
- --export-headers-types export headers types
470
- --export-headers export headers
471
- --export-examples export examples
472
- --export-links export links
473
- --export-callbacks export callbacks
474
- --template generate app file and handler stubs
475
- --test generate empty *.test.ts files
476
- --base-path <path> api prefix (default: /)
477
- -h, --help display help for command`,
478
- });
479
- });
480
- });
481
- describe('honoTakibi -h', () => {
482
- beforeAll(() => {
483
- process.argv = ['*/*/bin/node', '*/dist/index.js', '-h'];
484
- });
485
- it('honoTakibi help requested -h', async () => {
486
- const result = await honoTakibi();
487
- expect(result).toStrictEqual({
488
- ok: true,
489
- value: `Usage: hono-takibi <input.{yaml,json,tsp}> -o <routes.ts> [options]
490
-
491
- Options:
492
- --export-schemas-types export schemas types
493
- --export-schemas export schemas
494
- --export-parameters-types export parameters types
495
- --export-parameters export parameters
496
- --export-security-schemes export securitySchemes
497
- --export-request-bodies export requestBodies
498
- --export-responses export responses
499
- --export-headers-types export headers types
500
- --export-headers export headers
501
- --export-examples export examples
502
- --export-links export links
503
- --export-callbacks export callbacks
504
- --template generate app file and handler stubs
505
- --test generate empty *.test.ts files
506
- --base-path <path> api prefix (default: /)
507
- -h, --help display help for command`,
508
- });
509
- });
510
- describe('honoTakibi missing output', () => {
511
- beforeAll(() => {
512
- process.argv = ['node', 'dist/index.js', 'openapi.yaml'];
513
- });
514
- it('should fail if output is not specified', async () => {
515
- const result = await honoTakibi();
516
- expect(result.ok).toBe(false);
517
- });
518
- });
519
- });
520
- }
@@ -78,6 +78,13 @@ type Config = {
78
78
  readonly split?: boolean;
79
79
  };
80
80
  };
81
+ /**
82
+ * Validates and parses a hono-takibi configuration object.
83
+ * When split is false and output doesn't end with .ts, normalizes to {output}/index.ts.
84
+ *
85
+ * @param config - The configuration object to validate
86
+ * @returns Result object with validated and normalized config or error message
87
+ */
81
88
  export declare function parseConfig(config: Config): {
82
89
  readonly ok: true;
83
90
  readonly value: Config;
@@ -85,6 +92,11 @@ export declare function parseConfig(config: Config): {
85
92
  readonly ok: false;
86
93
  readonly error: string;
87
94
  };
95
+ /**
96
+ * Reads and validates the hono-takibi configuration from hono-takibi.config.ts.
97
+ *
98
+ * @returns Result object with validated config or error message
99
+ */
88
100
  export declare function readConfig(): Promise<{
89
101
  readonly ok: true;
90
102
  readonly value: Config;
@@ -95,7 +107,8 @@ export declare function readConfig(): Promise<{
95
107
  /**
96
108
  * Helper to define a config with full type completion.
97
109
  *
98
- * @see config
110
+ * @param config - The configuration object
111
+ * @returns The same configuration object (identity function for type inference)
99
112
  */
100
113
  export declare function defineConfig(config: Config): Config;
101
114
  export {};
@@ -2,11 +2,17 @@ import { existsSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { register } from 'tsx/esm/api';
5
+ /**
6
+ * Validates and parses a hono-takibi configuration object.
7
+ * When split is false and output doesn't end with .ts, normalizes to {output}/index.ts.
8
+ *
9
+ * @param config - The configuration object to validate
10
+ * @returns Result object with validated and normalized config or error message
11
+ */
5
12
  export function parseConfig(config) {
6
13
  const isYamlOrJsonOrTsp = (i) => typeof i === 'string' && (i.endsWith('.yaml') || i.endsWith('.json') || i.endsWith('.tsp'));
7
- // ts
8
14
  const isTs = (o) => typeof o === 'string' && o.endsWith('.ts');
9
- const parseComponentsValue = (k, v) => {
15
+ const validateComponentsValue = (k, v) => {
10
16
  if (v === undefined)
11
17
  return { ok: false, error: `Invalid config: zod-openapi.components.${k} is undefined` };
12
18
  if (k === 'schemas' || k === 'parameters' || k === 'headers') {
@@ -25,22 +31,12 @@ export function parseConfig(config) {
25
31
  error: `Invalid split format for components.${k}: ${String(splitValue)}`,
26
32
  };
27
33
  }
28
- const isSplit = splitValue ?? false;
29
- if (isSplit) {
30
- if (isTs(v.output)) {
31
- return {
32
- ok: false,
33
- error: `Invalid ${k} output path for split mode (must be a directory, not .ts): ${v.output}`,
34
- };
35
- }
36
- }
37
- else {
38
- if (!isTs(v.output)) {
39
- return {
40
- ok: false,
41
- error: `Invalid ${k} output path for non-split mode (must be .ts file): ${v.output}`,
42
- };
43
- }
34
+ // split: true requires directory (no .ts)
35
+ if (splitValue === true && isTs(v.output)) {
36
+ return {
37
+ ok: false,
38
+ error: `Invalid ${k} output path for split mode (must be a directory, not .ts): ${v.output}`,
39
+ };
44
40
  }
45
41
  if (v.import !== undefined && typeof v.import !== 'string') {
46
42
  return {
@@ -48,7 +44,7 @@ export function parseConfig(config) {
48
44
  error: `Invalid import format for components.${k}: ${String(v.import)}`,
49
45
  };
50
46
  }
51
- return { ok: true, value: undefined };
47
+ return { ok: true };
52
48
  };
53
49
  if (!isYamlOrJsonOrTsp(config.input)) {
54
50
  return {
@@ -151,21 +147,12 @@ export function parseConfig(config) {
151
147
  error: `Invalid split format for routes: ${String(config['zod-openapi'].routes.split)}`,
152
148
  };
153
149
  }
154
- if (config['zod-openapi'].routes.split === true) {
155
- if (isTs(config['zod-openapi'].routes.output)) {
156
- return {
157
- ok: false,
158
- error: `Invalid routes output path for split mode (must be a directory, not .ts): ${config['zod-openapi'].routes.output}`,
159
- };
160
- }
161
- }
162
- else {
163
- if (!isTs(config['zod-openapi'].routes.output)) {
164
- return {
165
- ok: false,
166
- error: `Invalid routes output path for non-split mode (must be .ts file): ${config['zod-openapi'].routes.output}`,
167
- };
168
- }
150
+ // split: true requires directory (no .ts)
151
+ if (config['zod-openapi'].routes.split === true && isTs(config['zod-openapi'].routes.output)) {
152
+ return {
153
+ ok: false,
154
+ error: `Invalid routes output path for split mode (must be a directory, not .ts): ${config['zod-openapi'].routes.output}`,
155
+ };
169
156
  }
170
157
  }
171
158
  if (config['zod-openapi']?.components !== undefined) {
@@ -179,7 +166,7 @@ export function parseConfig(config) {
179
166
  k === 'examples' ||
180
167
  k === 'links' ||
181
168
  k === 'callbacks') {
182
- const result = parseComponentsValue(k, config['zod-openapi'].components[k]);
169
+ const result = validateComponentsValue(k, config['zod-openapi'].components[k]);
183
170
  if (!result.ok)
184
171
  return { ok: false, error: result.error };
185
172
  }
@@ -205,26 +192,135 @@ export function parseConfig(config) {
205
192
  if (config.rpc.split !== undefined && typeof config.rpc.split !== 'boolean') {
206
193
  return { ok: false, error: `Invalid split format for rpc: ${String(config.rpc.split)}` };
207
194
  }
208
- // split
209
- if (config.rpc.split === true) {
210
- if (isTs(config.rpc.output)) {
211
- return {
212
- ok: false,
213
- error: `Invalid rpc output path for split mode (must be a directory, not .ts): ${config.rpc.output}`,
214
- };
215
- }
216
- }
217
- else {
218
- if (!isTs(config.rpc.output)) {
219
- return {
220
- ok: false,
221
- error: `Invalid output format for rpc (non-split mode must be .ts file): ${String(config.rpc.output)}`,
222
- };
223
- }
195
+ // split: true requires directory (no .ts)
196
+ if (config.rpc.split === true && isTs(config.rpc.output)) {
197
+ return {
198
+ ok: false,
199
+ error: `Invalid rpc output path for split mode (must be a directory, not .ts): ${config.rpc.output}`,
200
+ };
224
201
  }
225
202
  }
226
- return { ok: true, value: config };
203
+ const result = {
204
+ ...config,
205
+ ...(config['zod-openapi'] && {
206
+ 'zod-openapi': {
207
+ ...config['zod-openapi'],
208
+ ...(config['zod-openapi'].routes && {
209
+ routes: {
210
+ ...config['zod-openapi'].routes,
211
+ output: config['zod-openapi'].routes.split !== true &&
212
+ !isTs(config['zod-openapi'].routes.output)
213
+ ? `${config['zod-openapi'].routes.output}/index.ts`
214
+ : config['zod-openapi'].routes.output,
215
+ },
216
+ }),
217
+ ...(config['zod-openapi'].components && {
218
+ components: {
219
+ ...config['zod-openapi'].components,
220
+ ...(config['zod-openapi'].components.schemas && {
221
+ schemas: {
222
+ ...config['zod-openapi'].components.schemas,
223
+ output: config['zod-openapi'].components.schemas.split !== true &&
224
+ !isTs(config['zod-openapi'].components.schemas.output)
225
+ ? `${config['zod-openapi'].components.schemas.output}/index.ts`
226
+ : config['zod-openapi'].components.schemas.output,
227
+ },
228
+ }),
229
+ ...(config['zod-openapi'].components.parameters && {
230
+ parameters: {
231
+ ...config['zod-openapi'].components.parameters,
232
+ output: config['zod-openapi'].components.parameters.split !== true &&
233
+ !isTs(config['zod-openapi'].components.parameters.output)
234
+ ? `${config['zod-openapi'].components.parameters.output}/index.ts`
235
+ : config['zod-openapi'].components.parameters.output,
236
+ },
237
+ }),
238
+ ...(config['zod-openapi'].components.securitySchemes && {
239
+ securitySchemes: {
240
+ ...config['zod-openapi'].components.securitySchemes,
241
+ output: config['zod-openapi'].components.securitySchemes.split !== true &&
242
+ !isTs(config['zod-openapi'].components.securitySchemes.output)
243
+ ? `${config['zod-openapi'].components.securitySchemes.output}/index.ts`
244
+ : config['zod-openapi'].components.securitySchemes.output,
245
+ },
246
+ }),
247
+ ...(config['zod-openapi'].components.requestBodies && {
248
+ requestBodies: {
249
+ ...config['zod-openapi'].components.requestBodies,
250
+ output: config['zod-openapi'].components.requestBodies.split !== true &&
251
+ !isTs(config['zod-openapi'].components.requestBodies.output)
252
+ ? `${config['zod-openapi'].components.requestBodies.output}/index.ts`
253
+ : config['zod-openapi'].components.requestBodies.output,
254
+ },
255
+ }),
256
+ ...(config['zod-openapi'].components.responses && {
257
+ responses: {
258
+ ...config['zod-openapi'].components.responses,
259
+ output: config['zod-openapi'].components.responses.split !== true &&
260
+ !isTs(config['zod-openapi'].components.responses.output)
261
+ ? `${config['zod-openapi'].components.responses.output}/index.ts`
262
+ : config['zod-openapi'].components.responses.output,
263
+ },
264
+ }),
265
+ ...(config['zod-openapi'].components.headers && {
266
+ headers: {
267
+ ...config['zod-openapi'].components.headers,
268
+ output: config['zod-openapi'].components.headers.split !== true &&
269
+ !isTs(config['zod-openapi'].components.headers.output)
270
+ ? `${config['zod-openapi'].components.headers.output}/index.ts`
271
+ : config['zod-openapi'].components.headers.output,
272
+ },
273
+ }),
274
+ ...(config['zod-openapi'].components.examples && {
275
+ examples: {
276
+ ...config['zod-openapi'].components.examples,
277
+ output: config['zod-openapi'].components.examples.split !== true &&
278
+ !isTs(config['zod-openapi'].components.examples.output)
279
+ ? `${config['zod-openapi'].components.examples.output}/index.ts`
280
+ : config['zod-openapi'].components.examples.output,
281
+ },
282
+ }),
283
+ ...(config['zod-openapi'].components.links && {
284
+ links: {
285
+ ...config['zod-openapi'].components.links,
286
+ output: config['zod-openapi'].components.links.split !== true &&
287
+ !isTs(config['zod-openapi'].components.links.output)
288
+ ? `${config['zod-openapi'].components.links.output}/index.ts`
289
+ : config['zod-openapi'].components.links.output,
290
+ },
291
+ }),
292
+ ...(config['zod-openapi'].components.callbacks && {
293
+ callbacks: {
294
+ ...config['zod-openapi'].components.callbacks,
295
+ output: config['zod-openapi'].components.callbacks.split !== true &&
296
+ !isTs(config['zod-openapi'].components.callbacks.output)
297
+ ? `${config['zod-openapi'].components.callbacks.output}/index.ts`
298
+ : config['zod-openapi'].components.callbacks.output,
299
+ },
300
+ }),
301
+ },
302
+ }),
303
+ },
304
+ }),
305
+ ...(config.rpc && {
306
+ rpc: {
307
+ ...config.rpc,
308
+ output: config.rpc.split !== true && !isTs(config.rpc.output)
309
+ ? `${config.rpc.output}/index.ts`
310
+ : config.rpc.output,
311
+ },
312
+ }),
313
+ };
314
+ return {
315
+ ok: true,
316
+ value: result,
317
+ };
227
318
  }
319
+ /**
320
+ * Reads and validates the hono-takibi configuration from hono-takibi.config.ts.
321
+ *
322
+ * @returns Result object with validated config or error message
323
+ */
228
324
  export async function readConfig() {
229
325
  const abs = resolve(process.cwd(), 'hono-takibi.config.ts');
230
326
  if (!existsSync(abs))
@@ -247,69 +343,9 @@ export async function readConfig() {
247
343
  /**
248
344
  * Helper to define a config with full type completion.
249
345
  *
250
- * @see config
346
+ * @param config - The configuration object
347
+ * @returns The same configuration object (identity function for type inference)
251
348
  */
252
349
  export function defineConfig(config) {
253
350
  return config;
254
351
  }
255
- // Test run
256
- // pnpm vitest run ./packages/hono-takibi/src/config/index.ts
257
- if (import.meta.vitest) {
258
- const { describe, it, expect, beforeEach, afterEach, vi } = import.meta.vitest;
259
- const fs = await import('node:fs');
260
- const os = await import('node:os');
261
- const path = await import('node:path');
262
- describe('loadConfig()', () => {
263
- const origCwd = process.cwd();
264
- beforeEach(() => {
265
- vi.resetModules();
266
- const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'hono-takibi-config-ts-'));
267
- process.chdir(tmpdir);
268
- });
269
- afterEach(() => {
270
- const cwd = process.cwd();
271
- process.chdir(origCwd);
272
- fs.rmSync(cwd, { recursive: true, force: true });
273
- });
274
- it('passes: legacy top-level output mode', async () => {
275
- const p = path.join(process.cwd(), 'hono-takibi.config.ts');
276
- const c = `
277
- export default {
278
- input: 'openapi.yaml',
279
- 'zod-openapi': {
280
- output: 'routes/index.ts',
281
- exportSchemasTypes: true,
282
- exportSchemas: true
283
- },
284
- rpc: {
285
- output: 'rpc/index.ts',
286
- import: '../client'
287
- }
288
- }
289
- `;
290
- fs.writeFileSync(p, c, 'utf-8');
291
- await expect(readConfig()).resolves.toStrictEqual({
292
- ok: true,
293
- value: {
294
- input: 'openapi.yaml',
295
- 'zod-openapi': {
296
- output: 'routes/index.ts',
297
- exportSchemasTypes: true,
298
- exportSchemas: true,
299
- },
300
- rpc: {
301
- output: 'rpc/index.ts',
302
- import: '../client',
303
- },
304
- },
305
- });
306
- });
307
- it('fails: config file missing', async () => {
308
- const result = await readConfig();
309
- expect(result.ok).toBe(false);
310
- if (!result.ok) {
311
- expect(result.error).toMatch(/Config not found:/);
312
- }
313
- });
314
- });
315
- }