zudoku 0.46.1 → 0.46.2

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 (168) hide show
  1. package/dist/config/validators/validate.d.ts +12 -12
  2. package/dist/config/validators/validate.js +3 -1
  3. package/dist/config/validators/validate.js.map +1 -1
  4. package/dist/lib/authentication/AuthenticationPlugin.d.ts +0 -5
  5. package/dist/lib/authentication/AuthenticationPlugin.js +0 -12
  6. package/dist/lib/authentication/AuthenticationPlugin.js.map +1 -1
  7. package/dist/lib/plugins/api-keys/SettingsApiKeys.js +110 -15
  8. package/dist/lib/plugins/api-keys/SettingsApiKeys.js.map +1 -1
  9. package/dist/lib/plugins/api-keys/index.d.ts +3 -3
  10. package/dist/lib/plugins/api-keys/index.js +14 -14
  11. package/dist/lib/plugins/api-keys/index.js.map +1 -1
  12. package/dist/lib/plugins/openapi/Sidecar.js +11 -91
  13. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  14. package/dist/lib/plugins/openapi/playground/BodyPanel.d.ts +5 -0
  15. package/dist/lib/plugins/openapi/playground/BodyPanel.js +22 -0
  16. package/dist/lib/plugins/openapi/playground/BodyPanel.js.map +1 -0
  17. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js +2 -1
  18. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js.map +1 -1
  19. package/dist/lib/plugins/openapi/playground/Headers.js +25 -25
  20. package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
  21. package/dist/lib/plugins/openapi/playground/IdentitySelector.js +1 -1
  22. package/dist/lib/plugins/openapi/playground/IdentitySelector.js.map +1 -1
  23. package/dist/lib/plugins/openapi/playground/ParamsGrid.js +2 -2
  24. package/dist/lib/plugins/openapi/playground/ParamsGrid.js.map +1 -1
  25. package/dist/lib/plugins/openapi/playground/PathParams.js +1 -1
  26. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
  27. package/dist/lib/plugins/openapi/playground/Playground.d.ts +3 -8
  28. package/dist/lib/plugins/openapi/playground/Playground.js +70 -65
  29. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  30. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js +1 -1
  31. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js.map +1 -1
  32. package/dist/lib/plugins/openapi/playground/QueryParams.js +1 -1
  33. package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
  34. package/dist/lib/plugins/openapi/playground/fileUtils.d.ts +2 -0
  35. package/dist/lib/plugins/openapi/playground/fileUtils.js +22 -0
  36. package/dist/lib/plugins/openapi/playground/fileUtils.js.map +1 -0
  37. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.d.ts +4 -1
  38. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js +29 -20
  39. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js.map +1 -1
  40. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js +2 -2
  41. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js.map +1 -1
  42. package/dist/lib/plugins/openapi/playground/useRememberSkipLoginDialog.d.ts +16 -0
  43. package/dist/lib/plugins/openapi/playground/useRememberSkipLoginDialog.js +10 -0
  44. package/dist/lib/plugins/openapi/playground/useRememberSkipLoginDialog.js.map +1 -0
  45. package/dist/lib/plugins/openapi/util/createHttpSnippet.d.ts +11 -0
  46. package/dist/lib/plugins/openapi/util/createHttpSnippet.js +89 -0
  47. package/dist/lib/plugins/openapi/util/createHttpSnippet.js.map +1 -0
  48. package/dist/lib/shiki.d.ts +8 -12
  49. package/dist/lib/shiki.js +11 -13
  50. package/dist/lib/shiki.js.map +1 -1
  51. package/dist/lib/ui/CodeBlock.js +1 -1
  52. package/dist/lib/ui/CodeBlock.js.map +1 -1
  53. package/dist/lib/ui/Dialog.d.ts +3 -1
  54. package/dist/lib/ui/Dialog.js +2 -2
  55. package/dist/lib/ui/Dialog.js.map +1 -1
  56. package/dist/lib/util/humanFileSize.d.ts +6 -0
  57. package/dist/lib/util/humanFileSize.js +14 -0
  58. package/dist/lib/util/humanFileSize.js.map +1 -0
  59. package/dist/lib/util/humanFileSize.test.d.ts +1 -0
  60. package/dist/lib/util/humanFileSize.test.js +22 -0
  61. package/dist/lib/util/humanFileSize.test.js.map +1 -0
  62. package/lib/{Callout-BkgOUkoZ.js → Callout-CoVxYafP.js} +2 -2
  63. package/lib/{Callout-BkgOUkoZ.js.map → Callout-CoVxYafP.js.map} +1 -1
  64. package/lib/{Dialog-Du6WMcIA.js → Dialog-BxpuVLh9.js} +25 -25
  65. package/lib/Dialog-BxpuVLh9.js.map +1 -0
  66. package/lib/{Markdown-BRAyzyUJ.js → Markdown-Cm4kj26S.js} +6 -5
  67. package/lib/{Markdown-BRAyzyUJ.js.map → Markdown-Cm4kj26S.js.map} +1 -1
  68. package/lib/{MdxPage-B3v1BSKr.js → MdxPage-fDGQtB5w.js} +5 -5
  69. package/lib/{MdxPage-B3v1BSKr.js.map → MdxPage-fDGQtB5w.js.map} +1 -1
  70. package/lib/{OasProvider-5jrFuhVk.js → OasProvider-CFBvfR3r.js} +3 -3
  71. package/lib/{OasProvider-5jrFuhVk.js.map → OasProvider-CFBvfR3r.js.map} +1 -1
  72. package/lib/{OperationList-BmoMLQPO.js → OperationList-Xs4KWmsh.js} +1139 -1131
  73. package/lib/OperationList-Xs4KWmsh.js.map +1 -0
  74. package/lib/{Pagination-Cr0fWZS3.js → Pagination-CCxhL836.js} +2 -2
  75. package/lib/{Pagination-Cr0fWZS3.js.map → Pagination-CCxhL836.js.map} +1 -1
  76. package/lib/{RouteGuard-PrSVLbSr.js → RouteGuard-CZ_uLv3g.js} +6 -6
  77. package/lib/{RouteGuard-PrSVLbSr.js.map → RouteGuard-CZ_uLv3g.js.map} +1 -1
  78. package/lib/{SchemaList-B4riYLoP.js → SchemaList-BWaNlmUJ.js} +6 -6
  79. package/lib/{SchemaList-B4riYLoP.js.map → SchemaList-BWaNlmUJ.js.map} +1 -1
  80. package/lib/{SchemaView-CPZ6RgsF.js → SchemaView-DdKJt2ln.js} +3 -3
  81. package/lib/{SchemaView-CPZ6RgsF.js.map → SchemaView-DdKJt2ln.js.map} +1 -1
  82. package/lib/{SignUp-CWaiH0tY.js → SignUp-B-1Pvc-8.js} +3 -3
  83. package/lib/{SignUp-CWaiH0tY.js.map → SignUp-B-1Pvc-8.js.map} +1 -1
  84. package/lib/{Slot-Bo6K4tnb.js → Slot-B99cbD-q.js} +11 -11
  85. package/lib/{Slot-Bo6K4tnb.js.map → Slot-B99cbD-q.js.map} +1 -1
  86. package/lib/{SyntaxHighlight-DedRjJNr.js → SyntaxHighlight-Cz6Me7-F.js} +4474 -3323
  87. package/lib/SyntaxHighlight-Cz6Me7-F.js.map +1 -0
  88. package/lib/{Toc-lL3fzNkl.js → Toc-Qe7A4uj_.js} +2 -2
  89. package/lib/{Toc-lL3fzNkl.js.map → Toc-Qe7A4uj_.js.map} +1 -1
  90. package/lib/{chunk-BAXFHI7N-C9WnHsLV.js → chunk-DQRVZFIR-BblmKnHy.js} +697 -697
  91. package/lib/chunk-DQRVZFIR-BblmKnHy.js.map +1 -0
  92. package/lib/{circular-oB4auIIg.js → circular-w5eL5J8a.js} +1812 -1807
  93. package/lib/circular-w5eL5J8a.js.map +1 -0
  94. package/lib/{createServer-DCB82j2t.js → createServer-p3yUA8Bu.js} +3648 -3493
  95. package/lib/createServer-p3yUA8Bu.js.map +1 -0
  96. package/lib/{hook-DawSLaZr.js → hook-k7PfUIsj.js} +10 -10
  97. package/lib/{hook-DawSLaZr.js.map → hook-k7PfUIsj.js.map} +1 -1
  98. package/lib/{index-BXYvD5-7.js → index-yqBxBqxF.js} +1053 -1095
  99. package/lib/index-yqBxBqxF.js.map +1 -0
  100. package/lib/index.esm-Cp4wkyud.js +1236 -0
  101. package/lib/index.esm-Cp4wkyud.js.map +1 -0
  102. package/lib/{mutation-oxMvODNQ.js → mutation-BSeQ8pEK.js} +2 -2
  103. package/lib/{mutation-oxMvODNQ.js.map → mutation-BSeQ8pEK.js.map} +1 -1
  104. package/lib/react-nprogress.esm-C2MPXjiJ.js +389 -0
  105. package/lib/react-nprogress.esm-C2MPXjiJ.js.map +1 -0
  106. package/lib/ui/CodeBlock.js +17 -16
  107. package/lib/ui/CodeBlock.js.map +1 -1
  108. package/lib/ui/Command.js +1 -1
  109. package/lib/ui/Dialog.js +25 -25
  110. package/lib/ui/Dialog.js.map +1 -1
  111. package/lib/ui/Form.js +1 -1
  112. package/lib/ui/SyntaxHighlight.js +2 -2
  113. package/lib/{useExposedProps-DG8J6ewJ.js → useExposedProps-BZQkZneR.js} +2 -2
  114. package/lib/{useExposedProps-DG8J6ewJ.js.map → useExposedProps-BZQkZneR.js.map} +1 -1
  115. package/lib/{useMutation-C_j3dA_L.js → useMutation-CZSmsIGW.js} +3 -3
  116. package/lib/{useMutation-C_j3dA_L.js.map → useMutation-CZSmsIGW.js.map} +1 -1
  117. package/lib/zudoku.auth-auth0.js +1 -1
  118. package/lib/zudoku.auth-clerk.js +2 -2
  119. package/lib/zudoku.auth-openid.js +57 -66
  120. package/lib/zudoku.auth-openid.js.map +1 -1
  121. package/lib/zudoku.components.js +1698 -2082
  122. package/lib/zudoku.components.js.map +1 -1
  123. package/lib/zudoku.hooks.js +11 -11
  124. package/lib/zudoku.plugin-api-catalog.js +5 -5
  125. package/lib/zudoku.plugin-api-keys.js +473 -4970
  126. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  127. package/lib/zudoku.plugin-custom-pages.js +2 -2
  128. package/lib/zudoku.plugin-markdown.js +1 -1
  129. package/lib/zudoku.plugin-openapi.js +3 -3
  130. package/lib/zudoku.plugin-redirect.js +1 -1
  131. package/lib/zudoku.plugin-search-pagefind.js +5 -5
  132. package/package.json +33 -34
  133. package/src/lib/authentication/AuthenticationPlugin.tsx +0 -14
  134. package/src/lib/plugins/api-keys/SettingsApiKeys.tsx +193 -48
  135. package/src/lib/plugins/api-keys/index.tsx +25 -18
  136. package/src/lib/plugins/openapi/Sidecar.tsx +11 -97
  137. package/src/lib/plugins/openapi/playground/BodyPanel.tsx +46 -0
  138. package/src/lib/plugins/openapi/playground/ExamplesDropdown.tsx +4 -1
  139. package/src/lib/plugins/openapi/playground/Headers.tsx +110 -106
  140. package/src/lib/plugins/openapi/playground/IdentitySelector.tsx +13 -11
  141. package/src/lib/plugins/openapi/playground/ParamsGrid.tsx +2 -2
  142. package/src/lib/plugins/openapi/playground/PathParams.tsx +1 -1
  143. package/src/lib/plugins/openapi/playground/Playground.tsx +127 -211
  144. package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +2 -1
  145. package/src/lib/plugins/openapi/playground/QueryParams.tsx +1 -1
  146. package/src/lib/plugins/openapi/playground/fileUtils.ts +32 -0
  147. package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +74 -39
  148. package/src/lib/plugins/openapi/playground/result-panel/ResultPanel.tsx +4 -1
  149. package/src/lib/plugins/openapi/playground/useRememberSkipLoginDialog.tsx +20 -0
  150. package/src/lib/plugins/openapi/util/createHttpSnippet.ts +107 -0
  151. package/src/lib/shiki.ts +21 -22
  152. package/src/lib/ui/CodeBlock.tsx +1 -0
  153. package/src/lib/ui/Dialog.tsx +11 -7
  154. package/src/lib/util/humanFileSize.test.ts +24 -0
  155. package/src/lib/util/humanFileSize.ts +16 -0
  156. package/dist/lib/plugins/openapi/playground/SubmitButton.d.ts +0 -7
  157. package/dist/lib/plugins/openapi/playground/SubmitButton.js +0 -19
  158. package/dist/lib/plugins/openapi/playground/SubmitButton.js.map +0 -1
  159. package/lib/Dialog-Du6WMcIA.js.map +0 -1
  160. package/lib/OperationList-BmoMLQPO.js.map +0 -1
  161. package/lib/SyntaxHighlight-DedRjJNr.js.map +0 -1
  162. package/lib/chunk-BAXFHI7N-C9WnHsLV.js.map +0 -1
  163. package/lib/circular-oB4auIIg.js.map +0 -1
  164. package/lib/createServer-DCB82j2t.js.map +0 -1
  165. package/lib/index-BXYvD5-7.js.map +0 -1
  166. package/lib/index.esm-DSfX_eMP.js +0 -1216
  167. package/lib/index.esm-DSfX_eMP.js.map +0 -1
  168. package/src/lib/plugins/openapi/playground/SubmitButton.tsx +0 -70
