zudoku 0.3.1-dev.2 → 0.3.1-dev.21
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/LICENSE.md +21 -0
- package/dist/config/config.d.ts +0 -3
- package/dist/lib/authentication/authentication.d.ts +1 -0
- package/dist/lib/authentication/components/CallbackHandler.d.ts +3 -0
- package/dist/lib/authentication/components/CallbackHandler.js +34 -0
- package/dist/lib/authentication/components/CallbackHandler.js.map +1 -0
- package/dist/lib/authentication/providers/auth0.js +11 -6
- package/dist/lib/authentication/providers/auth0.js.map +1 -1
- package/dist/lib/authentication/providers/openid.d.ts +4 -13
- package/dist/lib/authentication/providers/openid.js +50 -49
- package/dist/lib/authentication/providers/openid.js.map +1 -1
- package/dist/lib/components/Header.js +2 -4
- package/dist/lib/components/Header.js.map +1 -1
- package/dist/lib/components/Layout.js +5 -1
- package/dist/lib/components/Layout.js.map +1 -1
- package/dist/lib/components/context/ZudokuProvider.js +1 -3
- package/dist/lib/components/context/ZudokuProvider.js.map +1 -1
- package/dist/lib/core/DevPortalContext.d.ts +1 -4
- package/dist/lib/core/DevPortalContext.js +2 -2
- package/dist/lib/core/DevPortalContext.js.map +1 -1
- package/dist/lib/core/plugins.d.ts +2 -4
- package/dist/lib/core/plugins.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationList.js +3 -4
- package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationListItem.js +2 -3
- package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/ParameterListItem.js +1 -1
- package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js +2 -2
- package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js.map +1 -1
- package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroup.d.ts +9 -0
- package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroup.js +14 -0
- package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroup.js.map +1 -0
- package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupConnector.d.ts +6 -0
- package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupConnector.js +17 -0
- package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupConnector.js.map +1 -0
- package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupItem.d.ts +7 -0
- package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupItem.js +10 -0
- package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupItem.js.map +1 -0
- package/dist/lib/plugins/openapi/schema/SchemaComponents.d.ts +3 -3
- package/dist/lib/plugins/openapi/schema/SchemaComponents.js +15 -15
- package/dist/lib/plugins/openapi/schema/SchemaComponents.js.map +1 -1
- package/dist/lib/plugins/openapi/schema/SchemaView.js +1 -1
- package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -1
- package/dist/lib/plugins/openapi/schema/utils.d.ts +6 -0
- package/dist/lib/plugins/openapi/schema/utils.js +5 -0
- package/dist/lib/plugins/openapi/schema/utils.js.map +1 -1
- package/lib/{AuthenticationPlugin-CH5NSVOu.js → AuthenticationPlugin-owbEUimP.js} +3 -3
- package/lib/{AuthenticationPlugin-CH5NSVOu.js.map → AuthenticationPlugin-owbEUimP.js.map} +1 -1
- package/lib/{CategoryHeading-z15xh7Jb.js → CategoryHeading-DnPprxtD.js} +2 -2
- package/lib/{CategoryHeading-z15xh7Jb.js.map → CategoryHeading-DnPprxtD.js.map} +1 -1
- package/lib/{Combination-DTfV-c98.js → Combination-DruV0zX_.js} +3 -3
- package/lib/{Combination-DTfV-c98.js.map → Combination-DruV0zX_.js.map} +1 -1
- package/lib/ErrorPage-PUg985n_.js +18 -0
- package/lib/ErrorPage-PUg985n_.js.map +1 -0
- package/lib/{Input-DB9VROFR.js → Input-CBfi9Yjc.js} +5 -4
- package/lib/{Input-DB9VROFR.js.map → Input-CBfi9Yjc.js.map} +1 -1
- package/lib/{Markdown-CEccPMI_.js → Markdown-Chb9VIBv.js} +2 -2
- package/lib/{Markdown-CEccPMI_.js.map → Markdown-Chb9VIBv.js.map} +1 -1
- package/lib/{MdxPage-CnqOoqvp.js → MdxPage-CIBHMwTd.js} +5 -5
- package/lib/{MdxPage-CnqOoqvp.js.map → MdxPage-CIBHMwTd.js.map} +1 -1
- package/lib/OperationList-CZiSz5JH.js +590 -0
- package/lib/OperationList-CZiSz5JH.js.map +1 -0
- package/lib/{Route-DfAFiR7v.js → Route-Cle-r-bq.js} +4 -4
- package/lib/{Route-DfAFiR7v.js.map → Route-Cle-r-bq.js.map} +1 -1
- package/lib/{Spinner-BT_AYFrA.js → SidebarBadge-Ba0PhibA.js} +66 -76
- package/lib/SidebarBadge-Ba0PhibA.js.map +1 -0
- package/lib/{SlotletProvider-ByLSCZQa.js → SlotletProvider-Dq80og6-.js} +4 -4
- package/lib/{SlotletProvider-ByLSCZQa.js.map → SlotletProvider-Dq80og6-.js.map} +1 -1
- package/lib/Spinner-CvXZ7QK4.js +15 -0
- package/lib/Spinner-CvXZ7QK4.js.map +1 -0
- package/lib/{ZudokuContext-BIZ8zHbZ.js → ZudokuContext-BQ45UjcB.js} +2 -2
- package/lib/{ZudokuContext-BIZ8zHbZ.js.map → ZudokuContext-BQ45UjcB.js.map} +1 -1
- package/lib/{index-D-9zqIOh.js → index-Br1MQPxy.js} +595 -587
- package/lib/index-Br1MQPxy.js.map +1 -0
- package/lib/{index-Dz4LyXZI.js → index-DCJ9wEIV.js} +3 -3
- package/lib/{index-Dz4LyXZI.js.map → index-DCJ9wEIV.js.map} +1 -1
- package/lib/{index-7kcHaXD6.js → index-Yjb2PyPF.js} +4 -4
- package/lib/{index-7kcHaXD6.js.map → index-Yjb2PyPF.js.map} +1 -1
- package/lib/{urql-DrBfkb92.js → urql-YhcsXYy8.js} +2 -2
- package/lib/urql-YhcsXYy8.js.map +1 -0
- package/lib/{utils-Bh4upQ0e.js → utils-pDHePxa0.js} +3 -3
- package/lib/{utils-Bh4upQ0e.js.map → utils-pDHePxa0.js.map} +1 -1
- package/lib/zudoku.auth-auth0.js +24 -22
- package/lib/zudoku.auth-auth0.js.map +1 -1
- package/lib/zudoku.auth-clerk.js +1 -1
- package/lib/zudoku.auth-openid.js +527 -469
- package/lib/zudoku.auth-openid.js.map +1 -1
- package/lib/zudoku.components.js +434 -443
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.openapi-worker.js +1 -1
- package/lib/zudoku.plugin-api-keys.js +6 -6
- package/lib/zudoku.plugin-custom-page.js +1 -1
- package/lib/zudoku.plugin-markdown.js +1 -1
- package/lib/zudoku.plugin-openapi.js +8 -7
- package/lib/zudoku.plugin-openapi.js.map +1 -1
- package/package.json +80 -100
- package/src/app/main.css +26 -1
- package/src/lib/authentication/authentication.ts +1 -0
- package/src/lib/authentication/components/CallbackHandler.tsx +59 -0
- package/src/lib/authentication/providers/auth0.tsx +13 -7
- package/src/lib/authentication/providers/openid.tsx +56 -58
- package/src/lib/components/Header.tsx +3 -10
- package/src/lib/components/Layout.tsx +6 -1
- package/src/lib/components/context/ZudokuProvider.tsx +1 -4
- package/src/lib/core/DevPortalContext.ts +2 -7
- package/src/lib/core/plugins.ts +1 -2
- package/src/lib/plugins/openapi/OperationList.tsx +3 -7
- package/src/lib/plugins/openapi/OperationListItem.tsx +3 -4
- package/src/lib/plugins/openapi/ParameterListItem.tsx +1 -1
- package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +27 -5
- package/src/lib/plugins/openapi/schema/LogicalGroup/LogicalGroup.tsx +47 -0
- package/src/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupConnector.tsx +54 -0
- package/src/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupItem.tsx +30 -0
- package/src/lib/plugins/openapi/schema/SchemaComponents.tsx +41 -41
- package/src/lib/plugins/openapi/schema/SchemaView.tsx +4 -4
- package/src/lib/plugins/openapi/schema/utils.ts +8 -0
- package/dist/lib/plugins/openapi/util/prose.d.ts +0 -1
- package/dist/lib/plugins/openapi/util/prose.js +0 -4
- package/dist/lib/plugins/openapi/util/prose.js.map +0 -1
- package/lib/OperationList-Cxiw2Z-v.js +0 -457
- package/lib/OperationList-Cxiw2Z-v.js.map +0 -1
- package/lib/Spinner-BT_AYFrA.js.map +0 -1
- package/lib/index-D-9zqIOh.js.map +0 -1
- package/lib/urql-DrBfkb92.js.map +0 -1
- package/src/lib/plugins/openapi/util/prose.ts +0 -7
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import logger from "loglevel";
|
|
2
2
|
import * as oauth from "oauth4webapi";
|
|
3
|
-
import { NavigateFunction } from "react-router-dom";
|
|
4
3
|
import { OpenIDAuthenticationConfig } from "../../../config/config.js";
|
|
5
|
-
import { CommonPlugin } from "../../core/plugins.js";
|
|
6
4
|
import {
|
|
7
5
|
AuthenticationProvider,
|
|
8
6
|
AuthenticationProviderInitializer,
|
|
9
7
|
} from "../authentication.js";
|
|
10
8
|
import { AuthenticationPlugin } from "../AuthenticationPlugin.js";
|
|
9
|
+
import { CallbackHandler } from "../components/CallbackHandler.js";
|
|
11
10
|
import { AuthorizationError, OAuthAuthorizationError } from "../errors.js";
|
|
12
11
|
import { useAuthState, UserProfile } from "../state.js";
|
|
13
12
|
|
|
@@ -23,7 +22,7 @@ interface TokenState {
|
|
|
23
22
|
class OpenIdAuthPlugin extends AuthenticationPlugin {
|
|
24
23
|
constructor(
|
|
25
24
|
private callbackUrlPath: string,
|
|
26
|
-
|
|
25
|
+
private handleCallback: () => Promise<string>,
|
|
27
26
|
) {
|
|
28
27
|
super();
|
|
29
28
|
}
|
|
@@ -32,7 +31,7 @@ class OpenIdAuthPlugin extends AuthenticationPlugin {
|
|
|
32
31
|
...super.getRoutes(),
|
|
33
32
|
{
|
|
34
33
|
path: this.callbackUrlPath,
|
|
35
|
-
element: <
|
|
34
|
+
element: <CallbackHandler handleCallback={this.handleCallback} />,
|
|
36
35
|
},
|
|
37
36
|
];
|
|
38
37
|
}
|
|
@@ -41,11 +40,8 @@ class OpenIdAuthPlugin extends AuthenticationPlugin {
|
|
|
41
40
|
export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
42
41
|
protected client: oauth.Client;
|
|
43
42
|
protected issuer: string;
|
|
44
|
-
protected authorizationEndpoint: string | undefined;
|
|
45
|
-
protected tokenEndpoint: string | undefined;
|
|
46
43
|
|
|
47
44
|
protected authorizationServer: oauth.AuthorizationServer | undefined;
|
|
48
|
-
protected tokens: TokenState | undefined;
|
|
49
45
|
|
|
50
46
|
protected callbackUrlPath = "/oauth/callback";
|
|
51
47
|
protected logoutRedirectUrlPath = "/";
|
|
@@ -61,8 +57,6 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
61
57
|
constructor({
|
|
62
58
|
issuer,
|
|
63
59
|
audience,
|
|
64
|
-
authorizationEndpoint,
|
|
65
|
-
tokenEndpoint,
|
|
66
60
|
clientId,
|
|
67
61
|
redirectToAfterSignUp,
|
|
68
62
|
redirectToAfterSignIn,
|
|
@@ -74,8 +68,6 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
74
68
|
};
|
|
75
69
|
this.audience = audience;
|
|
76
70
|
this.issuer = issuer;
|
|
77
|
-
this.authorizationEndpoint = authorizationEndpoint;
|
|
78
|
-
this.tokenEndpoint = tokenEndpoint;
|
|
79
71
|
this.redirectToAfterSignUp = redirectToAfterSignUp ?? "/";
|
|
80
72
|
this.redirectToAfterSignIn = redirectToAfterSignIn ?? "/";
|
|
81
73
|
this.redirectToAfterSignOut = redirectToAfterSignOut ?? "/";
|
|
@@ -83,21 +75,12 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
83
75
|
|
|
84
76
|
protected async getAuthServer() {
|
|
85
77
|
if (!this.authorizationServer) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
};
|
|
93
|
-
} else {
|
|
94
|
-
const issuerUrl = new URL(this.issuer);
|
|
95
|
-
const response = await oauth.discoveryRequest(issuerUrl);
|
|
96
|
-
this.authorizationServer = await oauth.processDiscoveryResponse(
|
|
97
|
-
issuerUrl,
|
|
98
|
-
response,
|
|
99
|
-
);
|
|
100
|
-
}
|
|
78
|
+
const issuerUrl = new URL(this.issuer);
|
|
79
|
+
const response = await oauth.discoveryRequest(issuerUrl);
|
|
80
|
+
this.authorizationServer = await oauth.processDiscoveryResponse(
|
|
81
|
+
issuerUrl,
|
|
82
|
+
response,
|
|
83
|
+
);
|
|
101
84
|
}
|
|
102
85
|
return this.authorizationServer;
|
|
103
86
|
}
|
|
@@ -118,13 +101,13 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
118
101
|
throw new AuthorizationError("No expires_in in response");
|
|
119
102
|
}
|
|
120
103
|
|
|
121
|
-
|
|
104
|
+
const tokens: TokenState = {
|
|
122
105
|
accessToken: response.access_token,
|
|
123
106
|
refreshToken: response.refresh_token,
|
|
124
107
|
expiresOn: new Date(Date.now() + response.expires_in * 1000),
|
|
125
108
|
tokenType: response.token_type,
|
|
126
109
|
};
|
|
127
|
-
sessionStorage.setItem("
|
|
110
|
+
sessionStorage.setItem("token-state", JSON.stringify(tokens));
|
|
128
111
|
}
|
|
129
112
|
|
|
130
113
|
async signUp({ redirectTo }: { redirectTo?: string } = {}) {
|
|
@@ -178,7 +161,7 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
178
161
|
authorizationUrl.searchParams.set("client_id", this.client.client_id);
|
|
179
162
|
authorizationUrl.searchParams.set("redirect_uri", redirectUrl.toString());
|
|
180
163
|
authorizationUrl.searchParams.set("response_type", "code");
|
|
181
|
-
authorizationUrl.searchParams.set("scope", "openid
|
|
164
|
+
authorizationUrl.searchParams.set("scope", "openid profile email");
|
|
182
165
|
authorizationUrl.searchParams.set("code_challenge", codeChallenge);
|
|
183
166
|
authorizationUrl.searchParams.set(
|
|
184
167
|
"code_challenge_method",
|
|
@@ -211,11 +194,14 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
211
194
|
|
|
212
195
|
async getAccessToken(): Promise<string> {
|
|
213
196
|
const as = await this.getAuthServer();
|
|
214
|
-
|
|
197
|
+
const tokenState = sessionStorage.getItem("token-state");
|
|
198
|
+
if (!tokenState) {
|
|
215
199
|
throw new AuthorizationError("User is not authenticated");
|
|
216
200
|
}
|
|
217
|
-
|
|
218
|
-
|
|
201
|
+
|
|
202
|
+
const state = JSON.parse(tokenState) as TokenState;
|
|
203
|
+
if (state.expiresOn < new Date()) {
|
|
204
|
+
if (!state.refreshToken) {
|
|
219
205
|
await this.signIn();
|
|
220
206
|
return "";
|
|
221
207
|
}
|
|
@@ -223,7 +209,7 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
223
209
|
const request = await oauth.refreshTokenGrantRequest(
|
|
224
210
|
as,
|
|
225
211
|
this.client,
|
|
226
|
-
|
|
212
|
+
state.refreshToken,
|
|
227
213
|
);
|
|
228
214
|
const response = await oauth.processRefreshTokenResponse(
|
|
229
215
|
as,
|
|
@@ -231,10 +217,16 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
231
217
|
request,
|
|
232
218
|
);
|
|
233
219
|
|
|
220
|
+
if (!response.access_token) {
|
|
221
|
+
throw new AuthorizationError("No access token in response");
|
|
222
|
+
}
|
|
223
|
+
|
|
234
224
|
this.setTokensFromResponse(response);
|
|
235
|
-
}
|
|
236
225
|
|
|
237
|
-
|
|
226
|
+
return response.access_token.toString();
|
|
227
|
+
} else {
|
|
228
|
+
return state.accessToken;
|
|
229
|
+
}
|
|
238
230
|
}
|
|
239
231
|
|
|
240
232
|
signOut = async () => {
|
|
@@ -243,7 +235,7 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
243
235
|
isPending: false,
|
|
244
236
|
profile: undefined,
|
|
245
237
|
});
|
|
246
|
-
|
|
238
|
+
sessionStorage.clear();
|
|
247
239
|
|
|
248
240
|
const as = await this.getAuthServer();
|
|
249
241
|
|
|
@@ -279,9 +271,8 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
279
271
|
// Authorization Code Grant Request & Response
|
|
280
272
|
const codeVerifier = sessionStorage.getItem(CODE_VERIFIER_KEY);
|
|
281
273
|
sessionStorage.removeItem(CODE_VERIFIER_KEY);
|
|
282
|
-
|
|
283
274
|
if (!codeVerifier) {
|
|
284
|
-
|
|
275
|
+
throw new AuthorizationError("No code verifier found in state.");
|
|
285
276
|
}
|
|
286
277
|
|
|
287
278
|
const authServer = await this.getAuthServer();
|
|
@@ -320,7 +311,7 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
320
311
|
// }
|
|
321
312
|
// throw new Error(); // Handle WWW-Authenticate Challenges as needed
|
|
322
313
|
// }
|
|
323
|
-
const oauthResult = await oauth.
|
|
314
|
+
const oauthResult = await oauth.processAuthorizationCodeOpenIDResponse(
|
|
324
315
|
authServer,
|
|
325
316
|
this.client,
|
|
326
317
|
response,
|
|
@@ -351,29 +342,36 @@ export class OpenIDAuthenticationProvider implements AuthenticationProvider {
|
|
|
351
342
|
profile,
|
|
352
343
|
});
|
|
353
344
|
|
|
354
|
-
|
|
345
|
+
sessionStorage.setItem(
|
|
346
|
+
"profile-state",
|
|
347
|
+
JSON.stringify(useAuthState.getState().profile),
|
|
348
|
+
);
|
|
355
349
|
|
|
356
|
-
|
|
350
|
+
const redirectTo = sessionStorage.getItem("redirect-to") ?? "/";
|
|
351
|
+
sessionStorage.removeItem("redirect-to");
|
|
352
|
+
return redirectTo;
|
|
357
353
|
};
|
|
358
354
|
|
|
355
|
+
pageLoad(): void {
|
|
356
|
+
const profileState = sessionStorage.getItem("profile-state");
|
|
357
|
+
if (profileState) {
|
|
358
|
+
try {
|
|
359
|
+
const profile = JSON.parse(profileState);
|
|
360
|
+
useAuthState.setState({
|
|
361
|
+
isAuthenticated: true,
|
|
362
|
+
isPending: false,
|
|
363
|
+
profile,
|
|
364
|
+
});
|
|
365
|
+
} catch (err) {
|
|
366
|
+
logger.error("Error parsing auth state", err);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
359
371
|
getAuthenticationPlugin() {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (typeof window === "undefined") return;
|
|
364
|
-
|
|
365
|
-
if (localStorage.getItem("auto-login")) {
|
|
366
|
-
localStorage.removeItem("auto-login");
|
|
367
|
-
|
|
368
|
-
await this.authorize({ redirectTo: window.location.pathname });
|
|
369
|
-
} else if (window.location.pathname === "/oauth/callback") {
|
|
370
|
-
const redirect = await this.handleCallback();
|
|
371
|
-
if (redirect) {
|
|
372
|
-
options.navigate(redirect);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
},
|
|
376
|
-
);
|
|
372
|
+
// TODO: This API is a bit messy, we need to refactor auth plugins/providers
|
|
373
|
+
// to remove the extra layers of abstraction.
|
|
374
|
+
return new OpenIdAuthPlugin(this.callbackUrlPath, this.handleCallback);
|
|
377
375
|
}
|
|
378
376
|
}
|
|
379
377
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { MoonStarIcon, SunIcon } from "lucide-react";
|
|
2
2
|
import { memo } from "react";
|
|
3
|
-
|
|
4
3
|
import { Link, useLocation } from "react-router-dom";
|
|
5
4
|
import { useAuth } from "../authentication/hook.js";
|
|
6
5
|
import { isProfileMenuPlugin, ProfileNavigationItem } from "../core/plugins.js";
|
|
@@ -45,6 +44,7 @@ const RecursiveMenu = ({ item }: { item: ProfileNavigationItem }) => {
|
|
|
45
44
|
};
|
|
46
45
|
|
|
47
46
|
export const Header = memo(function HeaderInner() {
|
|
47
|
+
const auth = useAuth();
|
|
48
48
|
const [isDark, toggleTheme] = useTheme();
|
|
49
49
|
const { isAuthenticated, profile, isAuthEnabled } = useAuth();
|
|
50
50
|
const { pathname } = useLocation();
|
|
@@ -97,15 +97,8 @@ export const Header = memo(function HeaderInner() {
|
|
|
97
97
|
<div className="items-center justify-self-end text-sm hidden lg:flex gap-2">
|
|
98
98
|
<Slotlet name="head-navigation-start" />
|
|
99
99
|
{isAuthEnabled && !isAuthenticated ? (
|
|
100
|
-
<Button variant="ghost"
|
|
101
|
-
|
|
102
|
-
to={{
|
|
103
|
-
pathname: "/signin",
|
|
104
|
-
search: `?redirect=${encodeURIComponent(pathname)}`,
|
|
105
|
-
}}
|
|
106
|
-
>
|
|
107
|
-
Login
|
|
108
|
-
</Link>
|
|
100
|
+
<Button variant="ghost" onClick={() => auth.login()}>
|
|
101
|
+
Login
|
|
109
102
|
</Button>
|
|
110
103
|
) : (
|
|
111
104
|
accountItems.length > 0 && (
|
|
@@ -14,13 +14,18 @@ import { Spinner } from "./Spinner.js";
|
|
|
14
14
|
export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
15
15
|
const location = useLocation();
|
|
16
16
|
const { setActiveAnchor } = useViewportAnchor();
|
|
17
|
-
const { meta } = useZudoku();
|
|
17
|
+
const { meta, authentication } = useZudoku();
|
|
18
18
|
|
|
19
19
|
useScrollToAnchor();
|
|
20
20
|
useScrollToTop();
|
|
21
21
|
|
|
22
22
|
const previousLocationPath = useRef(location.pathname);
|
|
23
23
|
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
// Initialize the authentication plugin
|
|
26
|
+
authentication?.pageLoad ? authentication.pageLoad() : null;
|
|
27
|
+
}, [authentication]);
|
|
28
|
+
|
|
24
29
|
useEffect(() => {
|
|
25
30
|
// always reset on location change
|
|
26
31
|
if (location.pathname !== previousLocationPath.current) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
2
2
|
import type { PropsWithChildren } from "react";
|
|
3
|
-
import { useNavigate } from "react-router-dom";
|
|
4
3
|
import { DevPortalContext } from "../../core/DevPortalContext.js";
|
|
5
4
|
import { ZudokuReactContext } from "./ZudokuContext.js";
|
|
6
5
|
|
|
@@ -8,11 +7,9 @@ export const ZudokuProvider = ({
|
|
|
8
7
|
children,
|
|
9
8
|
context,
|
|
10
9
|
}: PropsWithChildren<{ context: DevPortalContext }>) => {
|
|
11
|
-
const navigate = useNavigate();
|
|
12
|
-
|
|
13
10
|
useSuspenseQuery({
|
|
14
11
|
queryFn: async () => {
|
|
15
|
-
await context.initialize(
|
|
12
|
+
await context.initialize();
|
|
16
13
|
return true;
|
|
17
14
|
},
|
|
18
15
|
queryKey: ["zudoku-initialize"],
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { QueryClient } from "@tanstack/react-query";
|
|
2
|
-
import { NavigateFunction } from "react-router-dom";
|
|
3
2
|
import type { SidebarConfig } from "../../config/validators/SidebarSchema.js";
|
|
4
3
|
import { type AuthenticationProvider } from "../authentication/authentication.js";
|
|
5
4
|
import type { ComponentsContextType } from "../components/context/ComponentsContext.js";
|
|
@@ -84,15 +83,11 @@ export class DevPortalContext {
|
|
|
84
83
|
this.page = config.page;
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
initialize = async ({
|
|
88
|
-
navigate,
|
|
89
|
-
}: {
|
|
90
|
-
navigate: NavigateFunction;
|
|
91
|
-
}): Promise<void> => {
|
|
86
|
+
initialize = async (): Promise<void> => {
|
|
92
87
|
await Promise.all(
|
|
93
88
|
this.plugins
|
|
94
89
|
.filter(needsInitialization)
|
|
95
|
-
.map((plugin) => plugin.initialize?.(this
|
|
90
|
+
.map((plugin) => plugin.initialize?.(this)),
|
|
96
91
|
);
|
|
97
92
|
};
|
|
98
93
|
|
package/src/lib/core/plugins.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactElement } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { type RouteObject } from "react-router-dom";
|
|
3
3
|
import type { Sidebar } from "../../config/validators/SidebarSchema.js";
|
|
4
4
|
import { MdxComponentsType } from "../util/MdxComponents.js";
|
|
5
5
|
import { DevPortalContext, type ApiIdentity } from "./DevPortalContext.js";
|
|
@@ -40,7 +40,6 @@ export type ProfileNavigationItem = {
|
|
|
40
40
|
export interface CommonPlugin {
|
|
41
41
|
initialize?: (
|
|
42
42
|
context: DevPortalContext,
|
|
43
|
-
options: { navigate: NavigateFunction },
|
|
44
43
|
) => Promise<void | boolean> | void | boolean;
|
|
45
44
|
getHead?: () => ReactElement | undefined;
|
|
46
45
|
getMdxComponents?: () => MdxComponentsType;
|
|
@@ -4,14 +4,13 @@ import { DeveloperHint } from "../../components/DeveloperHint.js";
|
|
|
4
4
|
import { ErrorPage } from "../../components/ErrorPage.js";
|
|
5
5
|
import { Heading } from "../../components/Heading.js";
|
|
6
6
|
import { InlineCode } from "../../components/InlineCode.js";
|
|
7
|
-
import { Markdown } from "../../components/Markdown.js";
|
|
7
|
+
import { Markdown, ProseClasses } from "../../components/Markdown.js";
|
|
8
8
|
import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
|
|
9
9
|
import { cn } from "../../util/cn.js";
|
|
10
10
|
import { OperationListItem } from "./OperationListItem.js";
|
|
11
11
|
import StaggeredRender from "./StaggeredRender.js";
|
|
12
12
|
import { useOasConfig } from "./context.js";
|
|
13
13
|
import { graphql } from "./graphql/index.js";
|
|
14
|
-
import { SchemaProseClasses } from "./util/prose.js";
|
|
15
14
|
import { useQuery } from "./util/urql.js";
|
|
16
15
|
|
|
17
16
|
export const OperationsFragment = graphql(/* GraphQL */ `
|
|
@@ -124,10 +123,7 @@ export const OperationList = () => {
|
|
|
124
123
|
return (
|
|
125
124
|
<div className="pt-[--padding-content-top]">
|
|
126
125
|
<div
|
|
127
|
-
className={cn(
|
|
128
|
-
SchemaProseClasses,
|
|
129
|
-
"mb-16 max-w-full prose-img:max-w-prose",
|
|
130
|
-
)}
|
|
126
|
+
className={cn(ProseClasses, "mb-16 max-w-full prose-img:max-w-prose")}
|
|
131
127
|
>
|
|
132
128
|
<CategoryHeading>Overview</CategoryHeading>
|
|
133
129
|
<Heading level={1} id="description" registerSidebarAnchor>
|
|
@@ -142,7 +138,7 @@ export const OperationList = () => {
|
|
|
142
138
|
{tag.name && <CategoryHeading>{tag.name}</CategoryHeading>}
|
|
143
139
|
{tag.description && (
|
|
144
140
|
<Markdown
|
|
145
|
-
className={`${
|
|
141
|
+
className={`${ProseClasses} max-w-full prose-img:max-w-prose w-full mt-2 mb-12`}
|
|
146
142
|
content={tag.description}
|
|
147
143
|
/>
|
|
148
144
|
)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import { Heading } from "../../components/Heading.js";
|
|
3
|
-
import { Markdown } from "../../components/Markdown.js";
|
|
3
|
+
import { Markdown, ProseClasses } from "../../components/Markdown.js";
|
|
4
4
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/Tabs.js";
|
|
5
5
|
import { groupBy } from "../../util/groupBy.js";
|
|
6
6
|
import { renderIf } from "../../util/renderIf.js";
|
|
@@ -9,7 +9,6 @@ import { ParameterList } from "./ParameterList.js";
|
|
|
9
9
|
import { Sidecar } from "./Sidecar.js";
|
|
10
10
|
import { FragmentType, useFragment } from "./graphql/index.js";
|
|
11
11
|
import { SchemaView } from "./schema/SchemaView.js";
|
|
12
|
-
import { SchemaProseClasses } from "./util/prose.js";
|
|
13
12
|
|
|
14
13
|
export const PARAM_GROUPS = ["path", "query", "header", "cookie"] as const;
|
|
15
14
|
export type ParameterGroup = (typeof PARAM_GROUPS)[number];
|
|
@@ -39,7 +38,7 @@ export const OperationListItem = ({
|
|
|
39
38
|
</Heading>
|
|
40
39
|
{operation.description && (
|
|
41
40
|
<Markdown
|
|
42
|
-
className={
|
|
41
|
+
className={`${ProseClasses} max-w-full prose-img:max-w-prose`}
|
|
43
42
|
content={operation.description}
|
|
44
43
|
/>
|
|
45
44
|
)}
|
|
@@ -89,7 +88,7 @@ export const OperationListItem = ({
|
|
|
89
88
|
))}
|
|
90
89
|
</TabsList>
|
|
91
90
|
)}
|
|
92
|
-
<ul className="list-none m-0 px-0
|
|
91
|
+
<ul className="list-none m-0 px-0">
|
|
93
92
|
{operation.responses.map((response) => (
|
|
94
93
|
<TabsContent
|
|
95
94
|
value={response.statusCode}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
|
2
|
-
import { CirclePlayIcon } from "lucide-react";
|
|
3
2
|
import { type PropsWithChildren, useState } from "react";
|
|
4
3
|
import {
|
|
5
4
|
Dialog,
|
|
@@ -11,16 +10,39 @@ import { Playground, type PlaygroundContentProps } from "./Playground.js";
|
|
|
11
10
|
|
|
12
11
|
export type PlaygroundDialogProps = PropsWithChildren<PlaygroundContentProps>;
|
|
13
12
|
|
|
13
|
+
const HeroPlayIcon = ({
|
|
14
|
+
className,
|
|
15
|
+
size = 16,
|
|
16
|
+
}: {
|
|
17
|
+
className?: string;
|
|
18
|
+
size?: number;
|
|
19
|
+
}) => (
|
|
20
|
+
<svg
|
|
21
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
22
|
+
viewBox="0 0 24 24"
|
|
23
|
+
fill="currentColor"
|
|
24
|
+
className={className}
|
|
25
|
+
width={size}
|
|
26
|
+
height={size}
|
|
27
|
+
>
|
|
28
|
+
<path
|
|
29
|
+
fillRule="evenodd"
|
|
30
|
+
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm14.024-.983a1.125 1.125 0 0 1 0 1.966l-5.603 3.113A1.125 1.125 0 0 1 9 15.113V8.887c0-.857.921-1.4 1.671-.983l5.603 3.113Z"
|
|
31
|
+
clipRule="evenodd"
|
|
32
|
+
/>
|
|
33
|
+
</svg>
|
|
34
|
+
);
|
|
35
|
+
|
|
14
36
|
const PlaygroundDialog = (props: PlaygroundDialogProps) => {
|
|
15
37
|
const [open, setOpen] = useState(false);
|
|
16
38
|
return (
|
|
17
39
|
<Dialog onOpenChange={(open) => setOpen(open)}>
|
|
18
40
|
<DialogTrigger asChild>
|
|
19
41
|
{props.children ?? (
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
size={
|
|
23
|
-
|
|
42
|
+
<button className="flex gap-1 items-center px-2 py-1 rounded-md bg-primary/80 hover:bg-primary transition text-primary-foreground text-xs">
|
|
43
|
+
Test
|
|
44
|
+
<HeroPlayIcon className="" size={14} />
|
|
45
|
+
</button>
|
|
24
46
|
)}
|
|
25
47
|
</DialogTrigger>
|
|
26
48
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as Collapsible from "@radix-ui/react-collapsible";
|
|
2
|
+
import { SquareMinusIcon, SquarePlusIcon } from "lucide-react";
|
|
3
|
+
import type { SchemaObject } from "../../../../oas/parser/index.js";
|
|
4
|
+
import { Card } from "../../../../ui/Card.js";
|
|
5
|
+
import type { LogicalGroupType } from "../utils.js";
|
|
6
|
+
import { LogicalGroupItem } from "./LogicalGroupItem.js";
|
|
7
|
+
|
|
8
|
+
const typeLabel = {
|
|
9
|
+
AND: "All of",
|
|
10
|
+
OR: "Any of",
|
|
11
|
+
ONE: "One of",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const LogicalGroup = ({
|
|
15
|
+
schemas,
|
|
16
|
+
type,
|
|
17
|
+
isOpen,
|
|
18
|
+
level,
|
|
19
|
+
toggleOpen,
|
|
20
|
+
}: {
|
|
21
|
+
schemas: SchemaObject[];
|
|
22
|
+
type: LogicalGroupType;
|
|
23
|
+
isOpen: boolean;
|
|
24
|
+
toggleOpen: () => void;
|
|
25
|
+
level: number;
|
|
26
|
+
}) => (
|
|
27
|
+
<Collapsible.Root open={isOpen} onOpenChange={toggleOpen} asChild>
|
|
28
|
+
<Card className="px-6">
|
|
29
|
+
<Collapsible.Trigger className="flex gap-2 items-center py-2 w-full text-sm text-muted-foreground -translate-x-1.5">
|
|
30
|
+
{isOpen ? <SquareMinusIcon size={14} /> : <SquarePlusIcon size={14} />}
|
|
31
|
+
<span>{typeLabel[type]}</span>
|
|
32
|
+
</Collapsible.Trigger>
|
|
33
|
+
|
|
34
|
+
<Collapsible.Content className="pb-4">
|
|
35
|
+
{schemas.map((subSchema, index) => (
|
|
36
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
37
|
+
<LogicalGroupItem
|
|
38
|
+
key={index}
|
|
39
|
+
type={type}
|
|
40
|
+
schema={subSchema}
|
|
41
|
+
level={level}
|
|
42
|
+
/>
|
|
43
|
+
))}
|
|
44
|
+
</Collapsible.Content>
|
|
45
|
+
</Card>
|
|
46
|
+
</Collapsible.Root>
|
|
47
|
+
);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChevronDownIcon,
|
|
3
|
+
CircleDotIcon,
|
|
4
|
+
CircleFadingPlusIcon,
|
|
5
|
+
CircleIcon,
|
|
6
|
+
} from "lucide-react";
|
|
7
|
+
import { cn } from "../../../../util/cn.js";
|
|
8
|
+
|
|
9
|
+
import type { LogicalGroupType } from "../utils.js";
|
|
10
|
+
|
|
11
|
+
const iconMap = {
|
|
12
|
+
AND: <CircleFadingPlusIcon size={16} className="fill-card" />,
|
|
13
|
+
OR: <CircleDotIcon size={16} className="fill-card" />,
|
|
14
|
+
ONE: <CircleIcon size={14} className="fill-card" />,
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
const colorClass = {
|
|
18
|
+
AND: "text-green-500 dark:text-green-300/60",
|
|
19
|
+
OR: "text-blue-400 dark:text-blue-500",
|
|
20
|
+
ONE: "text-purple-500 dark:text-purple-300/60",
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
23
|
+
export const LogicalGroupConnector = ({
|
|
24
|
+
type,
|
|
25
|
+
isOpen,
|
|
26
|
+
className,
|
|
27
|
+
}: {
|
|
28
|
+
type: LogicalGroupType;
|
|
29
|
+
isOpen: boolean;
|
|
30
|
+
className?: string;
|
|
31
|
+
}) => {
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
className={cn(
|
|
35
|
+
colorClass[type],
|
|
36
|
+
"relative text-sm flex py-2",
|
|
37
|
+
"before:border-l before:absolute before:-top-2 before:-bottom-2 before:border-border before:border-dashed before:content-['']",
|
|
38
|
+
className,
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
41
|
+
<div className="-translate-x-[7px] flex gap-1 items-center">
|
|
42
|
+
{iconMap[type]}
|
|
43
|
+
<div
|
|
44
|
+
className={cn(
|
|
45
|
+
"translate-y-px mx-px opacity-0 group-hover:opacity-100 transition",
|
|
46
|
+
!isOpen && "-rotate-90",
|
|
47
|
+
)}
|
|
48
|
+
>
|
|
49
|
+
<ChevronDownIcon size={16} />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as Collapsible from "@radix-ui/react-collapsible";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import type { SchemaObject } from "../../../../oas/parser/index.js";
|
|
4
|
+
import { SchemaView } from "../SchemaView.js";
|
|
5
|
+
import type { LogicalGroupType } from "../utils.js";
|
|
6
|
+
import { LogicalGroupConnector } from "./LogicalGroupConnector.js";
|
|
7
|
+
|
|
8
|
+
export const LogicalGroupItem = (props: {
|
|
9
|
+
type: LogicalGroupType;
|
|
10
|
+
schema: SchemaObject;
|
|
11
|
+
level: number;
|
|
12
|
+
}) => {
|
|
13
|
+
const [isOpen, setIsOpen] = useState(true);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Collapsible.Root
|
|
17
|
+
open={isOpen}
|
|
18
|
+
onOpenChange={() => setIsOpen((prev) => !prev)}
|
|
19
|
+
className="group"
|
|
20
|
+
>
|
|
21
|
+
<Collapsible.Trigger>
|
|
22
|
+
<LogicalGroupConnector type={props.type} isOpen={isOpen} />
|
|
23
|
+
</Collapsible.Trigger>
|
|
24
|
+
{!isOpen && <div className="wavy-line bg-border translate-y-1" />}
|
|
25
|
+
<Collapsible.Content>
|
|
26
|
+
<SchemaView schema={props.schema} level={props.level + 1} />
|
|
27
|
+
</Collapsible.Content>
|
|
28
|
+
</Collapsible.Root>
|
|
29
|
+
);
|
|
30
|
+
};
|