zudoku 0.35.6 → 0.36.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 (121) hide show
  1. package/dist/app/entry.server.js +5 -1
  2. package/dist/app/entry.server.js.map +1 -1
  3. package/dist/config/validators/common.d.ts +176 -176
  4. package/dist/config/validators/common.js +8 -6
  5. package/dist/config/validators/common.js.map +1 -1
  6. package/dist/config/validators/validate.d.ts +63 -63
  7. package/dist/lib/components/Layout.js +3 -14
  8. package/dist/lib/components/Layout.js.map +1 -1
  9. package/dist/lib/components/Zudoku.js +3 -1
  10. package/dist/lib/components/Zudoku.js.map +1 -1
  11. package/dist/lib/components/cache.d.ts +7 -0
  12. package/dist/lib/components/cache.js +7 -0
  13. package/dist/lib/components/cache.js.map +1 -1
  14. package/dist/lib/components/context/ViewportAnchorContext.js +3 -6
  15. package/dist/lib/components/context/ViewportAnchorContext.js.map +1 -1
  16. package/dist/lib/components/context/ZudokuContext.d.ts +1 -1
  17. package/dist/lib/components/context/ZudokuContext.js +4 -3
  18. package/dist/lib/components/context/ZudokuContext.js.map +1 -1
  19. package/dist/lib/core/ZudokuContext.d.ts +8 -6
  20. package/dist/lib/core/ZudokuContext.js +4 -2
  21. package/dist/lib/core/ZudokuContext.js.map +1 -1
  22. package/dist/lib/core/plugins.d.ts +3 -3
  23. package/dist/lib/hooks/useEvent.test.js +1 -1
  24. package/dist/lib/hooks/useEvent.test.js.map +1 -1
  25. package/dist/lib/oas/graphql/index.d.ts +13 -2
  26. package/dist/lib/oas/graphql/index.js +59 -39
  27. package/dist/lib/oas/graphql/index.js.map +1 -1
  28. package/dist/lib/plugins/openapi/OperationList.js +19 -5
  29. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  30. package/dist/lib/plugins/openapi/OperationListItem.js +1 -1
  31. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  32. package/dist/lib/plugins/openapi/Sidecar.js +2 -2
  33. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  34. package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +1 -1
  35. package/dist/lib/plugins/openapi/client/useCreateQuery.js +2 -1
  36. package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -1
  37. package/dist/lib/plugins/openapi/graphql/gql.d.ts +4 -4
  38. package/dist/lib/plugins/openapi/graphql/gql.js +3 -3
  39. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  40. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +33 -44
  41. package/dist/lib/plugins/openapi/graphql/graphql.js +19 -29
  42. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  43. package/dist/lib/plugins/openapi/index.d.ts +5 -10
  44. package/dist/lib/plugins/openapi/index.js +29 -60
  45. package/dist/lib/plugins/openapi/index.js.map +1 -1
  46. package/dist/lib/plugins/openapi/interfaces.d.ts +3 -1
  47. package/dist/lib/plugins/openapi/util/createSidebarCategory.js +5 -7
  48. package/dist/lib/plugins/openapi/util/createSidebarCategory.js.map +1 -1
  49. package/dist/lib/util/traverse.js +2 -2
  50. package/dist/lib/util/traverse.js.map +1 -1
  51. package/dist/lib/util/useScrollToAnchor.js +2 -0
  52. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  53. package/dist/vite/api/schema-codegen.js +19 -4
  54. package/dist/vite/api/schema-codegen.js.map +1 -1
  55. package/dist/vite/api/schema-codegen.test.js +61 -0
  56. package/dist/vite/api/schema-codegen.test.js.map +1 -1
  57. package/dist/vite/plugin-api.js +3 -11
  58. package/dist/vite/plugin-api.js.map +1 -1
  59. package/lib/{AuthenticationPlugin-4ip08maU.js → AuthenticationPlugin-Cr6xjOJD.js} +2 -2
  60. package/lib/{AuthenticationPlugin-4ip08maU.js.map → AuthenticationPlugin-Cr6xjOJD.js.map} +1 -1
  61. package/lib/{Markdown-C0eXdzGn.js → Markdown-BlioIqkZ.js} +8 -6
  62. package/lib/Markdown-BlioIqkZ.js.map +1 -0
  63. package/lib/{MdxPage-BKkG1cm1.js → MdxPage-7XnN9J9R.js} +3 -3
  64. package/lib/{MdxPage-BKkG1cm1.js.map → MdxPage-7XnN9J9R.js.map} +1 -1
  65. package/lib/{OasProvider-CwhKwrwl.js → OasProvider-BaRRMSsD.js} +3 -3
  66. package/lib/{OasProvider-CwhKwrwl.js.map → OasProvider-BaRRMSsD.js.map} +1 -1
  67. package/lib/{OperationList-DGYoFitT.js → OperationList-BjL1hzSx.js} +987 -960
  68. package/lib/OperationList-BjL1hzSx.js.map +1 -0
  69. package/lib/{SlotletProvider-BJC58V32.js → SlotletProvider-CXb3wQiR.js} +2 -2
  70. package/lib/{SlotletProvider-BJC58V32.js.map → SlotletProvider-CXb3wQiR.js.map} +1 -1
  71. package/lib/{circular-v7K6lDDh.js → circular-ByJI6Mci.js} +4887 -4419
  72. package/lib/circular-ByJI6Mci.js.map +1 -0
  73. package/lib/{createServer-CbL1Uh2Q.js → createServer-DjgKDpGV.js} +3301 -3747
  74. package/lib/createServer-DjgKDpGV.js.map +1 -0
  75. package/lib/{hook-CfCFKZ-2.js → hook-Bo80UX00.js} +75 -74
  76. package/lib/hook-Bo80UX00.js.map +1 -0
  77. package/lib/{index-Dm1QJHVl.js → index-D5m8_oyY.js} +605 -645
  78. package/lib/index-D5m8_oyY.js.map +1 -0
  79. package/lib/post-processors/traverse.js +2 -2
  80. package/lib/post-processors/traverse.js.map +1 -1
  81. package/lib/zudoku.auth-auth0.js +1 -1
  82. package/lib/zudoku.auth-clerk.js +2 -2
  83. package/lib/zudoku.auth-openid.js +2 -2
  84. package/lib/zudoku.components.js +395 -397
  85. package/lib/zudoku.components.js.map +1 -1
  86. package/lib/zudoku.hooks.js +1 -1
  87. package/lib/zudoku.plugin-api-catalog.js +2 -2
  88. package/lib/zudoku.plugin-api-keys.js +2 -2
  89. package/lib/zudoku.plugin-custom-pages.js +1 -1
  90. package/lib/zudoku.plugin-markdown.js +1 -1
  91. package/lib/zudoku.plugin-openapi.js +4 -5
  92. package/lib/zudoku.plugin-openapi.js.map +1 -1
  93. package/lib/zudoku.plugins.js.map +1 -1
  94. package/package.json +1 -1
  95. package/src/app/entry.server.tsx +7 -1
  96. package/src/lib/components/Layout.tsx +3 -16
  97. package/src/lib/components/Zudoku.tsx +5 -1
  98. package/src/lib/components/cache.ts +8 -0
  99. package/src/lib/components/context/ViewportAnchorContext.tsx +3 -6
  100. package/src/lib/components/context/ZudokuContext.ts +5 -4
  101. package/src/lib/core/ZudokuContext.ts +11 -8
  102. package/src/lib/core/plugins.ts +4 -4
  103. package/src/lib/hooks/useEvent.test.tsx +1 -1
  104. package/src/lib/oas/graphql/index.ts +104 -64
  105. package/src/lib/plugins/openapi/OperationList.tsx +30 -36
  106. package/src/lib/plugins/openapi/OperationListItem.tsx +1 -1
  107. package/src/lib/plugins/openapi/Sidecar.tsx +2 -2
  108. package/src/lib/plugins/openapi/client/useCreateQuery.ts +2 -1
  109. package/src/lib/plugins/openapi/graphql/gql.ts +17 -17
  110. package/src/lib/plugins/openapi/graphql/graphql.ts +57 -75
  111. package/src/lib/plugins/openapi/index.tsx +40 -84
  112. package/src/lib/plugins/openapi/interfaces.ts +4 -1
  113. package/src/lib/plugins/openapi/util/createSidebarCategory.tsx +5 -7
  114. package/src/lib/util/traverse.ts +2 -2
  115. package/src/lib/util/useScrollToAnchor.ts +2 -0
  116. package/lib/Markdown-C0eXdzGn.js.map +0 -1
  117. package/lib/OperationList-DGYoFitT.js.map +0 -1
  118. package/lib/circular-v7K6lDDh.js.map +0 -1
  119. package/lib/createServer-CbL1Uh2Q.js.map +0 -1
  120. package/lib/hook-CfCFKZ-2.js.map +0 -1
  121. package/lib/index-Dm1QJHVl.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { type ResultOf } from "@graphql-typed-document-node/core";
