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.cjs CHANGED
@@ -1125,7 +1125,7 @@ async function generateOpenApi(rootRouter, options = {}) {
1125
1125
  astRoutes = await getAstRoutes$1(applications);
1126
1126
  } catch (e) {
1127
1127
  }
1128
- const collect = (router, prefix = "", currentGroup = defaultTagGroup, defaultTag = defaultTagName, inheritedMiddleware = []) => {
1128
+ const collect = (router, prefix = "", currentGroup = defaultTagGroup, defaultTag = defaultTagName, inheritedMiddleware = [], isRootLevel = true) => {
1129
1129
  let group = currentGroup;
1130
1130
  let tag = defaultTag;
1131
1131
  if (router.config?.group) group = router.config.group;
@@ -1133,7 +1133,7 @@ async function generateOpenApi(rootRouter, options = {}) {
1133
1133
  tag = router.config.name;
1134
1134
  } else {
1135
1135
  const mountPath = router[$mountPath];
1136
- if (mountPath && mountPath !== "/") {
1136
+ if (isRootLevel && mountPath && mountPath !== "/") {
1137
1137
  const segments = mountPath.split("/").filter(Boolean);
1138
1138
  if (segments.length > 0) {
1139
1139
  const lastSegment = segments[segments.length - 1];
@@ -1340,7 +1340,7 @@ async function generateOpenApi(rootRouter, options = {}) {
1340
1340
  const cleanPrefix = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
1341
1341
  const cleanMount = mountPath.startsWith("/") ? mountPath : "/" + mountPath;
1342
1342
  const nextPrefix = cleanPrefix + cleanMount || "/";
1343
- collect(child, nextPrefix, group, tag, [...inheritedMiddleware, ...routerMiddleware]);
1343
+ collect(child, nextPrefix, group, tag, [...inheritedMiddleware, ...routerMiddleware], false);
1344
1344
  }
1345
1345
  };
1346
1346
  collect(rootRouter);
@@ -3592,7 +3592,25 @@ function ApiExplorerApp({ spec, asyncSpec, config }) {
3592
3592
  }
3593
3593
  return "Ungrouped";
3594
3594
  };
3595
- const createSubgroups = (routes, depth = 0) => {
3595
+ const findCommonPrefix = (routes) => {
3596
+ if (routes.length === 0) return [];
3597
+ const allSegments = routes.map((r) => {
3598
+ const cleaned = r.path.replace(/^\/|\/$/g, "");
3599
+ return cleaned.split("/");
3600
+ });
3601
+ const minLength = Math.min(...allSegments.map((s) => s.length));
3602
+ const commonPrefix = [];
3603
+ for (let i = 0; i < minLength; i++) {
3604
+ const segment = allSegments[0][i];
3605
+ if (allSegments.every((segments) => segments[i] === segment)) {
3606
+ commonPrefix.push(segment);
3607
+ } else {
3608
+ break;
3609
+ }
3610
+ }
3611
+ return commonPrefix;
3612
+ };
3613
+ const createSubgroups = (routes, depth = 0, commonPrefixLength = 0) => {
3596
3614
  if (routes.length < 3 || depth > 5) {
3597
3615
  return routes.map((route) => ({
3598
3616
  name: route.path,
@@ -3603,7 +3621,8 @@ function ApiExplorerApp({ spec, asyncSpec, config }) {
3603
3621
  }
3604
3622
  const pathSegments = routes.map((r) => {
3605
3623
  const cleaned = r.path.replace(/^\/|\/$/g, "");
3606
- return cleaned.split("/");
3624
+ const segments = cleaned.split("/");
3625
+ return segments.slice(commonPrefixLength);
3607
3626
  });
3608
3627
  const prefixGroups = /* @__PURE__ */ new Map();
3609
3628
  const ungrouped = [];
@@ -3622,13 +3641,30 @@ function ApiExplorerApp({ spec, asyncSpec, config }) {
3622
3641
  const result = [];
3623
3642
  prefixGroups.forEach((groupRoutes, prefix) => {
3624
3643
  if (groupRoutes.length >= 3) {
3625
- const prefixName = prefix.split("/").pop() || prefix;
3626
- result.push({
3627
- name: prefixName,
3628
- type: "subgroup",
3629
- path: "/" + prefix,
3630
- children: createSubgroups(groupRoutes, depth + 1)
3644
+ const nextSegments = /* @__PURE__ */ new Set();
3645
+ groupRoutes.forEach((route, idx) => {
3646
+ const routeIdx = routes.indexOf(route);
3647
+ const segments = pathSegments[routeIdx];
3648
+ if (segments.length > depth + 1) {
3649
+ nextSegments.add(segments[depth + 1]);
3650
+ }
3651
+ });
3652
+ const hasDivergingPaths = nextSegments.size >= 2;
3653
+ const allTerminal = groupRoutes.every((route, idx) => {
3654
+ const routeIdx = routes.indexOf(route);
3655
+ return pathSegments[routeIdx].length === depth + 1;
3631
3656
  });
3657
+ if (hasDivergingPaths || allTerminal) {
3658
+ const prefixName = prefix.split("/").pop() || prefix;
3659
+ result.push({
3660
+ name: prefixName,
3661
+ type: "subgroup",
3662
+ path: "/" + prefix,
3663
+ children: createSubgroups(groupRoutes, depth + 1, commonPrefixLength)
3664
+ });
3665
+ } else {
3666
+ result.push(...createSubgroups(groupRoutes, depth + 1, commonPrefixLength));
3667
+ }
3632
3668
  } else {
3633
3669
  ungrouped.push(...groupRoutes);
3634
3670
  }
@@ -3659,25 +3695,17 @@ function ApiExplorerApp({ spec, asyncSpec, config }) {
3659
3695
  addRoute(groupKey, route);
3660
3696
  });
3661
3697
  });
3662
- Object.entries(asyncSpec?.channels || {}).forEach(([name, ch]) => {
3663
- const operations = [];
3664
- if (ch.publish) operations.push({ method: "recv", op: ch.publish });
3665
- if (ch.subscribe) operations.push({ method: "send", op: ch.subscribe });
3666
- operations.forEach(({ method, op }) => {
3667
- if (!op.operationId) op.operationId = `${method}-${name.replace(/[^a-zA-Z0-9]/g, "-")}`;
3668
- const route = { method, path: name, op };
3669
- const source = op["x-shokupan-source"] || op["x-source-info"];
3670
- const groupKey = getGroupKey(op, source);
3671
- addRoute(groupKey, route);
3672
- });
3673
- });
3674
3698
  const hierarchicalGroups = Array.from(hierarchy.entries()).map(([name, routes]) => {
3675
3699
  routes.sort((a, b) => a.path.localeCompare(b.path));
3676
- const children = createSubgroups(routes);
3700
+ const commonPrefix = findCommonPrefix(routes);
3701
+ const commonPrefixPath = "/" + commonPrefix.join("/");
3702
+ const children = createSubgroups(routes, 0, commonPrefix.length);
3677
3703
  return {
3678
3704
  name,
3679
3705
  type: "group",
3680
- children
3706
+ children,
3707
+ commonPrefixPath
3708
+ // Store for display stripping
3681
3709
  };
3682
3710
  }).sort((a, b) => {
3683
3711
  if (a.name === "Ungrouped") return 1;
@@ -3716,11 +3744,25 @@ function ApiExplorerApp({ spec, asyncSpec, config }) {
3716
3744
  ] });
3717
3745
  }
3718
3746
  function Sidebar$1({ spec, hierarchicalGroups }) {
3719
- const renderNavNode = (node, depth = 0) => {
3747
+ const stripPrefix = (path2, prefix) => {
3748
+ if (!prefix || prefix === "/") return path2;
3749
+ if (path2.startsWith(prefix)) {
3750
+ const stripped = path2.substring(prefix.length);
3751
+ return stripped || "/";
3752
+ }
3753
+ return path2;
3754
+ };
3755
+ const formatAndHighlightPath = (path2) => {
3756
+ const converted = path2.replace(/\{([^}]+)\}/g, ":$1");
3757
+ return converted.replace(/:([a-zA-Z0-9_]+)/g, '<span class="param-highlight">:$1</span>');
3758
+ };
3759
+ const renderNavNode = (node, depth = 0, commonPrefix = "") => {
3720
3760
  if (node.type === "route") {
3721
3761
  const route = node.routes[0];
3722
3762
  const source = route.op["x-shokupan-source"] || route.op["x-source-info"];
3723
3763
  const isRuntime = route.op["x-source-info"]?.isRuntime;
3764
+ const displayPath = stripPrefix(route.path, commonPrefix);
3765
+ const highlightedPath = formatAndHighlightPath(displayPath);
3724
3766
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "nav-item-wrapper", style: `padding-left: ${depth * 12}px;`, children: [
3725
3767
  /* @__PURE__ */ jsxRuntime.jsxs(
3726
3768
  "a",
@@ -3731,7 +3773,7 @@ function Sidebar$1({ spec, hierarchicalGroups }) {
3731
3773
  title: route.path,
3732
3774
  children: [
3733
3775
  /* @__PURE__ */ jsxRuntime.jsx("span", { class: `badge badge-${route.method.toUpperCase()}`, children: route.method.toUpperCase() }),
3734
- /* @__PURE__ */ jsxRuntime.jsx("span", { class: "nav-label", children: node.name }),
3776
+ /* @__PURE__ */ jsxRuntime.jsx("span", { class: "nav-label", dangerouslySetInnerHTML: { __html: highlightedPath } }),
3735
3777
  isRuntime && /* @__PURE__ */ jsxRuntime.jsx("span", { class: "nav-warning", title: "Static Analysis Failed", children: /* @__PURE__ */ jsxRuntime.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: [
3736
3778
  /* @__PURE__ */ jsxRuntime.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" }),
3737
3779
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
@@ -3760,7 +3802,7 @@ function Sidebar$1({ spec, hierarchicalGroups }) {
3760
3802
  /* @__PURE__ */ jsxRuntime.jsx("span", { class: "chevron", children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("polyline", { points: "9 18 15 12 9 6" }) }) }),
3761
3803
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: node.name })
3762
3804
  ] }),
3763
- /* @__PURE__ */ jsxRuntime.jsx("div", { class: "nav-subgroup-items", children: node.children?.map((child) => renderNavNode(child, depth + 1)) })
3805
+ /* @__PURE__ */ jsxRuntime.jsx("div", { class: "nav-subgroup-items", children: node.children?.map((child) => renderNavNode(child, depth + 1, commonPrefix)) })
3764
3806
  ] });
3765
3807
  }
3766
3808
  };
@@ -3778,7 +3820,7 @@ function Sidebar$1({ spec, hierarchicalGroups }) {
3778
3820
  " ",
3779
3821
  group.name
3780
3822
  ] }),
3781
- /* @__PURE__ */ jsxRuntime.jsx("div", { class: "nav-items", children: group.children?.map((child) => renderNavNode(child, 0)) })
3823
+ /* @__PURE__ */ jsxRuntime.jsx("div", { class: "nav-items", children: group.children?.map((child) => renderNavNode(child, 0, group.commonPrefixPath || "")) })
3782
3824
  ] }, group.name)) })
3783
3825
  ] });
3784
3826
  }