zudoku 0.27.0 → 0.28.0

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.
Files changed (103) hide show
  1. package/dist/lib/components/navigation/SidebarCategory.js +3 -3
  2. package/dist/lib/components/navigation/SidebarCategory.js.map +1 -1
  3. package/dist/lib/oas/graphql/index.d.ts +1 -0
  4. package/dist/lib/oas/graphql/index.js +41 -23
  5. package/dist/lib/oas/graphql/index.js.map +1 -1
  6. package/dist/lib/plugins/openapi/Endpoint.js +2 -2
  7. package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
  8. package/dist/lib/plugins/openapi/{Route.d.ts → OpenApiRoute.d.ts} +2 -1
  9. package/dist/lib/plugins/openapi/{Route.js → OpenApiRoute.js} +3 -4
  10. package/dist/lib/plugins/openapi/OpenApiRoute.js.map +1 -0
  11. package/dist/lib/plugins/openapi/OperationList.d.ts +4 -1
  12. package/dist/lib/plugins/openapi/OperationList.js +20 -14
  13. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  14. package/dist/lib/plugins/openapi/RequestBodySidecarBox.d.ts +1 -1
  15. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +2 -0
  16. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
  17. package/dist/lib/plugins/openapi/Sidecar.js +1 -1
  18. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  19. package/dist/lib/plugins/openapi/SidecarExamples.js +17 -14
  20. package/dist/lib/plugins/openapi/SidecarExamples.js.map +1 -1
  21. package/dist/lib/plugins/openapi/graphql/gql.d.ts +6 -2
  22. package/dist/lib/plugins/openapi/graphql/gql.js +3 -2
  23. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  24. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +47 -26
  25. package/dist/lib/plugins/openapi/graphql/graphql.js +20 -16
  26. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  27. package/dist/lib/plugins/openapi/index.js +97 -54
  28. package/dist/lib/plugins/openapi/index.js.map +1 -1
  29. package/dist/lib/plugins/openapi/interfaces.d.ts +1 -0
  30. package/dist/lib/util/joinUrl.js +1 -1
  31. package/dist/lib/util/joinUrl.js.map +1 -1
  32. package/dist/lib/util/joinUrl.test.d.ts +1 -0
  33. package/dist/lib/util/joinUrl.test.js +43 -0
  34. package/dist/lib/util/joinUrl.test.js.map +1 -0
  35. package/dist/vite/plugin-api.js +7 -1
  36. package/dist/vite/plugin-api.js.map +1 -1
  37. package/lib/{AuthenticationPlugin-CO_YCd2x.js → AuthenticationPlugin-Du8cLBSr.js} +2 -2
  38. package/lib/{AuthenticationPlugin-CO_YCd2x.js.map → AuthenticationPlugin-Du8cLBSr.js.map} +1 -1
  39. package/lib/{Markdown-B8o9Qz4q.js → Markdown-Cyrx_JrO.js} +8 -9
  40. package/lib/{Markdown-B8o9Qz4q.js.map → Markdown-Cyrx_JrO.js.map} +1 -1
  41. package/lib/{MdxPage-BxRt3Ly7.js → MdxPage-DewragjB.js} +5 -5
  42. package/lib/{MdxPage-BxRt3Ly7.js.map → MdxPage-DewragjB.js.map} +1 -1
  43. package/lib/OpenApiRoute-UrC_t0e5.js +36 -0
  44. package/lib/OpenApiRoute-UrC_t0e5.js.map +1 -0
  45. package/lib/{OperationList-DH-zIgtq.js → OperationList-D_ejrepA.js} +1373 -1372
  46. package/lib/OperationList-D_ejrepA.js.map +1 -0
  47. package/lib/{Select-B7UXR0SB.js → Select-CnCZ4WhS.js} +3 -3
  48. package/lib/{Select-B7UXR0SB.js.map → Select-CnCZ4WhS.js.map} +1 -1
  49. package/lib/{SlotletProvider-CtIp8rP3.js → SlotletProvider-mQiPDQIH.js} +2 -2
  50. package/lib/{SlotletProvider-CtIp8rP3.js.map → SlotletProvider-mQiPDQIH.js.map} +1 -1
  51. package/lib/{SyntaxHighlight-C1w1QPdY.js → SyntaxHighlight-B0L4SC_N.js} +11 -5
  52. package/lib/SyntaxHighlight-B0L4SC_N.js.map +1 -0
  53. package/lib/{ZudokuContext-8jts0fF3.js → ZudokuContext-BTUJPpQl.js} +21 -21
  54. package/lib/{ZudokuContext-8jts0fF3.js.map → ZudokuContext-BTUJPpQl.js.map} +1 -1
  55. package/lib/{createServer-BV0tHzLK.js → createServer-BydbkTsd.js} +821 -808
  56. package/lib/{createServer-BV0tHzLK.js.map → createServer-BydbkTsd.js.map} +1 -1
  57. package/lib/{hook-BG02esyv.js → hook-FT3SJLe_.js} +2 -2
  58. package/lib/{hook-BG02esyv.js.map → hook-FT3SJLe_.js.map} +1 -1
  59. package/lib/{index-LNp6rxyU.js → index-CjJS0l4l.js} +2 -2
  60. package/lib/{index-LNp6rxyU.js.map → index-CjJS0l4l.js.map} +1 -1
  61. package/lib/{index-DmqsUPcm.js → index-DGugJOLc.js} +836 -777
  62. package/lib/index-DGugJOLc.js.map +1 -0
  63. package/lib/{joinUrl-BTy9bvoK.js → joinUrl-nLx9pD-Z.js} +2 -2
  64. package/lib/joinUrl-nLx9pD-Z.js.map +1 -0
  65. package/lib/{useScrollToAnchor-Bl6mz9_x.js → useScrollToAnchor-eRM9tVvD.js} +10 -9
  66. package/lib/useScrollToAnchor-eRM9tVvD.js.map +1 -0
  67. package/lib/zudoku.auth-clerk.js +1 -1
  68. package/lib/zudoku.auth-openid.js +3 -3
  69. package/lib/zudoku.components.js +153 -154
  70. package/lib/zudoku.components.js.map +1 -1
  71. package/lib/zudoku.plugin-api-catalog.js +3 -3
  72. package/lib/zudoku.plugin-api-keys.js +4 -4
  73. package/lib/zudoku.plugin-custom-pages.js +1 -1
  74. package/lib/zudoku.plugin-markdown.js +1 -1
  75. package/lib/zudoku.plugin-openapi.js +6 -5
  76. package/lib/zudoku.plugin-openapi.js.map +1 -1
  77. package/package.json +1 -1
  78. package/src/lib/components/navigation/SidebarCategory.tsx +3 -2
  79. package/src/lib/oas/graphql/index.ts +63 -35
  80. package/src/lib/plugins/openapi/Endpoint.tsx +2 -2
  81. package/src/lib/plugins/openapi/{Route.tsx → OpenApiRoute.tsx} +3 -3
  82. package/src/lib/plugins/openapi/OperationList.tsx +34 -12
  83. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +2 -0
  84. package/src/lib/plugins/openapi/Sidecar.tsx +1 -1
  85. package/src/lib/plugins/openapi/SidecarExamples.tsx +24 -24
  86. package/src/lib/plugins/openapi/graphql/gql.ts +12 -4
  87. package/src/lib/plugins/openapi/graphql/graphql.ts +66 -43
  88. package/src/lib/plugins/openapi/index.tsx +125 -67
  89. package/src/lib/plugins/openapi/interfaces.ts +1 -0
  90. package/src/lib/util/joinUrl.test.ts +62 -0
  91. package/src/lib/util/joinUrl.ts +1 -1
  92. package/dist/lib/plugins/openapi/Route.js.map +0 -1
  93. package/lib/OperationList-DH-zIgtq.js.map +0 -1
  94. package/lib/Route-DJ0ZlVq1.js +0 -35
  95. package/lib/Route-DJ0ZlVq1.js.map +0 -1
  96. package/lib/StaggeredRender-DgsamH_G.js +0 -17
  97. package/lib/StaggeredRender-DgsamH_G.js.map +0 -1
  98. package/lib/SyntaxHighlight-C1w1QPdY.js.map +0 -1
  99. package/lib/index-Bn6Lc9tq.js +0 -9
  100. package/lib/index-Bn6Lc9tq.js.map +0 -1
  101. package/lib/index-DmqsUPcm.js.map +0 -1
  102. package/lib/joinUrl-BTy9bvoK.js.map +0 -1
  103. package/lib/useScrollToAnchor-Bl6mz9_x.js.map +0 -1
