hono-takibi 0.9.22 → 0.9.24

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.
@@ -31,9 +31,9 @@
31
31
  * - `{ ok: false, error: string }` on validation or generation errors
32
32
  */
33
33
  export declare function honoTakibi(): Promise<{
34
- ok: true;
35
- value: string;
34
+ readonly ok: true;
35
+ readonly value: string;
36
36
  } | {
37
- ok: false;
38
- error: string;
37
+ readonly ok: false;
38
+ readonly error: string;
39
39
  }>;
package/dist/cli/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
3
  import { config } from '../config/index.js';
4
- import core from '../core/core.js';
4
+ import { core } from '../core/core.js';
5
+ import { route } from '../core/route.js';
6
+ import { schema } from '../core/schema.js';
5
7
  import { takibi } from '../core/takibi.js';
6
- import rpc from '../generator/rpc/index.js';
8
+ import { rpc } from '../generator/rpc/index.js';
7
9
  // import { honoRpcWithSWR } from '../generator/swr/index.js'
8
10
  import { parseCli } from '../utils/index.js';
9
11
  const HELP_TEXT = `Usage: hono-takibi <input.{yaml,json,tsp}> -o <routes.ts> [options]
@@ -81,15 +83,29 @@ export async function honoTakibi() {
81
83
  return { ok: false, error: configResult.error };
82
84
  }
83
85
  const c = configResult.value;
84
- const takibiResult = c['hono-takibi']
85
- ? await takibi(c['hono-takibi']?.input, c['hono-takibi']?.output, c['hono-takibi']?.exportSchema ?? false, c['hono-takibi']?.exportType ?? false, false, // template
86
+ const takibiResult = c['zod-openapi']
87
+ ? await takibi(c.input, c['zod-openapi']?.output, c['zod-openapi']?.exportSchema ?? false, c['zod-openapi']?.exportType ?? false, false, // template
86
88
  false)
87
89
  : undefined;
88
90
  if (takibiResult && !takibiResult.ok) {
89
91
  return { ok: false, error: takibiResult.error };
90
92
  }
93
+ // schema
94
+ const schemaResult = c['zod-openapi']?.schema
95
+ ? await schema(c.input, c['zod-openapi'].schema.output, c['zod-openapi'].schema.exportType ?? false, c['zod-openapi']?.schema.split ?? false)
96
+ : undefined;
97
+ if (schemaResult && !schemaResult.ok) {
98
+ return { ok: false, error: schemaResult.error };
99
+ }
100
+ // route
101
+ const routeResult = c['zod-openapi']?.route
102
+ ? await route(c.input, c['zod-openapi'].route.output, c['zod-openapi'].route.import, c['zod-openapi'].route.split ?? false)
103
+ : undefined;
104
+ if (routeResult && !routeResult.ok) {
105
+ return { ok: false, error: routeResult.error };
106
+ }
91
107
  const rpcResult = c.rpc
92
- ? await core(c.rpc.input, c.rpc.output, c.rpc.import, 'Generated RPC code written to', rpc)
108
+ ? await core(c.input, c.rpc.output, c.rpc.import, 'Generated RPC code written to', rpc)
93
109
  : undefined;
94
110
  if (rpcResult && !rpcResult.ok) {
95
111
  return { ok: false, error: rpcResult.error };
@@ -1,27 +1,31 @@
1
1
  type Config = {
2
- 'hono-takibi'?: {
3
- input: `${string}.yaml` | `${string}.json` | `${string}.tsp`;
4
- output: `${string}.ts`;
5
- exportType?: boolean;
6
- exportSchema?: boolean;
2
+ readonly input: `${string}.yaml` | `${string}.json` | `${string}.tsp`;
3
+ readonly 'zod-openapi'?: {
4
+ readonly output: `${string}.ts`;
5
+ readonly exportType?: boolean;
6
+ readonly exportSchema?: boolean;
7
+ readonly schema?: {
8
+ readonly output: string | `${string}.ts`;
9
+ readonly exportType?: boolean;
10
+ readonly split?: boolean;
11
+ };
12
+ readonly route?: {
13
+ readonly output: string | `${string}.ts`;
14
+ readonly import: string;
15
+ readonly split?: boolean;
16
+ };
7
17
  };
8
- rpc?: {
9
- input: `${string}.yaml` | `${string}.json` | `${string}.tsp`;
10
- output: `${string}.ts`;
11
- import: string;
12
- };
13
- swr?: {
14
- input: `${string}.yaml` | `${string}.json` | `${string}.tsp`;
15
- output: `${string}.ts`;
16
- import: string;
18
+ readonly rpc?: {
19
+ readonly output: `${string}.ts`;
20
+ readonly import: string;
17
21
  };
18
22
  };
19
23
  export declare function config(): Promise<{
20
- ok: true;
21
- value: Config;
24
+ readonly ok: true;
25
+ readonly value: Config;
22
26
  } | {
23
- ok: false;
24
- error: string;
27
+ readonly ok: false;
28
+ readonly error: string;
25
29
  }>;
26
- export default function defineConfig(config: Config): Config;
30
+ export declare function defineConfig(c: Config): Config;
27
31
  export {};
@@ -1,23 +1,150 @@
1
+ import { existsSync } from 'node:fs';
1
2
  import { resolve } from 'node:path';
2
3
  import { pathToFileURL } from 'node:url';
3
4
  import { register } from 'tsx/esm/api';
5
+ const isYamlOrJsonOrTsp = (i) => typeof i === 'string' && (i.endsWith('.yaml') || i.endsWith('.json') || i.endsWith('.tsp'));
6
+ const isTs = (o) => typeof o === 'string' && o.endsWith('.ts');
4
7
  export async function config() {
5
8
  const abs = resolve(process.cwd(), 'hono-takibi.config.ts');
6
- // if (!existsSync(abs)) {
7
- // return { ok: false, error: `Config not found: ${abs}` }
8
- // }
9
- register();
9
+ if (!existsSync(abs))
10
+ return { ok: false, error: `Config not found: ${abs}` };
10
11
  try {
12
+ register();
11
13
  const mod = await import(pathToFileURL(abs).href);
12
14
  if (!('default' in mod)) {
13
15
  return { ok: false, error: 'Config must export default object' };
14
16
  }
17
+ if (mod.default) {
18
+ // input
19
+ if (!isYamlOrJsonOrTsp(mod.default.input)) {
20
+ return {
21
+ ok: false,
22
+ error: `Invalid input format for zod-openapi: ${String(mod.default.input)}`,
23
+ };
24
+ }
25
+ // zod-openapi
26
+ const zo = mod.default['zod-openapi'];
27
+ if (zo) {
28
+ // boolean flags
29
+ if (zo.exportSchema !== undefined && typeof zo.exportSchema !== 'boolean') {
30
+ return {
31
+ ok: false,
32
+ error: `Invalid exportSchema format for zod-openapi: ${String(zo.exportSchema)}`,
33
+ };
34
+ }
35
+ if (zo.exportType !== undefined && typeof zo.exportType !== 'boolean') {
36
+ return {
37
+ ok: false,
38
+ error: `Invalid exportType format for zod-openapi: ${String(zo.exportType)}`,
39
+ };
40
+ }
41
+ const hasSchema = zo.schema !== undefined;
42
+ const hasRoute = zo.route !== undefined;
43
+ if (hasSchema || hasRoute) {
44
+ if (Object.hasOwn(zo, 'output')) {
45
+ return {
46
+ ok: false,
47
+ error: "Invalid config: When using 'zod-openapi.schema' or 'zod-openapi.route', do NOT set 'zod-openapi.output'.",
48
+ };
49
+ }
50
+ }
51
+ else {
52
+ if (!isTs(zo.output)) {
53
+ return {
54
+ ok: false,
55
+ error: `Invalid output format for zod-openapi: ${String(zo.output)}`,
56
+ };
57
+ }
58
+ }
59
+ if (hasSchema) {
60
+ const s = zo.schema;
61
+ if (!s)
62
+ return { ok: false, error: 'Invalid config: zod-openapi.schema is undefined' };
63
+ if (s.split !== undefined && typeof s.split !== 'boolean') {
64
+ return {
65
+ ok: false,
66
+ error: `Invalid schema split format for zod-openapi: ${String(s.split)}`,
67
+ };
68
+ }
69
+ if (typeof s.output !== 'string') {
70
+ return { ok: false, error: `Invalid schema output path: ${String(s.output)}` };
71
+ }
72
+ if (s.split === true) {
73
+ if (isTs(s.output)) {
74
+ return {
75
+ ok: false,
76
+ error: `Invalid schema output path for split mode (must be a directory, not .ts): ${s.output}`,
77
+ };
78
+ }
79
+ }
80
+ else {
81
+ if (!isTs(s.output)) {
82
+ return {
83
+ ok: false,
84
+ error: `Invalid schema output path for non-split mode (must be .ts file): ${s.output}`,
85
+ };
86
+ }
87
+ }
88
+ if (s.exportType !== undefined && typeof s.exportType !== 'boolean') {
89
+ return {
90
+ ok: false,
91
+ error: `Invalid schema exportType format for zod-openapi: ${String(s.exportType)}`,
92
+ };
93
+ }
94
+ }
95
+ if (hasRoute) {
96
+ const r = zo.route;
97
+ if (!r)
98
+ return { ok: false, error: 'Invalid config: zod-openapi.route is undefined' };
99
+ if (typeof r.import !== 'string') {
100
+ return {
101
+ ok: false,
102
+ error: `Invalid route import format for zod-openapi: ${String(r.import)}`,
103
+ };
104
+ }
105
+ if (r.split !== undefined && typeof r.split !== 'boolean') {
106
+ return {
107
+ ok: false,
108
+ error: `Invalid route split format for zod-openapi: ${String(r.split)}`,
109
+ };
110
+ }
111
+ if (typeof r.output !== 'string') {
112
+ return { ok: false, error: `Invalid route output path: ${String(r.output)}` };
113
+ }
114
+ if (r.split === true) {
115
+ if (isTs(r.output)) {
116
+ return {
117
+ ok: false,
118
+ error: `Invalid route output path for split mode (must be a directory, not .ts): ${r.output}`,
119
+ };
120
+ }
121
+ }
122
+ else {
123
+ if (!isTs(r.output)) {
124
+ return {
125
+ ok: false,
126
+ error: `Invalid route output path for non-split mode (must be .ts file): ${r.output}`,
127
+ };
128
+ }
129
+ }
130
+ }
131
+ }
132
+ const rpc = mod.default.rpc;
133
+ if (rpc) {
134
+ if (!isTs(rpc.output)) {
135
+ return { ok: false, error: `Invalid output format for rpc: ${String(rpc.output)}` };
136
+ }
137
+ if (typeof rpc.import !== 'string') {
138
+ return { ok: false, error: `Invalid import format for rpc: ${String(rpc.import)}` };
139
+ }
140
+ }
141
+ }
15
142
  return { ok: true, value: mod.default };
16
143
  }
17
144
  catch (e) {
18
145
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
19
146
  }
20
147
  }
21
- export default function defineConfig(config) {
22
- return config;
148
+ export function defineConfig(c) {
149
+ return c;
23
150
  }
@@ -1,8 +1,8 @@
1
1
  import { type OpenAPI } from '../openapi/index.js';
2
- export default function core(input: `${string}.yaml` | `${string}.json` | `${string}.tsp`, output: `${string}.ts`, importCode: string, value: string, fn: (openapi: OpenAPI, importCode: string) => string): Promise<{
3
- ok: true;
4
- value: string;
2
+ export declare function core(input: `${string}.yaml` | `${string}.json` | `${string}.tsp`, output: `${string}.ts`, importCode: string, value: string, fn: (openapi: OpenAPI, importCode: string) => string): Promise<{
3
+ readonly ok: true;
4
+ readonly value: string;
5
5
  } | {
6
- ok: false;
7
- error: string;
6
+ readonly ok: false;
7
+ readonly error: string;
8
8
  }>;
package/dist/core/core.js CHANGED
@@ -2,21 +2,21 @@ import path from 'node:path';
2
2
  import { fmt } from '../format/index.js';
3
3
  import { mkdir, writeFile } from '../fsp/index.js';
4
4
  import { parseOpenAPI } from '../openapi/index.js';
5
- export default async function core(input, output, importCode, value, fn) {
5
+ export async function core(input, output, importCode, value, fn) {
6
6
  const openAPIResult = await parseOpenAPI(input);
7
7
  if (!openAPIResult.ok) {
8
8
  return { ok: false, error: openAPIResult.error };
9
9
  }
10
10
  const openAPI = openAPIResult.value;
11
- const honoRpcResult = await fmt(fn(openAPI, importCode));
12
- if (!honoRpcResult.ok) {
13
- return { ok: false, error: honoRpcResult.error };
11
+ const fnResult = await fmt(fn(openAPI, importCode));
12
+ if (!fnResult.ok) {
13
+ return { ok: false, error: fnResult.error };
14
14
  }
15
15
  const mkdirResult = await mkdir(path.dirname(output));
16
16
  if (!mkdirResult.ok) {
17
17
  return { ok: false, error: mkdirResult.error };
18
18
  }
19
- const writeResult = await writeFile(output, honoRpcResult.value);
19
+ const writeResult = await writeFile(output, fnResult.value);
20
20
  if (!writeResult.ok) {
21
21
  return { ok: false, error: writeResult.error };
22
22
  }
@@ -0,0 +1,7 @@
1
+ export declare function route(input: `${string}.yaml` | `${string}.json` | `${string}.tsp`, output: string | `${string}.ts`, importPath: string, split?: boolean): Promise<{
2
+ readonly ok: true;
3
+ readonly value: string;
4
+ } | {
5
+ readonly ok: false;
6
+ readonly error: string;
7
+ }>;
@@ -0,0 +1,96 @@
1
+ import path from 'node:path';
2
+ import { fmt } from '../format/index.js';
3
+ import { mkdir, writeFile } from '../fsp/index.js';
4
+ import { routeCode } from '../generator/zod-openapi-hono/openapi/route/index.js';
5
+ import { parseOpenAPI } from '../openapi/index.js';
6
+ const findSchemaTokens = (code) => Array.from(new Set(Array.from(code.matchAll(/\b([A-Za-z_$][A-Za-z0-9_$]*Schema)\b/g))
7
+ .map((m) => m[1] ?? '')
8
+ .filter(Boolean)));
9
+ const extractRouteBlocks = (src) => {
10
+ const re = /export\s+const\s+([A-Za-z_$][A-Za-z0-9_$]*)Route\s*=/g;
11
+ const hits = [];
12
+ for (const m of src.matchAll(re)) {
13
+ const name = (m[1] ?? '').trim();
14
+ const start = m.index ?? 0;
15
+ if (name)
16
+ hits.push({ name, start });
17
+ }
18
+ return hits.map((h, i) => {
19
+ const start = h.start;
20
+ const end = i + 1 < hits.length ? (hits[i + 1]?.start ?? src.length) : src.length;
21
+ return { name: h.name, block: src.slice(start, end).trim() };
22
+ });
23
+ };
24
+ const lowerFirst = (s) => (s ? s.charAt(0).toLowerCase() + s.slice(1) : s);
25
+ export async function route(input, output, importPath, split) {
26
+ const openAPIResult = await parseOpenAPI(input);
27
+ if (!openAPIResult.ok) {
28
+ return { ok: false, error: openAPIResult.error };
29
+ }
30
+ const openAPI = openAPIResult.value;
31
+ const routesSrc = routeCode(openAPI.paths);
32
+ if (!split) {
33
+ const includeZ = routesSrc.includes('z.');
34
+ const schemaTokens = findSchemaTokens(routesSrc);
35
+ const importHono = `import { createRoute${includeZ ? ', z' : ''} } from '@hono/zod-openapi'`;
36
+ const importSchemas = schemaTokens.length > 0 ? `import { ${schemaTokens.join(',')} } from '${importPath}'` : '';
37
+ const finalSrc = [importHono, importSchemas, '\n', routesSrc].filter(Boolean).join('\n');
38
+ const fmtCode = await fmt(finalSrc);
39
+ if (!fmtCode.ok)
40
+ return { ok: false, error: fmtCode.error };
41
+ const mk = await mkdir(path.dirname(output));
42
+ if (!mk.ok)
43
+ return { ok: false, error: mk.error };
44
+ const wr = await writeFile(output, fmtCode.value);
45
+ return wr.ok
46
+ ? { ok: true, value: `Generated route code written to ${output}` }
47
+ : { ok: false, error: wr.error };
48
+ }
49
+ const outDir = output.replace(/\.ts$/, '');
50
+ const blocks = extractRouteBlocks(routesSrc);
51
+ if (blocks.length === 0) {
52
+ const includeZ = routesSrc.includes('z.');
53
+ const schemaTokens = findSchemaTokens(routesSrc);
54
+ const importHono = `import { createRoute${includeZ ? ', z' : ''} } from '@hono/zod-openapi'`;
55
+ const importSchemas = schemaTokens.length > 0 ? `import { ${schemaTokens.join(',')} } from '${importPath}'` : '';
56
+ const finalSrc = [importHono, importSchemas, '\n', routesSrc].filter(Boolean).join('\n');
57
+ const fmtCode = await fmt(finalSrc);
58
+ if (!fmtCode.ok)
59
+ return { ok: false, error: fmtCode.error };
60
+ const mk = await mkdir(path.dirname(output));
61
+ if (!mk.ok)
62
+ return { ok: false, error: mk.error };
63
+ const wr = await writeFile(output, fmtCode.value);
64
+ return wr.ok
65
+ ? { ok: true, value: `Generated route code written to ${output}` }
66
+ : { ok: false, error: wr.error };
67
+ }
68
+ for (const { name, block } of blocks) {
69
+ const includeZ = block.includes('z.');
70
+ const schemaTokens = findSchemaTokens(block);
71
+ const importHono = `import { createRoute${includeZ ? ', z' : ''} } from '@hono/zod-openapi'`;
72
+ const importSchemas = schemaTokens.length > 0 ? `import { ${schemaTokens.join(',')} } from '${importPath}'` : '';
73
+ const fileSrc = [importHono, importSchemas, '\n', block, ''].filter(Boolean).join('\n');
74
+ const fmtCode = await fmt(fileSrc);
75
+ if (!fmtCode.ok)
76
+ return { ok: false, error: fmtCode.error };
77
+ const filePath = `${outDir}/${lowerFirst(name)}.ts`;
78
+ const mk = await mkdir(path.dirname(filePath));
79
+ if (!mk.ok)
80
+ return { ok: false, error: mk.error };
81
+ const wr = await writeFile(filePath, fmtCode.value);
82
+ if (!wr.ok)
83
+ return { ok: false, error: wr.error };
84
+ }
85
+ const indexBody = `${blocks.map(({ name }) => `export * from './${lowerFirst(name)}'`).join('\n')}\n`;
86
+ const indexFmt = await fmt(indexBody);
87
+ if (!indexFmt.ok)
88
+ return { ok: false, error: indexFmt.error };
89
+ const mkIndex = await mkdir(path.dirname(`${outDir}/index.ts`));
90
+ if (!mkIndex.ok)
91
+ return { ok: false, error: mkIndex.error };
92
+ const wrIndex = await writeFile(`${outDir}/index.ts`, indexFmt.value);
93
+ if (!wrIndex.ok)
94
+ return { ok: false, error: wrIndex.error };
95
+ return { ok: true, value: `Generated route code written to ${outDir}/*.ts (index.ts included)` };
96
+ }
@@ -0,0 +1,7 @@
1
+ export declare function schema(input: `${string}.yaml` | `${string}.json` | `${string}.tsp`, output: string | `${string}.ts`, exportType: boolean, split?: boolean): Promise<{
2
+ readonly ok: true;
3
+ readonly value: string;
4
+ } | {
5
+ readonly ok: false;
6
+ readonly error: string;
7
+ }>;
@@ -0,0 +1,103 @@
1
+ import path from 'node:path';
2
+ import { fmt } from '../format/index.js';
3
+ import { mkdir, writeFile } from '../fsp/index.js';
4
+ import { zodToOpenAPI } from '../generator/zod-to-openapi/index.js';
5
+ import { resolveSchemasDependencies } from '../helper/resolve-schemas-dependencies.js';
6
+ import { zodToOpenAPISchema } from '../helper/zod-to-openapi-schema.js';
7
+ import { parseOpenAPI } from '../openapi/index.js';
8
+ const lowerFirst = (s) => (s ? (s[0]?.toLowerCase() ?? '') + s.slice(1) : s);
9
+ const findSchemaRefs = (code, selfName) => {
10
+ const re = /\b([A-Za-z_$][A-Za-z0-9_$]*)Schema\b/g;
11
+ const out = new Set();
12
+ for (const m of code.matchAll(re)) {
13
+ const base = m[1] ?? '';
14
+ if (base !== selfName && base)
15
+ out.add(base);
16
+ }
17
+ return Array.from(out);
18
+ };
19
+ export async function schema(input, output, exportType, split) {
20
+ const openAPIResult = await parseOpenAPI(input);
21
+ if (!openAPIResult.ok) {
22
+ return { ok: false, error: openAPIResult.error };
23
+ }
24
+ const openAPI = openAPIResult.value;
25
+ const { schemas } = openAPI.components ? openAPI.components : {};
26
+ if (!schemas) {
27
+ return { ok: false, error: 'No schemas found' };
28
+ }
29
+ const orderedSchemas = resolveSchemasDependencies(schemas);
30
+ if (orderedSchemas.length === 0) {
31
+ return { ok: true, value: 'No schemas found' };
32
+ }
33
+ // split
34
+ if (split) {
35
+ const outDir = output.replace(/\.ts$/, '');
36
+ for (const schemaName of orderedSchemas) {
37
+ const schema = schemas[schemaName];
38
+ const z = zodToOpenAPI(schema);
39
+ const zs = zodToOpenAPISchema(schemaName, z, true, exportType);
40
+ const importZ = `import { z } from '@hono/zod-openapi'`;
41
+ const deps = findSchemaRefs(zs, schemaName).filter((d) => d in schemas);
42
+ const depImports = deps.length > 0
43
+ ? deps.map((d) => `import { ${d}Schema } from './${lowerFirst(d)}'`).join('\n')
44
+ : '';
45
+ const fileCode = [importZ, depImports, '\n', zs].filter(Boolean).join('\n');
46
+ const fmtCode = await fmt(fileCode);
47
+ if (!fmtCode.ok) {
48
+ return { ok: false, error: fmtCode.error };
49
+ }
50
+ const filePath = `${outDir}/${lowerFirst(schemaName)}.ts`;
51
+ const mkdirResult = await mkdir(path.dirname(filePath));
52
+ if (!mkdirResult.ok) {
53
+ return { ok: false, error: mkdirResult.error };
54
+ }
55
+ const writeResult = await writeFile(filePath, fmtCode.value);
56
+ if (!writeResult.ok) {
57
+ return { ok: false, error: writeResult.error };
58
+ }
59
+ }
60
+ // index.ts
61
+ const indexBody = `${orderedSchemas
62
+ .map((n) => `export * from './${lowerFirst(n)}'`)
63
+ .join('\n')}\n`;
64
+ const indexFmt = await fmt(indexBody);
65
+ if (!indexFmt.ok) {
66
+ return { ok: false, error: indexFmt.error };
67
+ }
68
+ const mkIndex = await mkdir(path.dirname(`${outDir}/index.ts`));
69
+ if (!mkIndex.ok) {
70
+ return { ok: false, error: mkIndex.error };
71
+ }
72
+ const wrIndex = await writeFile(`${outDir}/index.ts`, indexFmt.value);
73
+ if (!wrIndex.ok) {
74
+ return { ok: false, error: wrIndex.error };
75
+ }
76
+ return {
77
+ ok: true,
78
+ value: `Generated schema code written to ${outDir}/*.ts (index.ts included)`,
79
+ };
80
+ }
81
+ const schemaDefinitions = orderedSchemas
82
+ .map((schemaName) => {
83
+ const schema = schemas[schemaName];
84
+ const z = zodToOpenAPI(schema);
85
+ return zodToOpenAPISchema(schemaName, z, true, exportType);
86
+ })
87
+ .join('\n\n');
88
+ const importCode = `import { z } from '@hono/zod-openapi'`;
89
+ const schemaDefinitionsCode = `${importCode}\n\n${schemaDefinitions}`;
90
+ const fmtCode = await fmt(schemaDefinitionsCode);
91
+ if (!fmtCode.ok) {
92
+ return { ok: false, error: fmtCode.error };
93
+ }
94
+ const mkdirResult = await mkdir(path.dirname(output));
95
+ if (!mkdirResult.ok) {
96
+ return { ok: false, error: mkdirResult.error };
97
+ }
98
+ const writeResult = await writeFile(output, fmtCode.value);
99
+ if (!writeResult.ok) {
100
+ return { ok: false, error: writeResult.error };
101
+ }
102
+ return { ok: true, value: `Generated schema code written to ${output}` };
103
+ }
@@ -46,9 +46,9 @@
46
46
  * @returns A `Result` containing a success message or an error string.
47
47
  */
