hono-takibi 0.9.99992 → 0.9.99993

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/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-DC4zuL9A.js";
5
5
  import { existsSync } from "node:fs";
6
6
  import { resolve } from "node:path";
7
7
  //#region src/cli/index.ts
@@ -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`;
@@ -616,8 +629,19 @@ function schemaToFaker(schema, propertyName, options = {}) {
616
629
  if (schema.type === "object" && schema.properties) return `{ ${renderProps(schema.properties, schema.required)} }`;
617
630
  if (schema.type === "object" && !schema.properties && schema.additionalProperties) return "{}";
618
631
  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)} }`;
632
+ if (!schema.properties && schema.allOf.every((s) => isKnownScalar(s, options.schemas))) {
633
+ const [only] = schema.allOf;
634
+ if (schema.allOf.length === 1 && only) return schemaToFaker(only, propertyName, options);
635
+ return schemaToFaker(schema.allOf.map((s) => resolveRef(s, options.schemas)).reduce((acc, s) => ({
636
+ ...acc,
637
+ ...s
638
+ }), {}), propertyName, options);
639
+ }
640
+ const merged = schema.allOf.filter((s) => !isKnownScalar(s, options.schemas)).map((s) => `...${schemaToFaker(s, propertyName, options)}`).join(", ");
641
+ if (schema.properties) {
642
+ const props = renderProps(schema.properties, schema.required);
643
+ return merged.length > 0 ? `{ ${merged}, ${props} }` : `{ ${props} }`;
644
+ }
621
645
  return `{ ${merged} }`;
622
646
  }
623
647
  const union = schema.oneOf && schema.oneOf.length > 0 ? schema.oneOf : schema.anyOf && schema.anyOf.length > 0 ? schema.anyOf : void 0;
@@ -710,11 +734,37 @@ function detectCircularSchemas$1(schemas) {
710
734
  }
711
735
  return circular;
712
736
  }
737
+ function collectSchemaRefs$1(node, refs) {
738
+ if (Array.isArray(node)) {
739
+ for (const item of node) collectSchemaRefs$1(item, refs);
740
+ return refs;
741
+ }
742
+ if (node !== null && typeof node === "object") for (const [key, value] of Object.entries(node)) if (key === "$ref" && typeof value === "string" && value.includes("/components/schemas/")) {
743
+ const name = value.split("/").at(-1);
744
+ if (name) refs.add(name);
745
+ } else collectSchemaRefs$1(value, refs);
746
+ return refs;
747
+ }
748
+ function schemaClosure(roots, schemas) {
749
+ const used = /* @__PURE__ */ new Set();
750
+ const stack = [...roots];
751
+ while (stack.length > 0) {
752
+ const name = stack.pop();
753
+ if (name === void 0 || used.has(name)) continue;
754
+ used.add(name);
755
+ const schema = schemas[name];
756
+ if (schema) {
757
+ for (const ref of collectSchemaRefs$1(schema, /* @__PURE__ */ new Set())) if (!used.has(ref)) stack.push(ref);
758
+ }
759
+ }
760
+ return used;
761
+ }
713
762
  function makeMockFunction(name, schema, schemas, isCircular) {
714
763
  const mockBody = schemaToFaker(schema, void 0, { schemas });
715
764
  const returnType = isCircular ? ": any" : "";
716
765
  const sanitized = name.replace(/\./g, "");
717
- return `function mock${sanitized}()${returnType}{return ${schema["x-brand"] ? `${mockBody} as z.infer<typeof ${sanitized}Schema>` : mockBody}}`;
766
+ const schemaConst = toIdentifierPascalCase(ensureSuffix(name, "Schema"));
767
+ return `function mock${sanitized}()${returnType}{return ${schema["x-brand"] ? `${mockBody} as z.infer<typeof ${schemaConst}>` : mockBody}}`;
718
768
  }
