zudoku 0.40.1 → 0.41.1

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 (132) hide show
  1. package/dist/app/ZuploBuildConfig.d.ts +155 -0
  2. package/dist/app/ZuploBuildConfig.js +29 -0
  3. package/dist/app/ZuploBuildConfig.js.map +1 -0
  4. package/dist/app/env.d.ts +33 -0
  5. package/dist/app/env.js +29 -0
  6. package/dist/app/env.js.map +1 -0
  7. package/dist/app/main.js +7 -4
  8. package/dist/app/main.js.map +1 -1
  9. package/dist/cli/cli.js +12 -1
  10. package/dist/cli/cli.js.map +1 -1
  11. package/dist/cli/common/machine-id/lib.js +1 -0
  12. package/dist/cli/common/machine-id/lib.js.map +1 -1
  13. package/dist/config/loader.js +0 -3
  14. package/dist/config/loader.js.map +1 -1
  15. package/dist/config/validators/common.d.ts +17 -11
  16. package/dist/config/validators/common.js +1 -1
  17. package/dist/config/validators/common.js.map +1 -1
  18. package/dist/config/validators/validate.d.ts +7 -5
  19. package/dist/lib/authentication/providers/openid.d.ts +3 -1
  20. package/dist/lib/authentication/providers/openid.js +17 -11
  21. package/dist/lib/authentication/providers/openid.js.map +1 -1
  22. package/dist/lib/components/BuildCheck.d.ts +4 -0
  23. package/dist/lib/components/BuildCheck.js +22 -0
  24. package/dist/lib/components/BuildCheck.js.map +1 -0
  25. package/dist/lib/components/Search.js +1 -1
  26. package/dist/lib/components/Search.js.map +1 -1
  27. package/dist/lib/components/Zudoku.js +1 -3
  28. package/dist/lib/components/Zudoku.js.map +1 -1
  29. package/dist/lib/components/context/ViewportAnchorContext.js +8 -9
  30. package/dist/lib/components/context/ViewportAnchorContext.js.map +1 -1
  31. package/dist/lib/components/index.d.ts +4 -0
  32. package/dist/lib/components/index.js +2 -0
  33. package/dist/lib/components/index.js.map +1 -1
  34. package/dist/lib/components/navigation/PoweredByZudoku.js +2 -1
  35. package/dist/lib/components/navigation/PoweredByZudoku.js.map +1 -1
  36. package/dist/lib/components/navigation/ZuploLogo.d.ts +3 -0
  37. package/dist/lib/components/navigation/ZuploLogo.js +4 -0
  38. package/dist/lib/components/navigation/ZuploLogo.js.map +1 -0
  39. package/dist/lib/oas/parser/index.d.ts +1 -1
  40. package/dist/lib/oas/parser/index.js +3 -2
  41. package/dist/lib/oas/parser/index.js.map +1 -1
  42. package/dist/lib/plugins/openapi/OperationList.d.ts +1 -1
  43. package/dist/lib/plugins/openapi/OperationList.js +6 -4
  44. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  45. package/dist/lib/plugins/openapi/util/getRoutes.js +20 -13
  46. package/dist/lib/plugins/openapi/util/getRoutes.js.map +1 -1
  47. package/dist/lib/util/ensureArray.d.ts +1 -0
  48. package/dist/lib/util/ensureArray.js +2 -0
  49. package/dist/lib/util/ensureArray.js.map +1 -0
  50. package/dist/vite/api/SchemaManager.d.ts +36 -0
  51. package/dist/vite/api/SchemaManager.js +120 -0
  52. package/dist/vite/api/SchemaManager.js.map +1 -0
  53. package/dist/vite/api/SchemaManager.test.d.ts +1 -0
  54. package/dist/vite/api/SchemaManager.test.js +106 -0
  55. package/dist/vite/api/SchemaManager.test.js.map +1 -0
  56. package/dist/vite/config.js +5 -1
  57. package/dist/vite/config.js.map +1 -1
  58. package/dist/vite/plugin-api.js +38 -110
  59. package/dist/vite/plugin-api.js.map +1 -1
  60. package/dist/vite/plugin-sidebar.d.ts +3 -1
  61. package/dist/vite/plugin-sidebar.js +8 -2
  62. package/dist/vite/plugin-sidebar.js.map +1 -1
  63. package/dist/zuplo/with-zuplo-processors.js +1 -1
  64. package/dist/zuplo/with-zuplo-processors.js.map +1 -1
  65. package/dist/zuplo/with-zuplo.d.ts +3 -0
  66. package/dist/zuplo/with-zuplo.js +6 -9
  67. package/dist/zuplo/with-zuplo.js.map +1 -1
  68. package/lib/{Markdown-C5h6bxbE.js → Markdown-DqDbGCj2.js} +314 -315
  69. package/lib/{Markdown-C5h6bxbE.js.map → Markdown-DqDbGCj2.js.map} +1 -1
  70. package/lib/{MdxPage-B66ht0D_.js → MdxPage-B7MyVLV5.js} +5 -5
  71. package/lib/{MdxPage-B66ht0D_.js.map → MdxPage-B7MyVLV5.js.map} +1 -1
  72. package/lib/{OasProvider-BKPF0Pbt.js → OasProvider-DY8kA-9x.js} +2 -2
  73. package/lib/{OasProvider-BKPF0Pbt.js.map → OasProvider-DY8kA-9x.js.map} +1 -1
  74. package/lib/{OperationList-0bhBU7ME.js → OperationList-BNneqYos.js} +798 -794
  75. package/lib/{OperationList-0bhBU7ME.js.map → OperationList-BNneqYos.js.map} +1 -1
  76. package/lib/{Pagination-Bt1czAiJ.js → Pagination-9TA5cwCh.js} +2 -2
  77. package/lib/{Pagination-Bt1czAiJ.js.map → Pagination-9TA5cwCh.js.map} +1 -1
  78. package/lib/{SchemaList-CsTIT1pi.js → SchemaList-CbFxph_p.js} +5 -5
  79. package/lib/{SchemaList-CsTIT1pi.js.map → SchemaList-CbFxph_p.js.map} +1 -1
  80. package/lib/{SchemaView-DCSWtnYr.js → SchemaView-DwlVvFfp.js} +2 -2
  81. package/lib/{SchemaView-DCSWtnYr.js.map → SchemaView-DwlVvFfp.js.map} +1 -1
  82. package/lib/{SlotletProvider-VUmTNmLZ.js → SlotletProvider-p4XaFFOh.js} +2 -2
  83. package/lib/{SlotletProvider-VUmTNmLZ.js.map → SlotletProvider-p4XaFFOh.js.map} +1 -1
  84. package/lib/{Toc-BK39DQvI.js → Toc-BVdRaIC0.js} +2 -2
  85. package/lib/{Toc-BK39DQvI.js.map → Toc-BVdRaIC0.js.map} +1 -1
  86. package/lib/{createServer-DLN7APz_.js → createServer-D_5UkLtY.js} +1459 -1485
  87. package/lib/createServer-D_5UkLtY.js.map +1 -0
  88. package/lib/{index-tpbiZmWp.js → index-BPmAnNGI.js} +686 -648
  89. package/lib/index-BPmAnNGI.js.map +1 -0
  90. package/lib/index-BpThvE5R.js +66 -0
  91. package/lib/index-BpThvE5R.js.map +1 -0
  92. package/lib/{index-Ba_X7EnR.js → index-pAogBEk7.js} +255 -249
  93. package/lib/{index-Ba_X7EnR.js.map → index-pAogBEk7.js.map} +1 -1
  94. package/lib/index-ueM1dihS.js +247 -0
  95. package/lib/index-ueM1dihS.js.map +1 -0
  96. package/lib/{index-DcHeSvkE.js → public-api-CrAQFYc4.js} +651 -684
  97. package/lib/public-api-CrAQFYc4.js.map +1 -0
  98. package/lib/zudoku.auth-openid.js +144 -138
  99. package/lib/zudoku.auth-openid.js.map +1 -1
  100. package/lib/zudoku.components.js +23 -22
  101. package/lib/zudoku.plugin-api-catalog.js +2 -2
  102. package/lib/zudoku.plugin-api-keys.js +1 -1
  103. package/lib/zudoku.plugin-custom-pages.js +1 -1
  104. package/lib/zudoku.plugin-markdown.js +1 -1
  105. package/lib/zudoku.plugin-openapi.js +1 -1
  106. package/package.json +2 -2
  107. package/src/app/ZuploBuildConfig.ts +33 -0
  108. package/src/app/env.ts +35 -0
  109. package/src/app/main.tsx +12 -1
  110. package/src/lib/authentication/providers/openid.tsx +23 -12
  111. package/src/lib/components/BuildCheck.tsx +60 -0
  112. package/src/lib/components/Search.tsx +1 -1
  113. package/src/lib/components/Zudoku.tsx +1 -3
  114. package/src/lib/components/context/ViewportAnchorContext.tsx +8 -15
  115. package/src/lib/components/index.ts +2 -0
  116. package/src/lib/components/navigation/PoweredByZudoku.tsx +8 -3
  117. package/src/lib/components/navigation/ZuploLogo.tsx +14 -0
  118. package/src/lib/oas/parser/index.ts +4 -2
  119. package/src/lib/plugins/openapi/OperationList.tsx +15 -3
  120. package/src/lib/plugins/openapi/util/getRoutes.tsx +21 -13
  121. package/src/lib/util/ensureArray.ts +3 -0
  122. package/client.d.ts +0 -8
  123. package/dist/lib/oas/parser/upgrade/index.d.ts +0 -9
  124. package/dist/lib/oas/parser/upgrade/index.js +0 -90
  125. package/dist/lib/oas/parser/upgrade/index.js.map +0 -1
  126. package/dist/zuplo/env.d.ts +0 -7
  127. package/dist/zuplo/env.js +0 -12
  128. package/dist/zuplo/env.js.map +0 -1
  129. package/lib/createServer-DLN7APz_.js.map +0 -1
  130. package/lib/index-DcHeSvkE.js.map +0 -1
  131. package/lib/index-tpbiZmWp.js.map +0 -1
  132. 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-BPmAnNGI.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-BPmAnNGI.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-B7MyVLV5.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-pAogBEk7.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.1",
