zudoku 0.40.0 → 0.41.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 (157) hide show
  1. package/dist/app/main.js +7 -4
  2. package/dist/app/main.js.map +1 -1
  3. package/dist/cli/cli.js +12 -1
  4. package/dist/cli/cli.js.map +1 -1
  5. package/dist/cli/common/machine-id/lib.js +1 -0
  6. package/dist/cli/common/machine-id/lib.js.map +1 -1
  7. package/dist/config/ZuploBuildConfig.d.ts +155 -0
  8. package/dist/config/ZuploBuildConfig.js +29 -0
  9. package/dist/config/ZuploBuildConfig.js.map +1 -0
  10. package/dist/config/loader.js +0 -3
  11. package/dist/config/loader.js.map +1 -1
  12. package/dist/config/validators/BuildSchema.d.ts +1 -0
  13. package/dist/config/validators/BuildSchema.js.map +1 -1
  14. package/dist/config/validators/common.d.ts +17 -11
  15. package/dist/config/validators/common.js +1 -1
  16. package/dist/config/validators/common.js.map +1 -1
  17. package/dist/config/validators/validate.d.ts +7 -5
  18. package/dist/lib/authentication/providers/openid.d.ts +3 -1
  19. package/dist/lib/authentication/providers/openid.js +17 -11
  20. package/dist/lib/authentication/providers/openid.js.map +1 -1
  21. package/dist/lib/components/BuildCheck.d.ts +4 -0
  22. package/dist/lib/components/BuildCheck.js +22 -0
  23. package/dist/lib/components/BuildCheck.js.map +1 -0
  24. package/dist/lib/components/Zudoku.js +1 -3
  25. package/dist/lib/components/Zudoku.js.map +1 -1
  26. package/dist/lib/components/context/ViewportAnchorContext.js +8 -9
  27. package/dist/lib/components/context/ViewportAnchorContext.js.map +1 -1
  28. package/dist/lib/components/index.d.ts +4 -0
  29. package/dist/lib/components/index.js +2 -0
  30. package/dist/lib/components/index.js.map +1 -1
  31. package/dist/lib/components/navigation/PoweredByZudoku.js +2 -1
  32. package/dist/lib/components/navigation/PoweredByZudoku.js.map +1 -1
  33. package/dist/lib/components/navigation/ZuploLogo.d.ts +3 -0
  34. package/dist/lib/components/navigation/ZuploLogo.js +4 -0
  35. package/dist/lib/components/navigation/ZuploLogo.js.map +1 -0
  36. package/dist/lib/oas/parser/index.d.ts +1 -1
  37. package/dist/lib/oas/parser/index.js +3 -2
  38. package/dist/lib/oas/parser/index.js.map +1 -1
  39. package/dist/lib/plugins/openapi/OperationList.d.ts +1 -1
  40. package/dist/lib/plugins/openapi/OperationList.js +6 -4
  41. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  42. package/dist/lib/plugins/openapi/processors/removeExtensions.d.ts +3 -2
  43. package/dist/lib/plugins/openapi/processors/removeExtensions.js +1 -1
  44. package/dist/lib/plugins/openapi/processors/removeExtensions.js.map +1 -1
  45. package/dist/lib/plugins/openapi/processors/removeExtensions.test.js +26 -6
  46. package/dist/lib/plugins/openapi/processors/removeExtensions.test.js.map +1 -1
  47. package/dist/lib/plugins/openapi/processors/removeParameters.d.ts +3 -1
  48. package/dist/lib/plugins/openapi/processors/removeParameters.js +1 -1
  49. package/dist/lib/plugins/openapi/processors/removeParameters.js.map +1 -1
  50. package/dist/lib/plugins/openapi/processors/removeParameters.test.js +53 -25
  51. package/dist/lib/plugins/openapi/processors/removeParameters.test.js.map +1 -1
  52. package/dist/lib/plugins/openapi/processors/removePaths.d.ts +3 -1
  53. package/dist/lib/plugins/openapi/processors/removePaths.js +1 -1
  54. package/dist/lib/plugins/openapi/processors/removePaths.js.map +1 -1
  55. package/dist/lib/plugins/openapi/processors/removePaths.test.js +75 -35
  56. package/dist/lib/plugins/openapi/processors/removePaths.test.js.map +1 -1
  57. package/dist/lib/plugins/openapi/util/getRoutes.js +20 -13
  58. package/dist/lib/plugins/openapi/util/getRoutes.js.map +1 -1
  59. package/dist/lib/util/ensureArray.d.ts +1 -0
  60. package/dist/lib/util/ensureArray.js +2 -0
  61. package/dist/lib/util/ensureArray.js.map +1 -0
  62. package/dist/vite/api/SchemaManager.d.ts +36 -0
  63. package/dist/vite/api/SchemaManager.js +120 -0
  64. package/dist/vite/api/SchemaManager.js.map +1 -0
  65. package/dist/vite/api/SchemaManager.test.d.ts +1 -0
  66. package/dist/vite/api/SchemaManager.test.js +106 -0
  67. package/dist/vite/api/SchemaManager.test.js.map +1 -0
  68. package/dist/vite/config.js +5 -1
  69. package/dist/vite/config.js.map +1 -1
  70. package/dist/vite/plugin-api.js +39 -117
  71. package/dist/vite/plugin-api.js.map +1 -1
  72. package/dist/vite/plugin-sidebar.d.ts +3 -1
  73. package/dist/vite/plugin-sidebar.js +8 -2
  74. package/dist/vite/plugin-sidebar.js.map +1 -1
  75. package/dist/zuplo/enrich-with-zuplo.d.ts +3 -3
  76. package/dist/zuplo/enrich-with-zuplo.js +18 -17
  77. package/dist/zuplo/enrich-with-zuplo.js.map +1 -1
  78. package/dist/zuplo/env.d.ts +26 -0
  79. package/dist/zuplo/env.js +17 -0
  80. package/dist/zuplo/env.js.map +1 -1
  81. package/dist/zuplo/with-zuplo-processors.d.ts +2 -2
  82. package/dist/zuplo/with-zuplo-processors.js +3 -3
  83. package/dist/zuplo/with-zuplo-processors.js.map +1 -1
  84. package/dist/zuplo/with-zuplo.d.ts +3 -0
  85. package/dist/zuplo/with-zuplo.js +6 -9
  86. package/dist/zuplo/with-zuplo.js.map +1 -1
  87. package/lib/{Markdown-C5h6bxbE.js → Markdown-DqDbGCj2.js} +314 -315
  88. package/lib/{Markdown-C5h6bxbE.js.map → Markdown-DqDbGCj2.js.map} +1 -1
  89. package/lib/{MdxPage-B66ht0D_.js → MdxPage-DGVqOhzg.js} +5 -5
  90. package/lib/{MdxPage-B66ht0D_.js.map → MdxPage-DGVqOhzg.js.map} +1 -1
  91. package/lib/{OasProvider-BKPF0Pbt.js → OasProvider-qeJNCq-h.js} +2 -2
  92. package/lib/{OasProvider-BKPF0Pbt.js.map → OasProvider-qeJNCq-h.js.map} +1 -1
  93. package/lib/{OperationList-0bhBU7ME.js → OperationList-CBpQALZd.js} +798 -794
  94. package/lib/{OperationList-0bhBU7ME.js.map → OperationList-CBpQALZd.js.map} +1 -1
  95. package/lib/{Pagination-Bt1czAiJ.js → Pagination-PfYTF0cF.js} +2 -2
  96. package/lib/{Pagination-Bt1czAiJ.js.map → Pagination-PfYTF0cF.js.map} +1 -1
  97. package/lib/{SchemaList-CsTIT1pi.js → SchemaList-BIzyXPnY.js} +5 -5
  98. package/lib/{SchemaList-CsTIT1pi.js.map → SchemaList-BIzyXPnY.js.map} +1 -1
  99. package/lib/{SchemaView-DCSWtnYr.js → SchemaView-DwlVvFfp.js} +2 -2
  100. package/lib/{SchemaView-DCSWtnYr.js.map → SchemaView-DwlVvFfp.js.map} +1 -1
  101. package/lib/{SlotletProvider-VUmTNmLZ.js → SlotletProvider-p4XaFFOh.js} +2 -2
  102. package/lib/{SlotletProvider-VUmTNmLZ.js.map → SlotletProvider-p4XaFFOh.js.map} +1 -1
  103. package/lib/{Toc-BK39DQvI.js → Toc-BVdRaIC0.js} +2 -2
  104. package/lib/{Toc-BK39DQvI.js.map → Toc-BVdRaIC0.js.map} +1 -1
  105. package/lib/{createServer-DLN7APz_.js → createServer-D_5UkLtY.js} +1459 -1485
  106. package/lib/createServer-D_5UkLtY.js.map +1 -0
  107. package/lib/{index-tpbiZmWp.js → index-B7lRu87v.js} +686 -648
  108. package/lib/index-B7lRu87v.js.map +1 -0
  109. package/lib/index-BpThvE5R.js +66 -0
  110. package/lib/index-BpThvE5R.js.map +1 -0
  111. package/lib/{index-Ba_X7EnR.js → index-CxdJ8TBB.js} +255 -249
  112. package/lib/{index-Ba_X7EnR.js.map → index-CxdJ8TBB.js.map} +1 -1
  113. package/lib/index-ueM1dihS.js +247 -0
  114. package/lib/index-ueM1dihS.js.map +1 -0
  115. package/lib/processors/removeExtensions.js +1 -1
  116. package/lib/processors/removeExtensions.js.map +1 -1
  117. package/lib/processors/removeParameters.js +1 -1
  118. package/lib/processors/removeParameters.js.map +1 -1
  119. package/lib/processors/removePaths.js +1 -1
  120. package/lib/processors/removePaths.js.map +1 -1
  121. package/lib/{index-DcHeSvkE.js → public-api-CrAQFYc4.js} +651 -684
  122. package/lib/public-api-CrAQFYc4.js.map +1 -0
  123. package/lib/zudoku.auth-openid.js +144 -138
  124. package/lib/zudoku.auth-openid.js.map +1 -1
  125. package/lib/zudoku.components.js +23 -22
  126. package/lib/zudoku.plugin-api-catalog.js +2 -2
  127. package/lib/zudoku.plugin-api-keys.js +1 -1
  128. package/lib/zudoku.plugin-custom-pages.js +1 -1
  129. package/lib/zudoku.plugin-markdown.js +1 -1
  130. package/lib/zudoku.plugin-openapi.js +1 -1
  131. package/package.json +3 -3
  132. package/src/app/main.tsx +12 -1
  133. package/src/lib/authentication/providers/openid.tsx +23 -12
  134. package/src/lib/components/BuildCheck.tsx +60 -0
  135. package/src/lib/components/Zudoku.tsx +1 -3
  136. package/src/lib/components/context/ViewportAnchorContext.tsx +8 -15
  137. package/src/lib/components/index.ts +2 -0
  138. package/src/lib/components/navigation/PoweredByZudoku.tsx +8 -3
  139. package/src/lib/components/navigation/ZuploLogo.tsx +14 -0
  140. package/src/lib/oas/parser/index.ts +4 -2
  141. package/src/lib/plugins/openapi/OperationList.tsx +15 -3
  142. package/src/lib/plugins/openapi/processors/removeExtensions.test.ts +29 -9
  143. package/src/lib/plugins/openapi/processors/removeExtensions.ts +5 -3
  144. package/src/lib/plugins/openapi/processors/removeParameters.test.ts +67 -33
  145. package/src/lib/plugins/openapi/processors/removeParameters.ts +5 -3
  146. package/src/lib/plugins/openapi/processors/removePaths.test.ts +85 -44
  147. package/src/lib/plugins/openapi/processors/removePaths.ts +5 -3
  148. package/src/lib/plugins/openapi/util/getRoutes.tsx +21 -13
  149. package/src/lib/util/ensureArray.ts +3 -0
  150. package/client.d.ts +0 -8
  151. package/dist/lib/oas/parser/upgrade/index.d.ts +0 -9
  152. package/dist/lib/oas/parser/upgrade/index.js +0 -90
  153. package/dist/lib/oas/parser/upgrade/index.js.map +0 -1
  154. package/lib/createServer-DLN7APz_.js.map +0 -1
  155. package/lib/index-DcHeSvkE.js.map +0 -1
  156. package/lib/index-tpbiZmWp.js.map +0 -1
  157. package/src/lib/oas/parser/upgrade/index.ts +0 -103