@@ -31,6 +31,8 @@ export type Scalars = {
31
31
  JSON: { input: any; output: any };
32
32
  /** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
33
33
  JSONObject: { input: any; output: any };
34
+ /** OpenAPI schema scalar type that handles circular references */
35
+ JSONSchema: { input: any; output: any };
34
36
  };
35
37
 
36
38
  export type EncodingItem = {
@@ -57,7 +59,7 @@ export type MediaTypeObject = {
57
59
  encoding?: Maybe<Array<EncodingItem>>;
58
60
  examples?: Maybe<Array<ExampleItem>>;
59
61
  mediaType: Scalars["String"]["output"];
60
- schema?: Maybe<Scalars["JSON"]["output"]>;
62
+ schema?: Maybe<Scalars["JSONSchema"]["output"]>;
61
63
  };
62
64
 
63
65
  export type OperationItem = {
@@ -89,7 +91,7 @@ export type ParameterItem = {
89
91
  in: ParameterIn;
90
92
  name: Scalars["String"]["output"];
91
93
  required?: Maybe<Scalars["Boolean"]["output"]>;
92
- schema?: Maybe<Scalars["JSON"]["output"]>;
94
+ schema?: Maybe<Scalars["JSONSchema"]["output"]>;
93
95
  style?: Maybe<Scalars["String"]["output"]>;
94
96
  };
95
97
 
@@ -132,11 +134,11 @@ export type Schema = {
132
134
  operations: Array<OperationItem>;
133
135
  paths: Array<PathItem>;
134
136
  servers: Array<Server>;
137
+ summary?: Maybe<Scalars["String"]["output"]>;
135
138
  tags: Array<SchemaTag>;
136
139
  title: Scalars["String"]["output"];
137
- url: Scalars["String"]["output"];
140
+ url?: Maybe<Scalars["String"]["output"]>;
138
141
  version: Scalars["String"]["output"];
139
- summary?: Maybe<Scalars["String"]["output"]>;
140
142
  };
141
143
 
142
144
  export type SchemaOperationsArgs = {
@@ -144,6 +146,7 @@ export type SchemaOperationsArgs = {
144
146
  operationId?: InputMaybe<Scalars["String"]["input"]>;
145
147
  path?: InputMaybe<Scalars["String"]["input"]>;
146
148
  tag?: InputMaybe<Scalars["String"]["input"]>;
149
+ untagged?: InputMaybe<Scalars["Boolean"]["input"]>;
147
150
  };
148
151
 
149
152
  export type SchemaTagsArgs = {
@@ -180,7 +183,7 @@ export type ServersQueryQuery = {
180
183
  __typename?: "Query";
181
184
  schema: {
182
185
  __typename?: "Schema";
183
- url: string;
186
+ url?: string | null;
184
187
  servers: Array<{ __typename?: "Server"; url: string }>;
185
188
  };
186
189
  };
@@ -255,6 +258,8 @@ export type OperationsFragmentFragment = {
255
258
  export type AllOperationsQueryVariables = Exact<{
256
259
  input: Scalars["JSON"]["input"];
257
260
  type: SchemaType;
261
+ tag?: InputMaybe<Scalars["String"]["input"]>;
262
+ untagged?: InputMaybe<Scalars["Boolean"]["input"]>;
258
263
  }>;
259
264
 
260
265
  export type AllOperationsQuery = {
@@ -264,20 +269,20 @@ export type AllOperationsQuery = {
264
269
  description?: string | null;
265
270
  summary?: string | null;
266
271
  title: string;
267
- url: string;
272
+ url?: string | null;
268
273
  version: string;
269
274
  tags: Array<{
270
275
  __typename?: "SchemaTag";
271
276
  name?: string | null;
272
277
  description?: string | null;
273
- operations: Array<
274
- { __typename?: "OperationItem"; slug: string } & {
275
- " $fragmentRefs"?: {
276
- OperationsFragmentFragment: OperationsFragmentFragment;
277
- };
278
- }
279
- >;
280
278
  }>;
279
+ operations: Array<
280
+ { __typename?: "OperationItem"; slug: string } & {
281
+ " $fragmentRefs"?: {
282
+ OperationsFragmentFragment: OperationsFragmentFragment;
283
+ };
284
+ }
285
+ >;
281
286
  };
282
287
  };
283
288
 
@@ -290,7 +295,7 @@ export type GetServerQueryQuery = {
290
295
  __typename?: "Query";
291
296
  schema: {
292
297
  __typename?: "Schema";
293
- url: string;
298
+ url?: string | null;
294
299
  servers: Array<{ __typename?: "Server"; url: string }>;
295
300
  };
296
301
  };
@@ -304,19 +309,30 @@ export type GetCategoriesQuery = {
304
309
  __typename?: "Query";
305
310
  schema: {
306
311
  __typename?: "Schema";
307
- url: string;
308
- tags: Array<{
309
- __typename: "SchemaTag";
310
- name?: string | null;
311
- operations: Array<{
312
- __typename: "OperationItem";
313
- slug: string;
314
- deprecated?: boolean | null;
315
- method: string;
316
- summary?: string | null;
317
- operationId?: string | null;
318
- path: string;
319
- }>;
312
+ url?: string | null;
313
+ tags: Array<{ __typename?: "SchemaTag"; name?: string | null }>;
314
+ };
315
+ };
316
+
317
+ export type GetOperationsQueryVariables = Exact<{
318
+ input: Scalars["JSON"]["input"];
319
+ type: SchemaType;
320
+ tag?: InputMaybe<Scalars["String"]["input"]>;
321
+ untagged?: InputMaybe<Scalars["Boolean"]["input"]>;
322
+ }>;
323
+
324
+ export type GetOperationsQuery = {
325
+ __typename?: "Query";
326
+ schema: {
327
+ __typename?: "Schema";
328
+ operations: Array<{
329
+ __typename?: "OperationItem";
330
+ slug: string;
331
+ deprecated?: boolean | null;
332
+ method: string;
333
+ summary?: string | null;
334
+ operationId?: string | null;
335
+ path: string;
320
336
  }>;
321
337
  };
322
338
  };
@@ -418,20 +434,20 @@ export const ServersQueryDocument = new TypedDocumentString(`
418
434
  ServersQueryQueryVariables
419
435
  >;
420
436
  export const AllOperationsDocument = new TypedDocumentString(`
421
- query AllOperations($input: JSON!, $type: SchemaType!) {
437
+ query AllOperations($input: JSON!, $type: SchemaType!, $tag: String, $untagged: Boolean) {
422
438
  schema(input: $input, type: $type) {
423
439
  description
424
440
  summary
425
441
  title
426
442
  url
427
443
  version
428
- tags {
444
+ tags(name: $tag) {
429
445
  name
430
446
  description
431
- operations {
432
- slug
433
- ...OperationsFragment
434
- }
447
+ }
448
+ operations(tag: $tag, untagged: $untagged) {
449
+ slug
450
+ ...OperationsFragment
435
451
  }
436
452
  }
437
453
  }
@@ -517,17 +533,7 @@ export const GetCategoriesDocument = new TypedDocumentString(`
517
533
  schema(input: $input, type: $type) {
518
534
  url
519
535
  tags {
520
- __typename
521
536
  name
522
- operations {
523
- __typename
524
- slug
525
- deprecated
526
- method
527
- summary
528
- operationId
529
- path
530
- }
531
537
  }
532
538
  }
533
539
  }
@@ -535,3 +541,20 @@ export const GetCategoriesDocument = new TypedDocumentString(`
535
541
  GetCategoriesQuery,
536
542
  GetCategoriesQueryVariables
537
543
  >;
544
+ export const GetOperationsDocument = new TypedDocumentString(`
545
+ query GetOperations($input: JSON!, $type: SchemaType!, $tag: String, $untagged: Boolean) {
546
+ schema(input: $input, type: $type) {
547
+ operations(tag: $tag, untagged: $untagged) {
548
+ slug
549
+ deprecated
550
+ method
551
+ summary
552
+ operationId
553
+ path
554
+ }
555
+ }
556
+ }
557
+ `) as unknown as TypedDocumentString<
558
+ GetOperationsQuery,
559
+ GetOperationsQueryVariables
560
+ >;
@@ -1,15 +1,15 @@
1
- import { matchPath } from "react-router";
2
- import { type ZudokuPlugin } from "../../core/plugins.js";
3
- import { graphql } from "./graphql/index.js";
4
-
1
+ import slugify from "@sindresorhus/slugify";
5
2
  import { CirclePlayIcon, LogInIcon } from "lucide-react";
3
+ import { matchPath, redirect, RouteObject } from "react-router";
6
4
  import type { SidebarItem } from "../../../config/validators/SidebarSchema.js";
7
5
  import { useAuth } from "../../authentication/hook.js";
8
6
  import { ColorMap } from "../../components/navigation/SidebarBadge.js";
7
+ import { type ZudokuPlugin } from "../../core/plugins.js";
9
8
  import type { SchemaImports } from "../../oas/graphql/index.js";
10
9
  import { Button } from "../../ui/Button.js";
11
- import { joinPath } from "../../util/joinPath.js";
10
+ import { joinUrl } from "../../util/joinUrl.js";
12
11
  import { GraphQLClient } from "./client/GraphQLClient.js";
12
+ import { graphql } from "./graphql/index.js";
13
13
  import { OasPluginConfig } from "./interfaces.js";
14
14
  import type { PlaygroundContentProps } from "./playground/Playground.js";
15
15
  import { PlaygroundDialog } from "./playground/PlaygroundDialog.js";
@@ -19,17 +19,27 @@ const GetCategoriesQuery = graphql(`
19
19
  schema(input: $input, type: $type) {
20
20
  url
21
21
  tags {
22
- __typename
23
22
  name
24
- operations {
25
- __typename
26
- slug
27
- deprecated
28
- method
29
- summary
30
- operationId
31
- path
32
- }
23
+ }
24
+ }
25
+ }
26
+ `);
27
+
28
+ const GetOperationsQuery = graphql(`
29
+ query GetOperations(
30
+ $input: JSON!
31
+ $type: SchemaType!
32
+ $tag: String
33
+ $untagged: Boolean
34
+ ) {
35
+ schema(input: $input, type: $type) {
36
+ operations(tag: $tag, untagged: $untagged) {
37
+ slug
38
+ deprecated
39
+ method
40
+ summary
41
+ operationId
42
+ path
33
43
  }
34
44
  }
35
45
  }
@@ -49,9 +59,10 @@ const MethodColorMap: Record<string, keyof typeof ColorMap> = {
49
59
 
50
60
  export type OpenApiPluginOptions = OasPluginConfig & InternalOasPluginConfig;
51
61
 
52
- export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
53
- const basePath = joinPath(config.navigationId ?? "/reference");
62
+ const UNTAGGED_PATH = "~endpoints";
54
63
 
64
+ export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
65
+ const basePath = joinUrl(config.navigationId ?? "/reference");
55
66
  const versions = config.type === "file" ? Object.keys(config.input) : [];
56
67
 
57
68
  const client = new GraphQLClient(config);
@@ -122,41 +133,60 @@ export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
122
133
  }
123
134
 
124
135
  try {
125
- const version =
126
- versions.find((v) => path === joinPath(basePath, v)) ??
127
- Object.keys(config.input).at(0);
136
+ const urlVersion = versions.find((v) =>
137
+ path.startsWith(joinUrl(basePath, v)),
138
+ );
139
+ const version = urlVersion ?? Object.keys(config.input).at(0);
128
140
 
129
141
  const data = await client.fetch(GetCategoriesQuery, {
130
142
  type: config.type,
131
143
  input: config.type === "file" ? config.input[version!] : config.input,
132
- version,
133
144
  });
134
145
 
135
- const categories = data.schema.tags
136
- .filter((tag) => tag.operations.length > 0)
137
- .map<SidebarItem>((tag) => ({
138
- type: "category",
139
- label: tag.name || "Other endpoints",
140
- collapsible: true,
141
- collapsed: false,
142
- items: tag.operations.map((operation) => ({
143
- type: "link",
144
- label: operation.summary ?? operation.path,
145
- href: `#${operation.slug}`,
146
- badge: {
147
- label: operation.method,
148
- color: MethodColorMap[operation.method.toLowerCase()]!,
149
- invert: true,
150
- },
151
- })),
152
- }));
146
+ const tag = config.tagPages?.find(
147
+ (tag) => path.split("/").at(-1) === slugify(tag),
148
+ );
153
149
 
154
- categories.unshift({
155
- type: "link",
156
- label: "Overview",
157
- href: "#description",
150
+ const operationsData = await client.fetch(GetOperationsQuery, {
151
+ type: config.type,
152
+ input: config.type === "file" ? config.input[version!] : config.input,
153
+ tag,
154
+ untagged: tag === undefined,
158
155
  });
159
156
 
157
+ const items = operationsData.schema.operations.map((operation) => ({
158
+ type: "link" as const,
159
+ label: operation.summary ?? operation.path,
160
+ href: `#${operation.slug}`,
161
+ badge: {
162
+ label: operation.method,
163
+ color: MethodColorMap[operation.method.toLowerCase()]!,
164
+ invert: true,
165
+ } as const,
166
+ }));
167
+
168
+ const categories = data.schema.tags
169
+ // .filter((tag) => tag.operations.length > 0)
170
+ .map<SidebarItem>((tag) => {
171
+ const categoryLink = joinUrl(
172
+ basePath,
173
+ urlVersion,
174
+ tag.name ? slugify(tag.name) : UNTAGGED_PATH,
175
+ );
176
+ return {
177
+ type: "category",
178
+ label: tag.name || "Other endpoints",
179
+ link: {
180
+ type: "doc" as const,
181
+ id: categoryLink,
182
+ label: tag.name!,
183
+ },
184
+ collapsible: false,
185
+ collapsed: true,
186
+ items: path === categoryLink ? items : [],
187
+ };
188
+ });
189
+
160
190
  return categories;
161
191
  } catch {
162
192
  return [];
@@ -165,31 +195,59 @@ export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
165
195
  getRoutes: () => {
166
196
  const versionsInPath = [null, ...versions];
167
197
 
168
- return versionsInPath.map((version) => ({
169
- path: basePath + (version ? `/${version}` : ""),
170
- async lazy() {
171
- const { OpenApiRoute } = await import("./Route.js");
172
- return {
173
- element: (
174
- <OpenApiRoute
175
- basePath={basePath}
176
- versions={versions}
177
- client={client}
178
- config={config}
179
- />
180
- ),
181
- };
182
- },
183
- children: [
184
- {
185
- index: true,
186
- async lazy() {
187
- const { OperationList } = await import("./OperationList.js");
188
- return { element: <OperationList /> };
189
- },
190
- },
191
- ],
198
+ const tagPages = (config.tagPages ?? []).map((tag) => ({
199
+ tag,
200
+ path: slugify(tag),
192
201
  }));
202
+
203
+ return versionsInPath.map((version) => {
204
+ const versionPath = joinUrl(basePath, version ? `/${version}` : "");
205
+
206
+ return {
207
+ path: versionPath,
208
+ async lazy() {
209
+ const { OpenApiRoute } = await import("./OpenApiRoute.js");
210
+ return {
211
+ element: (
212
+ <OpenApiRoute
213
+ version={version ?? undefined}
214
+ basePath={basePath}
215
+ versions={versions}
216
+ client={client}
217
+ config={config}
218
+ />
219
+ ),
220
+ };
221
+ },
222
+ children: [
223
+ {
224
+ index: true,
225
+ loader: () =>
226
+ redirect(
227
+ joinUrl(versionPath, tagPages.at(0)?.path ?? UNTAGGED_PATH),
228
+ ),
229
+ },
230
+ {
231
+ path: joinUrl(versionPath, UNTAGGED_PATH),
232
+ async lazy() {
233
+ const { OperationList } = await import("./OperationList.js");
234
+ return { element: <OperationList untagged={true} /> };
235
+ },
236
+ },
237
+ ...tagPages.map<RouteObject>((tag) => {
238
+ return {
239
+ path: joinUrl(versionPath, tag.path),
240
+ async lazy() {
241
+ const { OperationList } = await import("./OperationList.js");
242
+ return {
243
+ element: <OperationList tag={tag.tag} />,
244
+ };
245
+ },
246
+ };
247
+ }),
248
+ ],
249
+ };
250
+ });
193
251
  },
194
252
  };
195
253
  };
@@ -20,6 +20,7 @@ export type OasPluginConfig = {
20
20
  server?: string;
21
21
  navigationId?: string;
22
22
  skipPreload?: boolean;
23
+ tagPages?: Array<string>;
23
24
  } & OasPluginConfigOptions &
24
25
  OasSource;
25
26
 
@@ -0,0 +1,62 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { joinUrl } from "./joinUrl.js";
3
+
4
+ describe("joinUrl", () => {
5
+ test("handles basic URL joining", () => {
6
+ expect(joinUrl("https://example.com", "path")).toBe(
7
+ "https://example.com/path",
8
+ );
9
+ expect(joinUrl("https://example.com/", "path")).toBe(
10
+ "https://example.com/path",
11
+ );
12
+ expect(joinUrl("https://example.com/", "/path")).toBe(
13
+ "https://example.com/path",
14
+ );
15
+ });
16
+
17
+ test("handles multiple path segments", () => {
18
+ expect(joinUrl("https://example.com", "api", "v1", "users")).toBe(
19
+ "https://example.com/api/v1/users",
20
+ );
21
+ });
22
+
23
+ // test("handles query parameters", () => {
24
+ // expect(joinUrl("https://example.com", "path?query=1")).toBe(
25
+ // "https://example.com/path?query=1",
26
+ // );
27
+ // expect(joinUrl("https://example.com?base=1", "path?query=1")).toBe(
28
+ // "https://example.com/path?query=1",
29
+ // );
30
+ // });
31
+
32
+ // test("handles repeated dots and question marks", () => {
33
+ // expect(joinUrl("https://example.com", "path/../../../test")).toBe(
34
+ // "https://example.com/path/../../../test",
35
+ // );
36
+ // expect(joinUrl("https://example.com", "test????a=1")).toBe(
37
+ // "https://example.com/test????a=1",
38
+ // );
39
+ // });
40
+
41
+ test("handles falsy values", () => {
42
+ expect(joinUrl("https://example.com", null, undefined, false, "path")).toBe(
43
+ "https://example.com/path",
44
+ );
45
+ });
46
+
47
+ test("handles numeric values", () => {
48
+ expect(joinUrl("https://example.com", "api", 123, "test")).toBe(
49
+ "https://example.com/api/123/test",
50
+ );
51
+ });
52
+
53
+ test("handles relative paths", () => {
54
+ expect(joinUrl("/api", "v1", "users")).toBe("/api/v1/users");
55
+ expect(joinUrl("api", "v1", "users")).toBe("/api/v1/users");
56
+ });
57
+
58
+ test("handles empty input", () => {
59
+ expect(joinUrl()).toBe("/");
60
+ expect(joinUrl("")).toBe("/");
61
+ });
62
+ });
@@ -1,5 +1,5 @@
1
1
  // Mostly adapted from https://github.com/moxystudio/js-proper-url-join
2
- const defaultUrlRegExp = /^(\w+:\/\/[^/?]+)?(.*?)(\?.+)?$/;
2
+ const defaultUrlRegExp = /^(\w+:\/\/[^/?]+)?([^?]*)(\?.*)?$/;
3
3
 
4
4
  const normalizeParts = (
5
5
  parts: (string | number | null | undefined | false)[],
@@ -1 +0,0 @@
1
- {"version":3,"file":"Route.js","sourceRoot":"","sources":["../../../../src/lib/plugins/openapi/Route.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGjD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAC3B,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,GAMP,EAAE,EAAE;IACH,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,EAAa,CAAC;IAE3C,MAAM,KAAK,GACT,MAAM,CAAC,IAAI,KAAK,MAAM;QACpB,CAAC,CAAC;YACE,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,OAAO;gBACZ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAE;gBACxB,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE;SACvC;QACH,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IAEjD,MAAM,cAAc,GAAG,OAAO,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEjD,OAAO,CACL,KAAC,iBAAiB,IAChB,KAAK,EAAE;YACL,MAAM,EAAE;gBACN,GAAG,MAAM;gBACT,OAAO,EAAE,cAAc;gBACvB,QAAQ,EAAE,MAAM,CAAC,WAAW,CAC1B,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAClE;gBACD,GAAG,KAAK;aACT;SACF,YAED,KAAC,eAAe,IAAC,MAAM,EAAE,MAAM,YAC7B,KAAC,MAAM,KAAG,GACM,GACA,CACrB,CAAC;AACJ,CAAC,CAAC"}