3
+ "version": "0.41.1",
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",
@@ -0,0 +1,33 @@
1
+ import { z } from "zod";
2
+
3
+ export const EntitlementsSchema = z.object({
4
+ devPortalZuploBranding: z.boolean(),
5
+ numberOfProjects: z.number(),
6
+ numberOfUsers: z.number(),
7
+ egressGbPerMonth: z.number(),
8
+ requestsPerMonth: z.number(),
9
+ numberOfApiKeys: z.number(),
10
+ customDomains: z.number(),
11
+ advancedAnalyticsEnabled: z.boolean(),
12
+ devPortalAnalyticsEnabled: z.boolean(),
13
+ premiumSupport: z.boolean(),
14
+ emergencyPhoneSupport: z.boolean(),
15
+ rbacEnabled: z.boolean(),
16
+ onPremEnabled: z.boolean(),
17
+ largeBuildRunners: z.boolean(),
18
+ });
19
+
20
+ export const BuildConfigSchema = z.object({
21
+ entitlements: EntitlementsSchema,
22
+ environmentType: z.string(),
23
+ deploymentName: z.string(),
24
+ deploymentUrl: z.string(),
25
+ projectId: z.string(),
26
+ projectType: z.string(),
27
+ sourceType: z.string(),
28
+ accountName: z.string(),
29
+ projectName: z.string(),
30
+ });
31
+
32
+ export type BuildConfig = z.infer<typeof BuildConfigSchema>;
33
+ export type Entitlements = z.infer<typeof EntitlementsSchema>;
package/src/app/env.ts ADDED
@@ -0,0 +1,35 @@
1
+ import { BuildConfigSchema } from "./ZuploBuildConfig.js";
2
+
3
+ const getZuploBuildConfig = () => {
4
+ if (!process.env.ZUPLO_BUILD_CONFIG) return undefined;
5
+
6
+ try {
7
+ const zuploBuildConfig = BuildConfigSchema.parse(
8
+ JSON.parse(process.env.ZUPLO_BUILD_CONFIG),
9
+ );
10
+ return zuploBuildConfig;
11
+ } catch (error) {
12
+ // eslint-disable-next-line no-console
13
+ console.error(
14
+ "ZUPLO_BUILD_CONFIG is a reserved environment variable and cannot be used for custom configuration. Please remove it from your environment variables.",
15
+ );
16
+ // eslint-disable-next-line no-console
17
+ console.log(error);
18
+ return undefined;
19
+ }
20
+ };
21
+
22
+ export const ZuploEnv = {
23
+ /**
24
+ * Indicates that the build is running in Zuplo "mode"
25
+ */
26
+ get isZuplo(): boolean {
27
+ return process.env.ZUPLO === "1";
28
+ },
29
+
30
+ get serverUrl(): string | undefined {
31
+ return process.env.ZUPLO_SERVER_URL;
32
+ },
33
+
34
+ buildConfig: getZuploBuildConfig(),
35
+ };
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 "./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
+ };
@@ -64,7 +64,7 @@ export const Search = ({ className }: { className?: string }) => {
64
64
  const KbdShortcut = () => {
65
65
  const os = detectOS();
66
66
  return (
67
- <kbd className="absolute right-[0.3rem] top-[0.35rem] hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[11px] font-medium opacity-100 sm:flex">
67
+ <kbd className="absolute right-1.5 hidden h-5 select-none items-center gap-1 rounded-sm border bg-muted px-1.5 font-mono text-[11px] font-medium opacity-100 sm:flex">
68
68
  {os === "macOS" ? "⌘" : "Ctrl"}+K
69
69
  </kbd>
70
70
  );
@@ -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
 
@@ -47,6 +47,22 @@ const createRoute = ({
47
47
  },
48
48
  });
49
49
 
50
+ const createAdditionalRoutes = (basePath: string) => [
51
+ // Category without tagged operations
52
+ createRoute({
53
+ path: joinUrl(basePath, UNTAGGED_PATH),
54
+ untagged: true,
55
+ }),
56
+ // Schema list route
57
+ {
58
+ path: joinUrl(basePath, "~schemas"),
59
+ lazy: async () => {
60
+ const { SchemaList } = await import("../SchemaList.js");
61
+ return { element: <SchemaList /> };
62
+ },
63
+ },
64
+ ];
65
+
50
66
  // Creates routes for a specific version, including tag-based routes and the untagged operations route.
51
67
  const createVersionRoutes = (
52
68
  versionPath: string,
@@ -64,18 +80,7 @@ const createVersionRoutes = (
64
80
  tag,
65
81
  }),
66
82
  ),