@@ -2,34 +2,35 @@ import "./RouteGuard-BZ_VsiXc.js";
2
2
  import "./index-DwT-v3zK.js";
3
3
  import "./chunk-KNED5TY2-BUPjb3LQ.js";
4
4
  import "./hook-pPrHCB6G.js";
5
- import "./SlotletProvider-VUmTNmLZ.js";
6
- import { e as d, f as S, B as k, C as l, j as h, l as B, H as E, d as c, L as H, M as L, g as M, R, S as Z, k as f, i as g, Z as y, a as A, h as b, c as j, m as v, b as w } from "./index-tpbiZmWp.js";
5
+ import "./SlotletProvider-p4XaFFOh.js";
6
+ import { e as d, f as k, n as l, B as S, C as h, j as B, l as c, H as E, d as H, L, M, g as R, R as Z, S as f, k as g, i as y, Z as A, a as b, h as j, c as v, m as w, b as x } from "./index-B7lRu87v.js";
7
7
  import "./ui/Button.js";
8
8
  import "./ui/Callout.js";
9
9
  import "./ClientOnly-E7hGysn1.js";
10
- import "./Markdown-C5h6bxbE.js";
10
+ import "./Markdown-DqDbGCj2.js";
11
11
  import "./Spinner-mNLZ6awP.js";