48
48
  export declare function takibi(input: `${string}.yaml` | `${string}.json` | `${string}.tsp`, output: `${string}.ts`, exportSchema: boolean, exportType: boolean, template: boolean, test: boolean, basePath?: string): Promise<{
49
- ok: true;
50
- value: string;
49
+ readonly ok: true;
50
+ readonly value: string;
51
51
  } | {
52
- ok: false;
53
- error: string;
52
+ readonly ok: false;
53
+ readonly error: string;
54
54
  }>;
@@ -2,9 +2,9 @@ import path from 'node:path';
2
2
  import { fmt } from '../format/index.js';
3
3
  import { mkdir, readdir, writeFile } from '../fsp/index.js';
4
4
  import { app } from '../generator/zod-openapi-hono/app/index.js';
5
- import zodOpenAPIHono from '../generator/zod-openapi-hono/openapi/index.js';
5
+ import { zodOpenAPIHono } from '../generator/zod-openapi-hono/openapi/index.js';
6
6
  import { parseOpenAPI } from '../openapi/index.js';
7
- import { groupHandlersByFileName, methodPath } from '../utils/index.js';
7
+ import { groupHandlersByFileName, isHttpMethod, methodPath } from '../utils/index.js';
8
8
  /**
9
9
  * Generates TypeScript code from an OpenAPI spec and optional templates.
10
10
  *
@@ -81,8 +81,7 @@ export async function takibi(input, output, exportSchema, exportType, template,
81
81
  if (!readdirResult.ok) {
82
82
  return { ok: false, error: readdirResult.error };
83
83
  }
84
- const files = readdirResult.value;
85
- const target = path.join(dir, files.includes('index.ts') ? 'main.ts' : 'index.ts');
84
+ const target = path.join(dir, readdirResult.value.includes('index.ts') ? 'main.ts' : 'index.ts');
86
85
  const writeResult = await writeFile(target, appResult.value);
87
86
  if (!writeResult.ok) {
88
87
  return { ok: false, error: writeResult.error };
@@ -109,13 +108,6 @@ export async function takibi(input, output, exportSchema, exportType, template,
109
108
  async function zodOpenapiHonoHandler(openapi, output, test) {
110
109
  const paths = openapi.paths;
111
110
  const handlers = [];
112
- const isHttpMethod = (v) => {
113
- for (const m of ['get', 'put', 'post', 'delete', 'patch', 'options', 'head', 'trace']) {
114
- if (m === v)
115
- return true;
116
- }
117
- return false;
118
- };
119
111
  for (const [path, pathItem] of Object.entries(paths)) {
120
112
  for (const [method] of Object.entries(pathItem)) {
121
113
  if (!isHttpMethod(method))
@@ -5,11 +5,9 @@
5
5
  * @returns A `Result` containing the formatted code or an error message.
6
6
  */
