shokupan 0.10.4 → 0.10.5

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
@@ -1083,7 +1083,7 @@ async function generateOpenApi(rootRouter, options = {}) {
1083
1083
  astRoutes = await getAstRoutes$1(applications);
1084
1084
  } catch (e) {
1085
1085
  }
1086
- const collect = (router, prefix = "", currentGroup = defaultTagGroup, defaultTag = defaultTagName, inheritedMiddleware = []) => {
1086
+ const collect = (router, prefix = "", currentGroup = defaultTagGroup, defaultTag = defaultTagName, inheritedMiddleware = [], isRootLevel = true) => {
1087
1087
  let group = currentGroup;
1088
1088
  let tag = defaultTag;
1089
1089
  if (router.config?.group) group = router.config.group;
@@ -1091,7 +1091,7 @@ async function generateOpenApi(rootRouter, options = {}) {
1091
1091
  tag = router.config.name;
1092
1092
  } else {
1093
1093
  const mountPath = router[$mountPath];
1094
- if (mountPath && mountPath !== "/") {
1094
+ if (isRootLevel && mountPath && mountPath !== "/") {
1095
1095
  const segments = mountPath.split("/").filter(Boolean);
1096
1096
  if (segments.length > 0) {
1097
1097
  const lastSegment = segments[segments.length - 1];
@@ -1298,7 +1298,7 @@ async function generateOpenApi(rootRouter, options = {}) {
1298
1298
  const cleanPrefix = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
1299
1299
  const cleanMount = mountPath.startsWith("/") ? mountPath : "/" + mountPath;
1300
1300
  const nextPrefix = cleanPrefix + cleanMount || "/";
1301
- collect(child, nextPrefix, group, tag, [...inheritedMiddleware, ...routerMiddleware]);
1301
+ collect(child, nextPrefix, group, tag, [...inheritedMiddleware, ...routerMiddleware], false);
1302
1302
  }
1303
1303
  };
1304
1304
  collect(rootRouter);
@@ -3550,7 +3550,25 @@ function ApiExplorerApp({ spec, asyncSpec, config }) {
3550
3550
  }
3551
3551
  return "Ungrouped";
3552
3552
  };
3553
- const createSubgroups = (routes, depth = 0) => {
3553
+ const findCommonPrefix = (routes) => {
3554
+ if (routes.length === 0) return [];
3555
+ const allSegments = routes.map((r) => {
3556
+ const cleaned = r.path.replace(/^\/|\/$/g, "");
3557
+ return cleaned.split("/");
3558
+ });
3559
+ const minLength = Math.min(...allSegments.map((s) => s.length));
3560
+ const commonPrefix = [];
3561
+ for (let i = 0; i < minLength; i++) {
3562
+ const segment = allSegments[0][i];
3563
+ if (allSegments.every((segments) => segments[i] === segment)) {
3564
+ commonPrefix.push(segment);
3565
+ } else {
3566
+ break;
3567
+ }
3568
+ }
3569
+ return commonPrefix;
3570
+ };
3571
+ const createSubgroups = (routes, depth = 0, commonPrefixLength = 0) => {
3554
3572
  if (routes.length < 3 || depth > 5) {
3555
3573
  return routes.map((route) => ({
3556
3574
  name: route.path,
@@ -3561,7 +3579,8 @@ function ApiExplorerApp({ spec, asyncSpec, config }) {
3561
3579
  }
3562
3580
  const pathSegments = routes.map((r) => {
3563
3581
  const cleaned = r.path.replace(/^\/|\/$/g, "");
3564
- return cleaned.split("/");
3582
+ const segments = cleaned.split("/");
3583
+ return segments.slice(commonPrefixLength);
3565
3584
  });
3566
3585
  const prefixGroups = /* @__PURE__ */ new Map();
3567
3586
  const ungrouped = [];
@@ -3580,13 +3599,30 @@ function ApiExplorerApp({ spec, asyncSpec, config }) {
3580
3599
  const result = [];
3581
3600
  prefixGroups.forEach((groupRoutes, prefix) => {
3582
3601
  if (groupRoutes.length >= 3) {
3583
- const prefixName = prefix.split("/").pop() || prefix;
3584
- result.push({
3585
- name: prefixName,
3586
- type: "subgroup",
3587
- path: "/" + prefix,
3588
- children: createSubgroups(groupRoutes, depth + 1)
3602
+ const nextSegments = /* @__PURE__ */ new Set();
3603
+ groupRoutes.forEach((route, idx) => {
3604
+ const routeIdx = routes.indexOf(route);
3605
+ const segments = pathSegments[routeIdx];
3606
+ if (segments.length > depth + 1) {
3607
+ nextSegments.add(segments[depth + 1]);
3608
+ }
3609
+ });
3610
+ const hasDivergingPaths = nextSegments.size >= 2;
3611
+ const allTerminal = groupRoutes.every((route, idx) => {
3612
+ const routeIdx = routes.indexOf(route);
3613
+ return pathSegments[routeIdx].length === depth + 1;
3589
3614
  });
3615
+ if (hasDivergingPaths || allTerminal) {
3616
+ const prefixName = prefix.split("/").pop() || prefix;
3617
+ result.push({
3618
+ name: prefixName,
3619
+ type: "subgroup",
3620
+ path: "/" + prefix,
3621
+ children: createSubgroups(groupRoutes, depth + 1, commonPrefixLength)
3622
+ });
3623
+ } else {
3624
+ result.push(...createSubgroups(groupRoutes, depth + 1, commonPrefixLength));
3625
+ }
3590
3626
  } else {
3591
3627
  ungrouped.push(...groupRoutes);
3592
3628
  }
@@ -3617,25 +3653,17 @@ function ApiExplorerApp({ spec, asyncSpec, config }) {
3617
3653
  addRoute(groupKey, route);
3618
3654
  });
3619
3655
  });
3620
- Object.entries(asyncSpec?.channels || {}).forEach(([name, ch]) => {
3621
- const operations = [];
3622
- if (ch.publish) operations.push({ method: "recv", op: ch.publish });
3623
- if (ch.subscribe) operations.push({ method: "send", op: ch.subscribe });
3624
- operations.forEach(({ method, op }) => {
3625
- if (!op.operationId) op.operationId = `${method}-${name.replace(/[^a-zA-Z0-9]/g, "-")}`;
3626
- const route = { method, path: name, op };
3627
- const source = op["x-shokupan-source"] || op["x-source-info"];
3628
- const groupKey = getGroupKey(op, source);
3629
- addRoute(groupKey, route);
3630
- });
3631
- });
3632
3656
  const hierarchicalGroups = Array.from(hierarchy.entries()).map(([name, routes]) => {
3633
3657
  routes.sort((a, b) => a.path.localeCompare(b.path));
3634
- const children = createSubgroups(routes);
3658
+ const commonPrefix = findCommonPrefix(routes);
3659
+ const commonPrefixPath = "/" + commonPrefix.join("/");
3660
+ const children = createSubgroups(routes, 0, commonPrefix.length);
3635
3661
  return {
3636
3662
  name,
3637
3663
  type: "group",
3638
- children
3664
+ children,
3665
+ commonPrefixPath
3666
+ // Store for display stripping
3639
3667
  };
3640
3668
  }).sort((a, b) => {
3641
3669
  if (a.name === "Ungrouped") return 1;
@@ -3674,11 +3702,25 @@ function ApiExplorerApp({ spec, asyncSpec, config }) {
3674
3702
  ] });
3675
3703
  }
3676
3704
  function Sidebar$1({ spec, hierarchicalGroups }) {
3677
- const renderNavNode = (node, depth = 0) => {
3705
+ const stripPrefix = (path, prefix) => {
3706
+ if (!prefix || prefix === "/") return path;
3707
+ if (path.startsWith(prefix)) {
3708
+ const stripped = path.substring(prefix.length);
3709
+ return stripped || "/";
3710
+ }
3711
+ return path;
3712
+ };
3713
+ const formatAndHighlightPath = (path) => {
3714
+ const converted = path.replace(/\{([^}]+)\}/g, ":$1");
3715
+ return converted.replace(/:([a-zA-Z0-9_]+)/g, '<span class="param-highlight">:$1</span>');
3716
+ };
3717
+ const renderNavNode = (node, depth = 0, commonPrefix = "") => {
3678
3718
  if (node.type === "route") {
3679
3719
  const route = node.routes[0];
3680
3720
  const source = route.op["x-shokupan-source"] || route.op["x-source-info"];
3681
3721
  const isRuntime = route.op["x-source-info"]?.isRuntime;
3722
+ const displayPath = stripPrefix(route.path, commonPrefix);
3723
+ const highlightedPath = formatAndHighlightPath(displayPath);
3682
3724
  return /* @__PURE__ */ jsxs("div", { class: "nav-item-wrapper", style: `padding-left: ${depth * 12}px;`, children: [
3683
3725
  /* @__PURE__ */ jsxs(
3684
3726
  "a",
@@ -3689,7 +3731,7 @@ function Sidebar$1({ spec, hierarchicalGroups }) {
3689
3731
  title: route.path,
3690
3732
  children: [
3691
3733
  /* @__PURE__ */ jsx("span", { class: `badge badge-${route.method.toUpperCase()}`, children: route.method.toUpperCase() }),
3692
- /* @__PURE__ */ jsx("span", { class: "nav-label", children: node.name }),
3734
+ /* @__PURE__ */ jsx("span", { class: "nav-label", dangerouslySetInnerHTML: { __html: highlightedPath } }),
3693
3735
  isRuntime && /* @__PURE__ */ jsx("span", { class: "nav-warning", title: "Static Analysis Failed", children: /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", children: [
3694
3736
  /* @__PURE__ */ jsx("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
3695
3737
  /* @__PURE__ */ jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
@@ -3718,7 +3760,7 @@ function Sidebar$1({ spec, hierarchicalGroups }) {
3718
3760
  /* @__PURE__ */ jsx("span", { class: "chevron", children: /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", children: /* @__PURE__ */ jsx("polyline", { points: "9 18 15 12 9 6" }) }) }),
3719
3761
  /* @__PURE__ */ jsx("span", { children: node.name })
3720
3762
  ] }),
3721
- /* @__PURE__ */ jsx("div", { class: "nav-subgroup-items", children: node.children?.map((child) => renderNavNode(child, depth + 1)) })
3763
+ /* @__PURE__ */ jsx("div", { class: "nav-subgroup-items", children: node.children?.map((child) => renderNavNode(child, depth + 1, commonPrefix)) })
3722
3764
  ] });
3723
3765
  }
3724
3766
  };
@@ -3736,7 +3778,7 @@ function Sidebar$1({ spec, hierarchicalGroups }) {
3736
3778
  " ",
3737
3779
  group.name
3738
3780
  ] }),
3739
- /* @__PURE__ */ jsx("div", { class: "nav-items", children: group.children?.map((child) => renderNavNode(child, 0)) })
3781
+ /* @__PURE__ */ jsx("div", { class: "nav-items", children: group.children?.map((child) => renderNavNode(child, 0, group.commonPrefixPath || "")) })
3740
3782
  ] }, group.name)) })
3741
3783
  ] });
3742
3784
  }