12
12
  export {
13
13
  d as Bootstrap,
14
- S as BootstrapStatic,
15
- k as Button,
16
- l as CACHE_KEYS,
17
- h as Callout,
18
- B as ClientOnly,
14
+ k as BootstrapStatic,
15
+ l as BuildCheck,
16
+ S as Button,
17
+ h as CACHE_KEYS,
18
+ B as Callout,
19
+ c as ClientOnly,
19
20
  E as Head,
20
- c as Layout,
21
- H as Link,
22
- L as Markdown,
23
- M as RouteGuard,
24
- R as RouterError,
25
- Z as ServerError,
26
- f as Spinner,
27
- g as StatusPage,
28
- y as Zudoku,
29
- A as useAuth,
30
- b as useCache,
31
- j as useMDXComponents,
32
- v as useTheme,
33
- w as useZudoku
21
+ H as Layout,
22
+ L as Link,
23
+ M as Markdown,
24
+ R as RouteGuard,
25
+ Z as RouterError,
26
+ f as ServerError,
27
+ g as Spinner,
28
+ y as StatusPage,
29
+ A as Zudoku,
30
+ b as useAuth,
31
+ j as useCache,
32
+ v as useMDXComponents,
33
+ w as useTheme,
34
+ x as useZudoku
34
35
  };