719
769
  function extractSecurityInfo(opSecurity, globalSecurity, securitySchemes) {
720
770
  return (opSecurity ?? globalSecurity ?? []).flatMap((secDef) => Object.keys(secDef).flatMap((schemeName) => {
@@ -834,7 +884,67 @@ function makeAuthCheck(security, has401) {
834
884
  if (authChecks.length === 0) return "";
835
885
  return `if(!(${authChecks.join(" || ")})){return c.json({ message: 'Unauthorized' }, 401)}`;
836
886
  }
837
- function makeHandlerBody(statusCode, jsonSchema, textSchema, schemas, allRefs) {
887
+ function extractMediaExample(media, components) {
888
+ if (media.example !== void 0) return media.example;
889
+ if (!media.examples) return void 0;
890
+ const first = Object.values(media.examples)[0];
891
+ if (!first) return void 0;
892
+ if ("$ref" in first && typeof first.$ref === "string") {
893
+ const name = first.$ref.split("/").at(-1);
894
+ const resolved = name ? components?.examples?.[name] : void 0;
895
+ return resolved && "value" in resolved ? resolved.value : void 0;
896
+ }
897
+ return "value" in first ? first.value : void 0;
898
+ }
899
+ const PAGE_PARAM_NAMES = [
900
+ "page",
901
+ "pageNumber",
902
+ "page_number"
903
+ ];
904
+ const OFFSET_PARAM_NAMES = ["offset", "skip"];
905
+ const ROWS_PARAM_NAMES = [
906
+ "rows",
907
+ "limit",
908
+ "perPage",
909
+ "per_page",
910
+ "pageSize",
911
+ "page_size",
912
+ "size",
913
+ "take"
914
+ ];
915
+ function detectPagination(operation) {
916
+ if (operation["x-pagination"] !== true) return void 0;
917
+ const queryParams = (operation.parameters ?? []).filter((p) => "in" in p && p.in === "query");
918
+ const rows = queryParams.find((p) => ROWS_PARAM_NAMES.includes(p.name));
919
+ if (!rows) return void 0;
920
+ const pageParam = queryParams.find((p) => PAGE_PARAM_NAMES.includes(p.name));
921
+ const offsetParam = pageParam ? void 0 : queryParams.find((p) => OFFSET_PARAM_NAMES.includes(p.name));
922
+ const cursor = pageParam ?? offsetParam;
923
+ if (!cursor) return void 0;
924
+ const defaultRows = typeof rows.schema?.default === "number" ? rows.schema.default : 20;
925
+ return {
926
+ rowsParam: rows.name,
927
+ cursorParam: cursor.name,
928
+ offsetStyle: offsetParam !== void 0,
929
+ defaultRows
930
+ };
931
+ }
932
+ function makeHandlerBody(args) {
933
+ const { statusCode, jsonSchema, textSchema, schemas, allRefs, exampleValue, exampleCast, pagination } = args;
934
+ if (exampleValue !== void 0) {
935
+ const cast = exampleCast ? ` as z.infer<typeof ${exampleCast}>` : "";
936
+ return `return c.json(${JSON.stringify(exampleValue)}${cast}, ${statusCode})`;
937
+ }
938
+ if (pagination && jsonSchema?.type === "array" && jsonSchema.items) {
939
+ collectRefs(jsonSchema, allRefs);
940
+ const itemFaker = schemaToFaker(Array.isArray(jsonSchema.items) ? jsonSchema.items[0] : jsonSchema.items, void 0, { schemas });
941
+ const startExpr = pagination.offsetStyle ? `const start = query.${pagination.cursorParam} ?? 0` : `const page = query.${pagination.cursorParam} ?? 1\nconst start = (page - 1) * rows`;
942
+ return `const query = c.req.valid('query')
943
+ const rows = query.${pagination.rowsParam} ?? ${pagination.defaultRows}
944
+ ${startExpr}
945
+ const items = Array.from({ length: ${pagination.totalConst} }, () => (${itemFaker}))
946
+ return c.json(items.slice(start, start + rows), ${statusCode})`;
947
+ }
838
948
  if (jsonSchema) {
839
949
  collectRefs(jsonSchema, allRefs);
840
950
  return `return c.json(${schemaToFaker(jsonSchema, void 0, { schemas })}, ${statusCode})`;
@@ -860,7 +970,26 @@ function makeMock(openapi, basePath, options = {}) {
860
970
  const successResponse = success?.response;
861
971
  const jsonMedia = successResponse?.content?.["application/json"];
862
972
  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);
973
+ const jsonSchema = jsonMedia && isMediaWithSchema(jsonMedia) ? jsonMedia.schema : void 0;
974
+ const textSchema = textMedia && isMediaWithSchema(textMedia) ? textMedia.schema : void 0;
975
+ const exampleValue = jsonMedia ? extractMediaExample(jsonMedia, openapi.components) : void 0;
976
+ const exampleCast = exampleValue !== void 0 && jsonSchema?.$ref ? toIdentifierPascalCase(ensureSuffix(jsonSchema.$ref.split("/").at(-1) ?? "", "Schema")) : void 0;
977
+ const pag = detectPagination(operation);
978
+ const pagination = pag && exampleValue === void 0 && jsonSchema?.type === "array" && jsonSchema.items ? {
979
+ totalConst: `${routeId}Total`,
980
+ ...pag
981
+ } : void 0;
982
+ const handlerBody = makeHandlerBody({
983
+ statusCode,
984
+ jsonSchema,
985
+ textSchema,
986
+ schemas,
987
+ allRefs,
988
+ exampleValue,
989
+ exampleCast,
990
+ pagination
991
+ });
992
+ const paginationDecl = pagination ? `const ${pagination.totalConst} = faker.number.int({ min: 0, max: ${Math.min(pagination.defaultRows * 3, 3e3)} })` : "";
864
993
  const authCheck = makeAuthCheck(security, operation.responses?.[String(401)] !== void 0);
865
994
  const handler = `const ${routeId}RouteHandler: RouteHandler<typeof ${routeId}Route> = async (${handlerBody.includes("c.") || authCheck !== "" ? "c" : "_c"}) => {${authCheck}${handlerBody}}`;
866
995
  return [{
@@ -870,17 +999,34 @@ function makeMock(openapi, basePath, options = {}) {
870
999
  path: p,
871
1000
  requiresAuth
872
1001
  },
873
- handler
1002
+ handler,
1003
+ paginationDecl
874
1004
  }];
875
1005
  }));
876
1006
  const routeMetas = processed.map(({ entry }) => entry);
877
1007
  const handlers = processed.map(({ handler }) => handler);
1008
+ const paginationDecls = processed.flatMap(({ paginationDecl }) => paginationDecl.length > 0 ? [paginationDecl] : []);
878
1009
  const allDeps = /* @__PURE__ */ new Set();
879
1010
  for (const ref of allRefs) collectAllDependencies(ref, schemas, allDeps);
880
1011
  const sortedRefs = topologicalSort(allDeps, schemas);
881
1012
  const circularSchemas = detectCircularSchemas$1(schemas);
882
1013
  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, {
1014
+ const rootRefs = collectSchemaRefs$1(filteredOpenapi.paths, /* @__PURE__ */ new Set());
1015
+ if (openapi.components) {
1016
+ collectSchemaRefs$1(openapi.components.responses, rootRefs);
1017
+ collectSchemaRefs$1(openapi.components.parameters, rootRefs);
1018
+ collectSchemaRefs$1(openapi.components.requestBodies, rootRefs);
1019
+ collectSchemaRefs$1(openapi.components.headers, rootRefs);
1020
+ collectSchemaRefs$1(openapi.components.callbacks, rootRefs);
1021
+ collectSchemaRefs$1(openapi.components.pathItems, rootRefs);
1022
+ collectSchemaRefs$1(openapi.components.links, rootRefs);
1023
+ }
1024
+ const usedSchemaNames = schemaClosure(rootRefs, schemas);
1025
+ const filteredComponents = openapi.components ? {
1026
+ ...openapi.components,
1027
+ schemas: Object.fromEntries(Object.entries(schemas).filter(([name]) => usedSchemaNames.has(name)))
1028
+ } : void 0;
1029
+ const components = filteredComponents ? componentsCode(filteredComponents, {
884
1030
  exportSchemasTypes: false,
885
1031
  exportSchemas: false,
886
1032
  exportParametersTypes: false,
@@ -913,6 +1059,7 @@ export default app`;
913
1059
  components,
914
1060
  routes,
915
1061
  mockFunctions.join("\n\n"),
1062
+ paginationDecls.join("\n"),
916
1063
  handlersJoined,
917
1064
  appCode
918
1065
  ].filter((s) => s.length > 0).join("\n\n");
@@ -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-DC4zuL9A.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.99993",
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",