67
- // Category without tagged operations
68
- createRoute({
69
- path: joinUrl(versionPath, UNTAGGED_PATH),
70
- untagged: true,
71
- }),
72
- {
73
- path: joinUrl(versionPath, "~schemas"),
74
- lazy: async () => {
75
- const { SchemaList } = await import("../SchemaList.js");
76
- return { element: <SchemaList /> };
77
- },
78
- },
83
+ ...createAdditionalRoutes(versionPath),
79
84
  ];
80
85
  };
81
86
 
@@ -100,7 +105,10 @@ export const getRoutes = ({
100
105
  createOasProvider({
101
106
  basePath,
102
107
  routePath: basePath,
103
- routes: [createRoute({ path: basePath + "/:tag?" })],
108
+ routes: [
109
+ createRoute({ path: basePath + "/:tag?" }),
110
+ ...createAdditionalRoutes(basePath),
111
+ ],
104
112
  client,
105
113
  config,
106
114
  }),
@@ -0,0 +1,3 @@
1
+ export const ensureArray = <T extends NonNullable<unknown>>(
2
+ value: T | T[],
3
+ ): T[] => (Array.isArray(value) ? value : [value]);
package/client.d.ts DELETED
@@ -1,8 +0,0 @@
1
- /* eslint-disable no-var */
2
- /// <reference types="vite/client" />
3
-
4
- declare var process: {
5
- env: {
6
- [key: string]: string | undefined;
7
- };
8
- };
@@ -1,9 +0,0 @@
1
- import { type RecordAny } from "../../../util/traverse.js";
2
- import type { OpenAPIDocument } from "../index.js";
3
- /**
4
- * Upgrade from OpenAPI 3.0.x to 3.1.0
5
- *
6
- * Taken from https://github.com/scalar/openapi-parser/blob/main/packages/openapi-parser/src/utils/upgradeFromThreeToThreeOne.ts
7
- * https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0
8
- */
9
- export declare const upgradeSchema: (schema: RecordAny) => OpenAPIDocument;