7
7
  export declare function fmt(code: string): Promise<{
8
- ok: true;
9
- value: string;
10
- error?: undefined;
8
+ readonly ok: true;
9
+ readonly value: string;
11
10
  } | {
12
- ok: false;
13
- error: string;
14
- value?: undefined;
11
+ readonly ok: false;
12
+ readonly error: string;
15
13
  }>;
@@ -5,11 +5,11 @@
5
5
  * @returns A `Result` that is `ok` on success, otherwise an error message.
6
6
  */
7
7
  export declare function mkdir(dir: string): Promise<{
8
- ok: false;
9
- error: string;
8
+ readonly ok: false;
9
+ readonly error: string;
10
10
  } | {
11
- ok: true;
12
- value: undefined;
11
+ readonly ok: true;
12
+ readonly value: undefined;
13
13
  }>;
14
14
  /**
15
15
  * Reads the contents of a directory.
@@ -18,11 +18,11 @@ export declare function mkdir(dir: string): Promise<{
18
18
  * @returns A `Result` with the file list on success, otherwise an error message.
19
19
  */
20
20
  export declare function readdir(dir: string): Promise<{
21
- ok: false;
21
+ readonly ok: false;
22
22
  error: string;
23
23
  } | {
24
- ok: true;
25
- value: string[];
24
+ readonly ok: true;
25
+ readonly value: string[];
26
26
  }>;