2
- import { useSuspenseQuery } from "@tanstack/react-query";
2
+ import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
3
3
  import { Helmet } from "@zudoku/react-helmet-async";
4
4
  import { ChevronsDownUpIcon, ChevronsUpDownIcon } from "lucide-react";
5
5
  import { useNavigate } from "react-router";
@@ -97,8 +97,16 @@ export const OperationsFragment = graphql(/* GraphQL */ `
97
97
 
98
98
  export type OperationListItemResult = ResultOf<typeof OperationsFragment>;
99
99
 
100
- const AllOperationsQuery = graphql(/* GraphQL */ `
101
- query AllOperations(
100
+ const SchemaWarmupQuery = graphql(/* GraphQL */ `
101
+ query SchemaWarmup($input: JSON!, $type: SchemaType!) {
102
+ schema(input: $input, type: $type) {
103
+ openapi
104
+ }
105
+ }
106
+ `);
107
+
108
+ const OperationsForTagQuery = graphql(/* GraphQL */ `
109
+ query OperationsForTag(
102
110
  $input: JSON!
103
111
  $type: SchemaType!
104
112
  $tag: String
@@ -133,7 +141,7 @@ export const OperationList = ({
133
141
  untagged?: boolean;
134
142
  }) => {
135
143
  const { input, type, versions, version, options } = useOasConfig();
136
- const query = useCreateQuery(AllOperationsQuery, {
144
+ const query = useCreateQuery(OperationsForTagQuery, {
137
145
  input,
138
146
  type,
139
147
  tag,
@@ -151,6 +159,14 @@ export const OperationList = ({
151
159
  const operations = schema.operations;
152
160
  const tagDescription = schema.tags.find((t) => t.name === tag)?.description;
153
161
 
162
+ // This is to warmup (i.e. load the schema in the background) the schema on the client, if the page has been rendered on the server
163
+ const warmupQuery = useCreateQuery(SchemaWarmupQuery, { input, type });
164
+ useQuery({
165
+ ...warmupQuery,
166
+ enabled: typeof window !== "undefined",
167
+ notifyOnChangeProps: [],
168
+ });
169
+
154
170
  // Prefetch for Playground
155
171
  useApiIdentities();
156
172
 
@@ -269,38 +285,16 @@ export const OperationList = ({
269
285
  <div className="my-4 flex items-center justify-end gap-4">
270
286
  <Endpoint />
271
287
  </div>
272
- {operations.map((fragment) => (
273
- <OperationListItem
274
- serverUrl={selectedServer}
275
- key={fragment.slug}
276
- operationFragment={fragment}
277
- />
278
- ))}
279
- {/* {schema.tags
280
- .filter((tag) => tag.operations.length > 0)
281
- .map((tag) => (
282
- // px, -mx is so that `content-visibility` doesn't cut off overflown heading anchor links '#'
283
- <div key={tag.name} className="px-6 -mx-6 [content-visibility:auto]">
284
- {tag.name && <CategoryHeading>{tag.name}</CategoryHeading>}
285
- {tag.description && (
286
- <Markdown
287
- className={`${ProseClasses} max-w-full prose-img:max-w-prose w-full mt-2 mb-12`}
288
- content={tag.description}
289
- />
290
- )}
291
- <div className="operation mb-12">
292
- <StaggeredRender>
293
- {tag.operations.map((fragment) => (
294
- <OperationListItem
295
- serverUrl={selectedServer ?? schema.url}
296
- key={fragment.slug}
297
- operationFragment={fragment}
298
- />
299
- ))}
300
- </StaggeredRender>
301
- </div>
302
- </div>
303
- ))} */}
288
+ {/* px, -mx is so that `content-visibility` doesn't cut off overflown heading anchor links '#' */}
289
+ <div className="px-6 -mx-6 [content-visibility:auto]">
290
+ {operations.map((fragment) => (
291
+ <OperationListItem
292
+ serverUrl={selectedServer}
293
+ key={fragment.slug}
294
+ operationFragment={fragment}
295
+ />
296
+ ))}
297
+ </div>
304
298
  </div>
305
299
  );
306
300
  };
@@ -63,7 +63,7 @@ export const OperationListItem = ({
63
63
  <SelectOnClick className="max-w-full truncate flex cursor-pointer">
64
64
  {serverUrl && (
65
65
  <div className="text-neutral-400 dark:text-neutral-500 truncate">
66
- {serverUrl}
66
+ {serverUrl.replace(/\/$/, "")}
67
67
  </div>
68
68
  )}
69
69
  <div className="text-neutral-900 dark:text-neutral-200">
@@ -173,9 +173,9 @@ export const Sidecar = ({
173
173
  const showPlayground =
174
174
  isOnScreen &&
175
175
  (operation.extensions["x-explorer-enabled"] === true ||
176
- operation.extensions["x-playground-enabled"] === true ||
176
+ operation.extensions["x-zudoku-playground-enabled"] === true ||
177
177
  (operation.extensions["x-explorer-enabled"] === undefined &&
178
- operation.extensions["x-playground-enabled"] === undefined &&
178
+ operation.extensions["x-zudoku-playground-enabled"] === undefined &&
179
179
  !options?.disablePlayground));
180
180
 
181
181
  return (
@@ -1,3 +1,4 @@
1
+ import { stripIgnoredCharacters } from "graphql";
1
2
  import { useContext } from "react";
2
3
  import type { TypedDocumentString } from "../graphql/graphql.js";
3
4
  import { GraphQLContext } from "./GraphQLContext.js";
@@ -13,6 +14,6 @@ export const useCreateQuery = <TResult, TVariables>(
13
14
 
14
15
  return {
15
16
  queryFn: () => graphQLClient.fetch(query, ...variables),
16
- queryKey: [query, variables[0]],
17
+ queryKey: [stripIgnoredCharacters(query.toString()), variables[0]],
17
18
  } as const;
18
19
  };
@@ -15,24 +15,24 @@ import * as types from "./graphql.js";
15
15
  type Documents = {
16
16
  "\n query ServersQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n": typeof types.ServersQueryDocument;
17
17
  "\n fragment OperationsFragment on OperationItem {\n slug\n summary\n method\n description\n operationId\n contentTypes\n path\n deprecated\n extensions\n parameters {\n name\n in\n description\n required\n schema\n style\n explode\n examples {\n name\n description\n externalValue\n value\n summary\n }\n }\n requestBody {\n content {\n mediaType\n encoding {\n name\n }\n examples {\n name\n description\n externalValue\n value\n summary\n }\n schema\n }\n description\n required\n }\n responses {\n statusCode\n links\n description\n content {\n examples {\n name\n description\n externalValue\n value\n summary\n }\n mediaType\n encoding {\n name\n }\n schema\n }\n }\n }\n": typeof types.OperationsFragmentFragmentDoc;
18
- "\n query AllOperations(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n servers {\n url\n }\n description\n summary\n title\n url\n version\n tags(name: $tag) {\n name\n description\n }\n operations(tag: $tag, untagged: $untagged) {\n slug\n ...OperationsFragment\n }\n }\n }\n": typeof types.AllOperationsDocument;
18
+ "\n query SchemaWarmup($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n openapi\n }\n }\n": typeof types.SchemaWarmupDocument;
19
+ "\n query OperationsForTag(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n servers {\n url\n }\n description\n summary\n title\n url\n version\n tags(name: $tag) {\n name\n description\n }\n operations(tag: $tag, untagged: $untagged) {\n slug\n ...OperationsFragment\n }\n }\n }\n": typeof types.OperationsForTagDocument;
19
20
  "\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n": typeof types.GetServerQueryDocument;
20
- "\n query GetCategories($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n tags {\n name\n }\n }\n }\n": typeof types.GetCategoriesDocument;
21
- "\n query GetOperations($input: JSON!, $type: SchemaType!, $tag: String) {\n schema(input: $input, type: $type) {\n operations(tag: $tag) {\n slug\n deprecated\n method\n summary\n operationId\n path\n tags {\n name\n }\n }\n untagged: operations(untagged: true) {\n slug\n deprecated\n method\n summary\n operationId\n path\n }\n }\n }\n": typeof types.GetOperationsDocument;
21
+ "\n query GetSidebarOperations($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n tags {\n slug\n name\n extensions\n operations {\n summary\n slug\n method\n operationId\n path\n }\n }\n }\n }\n": typeof types.GetSidebarOperationsDocument;
22
22
  };
23
23
  const documents: Documents = {
24
24
  "\n query ServersQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n":
25
25
  types.ServersQueryDocument,
26
26
  "\n fragment OperationsFragment on OperationItem {\n slug\n summary\n method\n description\n operationId\n contentTypes\n path\n deprecated\n extensions\n parameters {\n name\n in\n description\n required\n schema\n style\n explode\n examples {\n name\n description\n externalValue\n value\n summary\n }\n }\n requestBody {\n content {\n mediaType\n encoding {\n name\n }\n examples {\n name\n description\n externalValue\n value\n summary\n }\n schema\n }\n description\n required\n }\n responses {\n statusCode\n links\n description\n content {\n examples {\n name\n description\n externalValue\n value\n summary\n }\n mediaType\n encoding {\n name\n }\n schema\n }\n }\n }\n":
27
27
  types.OperationsFragmentFragmentDoc,
28
- "\n query AllOperations(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n servers {\n url\n }\n description\n summary\n title\n url\n version\n tags(name: $tag) {\n name\n description\n }\n operations(tag: $tag, untagged: $untagged) {\n slug\n ...OperationsFragment\n }\n }\n }\n":
29
- types.AllOperationsDocument,
28
+ "\n query SchemaWarmup($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n openapi\n }\n }\n":
29
+ types.SchemaWarmupDocument,
30
+ "\n query OperationsForTag(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n servers {\n url\n }\n description\n summary\n title\n url\n version\n tags(name: $tag) {\n name\n description\n }\n operations(tag: $tag, untagged: $untagged) {\n slug\n ...OperationsFragment\n }\n }\n }\n":
31
+ types.OperationsForTagDocument,
30
32
  "\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n":
31
33
  types.GetServerQueryDocument,
32
- "\n query GetCategories($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n tags {\n name\n }\n }\n }\n":
33
- types.GetCategoriesDocument,
34
- "\n query GetOperations($input: JSON!, $type: SchemaType!, $tag: String) {\n schema(input: $input, type: $type) {\n operations(tag: $tag) {\n slug\n deprecated\n method\n summary\n operationId\n path\n tags {\n name\n }\n }\n untagged: operations(untagged: true) {\n slug\n deprecated\n method\n summary\n operationId\n path\n }\n }\n }\n":
35
- types.GetOperationsDocument,
34
+ "\n query GetSidebarOperations($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n tags {\n slug\n name\n extensions\n operations {\n summary\n slug\n method\n operationId\n path\n }\n }\n }\n }\n":
35
+ types.GetSidebarOperationsDocument,
36
36
  };
37
37
 
38
38
  /**
@@ -51,26 +51,26 @@ export function graphql(
51
51
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
52
52
  */
53
53
  export function graphql(
54
- source: "\n query AllOperations(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n servers {\n url\n }\n description\n summary\n title\n url\n version\n tags(name: $tag) {\n name\n description\n }\n operations(tag: $tag, untagged: $untagged) {\n slug\n ...OperationsFragment\n }\n }\n }\n",
55
- ): typeof import("./graphql.js").AllOperationsDocument;
54
+ source: "\n query SchemaWarmup($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n openapi\n }\n }\n",
55
+ ): typeof import("./graphql.js").SchemaWarmupDocument;
56
56
  /**
57
57
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
58
58
  */
59
59
  export function graphql(
60
- source: "\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n",
61
- ): typeof import("./graphql.js").GetServerQueryDocument;
60
+ source: "\n query OperationsForTag(\n $input: JSON!\n $type: SchemaType!\n $tag: String\n $untagged: Boolean\n ) {\n schema(input: $input, type: $type) {\n servers {\n url\n }\n description\n summary\n title\n url\n version\n tags(name: $tag) {\n name\n description\n }\n operations(tag: $tag, untagged: $untagged) {\n slug\n ...OperationsFragment\n }\n }\n }\n",
61
+ ): typeof import("./graphql.js").OperationsForTagDocument;
62
62
  /**
63
63
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
64
64
  */
65
65
  export function graphql(
66
- source: "\n query GetCategories($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n tags {\n name\n }\n }\n }\n",
67
- ): typeof import("./graphql.js").GetCategoriesDocument;
66
+ source: "\n query getServerQuery($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n url\n servers {\n url\n }\n }\n }\n",
67
+ ): typeof import("./graphql.js").GetServerQueryDocument;
68
68
  /**
69
69
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
70
70
  */
71
71
  export function graphql(
72
- source: "\n query GetOperations($input: JSON!, $type: SchemaType!, $tag: String) {\n schema(input: $input, type: $type) {\n operations(tag: $tag) {\n slug\n deprecated\n method\n summary\n operationId\n path\n tags {\n name\n }\n }\n untagged: operations(untagged: true) {\n slug\n deprecated\n method\n summary\n operationId\n path\n }\n }\n }\n",
73
- ): typeof import("./graphql.js").GetOperationsDocument;
72
+ source: "\n query GetSidebarOperations($input: JSON!, $type: SchemaType!) {\n schema(input: $input, type: $type) {\n tags {\n slug\n name\n extensions\n operations {\n summary\n slug\n method\n operationId\n path\n }\n }\n }\n }\n",
73
+ ): typeof import("./graphql.js").GetSidebarOperationsDocument;
74
74
 
75
75
  export function graphql(source: string) {
76
76
  return (documents as any)[source] ?? {};
@@ -160,8 +160,10 @@ export type SchemaTagsArgs = {
160
160
  export type SchemaTag = {
161
161
  __typename?: "SchemaTag";
162
162
  description?: Maybe<Scalars["String"]["output"]>;
163
- name: Scalars["String"]["output"];
163
+ extensions?: Maybe<Scalars["JSONObject"]["output"]>;
164
+ name?: Maybe<Scalars["String"]["output"]>;
164
165
  operations: Array<OperationItem>;
166
+ slug?: Maybe<Scalars["String"]["output"]>;
165
167
  };
166
168
 
167
169
  export type SchemaType = "file" | "raw" | "url";
@@ -263,14 +265,24 @@ export type OperationsFragmentFragment = {
263
265
  }>;
264
266
  } & { " $fragmentName"?: "OperationsFragmentFragment" };
265
267
 
266
- export type AllOperationsQueryVariables = Exact<{
268
+ export type SchemaWarmupQueryVariables = Exact<{
269
+ input: Scalars["JSON"]["input"];
270
+ type: SchemaType;
271
+ }>;
272
+
273
+ export type SchemaWarmupQuery = {
274
+ __typename?: "Query";
275
+ schema: { __typename?: "Schema"; openapi: string };
276
+ };
277
+
278
+ export type OperationsForTagQueryVariables = Exact<{
267
279
  input: Scalars["JSON"]["input"];
268
280
  type: SchemaType;
269
281
  tag?: InputMaybe<Scalars["String"]["input"]>;
270
282
  untagged?: InputMaybe<Scalars["Boolean"]["input"]>;
271
283
  }>;
272
284
 
273
- export type AllOperationsQuery = {
285
+ export type OperationsForTagQuery = {
274
286
  __typename?: "Query";
275
287
  schema: {
276
288
  __typename?: "Schema";
@@ -282,7 +294,7 @@ export type AllOperationsQuery = {
282
294
  servers: Array<{ __typename?: "Server"; url: string }>;
283
295
  tags: Array<{
284
296
  __typename?: "SchemaTag";
285
- name: string;
297
+ name?: string | null;
286
298
  description?: string | null;
287
299
  }>;
288
300
  operations: Array<
@@ -309,48 +321,28 @@ export type GetServerQueryQuery = {
309
321
  };
310
322
  };
311
323
 
312
- export type GetCategoriesQueryVariables = Exact<{
313
- input: Scalars["JSON"]["input"];
314
- type: SchemaType;
315
- }>;
316
-
317
- export type GetCategoriesQuery = {
318
- __typename?: "Query";
319
- schema: {
320
- __typename?: "Schema";
321
- url?: string | null;
322
- tags: Array<{ __typename?: "SchemaTag"; name: string }>;
323
- };
324
- };
325
-
326
- export type GetOperationsQueryVariables = Exact<{
324
+ export type GetSidebarOperationsQueryVariables = Exact<{
327
325
  input: Scalars["JSON"]["input"];
328
326
  type: SchemaType;
329
- tag?: InputMaybe<Scalars["String"]["input"]>;
330
327
  }>;
331
328
 
332
- export type GetOperationsQuery = {
329
+ export type GetSidebarOperationsQuery = {
333
330
  __typename?: "Query";
334
331
  schema: {
335
332
  __typename?: "Schema";
336
- operations: Array<{
337
- __typename?: "OperationItem";
338
- slug: string;
339
- deprecated?: boolean | null;
340
- method: string;
341
- summary?: string | null;
342
- operationId?: string | null;
343
- path: string;
344
- tags?: Array<{ __typename?: "TagItem"; name: string }> | null;
345
- }>;
346
- untagged: Array<{
347
- __typename?: "OperationItem";
348
- slug: string;
349
- deprecated?: boolean | null;
350
- method: string;
351
- summary?: string | null;
352
- operationId?: string | null;
353
- path: string;
333
+ tags: Array<{
334
+ __typename?: "SchemaTag";
335
+ slug?: string | null;
336
+ name?: string | null;
337
+ extensions?: any | null;
338
+ operations: Array<{
339
+ __typename?: "OperationItem";
340
+ summary?: string | null;
341
+ slug: string;
342
+ method: string;
343
+ operationId?: string | null;
344
+ path: string;
345
+ }>;
354
346
  }>;
355
347
  };
356
348
  };
@@ -455,8 +447,18 @@ export const ServersQueryDocument = new TypedDocumentString(`
455
447
  ServersQueryQuery,
456
448
  ServersQueryQueryVariables
457
449
  >;
458
- export const AllOperationsDocument = new TypedDocumentString(`
459
- query AllOperations($input: JSON!, $type: SchemaType!, $tag: String, $untagged: Boolean) {
450
+ export const SchemaWarmupDocument = new TypedDocumentString(`
451
+ query SchemaWarmup($input: JSON!, $type: SchemaType!) {
452
+ schema(input: $input, type: $type) {
453
+ openapi
454
+ }
455
+ }
456
+ `) as unknown as TypedDocumentString<
457
+ SchemaWarmupQuery,
458
+ SchemaWarmupQueryVariables
459
+ >;
460
+ export const OperationsForTagDocument = new TypedDocumentString(`
461
+ query OperationsForTag($input: JSON!, $type: SchemaType!, $tag: String, $untagged: Boolean) {
460
462
  schema(input: $input, type: $type) {
461
463
  servers {
462
464
  url
@@ -540,8 +542,8 @@ export const AllOperationsDocument = new TypedDocumentString(`
540
542
  }
541
543
  }
542
544
  }`) as unknown as TypedDocumentString<
543
- AllOperationsQuery,
544
- AllOperationsQueryVariables
545
+ OperationsForTagQuery,
546
+ OperationsForTagQueryVariables
545
547
  >;
546
548
  export const GetServerQueryDocument = new TypedDocumentString(`
547
549
  query getServerQuery($input: JSON!, $type: SchemaType!) {
@@ -556,44 +558,24 @@ export const GetServerQueryDocument = new TypedDocumentString(`
556
558
  GetServerQueryQuery,
557
559
  GetServerQueryQueryVariables
558
560
  >;
559
- export const GetCategoriesDocument = new TypedDocumentString(`
560
- query GetCategories($input: JSON!, $type: SchemaType!) {
561
+ export const GetSidebarOperationsDocument = new TypedDocumentString(`
562
+ query GetSidebarOperations($input: JSON!, $type: SchemaType!) {
561
563
  schema(input: $input, type: $type) {
562
- url
563
564
  tags {
564
- name
565
- }
566
- }
567
- }
568
- `) as unknown as TypedDocumentString<
569
- GetCategoriesQuery,
570
- GetCategoriesQueryVariables
571
- >;
572
- export const GetOperationsDocument = new TypedDocumentString(`
573
- query GetOperations($input: JSON!, $type: SchemaType!, $tag: String) {
574
- schema(input: $input, type: $type) {
575
- operations(tag: $tag) {
576
565
  slug
577
- deprecated
578
- method
579
- summary
580
- operationId
581
- path
582
- tags {
583
- name
566
+ name
567
+ extensions
568
+ operations {
569
+ summary
570
+ slug
571
+ method
572
+ operationId
573
+ path
584
574
  }
585
575
  }
586
- untagged: operations(untagged: true) {
587
- slug
588
- deprecated
589
- method
590
- summary
591
- operationId
592
- path
593
- }
594
576
  }
595
577
  }
596
578
  `) as unknown as TypedDocumentString<
597
- GetOperationsQuery,
598
- GetOperationsQueryVariables
579
+ GetSidebarOperationsQuery,
580
+ GetSidebarOperationsQueryVariables
599
581
  >;
@@ -1,14 +1,12 @@
1
- import type { ResultOf } from "@graphql-typed-document-node/core";
2
- import slugify from "@sindresorhus/slugify";
3
1
  import { CirclePlayIcon, LogInIcon } from "lucide-react";
4
2
  import { type ReactNode } from "react";
5
3
  import { matchPath } from "react-router";
6
4
  import { useAuth } from "../../authentication/hook.js";
7
5
  import { type ZudokuPlugin } from "../../core/plugins.js";
8
- import type { SchemaImports } from "../../oas/graphql/index.js";
9
6
  import { Button } from "../../ui/Button.js";
10
7
  import { joinUrl } from "../../util/joinUrl.js";
11
8
  import { GraphQLClient } from "./client/GraphQLClient.js";
9
+ import type { GetSidebarOperationsQuery } from "./graphql/graphql.js";
12
10
  import { graphql } from "./graphql/index.js";
13
11
  import { type OasPluginConfig } from "./interfaces.js";
14
12
  import type { PlaygroundContentProps } from "./playground/Playground.js";
@@ -16,54 +14,33 @@ import { PlaygroundDialog } from "./playground/PlaygroundDialog.js";
16
14
  import { createSidebarCategory } from "./util/createSidebarCategory.js";
17
15
  import { getRoutes, getVersions } from "./util/getRoutes.js";
18
16
 
19
- const GetCategoriesQuery = graphql(`
20
- query GetCategories($input: JSON!, $type: SchemaType!) {
17
+ const GetSidebarOperationsQuery = graphql(`
18
+ query GetSidebarOperations($input: JSON!, $type: SchemaType!) {
21
19
  schema(input: $input, type: $type) {
22
- url
23
20
  tags {
24
- name
25
- }
26
- }
27
- }
28
- `);
29
-
30
- const GetOperationsQuery = graphql(`
31
- query GetOperations($input: JSON!, $type: SchemaType!, $tag: String) {
32
- schema(input: $input, type: $type) {
33
- operations(tag: $tag) {
34
21
  slug
35
- deprecated
36
- method
37
- summary
38
- operationId
39
- path
40
- tags {
41
- name
22
+ name
23
+ extensions
24
+ operations {
25
+ summary
26
+ slug
27
+ method
28
+ operationId
29
+ path
42
30
  }
43
31
  }
44
- untagged: operations(untagged: true) {
45
- slug
46
- deprecated
47
- method
48
- summary
49
- operationId
50
- path
51
- }
52
32
  }
53
33
  }
54
34
  `);
55
35
 
56
- export type OperationResult = ResultOf<
57
- typeof GetOperationsQuery
58
- >["schema"]["operations"][number];
59
-
60
- type InternalOasPluginConfig = { schemaImports?: SchemaImports };
36
+ export type OperationResult =
37
+ GetSidebarOperationsQuery["schema"]["tags"][number]["operations"][number];
61
38
 
62
- export type OpenApiPluginOptions = OasPluginConfig & InternalOasPluginConfig;
39
+ export type OpenApiPluginOptions = OasPluginConfig;
63
40
 
64
41
  export const UNTAGGED_PATH = "~endpoints";
65
42
 
66
- export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
43
+ export const openApiPlugin = (config: OasPluginConfig): ZudokuPlugin => {
67
44
  const basePath = joinUrl(config.navigationId ?? "/reference");
68
45
  const client = new GraphQLClient(config);
69
46
 
@@ -134,7 +111,7 @@ export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
134
111
  );
135
112
  },
136
113
  }),
137
- getSidebar: async (path: string) => {
114
+ getSidebar: async (path, context) => {
138
115
  if (!matchPath({ path: basePath, end: false }, path)) {
139
116
  return [];
140
117
  }
@@ -147,67 +124,46 @@ export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
147
124
  try {
148
125
  const versionParam = match?.params.version;
149
126
  const version = versionParam ?? getVersions(config).at(0);
150
- const { type, options } = config;
127
+ const { type } = config;
151
128
  const input = type === "file" ? config.input[version!] : config.input;
152
129
 
153
- const collapsible = options?.loadTags === true || config.type === "url";
154
- const collapsed = !options?.loadTags && config.type !== "url";
155
-
156
- // find tag name by slug in config.tagPages
157
- const tagName = config.tagPages?.find(
158
- (tag) => slugify(tag) === match?.params.tag,
159
- );
130
+ const data = await context.queryClient.ensureQueryData({
131
+ queryKey: ["sidebar-operations-query", input],
132
+ queryFn: () =>
133
+ client.fetch(GetSidebarOperationsQuery, { type, input }),
134
+ });
160
135
 
161
- const [tagData, operationsData] = await Promise.all([
162
- client.fetch(GetCategoriesQuery, { type, input }),
163
- client.fetch(GetOperationsQuery, {
164
- type,
165
- input,
166
- tag: !options?.loadTags ? tagName : undefined,
167
- }),
168
- ]);
169
-
170
- const categories = tagData.schema.tags.flatMap((tag) => {
171
- const categoryPath = joinUrl(
172
- basePath,
173
- versionParam,
174
- slugify(tag.name),
175
- );
136
+ const categories = data.schema.tags.flatMap((tag) => {
137
+ if (!tag.name || tag.operations.length === 0) return [];
176
138
 
177
- const operations = operationsData.schema.operations.filter(
178
- (operation) =>
179
- operation.tags?.length !== 0 &&
180
- operation.tags?.map((t) => t.name).includes(tag.name),
181
- );
139
+ const categoryPath = joinUrl(basePath, versionParam, tag.slug);
182
140
 
183
- // skip empty categories
184
- if (options?.loadTags && operations.length === 0) {
185
- return [];
186
- }
141
+ const isCollapsed =
142
+ tag.extensions?.["x-zudoku-collapsed"] ??
143
+ !config.options?.expandAllTags;
144
+ const isCollapsible =
145
+ tag.extensions?.["x-zudoku-collapsible"] ?? true;
187
146
 
188
147
  return createSidebarCategory({
189
148
  label: tag.name,
190
149
  path: categoryPath,
191
- operations:
192
- match?.params.tag !== UNTAGGED_PATH || options?.loadTags
193
- ? operations
194
- : [],
195
- collapsible,
196
- collapsed,
150
+ operations: tag.operations,
151
+ collapsed: isCollapsed,
152
+ collapsible: isCollapsible,
197
153
  });
198
154
  });
199
155
 
200
- if (operationsData.schema.untagged.length > 0) {
156
+ const untaggedOperations = data.schema.tags.find(
157
+ (tag) => !tag.name,
158
+ )?.operations;
159
+
160
+ if (untaggedOperations) {
201
161
  categories.push(
202
162
  createSidebarCategory({
203
163
  label: "Other endpoints",
204
164
  path: joinUrl(basePath, versionParam, UNTAGGED_PATH),
205
- operations:
206
- match?.params.tag === UNTAGGED_PATH || options?.loadTags
207
- ? operationsData.schema.untagged
208
- : [],
209
- collapsible,
210
- collapsed,
165
+ operations: untaggedOperations,
166
+ collapsed: !config.options?.expandAllTags,
211
167
  }),
212
168
  );
213
169
  }
@@ -1,3 +1,5 @@
1
+ import type { SchemaImports } from "../../oas/graphql/index.js";
2
+
1
3
  type DynamicInput = () => Promise<unknown>;
2
4
 
3
5
  type OasSource =
@@ -15,11 +17,12 @@ type BaseOasConfig = {
15
17
  navigationId?: string;
16
18
  skipPreload?: boolean;
17
19
  tagPages?: Array<string>;
20
+ schemaImports?: SchemaImports;
18
21
  options?: {
19
22
  examplesLanguage?: string;
20
23
  disablePlayground?: boolean;
21
- loadTags?: boolean;
22
24
  showVersionSelect?: "always" | "if-available" | "hide";
25
+ expandAllTags?: boolean;
23
26
  };
24
27
  };
25
28
 
@@ -28,12 +28,10 @@ export const createSidebarCategory = ({
28
28
  type: "link" as const,
29
29
  label: operation.summary ?? operation.path,
30
30
  href: `${path}#${operation.slug}`,
31
- ...(operation.method && {
32
- badge: {
33
- label: operation.method,
34
- color: MethodColorMap[operation.method.toLowerCase()]!,
35
- invert: true,
36
- } as const,
37
- }),
31
+ badge: {
32
+ label: operation.method,
33
+ color: MethodColorMap[operation.method.toLowerCase()]!,
34
+ invert: true,
35
+ },
38
36
  })),
39
37
  });
@@ -20,11 +20,11 @@ export const traverse = <T extends JsonValue = RecordAny>(
20
20
  for (const [key, value] of Object.entries(transformed)) {
21
21
  if (Array.isArray(value)) {
22
22
  result[key] = value.map((item) =>
23
- typeof item === "object" && item !== null
23
+ typeof item === "object" && item != null
24
24
  ? traverse(item, transform)
25
25
  : item,
26
26
  );
27
- } else if (typeof value === "object" && value !== null) {
27
+ } else if (typeof value === "object" && value != null) {
28
28
  result[key] = traverse(value, transform);
29
29
  } else {
30
30
  result[key] = value;