zudoku 0.3.0-dev.57 → 0.3.0-dev.59
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.
- package/dist/app/entry.client.js +1 -1
- package/dist/app/entry.client.js.map +1 -1
- package/dist/app/entry.server.d.ts +2 -0
- package/dist/app/entry.server.js +1 -0
- package/dist/app/entry.server.js.map +1 -1
- package/dist/app/main.js +8 -2
- package/dist/app/main.js.map +1 -1
- package/dist/config/validators/validate.d.ts +121 -102
- package/dist/config/validators/validate.js +4 -0
- package/dist/config/validators/validate.js.map +1 -1
- package/dist/lib/authentication/AuthenticationPlugin.d.ts +16 -0
- package/dist/lib/authentication/AuthenticationPlugin.js +31 -0
- package/dist/lib/authentication/AuthenticationPlugin.js.map +1 -0
- package/dist/lib/authentication/authentication.d.ts +3 -4
- package/dist/lib/authentication/components/Login.d.ts +1 -0
- package/dist/lib/authentication/components/Login.js +10 -0
- package/dist/lib/authentication/components/Login.js.map +1 -0
- package/dist/lib/authentication/components/Logout.d.ts +1 -0
- package/dist/lib/authentication/components/Logout.js +10 -0
- package/dist/lib/authentication/components/Logout.js.map +1 -0
- package/dist/lib/authentication/providers/clerk.js +43 -27
- package/dist/lib/authentication/providers/clerk.js.map +1 -1
- package/dist/lib/authentication/providers/openid.d.ts +11 -3
- package/dist/lib/authentication/providers/openid.js +22 -11
- package/dist/lib/authentication/providers/openid.js.map +1 -1
- package/dist/lib/authentication/routes.d.ts +5 -0
- package/dist/lib/authentication/routes.js +12 -0
- package/dist/lib/authentication/routes.js.map +1 -0
- package/dist/lib/components/Bootstrap.d.ts +2 -1
- package/dist/lib/components/Bootstrap.js +4 -1
- package/dist/lib/components/Bootstrap.js.map +1 -1
- package/dist/lib/components/DevPortal.js +14 -2
- package/dist/lib/components/DevPortal.js.map +1 -1
- package/dist/lib/components/Header.js +16 -2
- package/dist/lib/components/Header.js.map +1 -1
- package/dist/lib/components/InlineCode.js +1 -1
- package/dist/lib/components/InlineCode.js.map +1 -1
- package/dist/lib/components/TopNavigation.js +1 -1
- package/dist/lib/components/TopNavigation.js.map +1 -1
- package/dist/lib/components/context/DevPortalProvider.js +18 -1
- package/dist/lib/components/context/DevPortalProvider.js.map +1 -1
- package/dist/lib/components/index.d.ts +5 -1
- package/dist/lib/components/index.js +4 -0
- package/dist/lib/components/index.js.map +1 -1
- package/dist/lib/components/navigation/SideNavigationItem.js +1 -1
- package/dist/lib/components/navigation/SideNavigationItem.js.map +1 -1
- package/dist/lib/core/DevPortalContext.d.ts +1 -1
- package/dist/lib/core/DevPortalContext.js.map +1 -1
- package/dist/lib/core/plugins.d.ts +10 -1
- package/dist/lib/core/plugins.js +1 -0
- package/dist/lib/core/plugins.js.map +1 -1
- package/dist/lib/plugins/api-keys/CreateApiKey.js +1 -1
- package/dist/lib/plugins/api-keys/CreateApiKey.js.map +1 -1
- package/dist/lib/plugins/api-keys/SettingsApiKeys.js +2 -2
- package/dist/lib/plugins/api-keys/SettingsApiKeys.js.map +1 -1
- package/dist/lib/plugins/api-keys/index.d.ts +2 -2
- package/dist/lib/plugins/api-keys/index.js +6 -0
- package/dist/lib/plugins/api-keys/index.js.map +1 -1
- package/dist/lib/plugins/custom-page/index.d.ts +8 -0
- package/dist/lib/plugins/custom-page/index.js +12 -0
- package/dist/lib/plugins/custom-page/index.js.map +1 -0
- package/dist/lib/plugins/markdown/MdxPage.js +1 -1
- package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationListItem.js +1 -1
- package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/ParameterList.js +1 -1
- package/dist/lib/plugins/openapi/ParameterList.js.map +1 -1
- package/dist/lib/plugins/openapi/SchemaListViewItem.js +3 -3
- package/dist/lib/plugins/openapi/SchemaListViewItem.js.map +1 -1
- package/dist/lib/plugins/openapi/SidecarBox.js +1 -1
- package/dist/lib/plugins/openapi/SidecarBox.js.map +1 -1
- package/dist/lib/plugins/openapi/StaggeredRender.d.ts +3 -0
- package/dist/lib/plugins/openapi/StaggeredRender.js +10 -5
- package/dist/lib/plugins/openapi/StaggeredRender.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/Playground.js +2 -2
- package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
- package/dist/lib/ui/Card.js +1 -1
- package/dist/lib/ui/Card.js.map +1 -1
- package/dist/lib/ui/DropdownMenu.d.ts +27 -0
- package/dist/lib/ui/DropdownMenu.js +36 -0
- package/dist/lib/ui/DropdownMenu.js.map +1 -0
- package/dist/lib/ui/button-variants.d.ts +2 -2
- package/dist/lib/ui/button-variants.js +1 -0
- package/dist/lib/ui/button-variants.js.map +1 -1
- package/dist/lib/util/MdxComponents.js +1 -1
- package/dist/lib/util/MdxComponents.js.map +1 -1
- package/dist/lib/util/joinPath.js +2 -1
- package/dist/lib/util/joinPath.js.map +1 -1
- package/dist/vite/build.js +5 -2
- package/dist/vite/build.js.map +1 -1
- package/dist/vite/config.d.ts +8 -1
- package/dist/vite/config.js +13 -6
- package/dist/vite/config.js.map +1 -1
- package/dist/vite/plugin-component.js +1 -0
- package/dist/vite/plugin-component.js.map +1 -1
- package/dist/vite/prerender.d.ts +1 -1
- package/dist/vite/prerender.js +23 -3
- package/dist/vite/prerender.js.map +1 -1
- package/lib/{AnchorLink-GNsUeGSX.js → AnchorLink-Bj1hwDuD.js} +3 -3
- package/lib/{AnchorLink-GNsUeGSX.js.map → AnchorLink-Bj1hwDuD.js.map} +1 -1
- package/lib/AuthenticationPlugin-CG6Bw32B.js +46 -0
- package/lib/AuthenticationPlugin-CG6Bw32B.js.map +1 -0
- package/lib/CategoryHeading-DMkTmmBh.js +10 -0
- package/lib/CategoryHeading-DMkTmmBh.js.map +1 -0
- package/lib/Combination-lAFQBd6U.js +2774 -0
- package/lib/Combination-lAFQBd6U.js.map +1 -0
- package/lib/DevPortalProvider-BBhQ8kgI.js +1125 -0
- package/lib/DevPortalProvider-BBhQ8kgI.js.map +1 -0
- package/lib/{Markdown-DtLFdxD1.js → Markdown-BjRJKl_E.js} +1376 -1379
- package/lib/Markdown-BjRJKl_E.js.map +1 -0
- package/lib/{MdxPage-CbwYRKf5.js → MdxPage-DJTFOCbZ.js} +17 -17
- package/lib/{MdxPage-CbwYRKf5.js.map → MdxPage-DJTFOCbZ.js.map} +1 -1
- package/lib/OperationList-DDTtK3I7.js +5403 -0
- package/lib/OperationList-DDTtK3I7.js.map +1 -0
- package/lib/{Route-C1LyvITr.js → Route-Bsrd0acQ.js} +2 -2
- package/lib/{Route-C1LyvITr.js.map → Route-Bsrd0acQ.js.map} +1 -1
- package/lib/Select-CEnkyfyn.js +2223 -0
- package/lib/Select-CEnkyfyn.js.map +1 -0
- package/lib/Spinner-Ciq_pWU7.js +359 -0
- package/lib/Spinner-Ciq_pWU7.js.map +1 -0
- package/lib/{hook-Biq3zYel.js → hook-Q_gAL2NZ.js} +20 -19
- package/lib/{hook-Biq3zYel.js.map → hook-Q_gAL2NZ.js.map} +1 -1
- package/lib/{index-Bg82-bqR.js → index-BE2a6gGC.js} +24 -23
- package/lib/{index-Bg82-bqR.js.map → index-BE2a6gGC.js.map} +1 -1
- package/lib/{jsx-runtime-CJZJivg2.js → jsx-runtime-BIr0WBt_.js} +119 -119
- package/lib/jsx-runtime-BIr0WBt_.js.map +1 -0
- package/lib/{router-CBw2vqJE.js → router-BiRCp01d.js} +671 -673
- package/lib/router-BiRCp01d.js.map +1 -0
- package/lib/zudoku.auth-clerk.js +47 -32
- package/lib/zudoku.auth-clerk.js.map +1 -1
- package/lib/zudoku.auth-openid.js +170 -159
- package/lib/zudoku.auth-openid.js.map +1 -1
- package/lib/zudoku.components.js +1514 -598
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.plugin-api-keys.js +30 -24
- package/lib/zudoku.plugin-api-keys.js.map +1 -1
- package/lib/zudoku.plugin-custom-page.js +13 -0
- package/lib/zudoku.plugin-custom-page.js.map +1 -0
- package/lib/zudoku.plugin-markdown.js +19 -20
- package/lib/zudoku.plugin-markdown.js.map +1 -1
- package/lib/zudoku.plugin-openapi.js +4 -4
- package/lib/zudoku.plugin-redirect.js +3 -3
- package/package.json +29 -14
- package/src/app/entry.client.tsx +1 -1
- package/src/app/entry.server.tsx +2 -0
- package/src/app/main.css +6 -0
- package/src/app/main.tsx +8 -2
- package/src/lib/authentication/AuthenticationPlugin.tsx +36 -0
- package/src/lib/authentication/authentication.ts +3 -4
- package/src/lib/authentication/components/Login.tsx +11 -0
- package/src/lib/authentication/components/Logout.tsx +11 -0
- package/src/lib/authentication/providers/clerk.tsx +43 -27
- package/src/lib/authentication/providers/openid.tsx +25 -13
- package/src/lib/authentication/routes.tsx +10 -0
- package/src/lib/components/Bootstrap.tsx +14 -7
- package/src/lib/components/DevPortal.tsx +29 -9
- package/src/lib/components/Header.tsx +80 -30
- package/src/lib/components/InlineCode.tsx +1 -1
- package/src/lib/components/TopNavigation.tsx +1 -1
- package/src/lib/components/context/DevPortalProvider.ts +22 -2
- package/src/lib/components/index.ts +4 -0
- package/src/lib/components/navigation/SideNavigationItem.tsx +1 -1
- package/src/lib/core/DevPortalContext.ts +1 -1
- package/src/lib/core/plugins.ts +16 -0
- package/src/lib/plugins/api-keys/CreateApiKey.tsx +1 -1
- package/src/lib/plugins/api-keys/SettingsApiKeys.tsx +4 -4
- package/src/lib/plugins/api-keys/index.tsx +8 -1
- package/src/lib/plugins/custom-page/index.tsx +22 -0
- package/src/lib/plugins/markdown/MdxPage.tsx +2 -2
- package/src/lib/plugins/openapi/OperationListItem.tsx +1 -4
- package/src/lib/plugins/openapi/ParameterList.tsx +1 -1
- package/src/lib/plugins/openapi/SchemaListViewItem.tsx +3 -3
- package/src/lib/plugins/openapi/SidecarBox.tsx +1 -1
- package/src/lib/plugins/openapi/StaggeredRender.tsx +19 -5
- package/src/lib/plugins/openapi/playground/Playground.tsx +2 -2
- package/src/lib/ui/Card.tsx +1 -1
- package/src/lib/ui/DropdownMenu.tsx +199 -0
- package/src/lib/ui/button-variants.ts +1 -0
- package/src/lib/util/MdxComponents.tsx +1 -1
- package/src/lib/util/joinPath.tsx +2 -1
- package/dist/app/zudoku-manifest.d.ts +0 -1
- package/dist/app/zudoku-manifest.js +0 -20
- package/dist/app/zudoku-manifest.js.map +0 -1
- package/lib/Button-DpHMZvVs.js +0 -4571
- package/lib/Button-DpHMZvVs.js.map +0 -1
- package/lib/DevPortalProvider-Do9oJqme.js +0 -1081
- package/lib/DevPortalProvider-Do9oJqme.js.map +0 -1
- package/lib/Markdown-DtLFdxD1.js.map +0 -1
- package/lib/OperationList-DypxLtSC.js +0 -5578
- package/lib/OperationList-DypxLtSC.js.map +0 -1
- package/lib/Spinner-Bhbs5aPI.js +0 -182
- package/lib/Spinner-Bhbs5aPI.js.map +0 -1
- package/lib/index-gsAuUwQh.js +0 -418
- package/lib/index-gsAuUwQh.js.map +0 -1
- package/lib/jsx-runtime-CJZJivg2.js.map +0 -1
- package/lib/router-CBw2vqJE.js.map +0 -1
- package/lib/util-_jwUlTBU.js +0 -41
- package/lib/util-_jwUlTBU.js.map +0 -1
- package/src/app/zudoku-manifest.ts +0 -22
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { RouteObject } from "react-router-dom";
|
|
2
|
+
import {
|
|
3
|
+
CommonPlugin,
|
|
4
|
+
NavigationPlugin,
|
|
5
|
+
ProfileMenuPlugin,
|
|
6
|
+
} from "../core/plugins.js";
|
|
7
|
+
import { Login } from "./components/Login.js";
|
|
8
|
+
import { Logout } from "./components/Logout.js";
|
|
9
|
+
|
|
10
|
+
type PluginInterface = NavigationPlugin & CommonPlugin & ProfileMenuPlugin;
|
|
11
|
+
|
|
12
|
+
export class AuthenticationPlugin implements PluginInterface {
|
|
13
|
+
constructor(private additionalRoutes: RouteObject[] = []) {}
|
|
14
|
+
getRoutes() {
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
path: "/logout",
|
|
18
|
+
element: <Logout />,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
path: "/login",
|
|
22
|
+
element: <Login />,
|
|
23
|
+
},
|
|
24
|
+
...this.additionalRoutes,
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getProfileMenuItems() {
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
label: "Logout",
|
|
32
|
+
path: "/logout",
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DevPortalPlugin } from "../core/plugins.js";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export interface AuthenticationProvider extends AuthenticationPlugin {
|
|
3
|
+
export interface AuthenticationProvider {
|
|
6
4
|
login(): Promise<void>;
|
|
7
5
|
getAccessToken(): Promise<string>;
|
|
8
6
|
logout(): Promise<void>;
|
|
7
|
+
getAuthenticationPlugin?(): DevPortalPlugin;
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useDevPortal } from "../../components/context/DevPortalProvider.js";
|
|
3
|
+
|
|
4
|
+
export const Login = () => {
|
|
5
|
+
const context = useDevPortal();
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
void context.authentication?.login();
|
|
8
|
+
}, [context.authentication]);
|
|
9
|
+
|
|
10
|
+
return null;
|
|
11
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useDevPortal } from "../../components/context/DevPortalProvider.js";
|
|
3
|
+
|
|
4
|
+
export const Logout = () => {
|
|
5
|
+
const context = useDevPortal();
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
void context.authentication?.logout();
|
|
8
|
+
}, [context.authentication]);
|
|
9
|
+
|
|
10
|
+
return null;
|
|
11
|
+
};
|
|
@@ -1,8 +1,42 @@
|
|
|
1
1
|
import type { Clerk } from "@clerk/clerk-js";
|
|
2
2
|
import { ClerkAuthenticationConfig } from "../../../config/config.js";
|
|
3
3
|
import { AuthenticationProviderInitializer } from "../authentication.js";
|
|
4
|
+
import { AuthenticationPlugin } from "../AuthenticationPlugin.js";
|
|
4
5
|
import { useAuthState } from "../state.js";
|
|
5
6
|
|
|
7
|
+
class ClerkAuthPlugin extends AuthenticationPlugin {
|
|
8
|
+
constructor(private clerk: Promise<Clerk | undefined>) {
|
|
9
|
+
super();
|
|
10
|
+
}
|
|
11
|
+
initialize = async () => {
|
|
12
|
+
const clerk = await this.clerk;
|
|
13
|
+
|
|
14
|
+
if (!clerk) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (clerk.session) {
|
|
19
|
+
useAuthState.setState({
|
|
20
|
+
isAuthenticated: true,
|
|
21
|
+
isPending: false,
|
|
22
|
+
profile: {
|
|
23
|
+
sub: clerk.session.user.id,
|
|
24
|
+
name: clerk.session.user.fullName ?? undefined,
|
|
25
|
+
email: clerk.session.user.emailAddresses[0]?.emailAddress,
|
|
26
|
+
emailVerified: false, // TODO: Check this
|
|
27
|
+
pictureUrl: clerk.session.user.imageUrl,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
} else {
|
|
31
|
+
useAuthState.setState({
|
|
32
|
+
isAuthenticated: false,
|
|
33
|
+
isPending: false,
|
|
34
|
+
profile: undefined,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
6
40
|
const clerkAuth: AuthenticationProviderInitializer<
|
|
7
41
|
ClerkAuthenticationConfig
|
|
8
42
|
> = ({ clerkPubKey }) => {
|
|
@@ -13,7 +47,8 @@ const clerkAuth: AuthenticationProviderInitializer<
|
|
|
13
47
|
const { Clerk } = await import("@clerk/clerk-js");
|
|
14
48
|
clerkApi = new Clerk(clerkPubKey);
|
|
15
49
|
|
|
16
|
-
await clerkApi.load(
|
|
50
|
+
await clerkApi.load();
|
|
51
|
+
return clerkApi;
|
|
17
52
|
})();
|
|
18
53
|
|
|
19
54
|
async function getAccessToken() {
|
|
@@ -29,38 +64,19 @@ const clerkAuth: AuthenticationProviderInitializer<
|
|
|
29
64
|
}
|
|
30
65
|
|
|
31
66
|
return {
|
|
32
|
-
initialize: async () => {
|
|
33
|
-
await ensureLoaded;
|
|
34
|
-
|
|
35
|
-
if (clerkApi.session) {
|
|
36
|
-
useAuthState.setState({
|
|
37
|
-
isAuthenticated: true,
|
|
38
|
-
isPending: false,
|
|
39
|
-
profile: {
|
|
40
|
-
sub: clerkApi.session.user.id,
|
|
41
|
-
name: clerkApi.session.user.fullName ?? undefined,
|
|
42
|
-
email: clerkApi.session.user.emailAddresses[0]?.emailAddress,
|
|
43
|
-
emailVerified: false, // TODO: Check this
|
|
44
|
-
// emailVerified: clerkApi.session.user.hasVerifiedEmailAddress,
|
|
45
|
-
pictureUrl: clerkApi.session.user.imageUrl,
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
} else {
|
|
49
|
-
useAuthState.setState({
|
|
50
|
-
isAuthenticated: false,
|
|
51
|
-
isPending: false,
|
|
52
|
-
profile: undefined,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
67
|
getAccessToken,
|
|
57
68
|
logout: async () => {
|
|
58
69
|
await clerkApi.signOut();
|
|
59
70
|
},
|
|
60
71
|
login: async () => {
|
|
61
|
-
await clerkApi.redirectToSignIn(
|
|
72
|
+
await clerkApi.redirectToSignIn({
|
|
73
|
+
signInForceRedirectUrl: "http://localhost:9000/",
|
|
74
|
+
signUpForceRedirectUrl: "http://localhost:9000/",
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
getAuthenticationPlugin() {
|
|
78
|
+
return new ClerkAuthPlugin(ensureLoaded);
|
|
62
79
|
},
|
|
63
|
-
getRoutes: () => [],
|
|
64
80
|
};
|
|
65
81
|
};
|
|
66
82
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import logger from "loglevel";
|
|
2
2
|
import * as oauth from "oauth4webapi";
|
|
3
|
-
import type { RouteObject } from "react-router-dom";
|
|
4
3
|
import { OpenIDAuthenticationConfig } from "../../../config/config.js";
|
|
5
4
|
import {
|
|
6
5
|
AuthenticationProvider,
|
|
7
6
|
AuthenticationProviderInitializer,
|
|
8
7
|
} from "../authentication.js";
|
|
8
|
+
import { AuthenticationPlugin } from "../AuthenticationPlugin.js";
|
|
9
9
|
import { Callback } from "../Callback.js";
|
|
10
10
|
import { AuthorizationError, OAuthAuthorizationError } from "../errors.js";
|
|
11
11
|
import { useAuthState, UserProfile } from "../state.js";
|
|
@@ -19,6 +19,24 @@ interface TokenState {
|
|
|
19
19
|
tokenType: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
class OpenIdAuthPlugin extends AuthenticationPlugin {
|
|
23
|
+
constructor(
|
|
24
|
+
private callbackUrlPath: string,
|
|
25
|
+
private handleCallback: () => Promise<void>,
|
|
26
|
+
) {
|
|
27
|
+
super();
|
|
28
|
+
}
|
|
29
|
+
getRoutes() {
|
|
30
|
+
return [
|
|
31
|
+
...super.getRoutes(),
|
|
32
|
+
{
|
|
33
|
+
path: this.callbackUrlPath,
|
|
34
|
+
element: <Callback handleCallback={this.handleCallback} />,
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
22
40
|
export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
23
41
|
protected client: oauth.Client;
|
|
24
42
|
protected issuer: string;
|
|
@@ -91,10 +109,6 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
91
109
|
};
|
|
92
110
|
}
|
|
93
111
|
|
|
94
|
-
async initialize() {
|
|
95
|
-
// No init needed
|
|
96
|
-
}
|
|
97
|
-
|
|
98
112
|
async login(): Promise<void> {
|
|
99
113
|
const code_challenge_method = "S256";
|
|
100
114
|
const authorizationServer = await this.getAuthServer();
|
|
@@ -207,14 +221,6 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
207
221
|
logoutUrl = redirectUrl;
|
|
208
222
|
}
|
|
209
223
|
}
|
|
210
|
-
getRoutes(): RouteObject[] {
|
|
211
|
-
return [
|
|
212
|
-
{
|
|
213
|
-
path: this.callbackUrlPath,
|
|
214
|
-
element: <Callback handleCallback={this.handleCallback} />,
|
|
215
|
-
},
|
|
216
|
-
];
|
|
217
|
-
}
|
|
218
224
|
|
|
219
225
|
handleCallback = async (): Promise<void> => {
|
|
220
226
|
const url = new URL(window.location.href);
|
|
@@ -305,6 +311,12 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
305
311
|
// Returning true because we are using react query
|
|
306
312
|
// return true;
|
|
307
313
|
};
|
|
314
|
+
|
|
315
|
+
getAuthenticationPlugin() {
|
|
316
|
+
return new OpenIdAuthPlugin(this.callbackUrlPath, () =>
|
|
317
|
+
this.handleCallback(),
|
|
318
|
+
);
|
|
319
|
+
}
|
|
308
320
|
}
|
|
309
321
|
|
|
310
322
|
const openIDAuth: AuthenticationProviderInitializer<
|
|
@@ -5,18 +5,25 @@ import {
|
|
|
5
5
|
type StaticHandlerContext,
|
|
6
6
|
StaticRouterProvider,
|
|
7
7
|
} from "react-router-dom/server.js";
|
|
8
|
+
import { StaggeredRenderContext } from "../plugins/openapi/StaggeredRender.js";
|
|
8
9
|
|
|
9
10
|
const Bootstrap = ({
|
|
10
11
|
router,
|
|
12
|
+
hydrate = false,
|
|
11
13
|
}: {
|
|
14
|
+
hydrate?: boolean;
|
|
12
15
|
router: ReturnType<typeof createBrowserRouter>;
|
|
13
|
-
}) =>
|
|
14
|
-
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
}) => {
|
|
17
|
+
return (
|
|
18
|
+
<StrictMode>
|
|
19
|
+
<HelmetProvider>
|
|
20
|
+
<StaggeredRenderContext.Provider value={{ stagger: !hydrate }}>
|
|
21
|
+
<RouterProvider router={router} />
|
|
22
|
+
</StaggeredRenderContext.Provider>
|
|
23
|
+
</HelmetProvider>
|
|
24
|
+
</StrictMode>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
20
27
|
|
|
21
28
|
const BootstrapStatic = ({
|
|
22
29
|
router,
|
|
@@ -5,10 +5,13 @@ import {
|
|
|
5
5
|
Fragment,
|
|
6
6
|
memo,
|
|
7
7
|
type PropsWithChildren,
|
|
8
|
+
useContext,
|
|
8
9
|
useEffect,
|
|
9
10
|
useMemo,
|
|
11
|
+
useState,
|
|
10
12
|
} from "react";
|
|
11
13
|
import { ErrorBoundary } from "react-error-boundary";
|
|
14
|
+
import { useNavigation } from "react-router-dom";
|
|
12
15
|
import {
|
|
13
16
|
DevPortalContext,
|
|
14
17
|
queryClient,
|
|
@@ -16,6 +19,7 @@ import {
|
|
|
16
19
|
} from "../core/DevPortalContext.js";
|
|
17
20
|
import { hasHead } from "../core/plugins.js";
|
|
18
21
|
import { TopLevelError } from "../errors/TopLevelError.js";
|
|
22
|
+
import { StaggeredRenderContext } from "../plugins/openapi/StaggeredRender.js";
|
|
19
23
|
import { MdxComponents } from "../util/MdxComponents.js";
|
|
20
24
|
import {
|
|
21
25
|
ComponentsProvider,
|
|
@@ -46,6 +50,20 @@ const DevPortalInner = ({
|
|
|
46
50
|
() => ({ ...MdxComponents, ...props.mdx?.components }),
|
|
47
51
|
[props.mdx?.components],
|
|
48
52
|
);
|
|
53
|
+
const { stagger } = useContext(StaggeredRenderContext);
|
|
54
|
+
const [didNavigate, setDidNavigate] = useState(false);
|
|
55
|
+
const staggeredValue = useMemo(
|
|
56
|
+
() => (didNavigate ? { stagger: true } : { stagger }),
|
|
57
|
+
[stagger, didNavigate],
|
|
58
|
+
);
|
|
59
|
+
const navigation = useNavigation();
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (didNavigate) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
setDidNavigate(true);
|
|
66
|
+
}, [didNavigate, navigation.location]);
|
|
49
67
|
|
|
50
68
|
const devPortalContext = useMemo(() => new DevPortalContext(props), [props]);
|
|
51
69
|
|
|
@@ -61,15 +79,17 @@ const DevPortalInner = ({
|
|
|
61
79
|
return (
|
|
62
80
|
<QueryClientProvider client={queryClient}>
|
|
63
81
|
<Helmet>{heads}</Helmet>
|
|
64
|
-
<
|
|
65
|
-
<
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
<StaggeredRenderContext.Provider value={staggeredValue}>
|
|
83
|
+
<DevPortalProvider value={devPortalContext}>
|
|
84
|
+
<MDXProvider components={mdxComponents}>
|
|
85
|
+
<ThemeProvider>
|
|
86
|
+
<ComponentsProvider value={components}>
|
|
87
|
+
<ViewportAnchorProvider>{children}</ViewportAnchorProvider>
|
|
88
|
+
</ComponentsProvider>
|
|
89
|
+
</ThemeProvider>
|
|
90
|
+
</MDXProvider>
|
|
91
|
+
</DevPortalProvider>
|
|
92
|
+
</StaggeredRenderContext.Provider>
|
|
73
93
|
</QueryClientProvider>
|
|
74
94
|
);
|
|
75
95
|
};
|
|
@@ -1,30 +1,77 @@
|
|
|
1
1
|
import { MoonStarIcon, SunIcon } from "lucide-react";
|
|
2
2
|
import { memo } from "react";
|
|
3
3
|
|
|
4
|
+
import { Link } from "react-router-dom";
|
|
4
5
|
import { useAuth } from "../authentication/hook.js";
|
|
6
|
+
import { isProfileMenuPlugin, NavigationItem } from "../core/plugins.js";
|
|
7
|
+
import { Button } from "../ui/Button.js";
|
|
8
|
+
import {
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
DropdownMenuContent,
|
|
11
|
+
DropdownMenuItem,
|
|
12
|
+
DropdownMenuLabel,
|
|
13
|
+
DropdownMenuPortal,
|
|
14
|
+
DropdownMenuSeparator,
|
|
15
|
+
DropdownMenuSub,
|
|
16
|
+
DropdownMenuSubContent,
|
|
17
|
+
DropdownMenuSubTrigger,
|
|
18
|
+
DropdownMenuTrigger,
|
|
19
|
+
} from "../ui/DropdownMenu.js";
|
|
20
|
+
import { cn } from "../util/cn.js";
|
|
5
21
|
import { TopNavigation } from "./TopNavigation.js";
|
|
6
22
|
import { useDevPortal } from "./context/DevPortalProvider.js";
|
|
7
23
|
import { useTheme } from "./context/ThemeContext.js";
|
|
8
24
|
|
|
25
|
+
const RecursiveMenu = ({ item }: { item: NavigationItem }) => {
|
|
26
|
+
return item.children ? (
|
|
27
|
+
<DropdownMenuSub key={item.label}>
|
|
28
|
+
<DropdownMenuSubTrigger>{item.label}</DropdownMenuSubTrigger>
|
|
29
|
+
<DropdownMenuPortal>
|
|
30
|
+
<DropdownMenuSubContent>
|
|
31
|
+
{item.children.map((item, i) => (
|
|
32
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
33
|
+
<RecursiveMenu key={i} item={item} />
|
|
34
|
+
))}
|
|
35
|
+
</DropdownMenuSubContent>
|
|
36
|
+
</DropdownMenuPortal>
|
|
37
|
+
</DropdownMenuSub>
|
|
38
|
+
) : (
|
|
39
|
+
<Link to={item.path ?? ""}>
|
|
40
|
+
<DropdownMenuItem key={item.label}>{item.label}</DropdownMenuItem>
|
|
41
|
+
</Link>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
9
45
|
export const Header = memo(function HeaderInner() {
|
|
10
46
|
const [isDark, toggleTheme] = useTheme();
|
|
11
47
|
const { isAuthenticated, profile, isAuthEnabled, login, logout } = useAuth();
|
|
12
|
-
const
|
|
48
|
+
const context = useDevPortal();
|
|
49
|
+
const { page, plugins } = context;
|
|
13
50
|
|
|
14
51
|
const ThemeIcon = isDark ? MoonStarIcon : SunIcon;
|
|
15
52
|
|
|
16
53
|
return (
|
|
17
54
|
<header className="fixed top-0 w-full z-10 bg-background/80 backdrop-blur">
|
|
18
55
|
<div className="max-w-screen-2xl mx-auto">
|
|
19
|
-
<div className="grid grid-cols-[calc(var(--side-nav-width))_1fr] lg:gap-12 items-center border-b
|
|
56
|
+
<div className="grid grid-cols-[calc(var(--side-nav-width))_1fr] lg:gap-12 items-center border-b px-12 h-[--top-header-height]">
|
|
20
57
|
<div className="flex items-center gap-3.5">
|
|
21
58
|
{page?.logo && (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
59
|
+
<>
|
|
60
|
+
<img
|
|
61
|
+
src={page.logo.src.light}
|
|
62
|
+
alt={page.logo.alt ?? page.pageTitle}
|
|
63
|
+
style={{ width: page.logo.width }}
|
|
64
|
+
className={cn("h-10", isDark && "hidden")}
|
|
65
|
+
loading="lazy"
|
|
66
|
+
/>
|
|
67
|
+
<img
|
|
68
|
+
src={page.logo.src.dark}
|
|
69
|
+
alt={page.logo.alt ?? page.pageTitle}
|
|
70
|
+
style={{ width: page.logo.width }}
|
|
71
|
+
className={cn("h-10", !isDark && "hidden")}
|
|
72
|
+
loading="lazy"
|
|
73
|
+
/>
|
|
74
|
+
</>
|
|
28
75
|
)}
|
|
29
76
|
<span className="font-bold text-2xl text-foreground/85 tracking-wide">
|
|
30
77
|
{page?.pageTitle}
|
|
@@ -37,34 +84,37 @@ export const Header = memo(function HeaderInner() {
|
|
|
37
84
|
<SearchIcon size={14} />
|
|
38
85
|
Search
|
|
39
86
|
</div>
|
|
40
|
-
<kbd className="absolute right-[0.3rem] top-[0.3rem] hidden h-5 select-none items-center gap-1 rounded border
|
|
87
|
+
<kbd className="absolute right-[0.3rem] top-[0.3rem] 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">
|
|
41
88
|
⌘K
|
|
42
89
|
</kbd>
|
|
43
90
|
</button>*/}
|
|
44
91
|
</div>
|
|
45
92
|
|
|
46
|
-
<div className="items-center justify-self-end text-sm hidden lg:flex">
|
|
47
|
-
{isAuthEnabled && (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
>
|
|
55
|
-
|
|
56
|
-
</
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
93
|
+
<div className="items-center justify-self-end text-sm hidden lg:flex gap-2">
|
|
94
|
+
{isAuthEnabled && !isAuthenticated ? (
|
|
95
|
+
<Button variant="ghost" asChild>
|
|
96
|
+
<Link to="/login">Login</Link>
|
|
97
|
+
</Button>
|
|
98
|
+
) : (
|
|
99
|
+
<DropdownMenu>
|
|
100
|
+
<DropdownMenuTrigger asChild>
|
|
101
|
+
<Button variant="ghost">
|
|
102
|
+
{profile?.email ? `${profile.email}` : "My Account"}
|
|
103
|
+
</Button>
|
|
104
|
+
</DropdownMenuTrigger>
|
|
105
|
+
<DropdownMenuContent className="w-56">
|
|
106
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
107
|
+
<DropdownMenuSeparator />
|
|
108
|
+
{plugins
|
|
109
|
+
.filter((p) => isProfileMenuPlugin(p))
|
|
110
|
+
.flatMap((p) => p.getProfileMenuItems(context))
|
|
111
|
+
.map((i) => (
|
|
112
|
+
<RecursiveMenu key={i.label} item={i} />
|
|
113
|
+
))}
|
|
114
|
+
</DropdownMenuContent>
|
|
115
|
+
</DropdownMenu>
|
|
67
116
|
)}
|
|
117
|
+
|
|
68
118
|
<button
|
|
69
119
|
type="button"
|
|
70
120
|
aria-label={
|
|
@@ -11,7 +11,7 @@ export const InlineCode = ({
|
|
|
11
11
|
<code
|
|
12
12
|
className={cn(
|
|
13
13
|
className,
|
|
14
|
-
"font-mono border
|
|
14
|
+
"font-mono border p-1 py-0.5 rounded bg-border/50 dark:bg-border/70 whitespace-nowrap",
|
|
15
15
|
)}
|
|
16
16
|
>
|
|
17
17
|
{children}
|
|
@@ -12,7 +12,7 @@ export const TopNavigation = () => {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
return (
|
|
15
|
-
<nav className="border-b
|
|
15
|
+
<nav className="border-b text-sm px-12 h-[--top-nav-height]">
|
|
16
16
|
<ul className="flex flex-row items-center gap-8">
|
|
17
17
|
{navigation.map((item) => (
|
|
18
18
|
<li key={item.label}>
|
|
@@ -2,6 +2,7 @@ import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
|
|
|
2
2
|
import { createContext, useContext } from "react";
|
|
3
3
|
import { matchPath, useLocation } from "react-router-dom";
|
|
4
4
|
import { DevPortalContext } from "../../core/DevPortalContext.js";
|
|
5
|
+
import { traverseNavigation } from "../../util/traverseNavigation.js";
|
|
5
6
|
|
|
6
7
|
const DevPortalReactContext = createContext<DevPortalContext | undefined>(
|
|
7
8
|
undefined,
|
|
@@ -31,8 +32,27 @@ export const useTopNavigationItem = () => {
|
|
|
31
32
|
const { navigation } = useDevPortal();
|
|
32
33
|
const location = useLocation();
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
// The `/` path needs this logic because it would always match:
|
|
36
|
+
// Only if a leaf node actually matches the current path it is active
|
|
37
|
+
for (const item of navigation) {
|
|
38
|
+
const foundNavItem = traverseNavigation(item, (_node, fullPath) => {
|
|
39
|
+
if (location.pathname === fullPath) {
|
|
40
|
+
return item;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (foundNavItem) {
|
|
45
|
+
return foundNavItem;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (location.pathname === "/") {
|
|
49
|
+
return navigation.find((item) => item.path === "/");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return navigation.find(
|
|
53
|
+
(item) =>
|
|
54
|
+
item.path !== "/" &&
|
|
55
|
+
matchPath({ path: item.path, end: false }, location.pathname),
|
|
36
56
|
);
|
|
37
57
|
};
|
|
38
58
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { useMDXComponents as useMDXComponentsImport } from "@mdx-js/react";
|
|
2
|
+
import { Helmet } from "@zudoku/react-helmet-async";
|
|
2
3
|
import { Link as LinkImport } from "react-router-dom";
|
|
3
4
|
import { RouterError as RouterErrorImport } from "../errors/RouterError.js";
|
|
4
5
|
import { ServerError as ServerErrorImport } from "../errors/ServerError.js";
|
|
6
|
+
import { Button as ButtonImport } from "../ui/Button.js";
|
|
5
7
|
import { Callout as CalloutImport } from "../ui/Callout.js";
|
|
6
8
|
import {
|
|
7
9
|
Bootstrap as BootstrapImport,
|
|
@@ -19,3 +21,5 @@ export const RouterError = /*@__PURE__*/ RouterErrorImport;
|
|
|
19
21
|
export const ServerError = /*@__PURE__*/ ServerErrorImport;
|
|
20
22
|
export const Bootstrap = /*@__PURE__*/ BootstrapImport;
|
|
21
23
|
export const BootstrapStatic = /*@__PURE__*/ BootstrapStaticImport;
|
|
24
|
+
export const Button = /*@__PURE__*/ ButtonImport;
|
|
25
|
+
export const Head = /*@__PURE__*/ Helmet;
|
|
@@ -114,7 +114,7 @@ export const SideNavigationItem = ({
|
|
|
114
114
|
{linkContent}
|
|
115
115
|
</Collapsible.Trigger>
|
|
116
116
|
<Collapsible.Content className="CollapsibleContent ms-[calc(var(--padding-nav-item)*1.125)]">
|
|
117
|
-
<ul className="mt-1 border-
|
|
117
|
+
<ul className="mt-1 border-l ps-1.5">
|
|
118
118
|
{item.children.map((child) => (
|
|
119
119
|
<SideNavigationItem
|
|
120
120
|
key={isPathItem(child) ? child.path : child.href}
|
|
@@ -95,7 +95,7 @@ export type ZudokuContextOptions = {
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
export class DevPortalContext {
|
|
98
|
-
|
|
98
|
+
public plugins: NonNullable<ZudokuContextOptions["plugins"]> = [];
|
|
99
99
|
public navigation: ZudokuContextOptions["navigation"];
|
|
100
100
|
public meta: ZudokuContextOptions["metadata"];
|
|
101
101
|
public page: ZudokuContextOptions["page"];
|
package/src/lib/core/plugins.ts
CHANGED
|
@@ -12,6 +12,7 @@ export type PluginNavigationCategory = {
|
|
|
12
12
|
|
|
13
13
|
export type DevPortalPlugin =
|
|
14
14
|
| CommonPlugin
|
|
15
|
+
| ProfileMenuPlugin
|
|
15
16
|
| NavigationPlugin
|
|
16
17
|
| ApiIdentityPlugin;
|
|
17
18
|
|
|
@@ -24,11 +25,26 @@ export interface ApiIdentityPlugin {
|
|
|
24
25
|
getIdentities: (context: DevPortalContext) => Promise<ApiIdentity[]>;
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
export interface ProfileMenuPlugin {
|
|
29
|
+
getProfileMenuItems: (context: DevPortalContext) => NavigationItem[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type NavigationItem = {
|
|
33
|
+
label: string;
|
|
34
|
+
path?: string;
|
|
35
|
+
children?: NavigationItem[];
|
|
36
|
+
};
|
|
37
|
+
|
|
27
38
|
export interface CommonPlugin {
|
|
28
39
|
initialize?: (context: DevPortalContext) => Promise<void> | void;
|
|
29
40
|
getHead?: () => ReactElement | undefined;
|
|
30
41
|
}
|
|
31
42
|
|
|
43
|
+
export const isProfileMenuPlugin = (
|
|
44
|
+
obj: DevPortalPlugin,
|
|
45
|
+
): obj is ProfileMenuPlugin =>
|
|
46
|
+
"getProfileMenuItems" in obj && typeof obj.getProfileMenuItems === "function";
|
|
47
|
+
|
|
32
48
|
export const isNavigationPlugin = (
|
|
33
49
|
obj: DevPortalPlugin,
|
|
34
50
|
): obj is NavigationPlugin =>
|
|
@@ -47,7 +47,7 @@ export const CreateApiKey = ({ service }: { service: ApiKeyService }) => {
|
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
49
|
<div className="max-w-screen-lg pt-[--padding-content-top] pb-[--padding-content-bottom]">
|
|
50
|
-
<div className="flex justify-between mb-4 border-b
|
|
50
|
+
<div className="flex justify-between mb-4 border-b pb-1">
|
|
51
51
|
<h1 className="font-medium text-2xl">New API Key</h1>
|
|
52
52
|
</div>
|
|
53
53
|
<form
|