27
27
  /**
28
28
  * Writes UTF-8 text to a file, creating it if necessary.
@@ -32,9 +32,9 @@ export declare function readdir(dir: string): Promise<{
32
32
  * @returns A `Result` that is `ok` on success, otherwise an error message.
33
33
  */
34
34
  export declare function writeFile(path: string, data: string): Promise<{
35
- ok: true;
36
- value: undefined;
35
+ readonly ok: true;
36
+ readonly value: undefined;
37
37
  } | {
38
- ok: false;
39
- error: string;
38
+ readonly ok: false;
39
+ readonly error: string;
40
40
  }>;
@@ -1,2 +1,2 @@
1
1
  import type { OpenAPI } from '../../openapi/index.js';
2
- export default function rpc(openapi: OpenAPI, importCode: string): string;
2
+ export declare function rpc(openapi: OpenAPI, importPath: string): string;
@@ -1,8 +1,6 @@
1
1
  import { methodPath } from '../../utils/index.js';
2
2
  /* ─────────────────────────────── Guards ─────────────────────────────── */
3
- /** Narrow to generic object records */
4
3
  const isRecord = (v) => typeof v === 'object' && v !== null;
5
- /** Narrow to OpenAPI paths object (shallow structural check) */
6
4
  const isOpenAPIPaths = (v) => {
7
5
  if (!isRecord(v))
8
6
  return false;
@@ -11,20 +9,11 @@ const isOpenAPIPaths = (v) => {
11
9
  return false;
12
10
  return true;
13
11
  };
14
- /** Treat any object as Schema (we rely on downstream field checks) */
15
12
  const isSchema = (v) => isRecord(v);
16
13
  /* ─────────────────────────────── Formatters ─────────────────────────────── */
17
- /** JS identifier check */
18
14
  const isValidIdent = (s) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(s);
19
- /** Escape single quotes and backslashes for single-quoted strings */
20
15
  const esc = (s) => s.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
21
- /**
22
- * Convert an OpenAPI path to a client access chain.
23
- * examples:
24
- * '/' -> ".index"
25
- * '/hono-x' -> "['hono-x']"
26
- * '/posts/hono/{id}' -> ".posts.hono[':id']"
27
- */
16
+ /** '/'->'.index' | '/hono-x'->"['hono-x']" | '/posts/hono/{id}'->".posts.hono[':id']" */
28
17
  const formatPath = (path) => {
29
18
  const segs = (path === '/' ? ['index'] : path.replace(/^\/+/, '').split('/')).filter(Boolean);
30
19
  return segs
@@ -35,7 +24,6 @@ const formatPath = (path) => {
35
24
  : `['${esc(seg)}']`)
36
25
  .join('');
37
26
  };
38
- /** 'type' to normalized list for uniform checks */
39
27
  const isJSONTypeName = (s) => typeof s === 'string' &&
40
28
  (s === 'object' ||
41
29
  s === 'array' ||
@@ -51,7 +39,6 @@ const toTypeArray = (t) => {
51
39
  return t.filter(isJSONTypeName);
52
40
  return [];
53
41
  };
54
- /** Build literal union from enum values (kept compact) */
55
42
  const literalFromEnum = (vals) => {
56
43
  const toLit = (v) => typeof v === 'string'
57
44
  ? `'${v.replace(/'/g, "\\'")}'`
@@ -62,7 +49,6 @@ const literalFromEnum = (vals) => {
62
49
  : 'unknown';
63
50
  return vals.map(toLit).join('|');
64
51
  };
65
- /** Create a $ref resolver for #/components/schemas/... */
66
52
  const createResolveRef = (schemas) => (ref) => {
67
53
  if (!ref)
68
54
  return undefined;
@@ -72,60 +58,52 @@ const createResolveRef = (schemas) => (ref) => {
72
58
  const target = schemas[m[1]];
73
59
  return isSchema(target) ? target : undefined;
74
60
  };
75
- /** Create a Schema->TypeScript type printer (single instance, recursive-safe) */
61
+ /** TS type printer (handles $ref / enums / combinators / additionalProperties / nullable) */
76
62
  const createTsTypeFromSchema = (resolveRef) => {
77
63
  const tt = (schema, seen = new Set()) => {
78
64
  if (!schema)
79
65
  return 'unknown';
80
- // $ref resolution
81
66
  if (schema.$ref) {
82
67
  const tgt = resolveRef(schema.$ref);
83
68
  return tt(tgt, seen);
84
69
  }
85
- // recursion guard
86
70
  if (seen.has(schema))
87
71
  return 'unknown';
88
- const nextSeen = new Set(seen);
89
- nextSeen.add(schema);
90
- // combinators
72
+ const next = new Set(seen);
73
+ next.add(schema);
91
74
  if (Array.isArray(schema.oneOf) && schema.oneOf.length)
92
- return schema.oneOf.map((s) => tt(s, nextSeen)).join('|') || 'unknown';
75
+ return schema.oneOf.map((s) => tt(s, next)).join('|') || 'unknown';
93
76
  if (Array.isArray(schema.anyOf) && schema.anyOf.length)
94
- return schema.anyOf.map((s) => tt(s, nextSeen)).join('|') || 'unknown';
77
+ return schema.anyOf.map((s) => tt(s, next)).join('|') || 'unknown';
95
78
  if (Array.isArray(schema.allOf) && schema.allOf.length)
96
- return schema.allOf.map((s) => tt(s, nextSeen)).join('&') || 'unknown';
97
- // enum
79
+ return schema.allOf.map((s) => tt(s, next)).join('&') || 'unknown';
98
80
  if (Array.isArray(schema.enum) && schema.enum.length) {
99
81
  const base = literalFromEnum(schema.enum);
100
82
  return schema.nullable ? `${base}|null` : base;
101
83
  }
102
84
  const types = toTypeArray(schema.type);
103
- // array
85
+ // array (parentheses when inner contains union/intersection)
104
86
  if (types.includes('array')) {
105
- const inner = tt(isSchema(schema.items) ? schema.items : undefined, nextSeen);
106
- const core = `${inner}[]`;
87
+ const item = isSchema(schema.items) ? schema.items : undefined;
88
+ const inner = tt(item, next);
89
+ const needParens = /[|&]/.test(inner) && !/^\(.*\)$/.test(inner);
90
+ const core = `${needParens ? `(${inner})` : inner}[]`;
107
91
  return schema.nullable ? `${core}|null` : core;
108
92
  }
109
- // object
110
93
  if (types.includes('object')) {
111
94
  const req = new Set(Array.isArray(schema.required) ? schema.required : []);
112
95
  const props = schema.properties ?? {};
113
96
  const fields = Object.entries(props).map(([k, v]) => {
114
97
  const opt = req.has(k) ? '' : '?';
115
98
  const child = isSchema(v) ? v : undefined;
116
- return `${k}${opt}:${tt(child, nextSeen)}`;
99
+ return `${k}${opt}:${tt(child, next)}`;
117
100
  });
118
101
  const ap = schema.additionalProperties;
119
- const addl = ap === true
120
- ? '[key:string]:unknown'
121
- : isSchema(ap)
122
- ? `[key:string]:${tt(ap, nextSeen)}`
123
- : '';
102
+ const addl = ap === true ? '[key:string]:unknown' : isSchema(ap) ? `[key:string]:${tt(ap, next)}` : '';
124
103
  const members = [...fields, addl].filter(Boolean).join(',');
125
104
  const core = `{${members}}`;
126
105
  return schema.nullable ? `${core}|null` : core;
127
106
  }
128
- // primitives
129
107
  if (types.length === 0)
130
108
  return schema.nullable ? 'unknown|null' : 'unknown';
131
109
  const prim = types
@@ -144,13 +122,11 @@ const isParameterObject = (v) => {
144
122
  const pos = v.in;
145
123
  return pos === 'path' || pos === 'query' || pos === 'header' || pos === 'cookie';
146
124
  };
147
- /** Extract components/parameters name from a ref-like value */
148
125
  const refParamName = (refLike) => {
149
126
  const ref = typeof refLike === 'string' ? refLike : isRefObject(refLike) ? refLike.$ref : undefined;
150
127
  const m = ref?.match(/^#\/components\/parameters\/(.+)$/);
151
128
  return m ? m[1] : undefined;
152
129
  };
153
- /** Build a resolver that returns normalized ParameterLike (resolving $ref) */
154
130
  const createResolveParameter = (componentsParameters) => (p) => {
155
131
  if (isParameterObject(p))
156
132
  return p;
@@ -158,7 +134,6 @@ const createResolveParameter = (componentsParameters) => (p) => {
158
134
  const cand = name ? componentsParameters[name] : undefined;
159
135
  return isParameterObject(cand) ? cand : undefined;
160
136
  };
161
- /** Convert raw parameters array into ParameterLike[] */
162
137
  const createToParameterLikes = (resolveParam) => (arr) => Array.isArray(arr)
163
138
  ? arr.reduce((acc, x) => {
164
139
  const r = resolveParam(x);
@@ -178,7 +153,7 @@ const HTTP_METHODS = [
178
153
  'patch',
179
154
  'trace',
180
155
  ];
181
- /** Extract the first suitable schema from requestBody.content by priority order */
156
+ const hasSchemaProp = (v) => isRecord(v) && 'schema' in v;
182
157
  const pickBodySchema = (op) => {
183
158
  const rb = op.requestBody;
184
159
  if (!isRecord(rb))
@@ -196,14 +171,12 @@ const pickBodySchema = (op) => {
196
171
  ];
197
172
  for (const k of order) {
198
173
  const media = isRecord(content[k]) ? content[k] : undefined;
199
- if (isRecord(media) && 'schema' in media && isSchema(media.schema)) {
174
+ if (hasSchemaProp(media) && isSchema(media.schema))
200
175
  return media.schema;
201
- }
202
176
  }
203
177
  return undefined;
204
178
  };
205
179
  /* ─────────────────────────────── Args builders ─────────────────────────────── */
206
- /** Build TS type for params arg (compact formatting) */
207
180
  const createBuildParamsType = (tsTypeFromSchema) => (pathParams, queryParams) => {
208
181
  const parts = [];
209
182
  if (pathParams.length) {
@@ -216,28 +189,14 @@ const createBuildParamsType = (tsTypeFromSchema) => (pathParams, queryParams) =>
216
189
  }
217
190
  return parts.length ? `{${parts.join(',')}}` : '';
218
191
  };
219
- /** Build function argument signature */
220
192
  const buildArgSignature = (paramsType, bodyType) => paramsType && bodyType
221
- ? `params:${paramsType}, body:${bodyType}`
193
+ ? `params:${paramsType},body:${bodyType}`
222
194
  : paramsType
223
195
  ? `params:${paramsType}`
224
196
  : bodyType
225
197
  ? `body:${bodyType}`
226
198
  : '';
227
- /** Build one query key:value piece with integer-to-string rules */
228
- const buildQueryPiece = (p) => {
229
- const types = toTypeArray(p.schema?.type);
230
- const isArr = types.includes('array');
231
- const itemsInt = isArr && isSchema(p.schema?.items) && toTypeArray(p.schema?.items?.type).includes('integer');
232
- const isInt = types.includes('integer');
233
- const rhs = itemsInt
234
- ? `(params.query.${p.name}??[]).map((v:unknown)=>String(v))`
235
- : isInt
236
- ? `String(params.query.${p.name})`
237
- : `params.query.${p.name}`;
238
- return `${p.name}:${rhs}`;
239
- };
240
- /** Build client call argument object (compact formatting) */
199
+ /** pass query as-is (keep numbers/arrays) */
241
200
  const buildClientArgs = (pathParams, queryParams, hasBody) => {
242
201
  const pieces = [];
243
202
  if (pathParams.length) {
@@ -245,7 +204,7 @@ const buildClientArgs = (pathParams, queryParams, hasBody) => {
245
204
  pieces.push(`param:{${inner}}`);
246
205
  }
247
206
  if (queryParams.length) {
248
- const inner = queryParams.map(buildQueryPiece).join(',');
207
+ const inner = queryParams.map((p) => `${p.name}:params.query.${p.name}`).join(',');
249
208
  pieces.push(`query:{${inner}}`);
250
209
  }
251
210
  if (hasBody)
@@ -276,36 +235,25 @@ const generateOperationCode = (path, method, item, deps) => {
276
235
  : `${deps.client}${clientAccess}.$${method}()`;
277
236
  const summary = typeof op.summary === 'string' ? op.summary : '';
278
237
  const description = typeof op.description === 'string' ? op.description : '';
279
- return ('/**\n' +
280
- (summary ? ` * ${summary}\n *\n` : '') +
281
- (description ? ` * ${description}\n *\n` : '') +
282
- ` * ${method.toUpperCase()} ${path}\n` +
283
- ' */\n' +
284
- `export async function ${funcName}(${argSig}) {\n` +
285
- ` return await ${call}\n` +
286
- '}');
238
+ const summaryBlock = summary ? ` * ${summary}\n *\n` : '';
239
+ const descriptionBlock = description ? ` * ${description}\n *\n` : '';
240
+ return `/**\n${summaryBlock}${descriptionBlock} * ${method.toUpperCase()} ${path}\n */\nexport async function ${funcName}(${argSig}){return await ${call}}`;
287
241
  };
288
242
  /* ─────────────────────────────── Entry ─────────────────────────────── */
289
- export default function rpc(openapi, importCode) {
243
+ export function rpc(openapi, importPath) {
290
244
  const client = 'client';
291
245
  const out = [];
292
- // import header (kept as-is, then a blank line if present)
293
- const header = (() => {
294
- const s = (importCode ?? '').trim();
295
- return s.length ? `${s}\n\n` : '';
296
- })();
297
- // paths guard
246
+ const s = `import { client } from '${importPath}'`;
247
+ const header = s.length ? `${s}\n\n` : '';
298
248
  const pathsMaybe = openapi.paths;
299
249
  if (!isOpenAPIPaths(pathsMaybe))
300
250
  return header;
301
- // schema & parameter resolvers
302
251
  const schemas = openapi.components?.schemas ?? {};
303
252
  const resolveRef = createResolveRef(schemas);
304
253
  const tsTypeFromSchema = createTsTypeFromSchema(resolveRef);
305
254
  const componentsParameters = openapi.components?.parameters ?? {};
306
255
  const resolveParameter = createResolveParameter(componentsParameters);
307
256
  const toParameterLikes = createToParameterLikes(resolveParameter);
308
- // iterate path items & operations
309
257
  for (const path in pathsMaybe) {
310
258
  const rawItem = pathsMaybe[path];
311
259
  if (!isRecord(rawItem))
@@ -331,6 +279,5 @@ export default function rpc(openapi, importCode) {
331
279
  out.push(code);
332
280
  }
333
281
  }
334
- // final string (compact; Prettier will handle formatting as configured)
335
- return header + out.join('\n\n') + (out.length ? '\n' : '');
282
+ return `${header}${out.join('\n\n')}${out.length ? '\n' : ''}`;
336
283
  }
@@ -7,4 +7,4 @@ import type { OpenAPI } from '../../../openapi/index.js';
7
7
  * @param exportType - Whether to export inferred TypeScript types
8
8
  * @returns The generated TypeScript code string
9
9
  */
10
- export default function zodOpenAPIHono(openapi: OpenAPI, exportSchema: boolean, exportType: boolean): string;
10
+ export declare function zodOpenAPIHono(openapi: OpenAPI, exportSchema: boolean, exportType: boolean): string;
@@ -8,7 +8,7 @@ import { routeCode } from './route/index.js';
8
8
  * @param exportType - Whether to export inferred TypeScript types
9
9
  * @returns The generated TypeScript code string
10
10
  */
11
- export default function zodOpenAPIHono(openapi, exportSchema, exportType) {
11
+ export function zodOpenAPIHono(openapi, exportSchema, exportType) {
12
12
  const components = openapi.components
13
13
  ? componentsCode(openapi.components, exportSchema, exportType)
14
14
  : '';
@@ -12,6 +12,6 @@ import type { Parameters } from '../../../../../openapi/index.js';
12
12
  * - Uses `zodToOpenAPI` for schema conversion and `zodToOpenAPISchema` for output generation.
13
13
  * - Supports conditional `export` statements for both schemas and types.
14
14
  */
15
- export declare function paramsObject(parameters: Parameters[]): {
15
+ export declare function paramsObject(parameters: readonly Parameters[]): {
16
16
  [section: string]: Record<string, string>;
17
17
  };
@@ -11,4 +11,4 @@ import type { Parameters, RequestBody } from '../../../../../openapi/index.js';
11
11
  * - Deduplicates schemas if multiple content types share the same body schema.
12
12
  * - Uses `params` instead of `path` for Hono compatibility.
13
13
  */
14
- export declare function requestParameter(parameters: Parameters[] | undefined, body: RequestBody | undefined): string;
14
+ export declare function requestParameter(parameters: readonly Parameters[] | undefined, body: RequestBody | undefined): string;
@@ -5,7 +5,7 @@ import type { OpenAPI } from '../openapi/index.js';
5
5
  * @param openapi - The OpenAPI specification object.
6
6
  * @returns An array of route mappings, each with route name, handler name, and path.
7
7
  */
8
- export declare function getRouteMaps(openapi: OpenAPI): {
8
+ export declare function getRouteMaps(openapi: OpenAPI): readonly {
9
9
  routeName: string;
10
10
  handlerName: string;
11
11
  path: string;
@@ -1,4 +1,4 @@
1
- import { methodPath } from '../utils/index.js';
1
+ import { isHttpMethod, methodPath } from '../utils/index.js';
2
2
  /**
3
3
  * Extracts route mappings from an OpenAPI specification.
4
4
  *
@@ -6,13 +6,6 @@ import { methodPath } from '../utils/index.js';
6
6
  * @returns An array of route mappings, each with route name, handler name, and path.
7
7
  */
8
8
  export function getRouteMaps(openapi) {
9
- const isHttpMethod = (v) => {
10
- for (const m of ['get', 'put', 'post', 'delete', 'patch', 'options', 'head', 'trace']) {
11
- if (m === v)
12
- return true;
13
- }
14
- return false;
15
- };
16
9
  const paths = openapi.paths;
17
10
  const routeMappings = Object.entries(paths).flatMap(([path, pathItem]) => {
18
11
  return Object.entries(pathItem).flatMap(([method]) => {
@@ -43,4 +43,4 @@ import type { Schema } from '../openapi/index.js';
43
43
  * )
44
44
  * // → 'z.object({user:userSchema.optional(),tags:z.array(tagSchema).optional()}).partial()'
45
45
  */
46
- export declare function propertiesSchema(properties: Record<string, Schema>, required: string[]): string;
46
+ export declare function propertiesSchema(properties: Record<string, Schema>, required: readonly string[]): string;
@@ -23,11 +23,11 @@ import SwaggerParser from '@apidevtools/swagger-parser';
23
23
  * in downstream code.
24
24
  */
25
25
  export declare function parseOpenAPI(input: string): Promise<{
26
- ok: true;
27
- value: OpenAPI;
26
+ readonly ok: true;
27
+ readonly value: OpenAPI;
28
28
  } | {
29
- ok: false;
30
- error: string;
29
+ readonly ok: false;
30
+ readonly error: string;
31
31
  }>;
32
32
  /**
33
33
  * Base OpenAPI type derived from SwaggerParser
@@ -25,9 +25,9 @@ import type { SupportedOpenAPIDocuments } from '@typespec/openapi3';
25
25
  * @returns `{ ok:true, value }` with an OpenAPI document, or `{ ok:false, error }` on failure.
26
26
  */
27
27
  export declare function typeSpecToOpenAPI(input: string): Promise<{
28
- ok: true;
29
- value: SupportedOpenAPIDocuments;
28
+ readonly ok: true;
29
+ readonly value: SupportedOpenAPIDocuments;
30
30
  } | {
31
- ok: false;
32
- error: string;
31
+ readonly ok: false;
32
+ readonly error: string;
33
33
  }>;
@@ -20,19 +20,19 @@
20
20
  * @returns `{ ok:true, value }` on success; `{ ok:false, error }` on invalid usage.
21
21
  */
22
22
  export declare function parseCli(args: readonly string[]): {
23
- ok: true;
24
- value: {
25
- input: `${string}.yaml` | `${string}.json` | `${string}.tsp`;
26
- output: `${string}.ts`;
27
- exportType: boolean;
28
- exportSchema: boolean;
29
- template: boolean;
30
- test: boolean;
31
- basePath?: string;
23
+ readonly ok: true;
24
+ readonly value: {
25
+ readonly input: `${string}.yaml` | `${string}.json` | `${string}.tsp`;
26
+ readonly output: `${string}.ts`;
27
+ readonly exportType: boolean;
28
+ readonly exportSchema: boolean;
29
+ readonly template: boolean;
30
+ readonly test: boolean;
31
+ readonly basePath?: string;
32
32
  };
33
33
  } | {
34
- ok: false;
35
- error: string;
34
+ readonly ok: false;
35
+ readonly error: string;
36
36
  };
37
37
  /**
38
38
  * Normalize a JSON Schema `type` value into an array of type strings.
@@ -77,8 +77,8 @@ export declare function normalizeTypes(t?: 'string' | 'number' | 'integer' | 'da
77
77
  * @returns Array of import statements.
78
78
  */
79
79
  export declare function importRoutes(importsMap: {
80
- [importPath: `${string}.ts`]: string[];
81
- }): string[];
80
+ [importPath: `${string}.ts`]: readonly string[];
81
+ }): readonly string[];
82
82
  /**
83
83
  * Generates registration code for OpenAPI `securitySchemes`.
84
84
  *
@@ -130,12 +130,12 @@ export declare function registerComponent(securitySchemes: {
130
130
  * @param output - The output TypeScript file name (e.g., 'user.ts'). Used to determine the import path.
131
131
  * @returns A record where each key is an import path (e.g., 'user.ts') and the value is an array of route names imported from that path.
132
132
  */
133
- export declare function importMap(routeMappings: {
134
- routeName: string;
135
- handlerName: string;
136
- path: string;
137
- }[], output: `${string}.ts`): {
138
- [importPath: `${string}.ts`]: string[];
133
+ export declare function importMap(routeMappings: Readonly<{
134
+ readonly routeName: string;
135
+ readonly handlerName: string;
136
+ readonly path: string;
137
+ }[]>, output: `${string}.ts`): {
138
+ [importPath: `${string}.ts`]: readonly string[];
139
139
  };
140
140
  /**
141
141
  * Generates import statements for handler functions.
@@ -159,8 +159,8 @@ export declare function importMap(routeMappings: {
159
159
  * @returns An array of import statement strings.
160
160
  */
161
161
  export declare function importHandlers(handlerImportsMap: {
162
- [fileName: string]: string[];
163
- }, output: `${string}.ts`): string[];
162
+ [fileName: string]: readonly string[];
163
+ }, output: `${string}.ts`): readonly string[];
164
164
  /**
165
165
  * Groups route handlers by file name.
166
166
  *
@@ -180,15 +180,15 @@ export declare function importHandlers(handlerImportsMap: {
180
180
  * @returns A deduplicated array of grouped handler definitions per file.
181
181
  */
182
182
  export declare function groupHandlersByFileName(handlers: {
183
- fileName: `${string}.ts`;
184
- testFileName: `${string}.ts`;
185
- routeHandlerContents: string[];
186
- routeNames: string[];
183
+ readonly fileName: `${string}.ts`;
184
+ readonly testFileName: `${string}.ts`;
185
+ readonly routeHandlerContents: readonly string[];
186
+ readonly routeNames: readonly string[];
187
187
  }[]): {
188
- fileName: `${string}.ts`;
189
- testFileName: `${string}.ts`;
190
- routeHandlerContents: string[];
191
- routeNames: string[];
188
+ readonly fileName: `${string}.ts`;
189
+ readonly testFileName: `${string}.ts`;
190
+ readonly routeHandlerContents: readonly string[];
191
+ readonly routeNames: readonly string[];
192
192
  }[];
193
193
  /**
194
194
  * Generates a map of handler file names to handler names.
@@ -210,12 +210,12 @@ export declare function groupHandlersByFileName(handlers: {
210
210
  * @param handlerMaps - Array of route mappings including route name, handler name, and path.
211
211
  * @returns A map where keys are handler file names and values are arrays of handler names.
212
212
  */
213
- export declare function getHandlerImports(handlerMaps: {
214
- routeName: string;
215
- handlerName: string;
216
- path: string;
217
- }[]): {
218
- [fileName: `${string}.ts`]: string[];
213
+ export declare function getHandlerImports(handlerMaps: Readonly<{
214
+ readonly routeName: string;
215
+ readonly handlerName: string;
216
+ readonly path: string;
217
+ }[]>): {
218
+ [fileName: `${string}.ts`]: readonly string[];
219
219
  };
220
220
  /**
221
221
  * Checks if a value is a non-null object (e.g., a potential `$ref` object).
@@ -238,9 +238,16 @@ export declare function getHandlerImports(handlerMaps: {
238
238
  * ```
239
239
  */
240
240
  export declare function isRefObject(value: unknown): value is {
241
- $ref?: string;
242
- [key: string]: unknown;
241
+ readonly $ref?: string;
242
+ readonly [key: string]: unknown;
243
243
  };
244
+ /**
245
+ * Checks if a string is a valid HTTP method.
246
+ *
247
+ * @param method - The HTTP method to check.
248
+ * @returns `true` if the method is a valid HTTP method; otherwise `false`.
249
+ */
250
+ export declare function isHttpMethod(method: string): method is 'get' | 'put' | 'post' | 'delete' | 'patch' | 'options' | 'head' | 'trace';
244
251
  /**
245
252
  * Checks if all given content types share the same schema definition.
246
253
  *
@@ -266,10 +273,10 @@ export declare function isRefObject(value: unknown): value is {
266
273
  * }) // true
267
274
  * ```
268
275
  */
269
- export declare function isUniqueContentSchema(contentTypes: string[], content: {
276
+ export declare function isUniqueContentSchema(contentTypes: readonly string[], content: {
270
277
  [key: string]: {
271
278
  schema: {
272
- $ref?: `#/components/schemas/${string}`;
279
+ readonly $ref?: `#/components/schemas/${string}`;
273
280
  };
274
281
  };
275
282
  }): boolean;
@@ -340,16 +347,16 @@ export declare function methodPath(method: string, path: string): string;
340
347
  * // => export const getUserRoute = createRoute({method:"get",path:"/user",request:{query:...},responses:{...}})
341
348
  */
342
349
  export declare function createRoute(args: {
343
- routeName: string;
344
- tags?: string;
345
- method: string;
346
- path: string;
347
- operationId?: string;
348
- summary?: string;
349
- description?: string;
350
- security?: string;
351
- requestParams: string;
352
- responses: string;
350
+ readonly routeName: string;
351
+ readonly tags?: string;
352
+ readonly method: string;
353
+ readonly path: string;
354
+ readonly operationId?: string;
355
+ readonly summary?: string;
356
+ readonly description?: string;
357
+ readonly security?: string;
358
+ readonly requestParams: string;
359
+ readonly responses: string;
353
360
  }): string;
354
361
  /**
355
362
  * Generates an array of Zod validator strings from OpenAPI parameter objects.
@@ -371,7 +378,7 @@ export declare function createRoute(args: {
371
378
  */
372
379
  export declare function requestParamsArray(parameters: {
373
380
  [section: string]: Record<string, string>;
374
- }): string[];
381
+ }): readonly string[];
375
382
  /**
376
383
  * Escapes a string for safe use in TypeScript string literals.
377
384
  *
@@ -302,6 +302,22 @@ export function getHandlerImports(handlerMaps) {
302
302
  export function isRefObject(value) {
303
303
  return typeof value === 'object' && value !== null;
304
304
  }
305
+ /**
306
+ * Checks if a string is a valid HTTP method.
307
+ *
308
+ * @param method - The HTTP method to check.
309
+ * @returns `true` if the method is a valid HTTP method; otherwise `false`.
310
+ */
311
+ export function isHttpMethod(method) {
312
+ return (method === 'get' ||
313
+ method === 'put' ||
314
+ method === 'post' ||
315
+ method === 'delete' ||
316
+ method === 'patch' ||
317
+ method === 'options' ||
318
+ method === 'head' ||
319
+ method === 'trace');
320
+ }
305
321
  /**
306
322
  * Checks if all given content types share the same schema definition.
307
323
  *
@@ -1,7 +1,7 @@
1
1
  import fsp from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { fmt } from '../format/index.js';
4
- import zodOpenAPIHono from '../generator/zod-openapi-hono/openapi/index.js';
4
+ import { zodOpenAPIHono } from '../generator/zod-openapi-hono/openapi/index.js';
5
5
  import { parseOpenAPI } from '../openapi/index.js';
6
6
  /**
7
7
  * Vite plugin to generate Hono routes from an OpenAPI or TypeSpec file.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hono-takibi",
3
3
  "description": "Hono Takibi is a CLI tool that generates Hono routes from OpenAPI specifications.",
4
- "version": "0.9.22",
4
+ "version": "0.9.24",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "keywords": [