@@ -1,8 +1,8 @@
1
1
  import { j as o } from "./jsx-runtime-C5mzlN2N.js";
2
2
  import a from "react";
3
- import { P as n } from "./Markdown-BRAyzyUJ.js";
3
+ import { P as n } from "./Markdown-Cm4kj26S.js";
4
4
  import { c } from "./cn-wvCW-ho6.js";
5
- import { u as p } from "./useExposedProps-DG8J6ewJ.js";
5
+ import { u as p } from "./useExposedProps-BZQkZneR.js";
6
6
  const u = ({
7
7
  element: t,
8
8
  render: s,
@@ -50,7 +50,7 @@ const m = (a) => ({
50
50
  const c = {
51
51
  path: e,
52
52
  lazy: async () => {
53
- const { MdxPage: u } = await import("./MdxPage-B3v1BSKr.js"), { default: h, ...p } = await i();
53
+ const { MdxPage: u } = await import("./MdxPage-fDGQtB5w.js"), { default: h, ...p } = await i();
54
54
  return {
55
55
  element: /* @__PURE__ */ f.jsx(
56
56
  u,
@@ -1,9 +1,9 @@
1
1
  import "./jsx-runtime-C5mzlN2N.js";
2
2
  import "lucide-react";
3
- import "./chunk-BAXFHI7N-C9WnHsLV.js";
4
- import "./hook-DawSLaZr.js";
3
+ import "./chunk-DQRVZFIR-BblmKnHy.js";
4
+ import "./hook-k7PfUIsj.js";
5
5
  import "./ui/Button.js";
6
- import { F as a, U as s, H as n } from "./index-BXYvD5-7.js";
6
+ import { F as a, U as s, H as n } from "./index-yqBxBqxF.js";
7
7
  export {
8
8
  a as GetSidebarOperationsQuery,
9
9
  s as UNTAGGED_PATH,
@@ -1,4 +1,4 @@
1
- import { r as o } from "./chunk-BAXFHI7N-C9WnHsLV.js";
1
+ import { r as o } from "./chunk-DQRVZFIR-BblmKnHy.js";
2
2
  const a = (r) => ({
3
3
  getRoutes: () => r.redirects.map(({ from: e, to: t }) => ({
4
4
  path: e,
@@ -1,14 +1,14 @@
1
1
  import { j as e } from "./jsx-runtime-C5mzlN2N.js";
2
2
  import { C as k } from "./ClientOnly-E7hGysn1.js";
3
3
  import { VisuallyHidden as v } from "@radix-ui/react-visually-hidden";
4
- import { u as N, e as y, m as j, o as _, l as L } from "./hook-DawSLaZr.js";
4
+ import { u as N, k as y, l as j, o as _, j as L } from "./hook-k7PfUIsj.js";
5
5
  import { useRef as S, useLayoutEffect as T, useState as w } from "react";
6
6
  import { B as E } from "./Button-BE9IVkWV.js";
7
- import { C as R, a as f, b as p, c as F, d as I, e as q, f as P } from "./Callout-BkgOUkoZ.js";
8
- import { c as $ } from "./Dialog-Du6WMcIA.js";
9
- import { S as A } from "./RouteGuard-PrSVLbSr.js";
7
+ import { C as R, a as f, b as p, c as F, d as I, e as q, f as P } from "./Callout-CoVxYafP.js";
8
+ import { c as $ } from "./Dialog-BxpuVLh9.js";
9
+ import { S as A } from "./RouteGuard-CZ_uLv3g.js";
10
10
  import { BracketsIcon as D, FileTextIcon as O } from "lucide-react";
11
- import { a as B, L as g } from "./chunk-BAXFHI7N-C9WnHsLV.js";
11
+ import { a as B, L as g } from "./chunk-DQRVZFIR-BblmKnHy.js";
12
12
  const U = async ({
13
13
  search: r,
14
14
  options: a,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.46.1",
3
+ "version": "0.46.2",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -136,34 +136,34 @@
136
136
  "@mdx-js/react": "3.1.0",
137
137
  "@mdx-js/rollup": "3.1.0",
138
138
  "@pothos/core": "4.6.0",
139
- "@radix-ui/react-accordion": "1.2.10",
140
- "@radix-ui/react-alert-dialog": "1.1.13",
141
- "@radix-ui/react-aspect-ratio": "1.1.6",
142
- "@radix-ui/react-checkbox": "1.3.1",
143
- "@radix-ui/react-collapsible": "1.1.10",
144
- "@radix-ui/react-dialog": "1.1.13",
145
- "@radix-ui/react-dropdown-menu": "2.1.14",
146
- "@radix-ui/react-hover-card": "1.1.13",
147
- "@radix-ui/react-label": "2.1.6",
148
- "@radix-ui/react-popover": "1.1.13",
149
- "@radix-ui/react-progress": "1.1.6",
150
- "@radix-ui/react-radio-group": "1.3.6",
139
+ "@radix-ui/react-accordion": "1.2.11",
140
+ "@radix-ui/react-alert-dialog": "1.1.14",
141
+ "@radix-ui/react-aspect-ratio": "1.1.7",
142
+ "@radix-ui/react-checkbox": "1.3.2",
143
+ "@radix-ui/react-collapsible": "1.1.11",
144
+ "@radix-ui/react-dialog": "1.1.14",
145
+ "@radix-ui/react-dropdown-menu": "2.1.15",
146
+ "@radix-ui/react-hover-card": "1.1.14",
147
+ "@radix-ui/react-label": "2.1.7",
148
+ "@radix-ui/react-popover": "1.1.14",
149
+ "@radix-ui/react-progress": "1.1.7",
150
+ "@radix-ui/react-radio-group": "1.3.7",
151
151
  "@radix-ui/react-scroll-area": "1.2.9",
152
- "@radix-ui/react-select": "2.2.4",
152
+ "@radix-ui/react-select": "2.2.5",
153
153
  "@radix-ui/react-slider": "1.3.5",
154
154
  "@radix-ui/react-slot": "1.2.3",
155
- "@radix-ui/react-switch": "1.2.4",
156
- "@radix-ui/react-tabs": "1.1.11",
157
- "@radix-ui/react-toggle": "1.1.8",
158
- "@radix-ui/react-toggle-group": "1.1.9",
159
- "@radix-ui/react-tooltip": "1.2.6",
160
- "@radix-ui/react-visually-hidden": "1.2.2",
155
+ "@radix-ui/react-switch": "1.2.5",
156
+ "@radix-ui/react-tabs": "1.1.12",
157
+ "@radix-ui/react-toggle": "1.1.9",
158
+ "@radix-ui/react-toggle-group": "1.1.10",
159
+ "@radix-ui/react-tooltip": "1.2.7",
160
+ "@radix-ui/react-visually-hidden": "1.2.3",
161
161
  "@scalar/openapi-parser": "0.10.17",
162
162
  "@sentry/node": "9.12.0",
163
- "@shikijs/langs": "3.2.2",
164
- "@shikijs/rehype": "3.2.2",
165
- "@shikijs/themes": "3.2.2",
166
- "@shikijs/transformers": "3.3.0",
163
+ "@shikijs/langs": "3.4.2",
164
+ "@shikijs/rehype": "3.4.2",
165
+ "@shikijs/themes": "3.4.2",
166
+ "@shikijs/transformers": "3.4.2",
167
167
  "@sindresorhus/slugify": "2.2.1",
168
168
  "@stefanprobst/rehype-extract-toc": "3.0.0",
169
169
  "@tailwindcss/typography": "0.5.16",
@@ -188,7 +188,7 @@
188
188
  "fast-equals": "5.2.2",
189
189
  "framer-motion": "^12.12.2",
190
190
  "glob": "11.0.1",
191
- "graphql": "16.10.0",
191
+ "graphql": "16.11.0",
192
192
  "graphql-type-json": "0.3.2",
193
193
  "graphql-yoga": "5.13.3",
194
194
  "gray-matter": "4.0.3",
@@ -210,10 +210,10 @@
210
210
  "piscina": "5.0.0-alpha.2",
211
211
  "posthog-node": "4.17.1",
212
212
  "react-error-boundary": "5.0.0",
213
- "react-hook-form": "7.55.0",
213
+ "react-hook-form": "7.57.0",
214
214
  "react-is": "19.1.0",
215
215
  "react-markdown": "10.1.0",
216
- "react-router": "7.5.2",
216
+ "react-router": "7.6.1",
217
217
  "rehype-mdx-import-media": "1.2.0",
218
218
  "rehype-raw": "7.0.0",
219
219
  "rehype-slug": "6.0.0",
@@ -225,9 +225,9 @@
225
225
  "remark-mdx-frontmatter": "5.1.0",
226
226
  "remark-parse": "^11.0.0",
227
227
  "remark-rehype": "^11.1.2",
228
- "rollup": "4.40.1",
228
+ "rollup": "4.41.1",
229
229
  "semver": "7.7.1",
230
- "shiki": "3.3.0",
230
+ "shiki": "3.4.2",
231
231
  "sitemap": "8.0.0",
232
232
  "spin-delay": "2.0.1",
233
233
  "strip-ansi": "7.1.0",
@@ -236,24 +236,23 @@
236
236
  "ulidx": "2.4.1",
237
237
  "unified": "^11.0.5",
238
238
  "unist-util-visit": "5.0.0",
239
- "urql": "4.2.1",
240
239
  "vaul": "1.1.2",
241
240
  "vfile": "6.0.3",
242
241
  "vite": "6.3.5",
243
242
  "yaml": "2.7.1",
244
243
  "yargs": "17.7.2",
245
244
  "zod": "3.24.2",
246
- "zod-validation-error": "3.4.0",
245
+ "zod-validation-error": "3.4.1",
247
246
  "zustand": "5.0.3"
248
247
  },
249
248
  "devDependencies": {
250
- "@graphql-codegen/cli": "5.0.5",
249
+ "@graphql-codegen/cli": "5.0.6",
251
250
  "@graphql-codegen/client-preset": "4.8.0",
252
251
  "@testing-library/dom": "10.4.0",
253
252
  "@testing-library/jest-dom": "6.6.3",
254
253
  "@testing-library/react": "16.3.0",
255
254
  "@types/estree": "1.0.7",
256
- "@types/express": "5.0.1",
255
+ "@types/express": "5.0.2",
257
256
  "@types/har-format": "1.2.16",
258
257
  "@types/hast": "^3.0.4",
259
258
  "@types/json-schema": "7.0.15",
@@ -267,7 +266,7 @@
267
266
  "@types/yargs": "17.0.33",
268
267
  "@vitest/coverage-v8": "3.1.1",
269
268
  "esbuild": "0.25.1",
270
- "happy-dom": "17.4.4",
269
+ "happy-dom": "17.5.6",
271
270
  "mdast-util-mdx": "3.0.0",
272
271
  "react": "19.1.0",
273
272
  "react-dom": "19.1.0",
@@ -28,20 +28,6 @@ export class CoreAuthenticationPlugin implements PluginInterface {
28
28
  ];
29
29
  }
30
30
 
31
- async getSidebar(path: string) {
32
- if (path.startsWith("/settings")) {
33
- return [
34
- {
35
- type: "link" as const,
36
- label: "Logout",
37
- href: "/signout",
38
- },
39
- ];
40
- }
41
-
42
- return [];
43
- }
44
-
45
31
  getProfileMenuItems() {
46
32
  return [
47
33
  {
@@ -3,17 +3,18 @@ import {
3
3
  useQueryClient,
4
4
  useSuspenseQuery,
5
5
  } from "@tanstack/react-query";
6
- import { AnimatePresence, motion } from "framer-motion";
6
+ import { AnimatePresence } from "framer-motion";
7
7
  import {
8
8
  CheckIcon,
9
9
  CopyIcon,
10
10
  EyeIcon,
11
11
  EyeOffIcon,
12
- LoaderPinwheelIcon,
12
+ PencilLineIcon,
13
13
  RotateCwIcon,
14
14
  TrashIcon,
15
+ XIcon,
15
16
  } from "lucide-react";
16
- import { useState } from "react";
17
+ import React, { useState } from "react";
17
18
  import { Link } from "react-router";
18
19
  import { Card, CardHeader } from "zudoku/ui/Card.js";
19
20
  import {
@@ -29,13 +30,18 @@ import {
29
30
  import { useZudoku } from "../../components/context/ZudokuContext.js";
30
31
  import { Slot } from "../../components/Slot.js";
31
32
  import { Button } from "../../ui/Button.js";
33
+ import { Input } from "../../ui/Input.js";
32
34
  import { cn } from "../../util/cn.js";
33
- import { type ApiKey, type ApiKeyService } from "./index.js";
35
+ import { ApiConsumer, type ApiKey, type ApiKeyService } from "./index.js";
34
36
 
35
37
  export const SettingsApiKeys = ({ service }: { service: ApiKeyService }) => {
36
38
  const context = useZudoku();
37
39
  const queryClient = useQueryClient();
38
- const { data } = useSuspenseQuery({
40
+ const [editingConsumerId, setEditingConsumerId] = useState<string | null>(
41
+ null,
42
+ );
43
+ const [editingLabel, setEditingLabel] = useState<string>("");
44
+ const { data, isFetching } = useSuspenseQuery({
39
45
  queryFn: () => service.getConsumers(context),
40
46
  queryKey: ["api-keys"],
41
47
  retry: false,
@@ -55,6 +61,80 @@ export const SettingsApiKeys = ({ service }: { service: ApiKeyService }) => {
55
61
 
56
62
  return service.deleteKey(consumerId, keyId, context);
57
63
  },
64
+ onMutate: async ({ consumerId, keyId }) => {
65
+ await queryClient.cancelQueries({ queryKey: ["api-keys"] });
66
+ const previousData = queryClient.getQueryData<ApiConsumer[]>([
67
+ "api-keys",
68
+ ]);
69
+ queryClient.setQueryData<ApiConsumer[]>(["api-keys"], (old) => {
70
+ if (!old) {
71
+ return old;
72
+ }
73
+
74
+ return old.map((consumer) => {
75
+ if (consumer.id === consumerId) {
76
+ return {
77
+ ...consumer,
78
+ apiKeys: consumer.apiKeys.filter((key) => key.id !== keyId),
79
+ };
80
+ }
81
+ return consumer;
82
+ });
83
+ });
84
+
85
+ return { previousData };
86
+ },
87
+ onError: (err, variables, context) => {
88
+ if (context?.previousData) {
89
+ queryClient.setQueryData(["api-keys"], context.previousData);
90
+ }
91
+ },
92
+ onSuccess: () => {
93
+ void queryClient.invalidateQueries({ queryKey: ["api-keys"] });
94
+ },
95
+ });
96
+
97
+ const updateConsumerMutation = useMutation({
98
+ mutationFn: ({
99
+ consumerId,
100
+ label,
101
+ }: {
102
+ consumerId: string;
103
+ label: string;
104
+ }) => {
105
+ if (!service.updateConsumer) {
106
+ throw new Error("updateConsumer not implemented");
107
+ }
108
+
109
+ return service.updateConsumer({ id: consumerId, label }, context);
110
+ },
111
+ onMutate: async ({ consumerId, label }) => {
112
+ await queryClient.cancelQueries({ queryKey: ["api-keys"] });
113
+
114
+ const previousData = queryClient.getQueryData(["api-keys"]);
115
+ queryClient.setQueryData<ApiConsumer[]>(["api-keys"], (old) => {
116
+ if (!old) {
117
+ return old;
118
+ }
119
+
120
+ return old.map((consumer) => {
121
+ if (consumer.id === consumerId) {
122
+ return {
123
+ ...consumer,
124
+ label,
125
+ };
126
+ }
127
+ return consumer;
128
+ });
129
+ });
130
+
131
+ return { previousData };
132
+ },
133
+ onError: (err, variables, context) => {
134
+ if (context?.previousData) {
135
+ queryClient.setQueryData(["api-keys"], context.previousData);
136
+ }
137
+ },
58
138
  onSuccess: () => {
59
139
  void queryClient.invalidateQueries({ queryKey: ["api-keys"] });
60
140
  },
@@ -71,6 +151,27 @@ export const SettingsApiKeys = ({ service }: { service: ApiKeyService }) => {
71
151
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ["api-keys"] }),
72
152
  });
73
153
 
154
+ const handleStartEdit = (consumerId: string, currentLabel: string) => {
155
+ setEditingConsumerId(consumerId);
156
+ setEditingLabel(currentLabel);
157
+ };
158
+
159
+ const handleSaveEdit = (consumerId: string) => {
160
+ if (editingLabel.trim()) {
161
+ updateConsumerMutation.mutate({
162
+ consumerId,
163
+ label: editingLabel.trim(),
164
+ });
165
+ }
166
+ setEditingConsumerId(null);
167
+ setEditingLabel("");
168
+ };
169
+
170
+ const handleCancelEdit = () => {
171
+ setEditingConsumerId(null);
172
+ setEditingLabel("");
173
+ };
174
+
74
175
  return (
75
176
  <div className="max-w-screen-lg h-full pt-(--padding-content-top) pb-(--padding-content-bottom)">
76
177
  <Slot.Target name="api-keys-list-page" />
@@ -90,11 +191,11 @@ export const SettingsApiKeys = ({ service }: { service: ApiKeyService }) => {
90
191
  <div className="h-8"></div>
91
192
  <div className="grid grid-cols-8">
92
193
  {data.length === 0 ? (
93
- <div className="flex flex-col justify-center gap-4 items-center p-8 border rounded-sm bg-muted/30 text-muted-foreground">
194
+ <div className="flex col-span-full flex-col justify-center gap-4 items-center p-8 border rounded-sm bg-muted/30 text-muted-foreground">
94
195
  <p className="text-center">
95
- No API keys created yet.
196
+ You have no API keys yet.
96
197
  <br />
97
- Get started and create your first key.
198
+ {service.createKey && "Get started and create your first key."}
98
199
  </p>
99
200
  {service.createKey && (
100
201
  <Button asChild variant="outline">
@@ -111,13 +212,49 @@ export const SettingsApiKeys = ({ service }: { service: ApiKeyService }) => {
111
212
  >
112
213
  {data.map((consumers) => (
113
214
  <Card
114
- className="grid grid-cols-subgrid col-span-full items-center mb-4"
215
+ className="grid grid-cols-subgrid col-span-full items-center mb-4 group"
115
216
  key={consumers.id}
116
217
  >
117
218
  <CardHeader className="border-b col-span-full grid-cols-subgrid grid">
118
- <div className="flex flex-col text-sm justify-center">
119
- <div className="font-medium text-lg">
120
- {consumers.name ?? consumers.id}
219
+ <div className="h-10 flex flex-col text-sm justify-center">
220
+ <div className="font-medium text-lg flex items-center gap-2">
221
+ {editingConsumerId === consumers.id ? (
222
+ <div className="flex items-center gap-2 w-full">
223
+ <Input
224
+ maxLength={32}
225
+ value={editingLabel}
226
+ onChange={(e) => setEditingLabel(e.target.value)}
227
+ onKeyDown={(e) => {
228
+ if (e.key === "Enter") {
229
+ handleSaveEdit(consumers.id);
230
+ } else if (e.key === "Escape") {
231
+ handleCancelEdit();
232
+ }
233
+ }}
234
+ className="text-lg font-medium"
235
+ autoFocus
236
+ />
237
+ <div className="flex items-center">
238
+ <Button
239
+ size="icon"
240
+ variant="ghost"
241
+ onClick={() => handleSaveEdit(consumers.id)}
242
+ disabled={!editingLabel.trim()}
243
+ >
244
+ <CheckIcon size={16} />
245
+ </Button>
246
+ <Button
247
+ size="icon"
248
+ variant="ghost"
249
+ onClick={handleCancelEdit}
250
+ >
251
+ <XIcon size={16} />
252
+ </Button>
253
+ </div>
254
+ </div>
255
+ ) : (
256
+ <>{consumers.label}</>
257
+ )}
121
258
  <div className="text-muted-foreground text-xs">
122
259
  {consumers.createdOn}
123
260
  </div>
@@ -139,6 +276,22 @@ export const SettingsApiKeys = ({ service }: { service: ApiKeyService }) => {
139
276
  </div>
140
277
 
141
278
  <div className="flex justify-end">
279
+ {service.updateConsumer && (
280
+ <Button
281
+ size="icon"
282
+ variant="ghost"
283
+ onClick={() =>
284
+ handleStartEdit(consumers.id, consumers.label)
285
+ }
286
+ className={cn(
287
+ editingConsumerId === consumers.id &&
288
+ "opacity-0! pointer-events-none",
289
+ )}
290
+ disabled={editingConsumerId === consumers.id}
291
+ >
292
+ <PencilLineIcon size={16} />
293
+ </Button>
294
+ )}
142
295
  {service.rollKey && (
143
296
  <Dialog>
144
297
  <DialogTrigger asChild>
@@ -147,6 +300,11 @@ export const SettingsApiKeys = ({ service }: { service: ApiKeyService }) => {
147
300
  title="Roll this key"
148
301
  variant="ghost"
149
302
  disabled={rollKeyMutation.isPending}
303
+ className={
304
+ rollKeyMutation.isPending
305
+ ? "animate-spin"
306
+ : undefined
307
+ }
150
308
  >
151
309
  <RotateCwIcon size={16} />
152
310
  </Button>
@@ -177,36 +335,27 @@ export const SettingsApiKeys = ({ service }: { service: ApiKeyService }) => {
177
335
  )}
178
336
  </div>
179
337
  </CardHeader>
180
- <div className="divide-y col-span-full grid-cols-subgrid grid">
338
+ <div className="col-span-full grid-cols-subgrid grid">
181
339
  <AnimatePresence>
182
- {rollKeyMutation.isPending && (
183
- <motion.div
184
- className={cn(
185
- "flex col-span-full items-center gap-2 px-6 py-1 text-xs bg-muted/30 font-medium",
186
- )}
187
- initial={{ opacity: 0, y: 0 }}
188
- animate={{ opacity: 1, y: 0 }}
189
- exit={{ opacity: 0, y: 0 }}
190
- transition={{ duration: 0.3 }}
191
- >
192
- <LoaderPinwheelIcon
193
- size={16}
194
- className="animate-spin opacity-80"
195
- />
196
- Rolling key...
197
- </motion.div>
198
- )}
199
340
  {consumers.apiKeys.map((apiKey) => (
200
- <RevealApiKey
201
- key={apiKey.id}
202
- apiKey={apiKey}
203
- onDeleteKey={() => {
204
- deleteKeyMutation.mutate({
205
- consumerId: consumers.id,
206
- keyId: apiKey.id,
207
- });
208
- }}
209
- />
341
+ <React.Fragment key={apiKey.id}>
342
+ <RevealApiKey
343
+ apiKey={apiKey}
344
+ onDeleteKey={() => {
345
+ deleteKeyMutation.mutate({
346
+ consumerId: consumers.id,
347
+ keyId: apiKey.id,
348
+ });
349
+ }}
350
+ className={
351
+ deleteKeyMutation.variables?.keyId === apiKey.id &&
352
+ (deleteKeyMutation.isPending || isFetching)
353
+ ? "opacity-10!"
354
+ : undefined
355
+ }
356
+ />
357
+ <div className="col-span-full h-px bg-border"></div>
358
+ </React.Fragment>
210
359
  ))}
211
360
  </AnimatePresence>
212
361
  </div>
@@ -241,9 +390,11 @@ const getTimeAgo = (date: string) => {
241
390
  const RevealApiKey = ({
242
391
  apiKey,
243
392
  onDeleteKey,
393
+ className,
244
394
  }: {
245
395
  apiKey: ApiKey;
246
396
  onDeleteKey: () => void;
397
+ className?: string;
247
398
  }) => {
248
399
  const [revealed, setRevealed] = useState(false);
249
400
  const [copied, setCopied] = useState(false);
@@ -259,13 +410,7 @@ const RevealApiKey = ({
259
410
  const expiresSoon = daysUntilExpiry <= 7 && !isExpired;
260
411
 
261
412
  return (
262
- <motion.div
263
- className="grid col-span-full grid-cols-subgrid p-6"
264
- initial={{ opacity: 0, y: 0 }}
265
- animate={{ opacity: 1, y: 0 }}
266
- exit={{ opacity: 0, y: 0 }}
267
- transition={{ duration: 0.3 }}
268
- >
413
+ <div className={cn("grid col-span-full grid-cols-subgrid p-6", className)}>
269
414
  <div className="flex flex-col gap-1">
270
415
  <div className="flex gap-2 items-center text-sm border rounded-md w-fit px-1">
271
416
  <div className="font-mono truncate h-9 items-center flex px-2 text-xs gap-2">
@@ -360,6 +505,6 @@ const RevealApiKey = ({
360
505
  </Dialog>
361
506
  )}
362
507
  </div>
363
- </motion.div>
508
+ </div>
364
509
  );
365
510
  };
@@ -21,8 +21,8 @@ export type ApiKeyService = {
21
21
  keyId: string,
22
22
  context: ZudokuContext,
23
23
  ) => Promise<void>;
24
- updateKeyDescription?: (
25
- apiKey: { id: string; description: string },
24
+ updateConsumer?: (
25
+ consumer: { id: string; label?: string },
26
26
  context: ZudokuContext,
27
27
  ) => Promise<void>;
28
28
  getUsage?: (apiKeys: string[], context: ZudokuContext) => Promise<void>;
@@ -47,7 +47,7 @@ export interface ApiKey {
47
47
 
48
48
  export interface ApiConsumer {
49
49
  id: string;
50
- name: string;
50
+ label: string;
51
51
  apiKeys: ApiKey[];
52
52
  description?: string;
53
53
  createdOn?: string;
@@ -71,6 +71,26 @@ const createDefaultHandler = (deploymentName: string): ApiKeyService => {
71
71
  const response = await fetch(request);
72
72
  invariant(response.ok, "Failed to delete API key");
73
73
  },
74
+ updateConsumer: async (consumer, context) => {
75
+ const response = await fetch(
76
+ await context.signRequest(
77
+ new Request(
78
+ DEFAULT_API_KEY_ENDPOINT +
79
+ `/${deploymentName}/consumers/${consumer.id}`,
80
+ {
81
+ method: "PATCH",
82
+ headers: {
83
+ "Content-Type": "application/json",
84
+ },
85
+ body: JSON.stringify({
86
+ label: consumer.label,
87
+ }),
88
+ },
89
+ ),
90
+ ),
91
+ );
92
+ invariant(response.ok, "Failed to update API key description");
93
+ },
74
94
  rollKey: async (consumerId, context) => {
75
95
  const response = await fetch(
76
96
  await context.signRequest(
@@ -104,7 +124,7 @@ const createDefaultHandler = (deploymentName: string): ApiKeyService => {
104
124
  data: [
105
125
  {
106
126
  id: string;
107
- name: string;
127
+ label: string;
108
128
  apiKeys: {
109
129
  data: ApiKey[];
110
130
  };
@@ -114,7 +134,7 @@ const createDefaultHandler = (deploymentName: string): ApiKeyService => {
114
134
 
115
135
  return data.data.map((consumer) => ({
116
136
  id: consumer.id,
117
- name: consumer.name,
137
+ label: consumer.label || "API Key",
118
138
  apiKeys: consumer.apiKeys.data,
119
139
  key: consumer.apiKeys.data.at(0),
120
140
  }));
@@ -142,20 +162,7 @@ export const apiKeyPlugin = (
142
162
  icon: KeyRoundIcon,
143
163
  },
144
164
  ],
145
- getSidebar: async (path) => {
146
- if (!path.startsWith("/settings")) {
147
- return [];
148
- }
149
165
 
150
- return [
151
- {
152
- type: "link",
153
- label: "API Keys",
154
- icon: KeyRoundIcon,
155
- href: "/settings/api-keys",
156
- },
157
- ];
158
- },
159
166
  getIdentities: async (context) => {
160
167
  try {
161
168
  const consumers = await service.getConsumers(context);