35
36
  //# sourceMappingURL=zudoku.components.js.map
@@ -3,8 +3,8 @@ import { s as h } from "./index-LNp6rxyU.js";
3
3
  import { d as b, m as x } from "./chunk-KNED5TY2-BUPjb3LQ.js";
4
4
  import { u as j, d as y, f as p } from "./hook-pPrHCB6G.js";
5
5
  import { H as v } from "./RouteGuard-BZ_VsiXc.js";
6
- import { L as N } from "./index-tpbiZmWp.js";
7
- import { H as S, M as w } from "./Markdown-C5h6bxbE.js";
6
+ import { L as N } from "./index-B7lRu87v.js";
7
+ import { H as S, M as w } from "./Markdown-DqDbGCj2.js";
8
8
  const H = ({
9
9
  items: r,
10
10
  filterCatalogItems: o = (n) => n,
@@ -1,6 +1,6 @@
1
1
  import { j as e } from "./jsx-runtime-C5mzlN2N.js";
2
2
  import { RotateCwIcon as j, TrashIcon as v, EyeOffIcon as w, EyeIcon as K, CheckIcon as b, CopyIcon as k, FileKey2Icon as N } from "lucide-react";
3
- import { D as I, S as x, R as S } from "./SlotletProvider-VUmTNmLZ.js";
3
+ import { D as I, S as x, R as S } from "./SlotletProvider-p4XaFFOh.js";
4
4
  import { i as c } from "./invariant-Caa8-XvF.js";
5
5
  import { e as h, g, l as A, d as C } from "./hook-pPrHCB6G.js";
6
6
  import { u as d, S as E, a as P, b as D, c as q, d as R, e as p } from "./Select-Dg5R11Dx.js";
@@ -1,6 +1,6 @@
1
1
  import { j as o } from "./jsx-runtime-C5mzlN2N.js";
2
2
  import a from "react";
3
- import { P as n } from "./Markdown-C5h6bxbE.js";
3
+ import { P as n } from "./Markdown-DqDbGCj2.js";
4
4
  import { c } from "./cn-qaFjX9_3.js";
5
5
  import { u as p } from "./useExposedProps-Dq2yUQIG.js";
6
6
  const u = ({
@@ -53,7 +53,7 @@ const P = (e) => ({
53
53
  const u = {
54
54
  path: r,
55
55
  lazy: async () => {
56
- const { MdxPage: p } = await import("./MdxPage-B66ht0D_.js"), { default: f, ...l } = await i();
56
+ const { MdxPage: p } = await import("./MdxPage-DGVqOhzg.js"), { default: f, ...l } = await i();
57
57
  return {
58
58
  element: /* @__PURE__ */ d.jsx(
59
59
  p,
@@ -3,7 +3,7 @@ import "lucide-react";
3
3
  import "./chunk-KNED5TY2-BUPjb3LQ.js";
4
4
  import "./hook-pPrHCB6G.js";
5
5
  import "./ui/Button.js";
6
- import { U as a, o as e } from "./index-Ba_X7EnR.js";
6
+ import { U as a, o as e } from "./index-CxdJ8TBB.js";
7
7
  export {
8
8
  a as UNTAGGED_PATH,
9
9
  e as openApiPlugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -153,7 +153,7 @@
153
153
  "@radix-ui/react-slot": "1.2.0",
154
154
  "@radix-ui/react-switch": "1.1.4",
155
155
  "@radix-ui/react-tabs": "1.1.4",
156
- "@radix-ui/react-toggle": "1.1.3",
156
+ "@radix-ui/react-toggle": "1.1.6",
157
157
  "@radix-ui/react-toggle-group": "1.1.3",
158
158
  "@radix-ui/react-tooltip": "1.2.0",
159
159
  "@radix-ui/react-visually-hidden": "1.1.3",
@@ -213,7 +213,7 @@
213
213
  "rehype-raw": "7.0.0",
214
214
  "rehype-slug": "6.0.0",
215
215
  "remark-comment": "1.0.0",
216
- "remark-directive": "4.0.0",
216
+ "remark-directive": "3.0.1",
217
217
  "remark-directive-rehype": "0.4.2",
218
218
  "remark-frontmatter": "5.0.0",
219
219
  "remark-gfm": "4.0.1",
package/src/app/main.tsx CHANGED
@@ -12,6 +12,7 @@ import { configuredSearchPlugin } from "virtual:zudoku-search-plugin";
12
12
  import { configuredSidebar } from "virtual:zudoku-sidebar";
13
13
  import "virtual:zudoku-theme.css";
14
14
  import {
15
+ BuildCheck,
15
16
  Layout,
16
17
  RouteGuard,
17
18
  RouterError,
@@ -21,6 +22,7 @@ import {
21
22
  import type { ZudokuConfig } from "../config/config.js";
22
23
  import type { ZudokuContextOptions } from "../lib/core/ZudokuContext.js";
23
24
  import { isNavigationPlugin } from "../lib/core/plugins.js";
25
+ import { ZuploEnv } from "../zuplo/env.js";
24
26
 
25
27
  export const convertZudokuConfigToOptions = (
26
28
  config: ZudokuConfig,
@@ -43,6 +45,9 @@ export const convertZudokuConfigToOptions = (
43
45
  protectedRoutes: config.protectedRoutes,
44
46
  page: {
45
47
  ...config.page,
48
+ showPoweredBy:
49
+ ZuploEnv.buildConfig?.entitlements.devPortalZuploBranding ??
50
+ config.page?.showPoweredBy,
46
51
  logo: {
47
52
  ...(isUsingFallback ? { width: "130px" } : {}),
48
53
  ...config.page?.logo,
@@ -115,12 +120,18 @@ export const getRoutesByOptions = (
115
120
 
116
121
  export const getRoutesByConfig = (config: ZudokuConfig): RouteObject[] => {
117
122
  const options = convertZudokuConfigToOptions(config);
118
- const routes = getRoutesByOptions(options, config.enableStatusPages);
123
+ const routes = getRoutesByOptions(
124
+ options,
125
+ import.meta.env.IS_ZUPLO || config.enableStatusPages,
126
+ );
119
127
 
120
128
  return [
121
129
  {
122
130
  element: (
123
131
  <Zudoku {...options}>
132
+ {import.meta.env.IS_ZUPLO && import.meta.env.ZUPLO_BUILD_ID && (
133
+ <BuildCheck buildId={import.meta.env.ZUPLO_BUILD_ID} />
134
+ )}
124
135
  <Layout />
125
136
  </Zudoku>
126
137
  ),
@@ -51,7 +51,8 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
51
51
 
52
52
  protected authorizationServer: oauth.AuthorizationServer | undefined;
53
53
 
54
- protected callbackUrlPath: string;
54
+ protected absoluteCallbackUrlPath: string;
55
+ protected relativeCallbackUrlPath: string;
55
56
  protected logoutRedirectUrlPath: string;
56
57
  protected onAuthorizationUrl?: (
57
58
  authorizationUrl: URL,
@@ -62,7 +63,7 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
62
63
  private readonly redirectToAfterSignOut: string;
63
64
  private readonly audience?: string;
64
65
  private readonly scopes: string[];
65
-
66
+ private readonly root: string;
66
67
  constructor({
67
68
  issuer,
68
69
  audience,
@@ -79,15 +80,19 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
79
80
  };
80
81
  this.audience = audience;
81
82
  this.issuer = issuer;
82
- this.callbackUrlPath = joinUrl(basePath, "/oauth/callback");
83
+ this.relativeCallbackUrlPath = "/oauth/callback";
84
+ this.absoluteCallbackUrlPath = joinUrl(
85
+ basePath,
86
+ this.relativeCallbackUrlPath,
87
+ );
83
88
  this.scopes = scopes ?? ["openid", "profile", "email"];
84
89
 
85
- const root = joinUrl(basePath, "/");
90
+ this.root = joinUrl(basePath, "/");
86
91
 
87
- this.logoutRedirectUrlPath = root;
88
- this.redirectToAfterSignUp = redirectToAfterSignUp ?? root;
89
- this.redirectToAfterSignIn = redirectToAfterSignIn ?? root;
90
- this.redirectToAfterSignOut = redirectToAfterSignOut ?? root;
92
+ this.logoutRedirectUrlPath = this.root;
93
+ this.redirectToAfterSignUp = redirectToAfterSignUp ?? this.root;
94
+ this.redirectToAfterSignIn = redirectToAfterSignIn ?? this.root;
95
+ this.redirectToAfterSignOut = redirectToAfterSignOut ?? this.root;
91
96
  }
92
97
 
93
98
  protected async getAuthServer() {
@@ -174,13 +179,16 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
174
179
  );
175
180
 
176
181
  const finalRedirect = redirectTo.startsWith(window.location.origin)
177
- ? redirectTo.slice(window.location.origin.length)
182
+ ? this.root !== "/" &&
183
+ redirectTo.startsWith(window.location.origin + this.root)
184
+ ? redirectTo.slice(window.location.origin.length + this.root.length)
185
+ : redirectTo.slice(window.location.origin.length)
178
186
  : redirectTo;
179
187
 
180
188
  sessionStorage.setItem("redirect-to", finalRedirect);
181
189
 
182
190
  const redirectUrl = new URL(window.location.origin);
183
- redirectUrl.pathname = this.callbackUrlPath;
191
+ redirectUrl.pathname = this.absoluteCallbackUrlPath;
184
192
  redirectUrl.search = "";
185
193
 
186
194
  authorizationUrl.searchParams.set("client_id", this.client.client_id);
@@ -330,7 +338,7 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
330
338
  }
331
339
 
332
340
  const redirectUrl = new URL(url);
333
- redirectUrl.pathname = this.callbackUrlPath;
341
+ redirectUrl.pathname = this.absoluteCallbackUrlPath;
334
342
  redirectUrl.search = "";
335
343
 
336
344
  const response = await oauth.authorizationCodeGrantRequest(
@@ -388,7 +396,10 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
388
396
  getAuthenticationPlugin() {
389
397
  // TODO: This API is a bit messy, we need to refactor auth plugins/providers
390
398
  // to remove the extra layers of abstraction.
391
- return new OpenIdAuthPlugin(this.callbackUrlPath, this.handleCallback);
399
+ return new OpenIdAuthPlugin(
400
+ this.relativeCallbackUrlPath,
401
+ this.handleCallback,
402
+ );
392
403
  }
393
404
  }
394
405
 
@@ -0,0 +1,60 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import { CircleFadingArrowUpIcon, LoaderCircleIcon } from "lucide-react";
3
+ import { Button } from "../ui/Button.js";
4
+
5
+ export const BuildCheck = ({
6
+ buildId,
7
+ endpoint = "/__zuplo/docs",
8
+ }: {
9
+ buildId?: string;
10
+ endpoint?: string;
11
+ }) => {
12
+ const buildStatusQuery = useQuery({
13
+ queryKey: ["zuplo-build-check", buildId, endpoint],
14
+ refetchInterval: 2000,
15
+ enabled: !!buildId,
16
+ queryFn: () =>
17
+ fetch(endpoint).then((res) => res.json()) as Promise<{
18
+ buildId: string;
19
+ timestamp: string;
20
+ status: "in-progress" | "completed" | "failed";
21
+ }>,
22
+ });
23
+
24
+ if (buildStatusQuery.data?.buildId === buildId) {
25
+ return null;
26
+ }
27
+
28
+ const isCompleted = buildStatusQuery.data?.status === "completed";
29
+
30
+ return (
31
+ <div className="fixed flex flex-col gap-3 p-4 rounded-xl w-96 border z-20 bg-background left-0 right-0 top-4 mx-auto shadow-lg">
32
+ {isCompleted ? (
33
+ <div className="flex flex-row items-center gap-2">
34
+ <CircleFadingArrowUpIcon size={16} />
35
+ <span className="text-sm">New version available</span>
36
+ </div>
37
+ ) : (
38
+ <div className="flex flex-row items-center gap-2">
39
+ <LoaderCircleIcon size={16} className="animate-spin" />
40
+ <span className="text-sm">Building new version...</span>
41
+ </div>
42
+ )}
43
+ <span className="text-xs">
44
+ {!isCompleted
45
+ ? "A new version of the developer portal will be available soon."
46
+ : "To see the new version, reload the page now."}
47
+ </span>
48
+ <Button
49
+ variant="outline"
50
+ size="sm"
51
+ className="w-full"
52
+ onClick={() => {
53
+ window.location.reload();
54
+ }}
55
+ >
56
+ Reload
57
+ </Button>
58
+ </div>
59
+ );
60
+ };
@@ -72,9 +72,7 @@ const ZudokoInner = memo(
72
72
  setDidNavigate(true);
73
73
  }, [didNavigate, navigation.location]);
74
74
 
75
- if (!zudokuContext) {
76
- zudokuContext = new ZudokuContext(props, queryClient);
77
- }
75
+ zudokuContext ??= new ZudokuContext(props, queryClient);
78
76
 
79
77
  const heads = props.plugins
80
78
  ?.flatMap((plugin) =>
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createContext,
3
+ use,
3
4
  useCallback,
4
- useContext,
5
5
  useEffect,
6
6
  useMemo,
7
7
  useRef,
@@ -16,21 +16,14 @@ type AnchorContextType = {
16
16
  unobserve: (element: HTMLElement | null) => void;
17
17
  };
18
18
 
19
- const ViewportAnchorContext = createContext<AnchorContextType | undefined>(
20
- undefined,
21
- );
19
+ const ViewportAnchorContext = createContext<AnchorContextType>({
20
+ activeAnchor: "",
21
+ setActiveAnchor: () => {},
22
+ observe: () => {},
23
+ unobserve: () => {},
24
+ });
22
25
 
23
- export const useViewportAnchor = () => {
24
- const context = useContext(ViewportAnchorContext);
25
-
26
- if (!context) {
27
- throw new Error(
28
- "useViewportAnchor must be used within a CurrentAnchorProvider",
29
- );
30
- }
31
-
32
- return context;
33
- };
26
+ export const useViewportAnchor = () => use(ViewportAnchorContext);
34
27
 
35
28
  export const useRegisterAnchorElement = () => {
36
29
  const elementRef = useRef<HTMLElement | null>(null);
@@ -12,6 +12,7 @@ import {
12
12
  Bootstrap as BootstrapImport,
13
13
  BootstrapStatic as BootstrapStaticImport,
14
14
  } from "./Bootstrap.js";
15
+ import { BuildCheck as BuildCheckImport } from "./BuildCheck.js";
15
16
  import {
16
17
  CACHE_KEYS as CACHE_KEYS_IMPORT,
17
18
  useCache as useCacheImport,
@@ -48,3 +49,4 @@ export const ClientOnly = /*@__PURE__*/ ClientOnlyImport;
48
49
  export const Button = /*@__PURE__*/ ButtonImport;
49
50
  export const Link = /*@__PURE__*/ LinkImport;
50
51
  export const useTheme = /*@__PURE__*/ useThemeImport;
52
+ export const BuildCheck = /*@__PURE__*/ BuildCheckImport;
@@ -1,10 +1,11 @@
1
1
  import { ChevronRightIcon } from "lucide-react";
2
2
  import { cn } from "../../util/cn.js";
3
3
  import ZudokuLogo from "./ZudokuLogo.js";
4
+ import ZuploLogo from "./ZuploLogo.js";
4
5
 
5
6
  export const PoweredByZudoku = ({ className }: { className?: string }) => (
6
7
  <a
7
- href="https://zudoku.dev"
8
+ href={import.meta.env.IS_ZUPLO ? "https://zuplo.com" : "https://zudoku.dev"}
8
9
  target="_blank"
9
10
  rel="noopener noreferrer"
10
11
  className={cn(
@@ -13,8 +14,12 @@ export const PoweredByZudoku = ({ className }: { className?: string }) => (
13
14
  )}
14
15
  >
15
16
  <div className="opacity-70 hover:opacity-100 transition-opacity gap-1.5 text-[11px] font-medium rounded-full h-7 flex items-center text-nowrap">
16
- <ZudokuLogo className="w-3.5 h-3.5 dark:fill-white" />
17
- powered by Zudoku
17
+ {import.meta.env.IS_ZUPLO ? (
18
+ <ZuploLogo className="w-3.5 h-3.5 dark:fill-white" />
19
+ ) : (
20
+ <ZudokuLogo className="w-3.5 h-3.5 dark:fill-white" />
21
+ )}
22
+ powered by {import.meta.env.IS_ZUPLO ? "Zuplo" : "Zudoku"}
18
23
  </div>
19
24
  <div className="text-xs font-medium opacity-70 hover:text-foreground transition-colors cursor-pointer">
20
25
  <ChevronRightIcon size={12} absoluteStrokeWidth strokeWidth={1.5} />
@@ -0,0 +1,14 @@
1
+ import type * as React from "react";
2
+
3
+ const ZuploLogo = (props: React.SVGProps<SVGSVGElement>) => (
4
+ <svg
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ viewBox="0 0 76 66"
7
+ fill="none"
8
+ {...props}
9
+ >
10
+ <path d="M55.331 43.537H32.527l24.295-24.343a1.642 1.642 0 0 0-1.157-2.802H17.07V.239h42.336a16.407 16.407 0 0 1 8.81 2.438 16.471 16.471 0 0 1 6.147 6.775 16.037 16.037 0 0 1-3.167 18.201L55.331 43.535v.002Z" />
11
+ <path d="M20.695 21.888H43.5L19.204 46.23a1.643 1.643 0 0 0 1.158 2.802h38.595v16.153H16.621a16.406 16.406 0 0 1-8.81-2.438 16.471 16.471 0 0 1-6.147-6.775 16.038 16.038 0 0 1 3.167-18.201L20.695 21.89v-.002Z" />
12
+ </svg>
13
+ );
14
+ export default ZuploLogo;
@@ -1,7 +1,6 @@
1
1
  import { GraphQLError } from "graphql/error/index.js";
2
2
  import { OpenAPIV3, type OpenAPIV3_1 } from "openapi-types";
3
3
  import { dereference, type JSONSchema } from "./dereference/index.js";
4
- import { upgradeSchema } from "./upgrade/index.js";
5
4
 
6
5
  // Must be an interface (not a type) to allow merging with OpenAPI types with index signatures
7
6
  interface WithRef {
@@ -95,7 +94,10 @@ export const validate = async (schemaInput: unknown) => {
95
94
  throw new GraphQLError("OpenAPI version is not defined");
96
95
  }
97
96
 
97
+ const { upgrade } = await import("@scalar/openapi-parser");
98
+
98
99
  const dereferenced = await dereference(schema);
100
+ const upgraded = upgrade(dereferenced);
99
101
 
100
- return upgradeSchema(dereferenced);
102
+ return upgraded.specification;
101
103
  };
@@ -2,7 +2,7 @@ import { type ResultOf } from "@graphql-typed-document-node/core";
2
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
- import { useNavigate } from "react-router";
5
+ import { useNavigate, useParams } from "react-router";
6
6
  import {
7
7
  Collapsible,
8
8
  CollapsibleContent,
@@ -151,10 +151,11 @@ export const OperationList = ({
151
151
  untagged?: boolean;
152
152
  }) => {
153
153
  const { input, type, versions, version, options } = useOasConfig();
154
+ const { tag: tagFromParams } = useParams<"tag">();
154
155
  const query = useCreateQuery(OperationsForTagQuery, {
155
156
  input,
156
157
  type,
157
- tag,
158
+ tag: tag ?? tagFromParams,
158
159
  untagged,
159
160
  });
160
161
  const result = useSuspenseQuery(query);
@@ -178,7 +179,18 @@ export const OperationList = ({
178
179
  // Prefetch for Playground
179
180
  useApiIdentities();
180
181
 
181
- if (!schema.tag) return null;
182
+ if (!schema.tag) {
183
+ return (
184
+ <div className="flex flex-col h-full items-center justify-center text-center">
185
+ <div className="text-muted-foreground font-medium">
186
+ No operations found
187
+ </div>
188
+ <div className="mt-2 text-sm text-muted-foreground">
189
+ This API doesn't have any operations defined yet.
190
+ </div>
191
+ </div>
192
+ );
193
+ }
182
194
 
183
195
  const { operations, next, prev, description: tagDescription } = schema.tag;
184
196
 
@@ -64,11 +64,15 @@ const baseDoc = {
64
64
  },
65
65
  },
66
66
  },
67
- };
67
+ } as any;
68
68
 
69
69
  describe("removeExtensions", () => {
70
70
  it("removes all x- extensions by default", () => {
71
- const processed = removeExtensions()(baseDoc);
71
+ const processed = removeExtensions()({
72
+ schema: baseDoc,
73
+ file: "/file.json",
74
+ dereference: async (id) => id,
75
+ });
72
76
 
73
77
  const removedExtensions = [
74
78
  "x-root-ext",
@@ -93,7 +97,7 @@ describe("removeExtensions", () => {
93
97
  ["paths", "/test", "get", "responses", "200", "description"],
94
98
  "OK",
95
99
  );
96
- expect(processed.tags[0].name).toBe("example");
100
+ expect(processed.tags?.[0]?.name).toBe("example");
97
101
  });
98
102
 
99
103
  it("removes only specified x- extensions when names are provided", () => {
@@ -104,7 +108,11 @@ describe("removeExtensions", () => {
104
108
 
105
109
  const processed = removeExtensions({
106
110
  keys: ["x-path-ext", "x-param-ext"],
107
- })(docWithExtraExtensions);
111
+ })({
112
+ schema: docWithExtraExtensions,
113
+ file: "/file.json",
114
+ dereference: async (id) => id,
115
+ }) as any;
108
116
 
109
117
  // Assert specified extensions are removed
110
118
  expect(processed.paths["/test"]["x-path-ext"]).toBeUndefined();
@@ -131,9 +139,13 @@ describe("removeExtensions", () => {
131
139
  },
132
140
  },
133
141
  },
134
- };
142
+ } as any;
135
143
 
136
- const processed = removeExtensions()(deeplyNested);
144
+ const processed = removeExtensions()({
145
+ schema: deeplyNested,
146
+ file: "/file.json",
147
+ dereference: async (id) => id,
148
+ }) as any;
137
149
 
138
150
  expect(processed.a.b.c["x-deep-ext"]).toBeUndefined();
139
151
  expect(processed.a.b.c.d["x-another-ext"]).toBeUndefined();
@@ -144,9 +156,13 @@ describe("removeExtensions", () => {
144
156
  const docWithoutExtensions = {
145
157
  openapi: "3.1.0",
146
158
  info: { title: "API without extensions" },
147
- };
159
+ } as any;
148
160
 
149
- const processed = removeExtensions()(docWithoutExtensions);
161
+ const processed = removeExtensions()({
162
+ schema: docWithoutExtensions,
163
+ file: "/file.json",
164
+ dereference: async (id) => id,
165
+ });
150
166
 
151
167
  expect(processed).toEqual(docWithoutExtensions);
152
168
  });
@@ -154,7 +170,11 @@ describe("removeExtensions", () => {
154
170
  it("removes extensions based on shouldRemove callback", () => {
155
171
  const processed = removeExtensions({
156
172
  shouldRemove: (key) => key.startsWith("x-zuplo"),
157
- })(baseDoc);
173
+ })({
174
+ schema: baseDoc,
175
+ file: "/file.json",
176
+ dereference: async (id) => id,
177
+ });
158
178
 
159
179
  // Should remove x-zuplo extensions
160
180
  const removedExtensions = [
@@ -1,3 +1,5 @@
1
+ import type { ProcessorArg } from "../../../../config/validators/BuildSchema.js";
2
+ import type { OpenAPIDocument } from "../../../oas/parser/index.js";
1
3
  import { type RecordAny, traverse } from "./traverse.js";
2
4
 
3
5
  interface RemoveExtensionsOptions {
@@ -8,8 +10,8 @@ interface RemoveExtensionsOptions {
8
10
  // Remove all `x-` prefixed key/value pairs, or filter by names if provided
9
11
  export const removeExtensions =
10
12
  ({ keys, shouldRemove }: RemoveExtensionsOptions = {}) =>
11
- (doc: RecordAny): RecordAny =>
12
- traverse(doc, (spec) => {
13
+ ({ schema }: ProcessorArg) =>
14
+ traverse(schema, (spec) => {
13
15
  const result: RecordAny = {};
14
16
 
15
17
  for (const [key, value] of Object.entries(spec)) {
@@ -24,4 +26,4 @@ export const removeExtensions =
24
26
  result[key] = value;
25
27
  }
26
28
  return result;
27
- });
29
+ }) as OpenAPIDocument;