hono-takibi 0.9.99992 → 0.9.99994

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 CHANGED
@@ -486,6 +486,11 @@ export default defineConfig({
486
486
 
487
487
  mock: {
488
488
  output: './src/mock.ts',
489
+ useExamples: true,
490
+ locale: 'en',
491
+ delay: false,
492
+ arrayMin: 1,
493
+ arrayMax: 10,
489
494
  },
490
495
 
491
496
  docs: {
@@ -280,6 +280,14 @@ declare const ConfigSchema: z.ZodReadonly<z.ZodObject<{
280
280
  }, z.core.$strip>>>;
281
281
  mock: z.ZodExactOptional<z.ZodReadonly<z.ZodObject<{
282
282
  output: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
283
+ useExamples: z.ZodExactOptional<z.ZodBoolean>;
284
+ locale: z.ZodExactOptional<z.ZodString>;
285
+ delay: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodLiteral<false>, z.ZodReadonly<z.ZodObject<{
286
+ min: z.ZodNumber;
287
+ max: z.ZodNumber;
288
+ }, z.core.$strip>>]>>;
289
+ arrayMin: z.ZodExactOptional<z.ZodNumber>;
290
+ arrayMax: z.ZodExactOptional<z.ZodNumber>;
283
291
  }, z.core.$strip>>>;
284
292
  docs: z.ZodExactOptional<z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
285
293
  output: z.ZodTemplateLiteral<`${string}.md`>;
@@ -565,6 +573,14 @@ declare function parseConfig(config: unknown): {
565
573
  }>;
566
574
  mock?: Readonly<{
567
575
  output: string;
576
+ useExamples?: boolean;
577
+ locale?: string;
578
+ delay?: number | false | Readonly<{
579
+ min: number;
580
+ max: number;
581
+ }>;
582
+ arrayMin?: number;
583
+ arrayMax?: number;
568
584
  }>;
569
585
  docs?: Readonly<{
570
586
  output: `${string}.md`;
@@ -852,6 +868,14 @@ declare function readConfig(): Promise<{
852
868
  }>;
853
869
  mock?: Readonly<{
854
870
  output: string;
871
+ useExamples?: boolean;
872
+ locale?: string;
873
+ delay?: number | false | Readonly<{
874
+ min: number;
875
+ max: number;
876
+ }>;
877
+ arrayMin?: number;
878
+ arrayMax?: number;
855
879
  }>;
856
880
  docs?: Readonly<{
857
881
  output: `${string}.md`;
@@ -1136,6 +1160,14 @@ declare function defineConfig(config: z.input<typeof ConfigSchema>): Readonly<{
1136
1160
  }>;
1137
1161
  mock?: Readonly<{
1138
1162
  output: string;
1163
+ useExamples?: boolean;
1164
+ locale?: string;
1165
+ delay?: number | false | Readonly<{
1166
+ min: number;
1167
+ max: number;
1168
+ }>;
1169
+ arrayMin?: number;
1170
+ arrayMax?: number;
1139
1171
  }>;
1140
1172
  docs?: Readonly<{
1141
1173
  output: `${string}.md`;
@@ -148,7 +148,21 @@ const ConfigSchema = z.object({
148
148
  "bun"
149
149
  ]).default("vitest").exactOptional()
150
150
  }).readonly().exactOptional(),
151
- mock: z.object({ output: FileOutputSchema }).readonly().exactOptional(),
151
+ mock: z.object({
152
+ output: FileOutputSchema,
153
+ useExamples: z.boolean().exactOptional(),
154
+ locale: z.string().regex(/^[A-Za-z_]{1,40}$/, { error: "Invalid faker locale. Use a code like 'ja', 'en', or 'zh_CN'." }).exactOptional(),
155
+ delay: z.union([
156
+ z.number().int().nonnegative().max(6e4),
157
+ z.literal(false),
158
+ z.object({
159
+ min: z.number().int().nonnegative().max(6e4),
160
+ max: z.number().int().nonnegative().max(6e4)
161
+ }).readonly().refine((v) => v.min <= v.max, { message: "delay.min must be <= delay.max. Swap the values or remove one." })
162
+ ]).exactOptional(),
163
+ arrayMin: z.number().int().nonnegative().max(1e3).exactOptional(),
164
+ arrayMax: z.number().int().nonnegative().max(1e3).exactOptional()
165
+ }).readonly().refine((v) => v.arrayMin === void 0 || v.arrayMax === void 0 || v.arrayMin <= v.arrayMax, { message: "arrayMin must be <= arrayMax. Swap the values or remove one." }).exactOptional(),
152
166
  docs: z.discriminatedUnion("curl", [z.object({
153
167
  output: z.templateLiteral([z.string().min(1), z.enum([".md"])], { error: "must be .md file" }),
154
168
  curl: z.literal(true),
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { readConfig } from "./config/index.js";
3
3
  import { r as setFormatOptions } from "./emit-CFR63U4L.js";
4
- import { n as parseOpenAPI, r as takibi, t as makeJob } from "./shared-DRGflwP5.js";
4
+ import { n as parseOpenAPI, r as takibi, t as makeJob } from "./shared-n3ZMLfl9.js";
5
5
  import { existsSync } from "node:fs";
6
6
  import { resolve } from "node:path";
7
7
  //#region src/cli/index.ts
@@ -514,7 +514,7 @@ const FORMAT_TO_FAKER = {
514
514
  gender: "faker.person.gender()",
515
515
  bic: "faker.finance.bic()",
516
516
  iban: "faker.finance.iban()",
517
- binary: "new Blob([faker.string.alphanumeric(100)])",
517
+ binary: "new File([faker.string.alphanumeric(100)], faker.system.fileName())",
518
518
  byte: "btoa(faker.string.alphanumeric(10))",
519
519
  int32: "faker.number.int({ min: -2147483648, max: 2147483647 })",
520
520
  int64: "faker.number.bigInt({ min: 0n, max: 9007199254740991n })",
@@ -594,6 +594,19 @@ function numericFaker(schema, range) {
594
594
  `fractionDigits: ${fractionDigits}`
595
595
  ]).join(", ")} })`;
596
596
  }
597
+ function resolveRef(schema, schemas) {
598
+ if (!schema.$ref) return schema;
599
+ const name = schema.$ref.split("/").at(-1);
600
+ return (name ? schemas?.[name] : void 0) ?? schema;
601
+ }
602
+ function isKnownScalar(schema, schemas) {
603
+ const resolved = resolveRef(schema, schemas);
604
+ if (resolved.enum || resolved.const !== void 0) return true;
605
+ if (resolved.properties || resolved.type === "object") return false;
606
+ if (resolved.allOf && resolved.allOf.length > 0) return resolved.allOf.every((s) => isKnownScalar(s, schemas));
607
+ if (resolved.oneOf || resolved.anyOf || resolved.type === "array") return false;
608
+ return typeof resolved.type === "string";
609
+ }
597
610
  function schemaToFaker(schema, propertyName, options = {}) {
598
611
  if (options.useExamples && schema.example !== void 0) return JSON.stringify(schema.example);
599
612
  if (schema.const !== void 0) return `${JSON.stringify(schema.const)} as const`;
@@ -602,7 +615,9 @@ function schemaToFaker(schema, propertyName, options = {}) {
602
615
  if (schema.type === "array" && schema.items) {
603
616
  const itemSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;
604
617
  if (!itemSchema) return "[]";
605
- return `Array.from({ length: faker.number.int({ min: 1, max: 5 }) }, () => (${schemaToFaker(itemSchema, void 0, options)}))`;
618
+ const itemFaker = schemaToFaker(itemSchema, void 0, options);
619
+ const min = schema.minItems ?? options.arrayMin ?? 1;
620
+ return `Array.from({ length: faker.number.int({ min: ${min}, max: ${schema.maxItems ?? options.arrayMax ?? Math.max(min, 10)} }) }, () => (${itemFaker}))`;
606
621
  }
607
622
  const renderProps = (properties, required) => {
608
623
  const requiredSet = new Set(required || []);
@@ -616,8 +631,19 @@ function schemaToFaker(schema, propertyName, options = {}) {
616
631
  if (schema.type === "object" && schema.properties) return `{ ${renderProps(schema.properties, schema.required)} }`;
617
632
  if (schema.type === "object" && !schema.properties && schema.additionalProperties) return "{}";
618
633
  if (schema.allOf && schema.allOf.length > 0) {
619
- const merged = schema.allOf.map((s) => schemaToFaker(s, propertyName, options)).map((m) => `...${m}`).join(", ");
620
- if (schema.properties) return `{ ${merged}, ${renderProps(schema.properties, schema.required)} }`;
634
+ if (!schema.properties && schema.allOf.every((s) => isKnownScalar(s, options.schemas))) {
635
+ const [only] = schema.allOf;
636
+ if (schema.allOf.length === 1 && only) return schemaToFaker(only, propertyName, options);
637
+ return schemaToFaker(schema.allOf.map((s) => resolveRef(s, options.schemas)).reduce((acc, s) => ({
638
+ ...acc,
639
+ ...s
640
+ }), {}), propertyName, options);
641
+ }
642
+ const merged = schema.allOf.filter((s) => !isKnownScalar(s, options.schemas)).map((s) => `...${schemaToFaker(s, propertyName, options)}`).join(", ");
643
+ if (schema.properties) {
644
+ const props = renderProps(schema.properties, schema.required);
645
+ return merged.length > 0 ? `{ ${merged}, ${props} }` : `{ ${props} }`;
646
+ }
621
647
  return `{ ${merged} }`;
622
648
  }
623
649
  const union = schema.oneOf && schema.oneOf.length > 0 ? schema.oneOf : schema.anyOf && schema.anyOf.length > 0 ? schema.anyOf : void 0;
@@ -631,7 +657,8 @@ function schemaToFaker(schema, propertyName, options = {}) {
631
657
  if (schema.type && typeof schema.type === "string" && TYPE_TO_FAKER[schema.type]) {
632
658
  if (schema.type === "string") {
633
659
  if (schema.pattern) return `faker.helpers.fromRegExp(/${escapeRegexLiteral(schema.pattern)}/)`;
634
- return `faker.string.alpha({ length: { min: ${schema.minLength ?? 5}, max: ${schema.maxLength ?? 20} } })`;
660
+ const min = schema.minLength ?? 5;
661
+ return `faker.string.alpha({ length: { min: ${min}, max: ${schema.maxLength ?? Math.max(min, 20)} } })`;
635
662
  }
636
663
  if (schema.type === "integer" || schema.type === "number") return numericFaker(schema, numericRange(schema, schema.type === "integer"));
637
664
  return TYPE_TO_FAKER[schema.type];
@@ -710,11 +737,40 @@ function detectCircularSchemas$1(schemas) {
710
737
  }
711
738
  return circular;
712
739
  }
713
- function makeMockFunction(name, schema, schemas, isCircular) {
714
- const mockBody = schemaToFaker(schema, void 0, { schemas });
740
+ function collectSchemaRefs$1(node, refs) {
741
+ if (Array.isArray(node)) {
742
+ for (const item of node) collectSchemaRefs$1(item, refs);
743
+ return refs;
744
+ }
745
+ if (node !== null && typeof node === "object") for (const [key, value] of Object.entries(node)) if (key === "$ref" && typeof value === "string" && value.includes("/components/schemas/")) {
746
+ const name = value.split("/").at(-1);
747
+ if (name) refs.add(name);
748
+ } else collectSchemaRefs$1(value, refs);
749
+ return refs;
750
+ }
751
+ function schemaClosure(roots, schemas) {
752
+ const used = /* @__PURE__ */ new Set();
753
+ const stack = [...roots];
754
+ while (stack.length > 0) {
755
+ const name = stack.pop();
756
+ if (name === void 0 || used.has(name)) continue;
757
+ used.add(name);
758
+ const schema = schemas[name];
759
+ if (schema) {
760
+ for (const ref of collectSchemaRefs$1(schema, /* @__PURE__ */ new Set())) if (!used.has(ref)) stack.push(ref);
761
+ }
762
+ }
763
+ return used;
764
+ }
765
+ function makeMockFunction(name, schema, schemas, isCircular, fakerOptions) {
766
+ const mockBody = schemaToFaker(schema, void 0, {
767
+ schemas,
768
+ ...fakerOptions
769
+ });
715
770
  const returnType = isCircular ? ": any" : "";
716
771
  const sanitized = name.replace(/\./g, "");
717
- return `function mock${sanitized}()${returnType}{return ${schema["x-brand"] ? `${mockBody} as z.infer<typeof ${sanitized}Schema>` : mockBody}}`;
772
+ const schemaConst = toIdentifierPascalCase(ensureSuffix(name, "Schema"));
773
+ return `function mock${sanitized}()${returnType}{return ${schema["x-brand"] ? `${mockBody} as z.infer<typeof ${schemaConst}>` : mockBody}}`;
718
774
  }
719
775
  function extractSecurityInfo(opSecurity, globalSecurity, securitySchemes) {
720
776
  return (opSecurity ?? globalSecurity ?? []).flatMap((secDef) => Object.keys(secDef).flatMap((schemeName) => {
@@ -834,16 +890,46 @@ function makeAuthCheck(security, has401) {
834
890
  if (authChecks.length === 0) return "";
835
891
  return `if(!(${authChecks.join(" || ")})){return c.json({ message: 'Unauthorized' }, 401)}`;
836
892
  }
837
- function makeHandlerBody(statusCode, jsonSchema, textSchema, schemas, allRefs) {
893
+ function extractMediaExample(media, components) {
894
+ if (media.example !== void 0) return media.example;
895
+ if (!media.examples) return void 0;
896
+ const first = Object.values(media.examples)[0];
897
+ if (!first) return void 0;
898
+ if ("$ref" in first && typeof first.$ref === "string") {
899
+ const name = first.$ref.split("/").at(-1);
900
+ const resolved = name ? components?.examples?.[name] : void 0;
901
+ return resolved && "value" in resolved ? resolved.value : void 0;
902
+ }
903
+ return "value" in first ? first.value : void 0;
904
+ }
905
+ function makeHandlerBody(args) {
906
+ const { statusCode, jsonSchema, textSchema, schemas, fakerOptions, allRefs, exampleValue, exampleCast } = args;
907
+ if (exampleValue !== void 0) {
908
+ const cast = exampleCast ? ` as z.infer<typeof ${exampleCast}>` : "";
909
+ return `return c.json(${JSON.stringify(exampleValue)}${cast}, ${statusCode})`;
910
+ }
838
911
  if (jsonSchema) {
839
912
  collectRefs(jsonSchema, allRefs);
840
- return `return c.json(${schemaToFaker(jsonSchema, void 0, { schemas })}, ${statusCode})`;
913
+ return `return c.json(${schemaToFaker(jsonSchema, void 0, {
914
+ schemas,
915
+ ...fakerOptions
916
+ })}, ${statusCode})`;
841
917
  }
842
- if (textSchema) return `return c.text(${schemaToFaker(textSchema, void 0, { schemas })}, ${statusCode})`;
918
+ if (textSchema) return `return c.text(${schemaToFaker(textSchema, void 0, {
919
+ schemas,
920
+ ...fakerOptions
921
+ })}, ${statusCode})`;
843
922
  if (statusCode === 204) return `return new Response(null, { status: 204 })`;
844
923
  return `return c.body(null, ${statusCode})`;
845
924
  }
925
+ function delayMiddlewareCode(delay) {
926
+ const ms = typeof delay === "number" ? `${delay}` : typeof delay === "object" && delay !== null ? `faker.number.int({ min: ${delay.min}, max: ${delay.max} })` : void 0;
927
+ if (ms === void 0) return "";
928
+ return `\n\napp.use(async (_c, next) => {\n await new Promise((resolve) => setTimeout(resolve, ${ms}))\n await next()\n})`;
929
+ }
846
930
  function makeMock(openapi, basePath, options = {}) {
931
+ const { useExamples: useExamplesOption, locale, delay, readonly: readonlyOption, ...fakerOptions } = options;
932
+ const useExamples = useExamplesOption ?? true;
847
933
  const filteredOpenapi = filterToJsonContentTypes(openapi);
848
934
  const paths = filteredOpenapi.paths;
849
935
  const schemas = openapi.components?.schemas ?? {};
@@ -860,7 +946,19 @@ function makeMock(openapi, basePath, options = {}) {
860
946
  const successResponse = success?.response;
861
947
  const jsonMedia = successResponse?.content?.["application/json"];
862
948
  const textMedia = successResponse?.content?.["text/plain"];
863
- const handlerBody = makeHandlerBody(statusCode, jsonMedia && isMediaWithSchema(jsonMedia) ? jsonMedia.schema : void 0, textMedia && isMediaWithSchema(textMedia) ? textMedia.schema : void 0, schemas, allRefs);
949
+ const jsonSchema = jsonMedia && isMediaWithSchema(jsonMedia) ? jsonMedia.schema : void 0;
950
+ const textSchema = textMedia && isMediaWithSchema(textMedia) ? textMedia.schema : void 0;
951
+ const exampleValue = useExamples && jsonMedia ? extractMediaExample(jsonMedia, openapi.components) : void 0;
952
+ const handlerBody = makeHandlerBody({
953
+ statusCode,
954
+ jsonSchema,
955
+ textSchema,
956
+ schemas,
957
+ fakerOptions,
958
+ allRefs,
959
+ exampleValue,
960
+ exampleCast: exampleValue !== void 0 && jsonSchema?.$ref ? toIdentifierPascalCase(ensureSuffix(jsonSchema.$ref.split("/").at(-1) ?? "", "Schema")) : void 0
961
+ });
864
962
  const authCheck = makeAuthCheck(security, operation.responses?.[String(401)] !== void 0);
865
963
  const handler = `const ${routeId}RouteHandler: RouteHandler<typeof ${routeId}Route> = async (${handlerBody.includes("c.") || authCheck !== "" ? "c" : "_c"}) => {${authCheck}${handlerBody}}`;
866
964
  return [{
@@ -879,8 +977,23 @@ function makeMock(openapi, basePath, options = {}) {
879
977
  for (const ref of allRefs) collectAllDependencies(ref, schemas, allDeps);
880
978
  const sortedRefs = topologicalSort(allDeps, schemas);
881
979
  const circularSchemas = detectCircularSchemas$1(schemas);
882
- const mockFunctions = sortedRefs.filter((refName) => schemas[refName]).map((refName) => makeMockFunction(refName, schemas[refName], schemas, circularSchemas.has(refName)));
883
- const components = openapi.components ? componentsCode(openapi.components, {
980
+ const mockFunctions = sortedRefs.filter((refName) => schemas[refName]).map((refName) => makeMockFunction(refName, schemas[refName], schemas, circularSchemas.has(refName), fakerOptions));
981
+ const rootRefs = collectSchemaRefs$1(filteredOpenapi.paths, /* @__PURE__ */ new Set());
982
+ if (openapi.components) {
983
+ collectSchemaRefs$1(openapi.components.responses, rootRefs);
984
+ collectSchemaRefs$1(openapi.components.parameters, rootRefs);
985
+ collectSchemaRefs$1(openapi.components.requestBodies, rootRefs);
986
+ collectSchemaRefs$1(openapi.components.headers, rootRefs);
987
+ collectSchemaRefs$1(openapi.components.callbacks, rootRefs);
988
+ collectSchemaRefs$1(openapi.components.pathItems, rootRefs);
989
+ collectSchemaRefs$1(openapi.components.links, rootRefs);
990
+ }
991
+ const usedSchemaNames = schemaClosure(rootRefs, schemas);
992
+ const filteredComponents = openapi.components ? {
993
+ ...openapi.components,
994
+ schemas: Object.fromEntries(Object.entries(schemas).filter(([name]) => usedSchemaNames.has(name)))
995
+ } : void 0;
996
+ const components = filteredComponents ? componentsCode(filteredComponents, {
884
997
  exportSchemasTypes: false,
885
998
  exportSchemas: false,
886
999
  exportParametersTypes: false,
@@ -896,14 +1009,16 @@ function makeMock(openapi, basePath, options = {}) {
896
1009
  exportPathItems: false,
897
1010
  exportMediaTypes: false,
898
1011
  exportMediaTypesTypes: false,
899
- ...options.readonly !== void 0 ? { readonly: options.readonly } : {}
1012
+ ...readonlyOption !== void 0 ? { readonly: readonlyOption } : {}
900
1013
  }) : "";
901
- const routes = routeCode(filteredOpenapi, options.readonly);
1014
+ const routes = routeCode(filteredOpenapi, readonlyOption);
902
1015
  const appSetup = routeMetas.map(({ routeId }) => `.openapi(${routeId}Route, ${routeId}RouteHandler)`).join("");
903
1016
  const handlersJoined = handlers.join("\n\n");
1017
+ const needsCookieImport = handlersJoined.includes("getCookie(c,");
904
1018
  const imports = `import { OpenAPIHono, createRoute, z, type RouteHandler } from '@hono/zod-openapi'
905
- import { faker } from '@faker-js/faker'${handlersJoined.includes("getCookie(c,") ? `\nimport { getCookie } from 'hono/cookie'` : ""}`;
906
- const appCode = `const app = new OpenAPIHono()${basePath !== "/" ? `.basePath('${basePath}')` : ""}
1019
+ ${locale ? `import { faker } from '@faker-js/faker/locale/${locale}'` : `import { faker } from '@faker-js/faker'`}${needsCookieImport ? `\nimport { getCookie } from 'hono/cookie'` : ""}`;
1020
+ const delayMiddleware = delayMiddlewareCode(delay);
1021
+ const appCode = `const app = new OpenAPIHono()${basePath !== "/" ? `.basePath('${basePath}')` : ""}${delayMiddleware}
907
1022
 
908
1023
  export const api = app${appSetup}
909
1024
 
@@ -919,8 +1034,8 @@ export default app`;
919
1034
  }
920
1035
  //#endregion
921
1036
  //#region src/core/mock/index.ts
922
- async function mock(openAPI, output, basePath, readonly) {
923
- const emitResult = await emit(makeMock(openAPI, basePath, readonly !== void 0 ? { readonly } : {}), path.dirname(output), output);
1037
+ async function mock(openAPI, output, basePath, options = {}) {
1038
+ const emitResult = await emit(makeMock(openAPI, basePath, options), path.dirname(output), output);
924
1039
  if (!emitResult.ok) return {
925
1040
  ok: false,
926
1041
  error: emitResult.error
@@ -2765,7 +2880,10 @@ function makeJob(openAPI, config) {
2765
2880
  name: "mock",
2766
2881
  output: config.mock.output,
2767
2882
  split: false,
2768
- run: (output) => mock(openAPI, output, config.basePath, config.readonly)
2883
+ run: (output) => mock(openAPI, output, config.basePath, {
2884
+ ...config.mock,
2885
+ ...config.readonly !== void 0 ? { readonly: config.readonly } : {}
2886
+ })
2769
2887
  } : void 0,
2770
2888
  config.docs ? {
2771
2889
  name: "docs",
@@ -1,7 +1,7 @@
1
1
  import { parseConfig } from "../config/index.js";
2
2
  import { r as setFormatOptions } from "../emit-CFR63U4L.js";
3
3
  import { f as isRecord } from "../guard-zvkMUVYD.js";
4
- import { n as parseOpenAPI, t as makeJob } from "../shared-DRGflwP5.js";
4
+ import { n as parseOpenAPI, t as makeJob } from "../shared-n3ZMLfl9.js";
5
5
  import path from "node:path";
6
6
  import fsp from "node:fs/promises";
7
7
  //#region src/vite-plugin/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono-takibi",
3
- "version": "0.9.99992",
3
+ "version": "0.9.99994",
4
4
  "description": "Hono Takibi is a code generator from OpenAPI to @hono/zod-openapi",
5
5
  "keywords": [
6
6
  "hono",
@@ -63,7 +63,7 @@
63
63
  "zod": "^4.4.3"
64
64
  },
65
65
  "devDependencies": {
66
- "@types/node": "^26.0.0",
66
+ "@types/node": "^26.0.1",
67
67
  "@typespec/http": "^1.13.0",
68
68
  "@typespec/rest": "^0.83.0",
69
69
  "@typespec/versioning": "^0.83.0",