@mehdad67/apitogo 0.1.26 → 0.1.28

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 (108) hide show
  1. package/dist/cli/cli.js +51 -36
  2. package/dist/declarations/config/config.d.ts +3 -0
  3. package/dist/declarations/config/validators/ZudokuConfig.d.ts +12 -0
  4. package/dist/declarations/lib/authentication/components/ProductionUnlockPage.d.ts +1 -0
  5. package/dist/declarations/lib/authentication/hook.d.ts +2 -0
  6. package/dist/declarations/lib/authentication/providers/dev-portal-constants.d.ts +16 -0
  7. package/dist/declarations/lib/authentication/providers/dev-portal-utils.d.ts +9 -0
  8. package/dist/declarations/lib/authentication/providers/dev-portal.d.ts +36 -0
  9. package/dist/declarations/lib/authentication/state.d.ts +3 -0
  10. package/dist/declarations/lib/ui/ActionButton.d.ts +1 -1
  11. package/dist/declarations/lib/ui/Command.d.ts +1 -1
  12. package/dist/declarations/lib/util/MdxComponents.d.ts +1 -1
  13. package/dist/flat-config.d.ts +6 -0
  14. package/docs/configuration/authentication.md +32 -2
  15. package/package.json +6 -1
  16. package/src/app/main.tsx +2 -2
  17. package/src/config/config.ts +4 -0
  18. package/src/config/validators/ZudokuConfig.ts +7 -0
  19. package/src/lib/auth/issuer.ts +3 -0
  20. package/src/lib/authentication/components/CallbackHandler.tsx +1 -1
  21. package/src/lib/authentication/components/ProductionUnlockPage.tsx +27 -0
  22. package/src/lib/authentication/components/SignIn.tsx +2 -2
  23. package/src/lib/authentication/components/SignUp.tsx +2 -2
  24. package/src/lib/authentication/hook.ts +2 -0
  25. package/src/lib/authentication/providers/clerk.tsx +1 -1
  26. package/src/lib/authentication/providers/dev-portal-constants.ts +21 -0
  27. package/src/lib/authentication/providers/dev-portal-utils.ts +107 -0
  28. package/src/lib/authentication/providers/dev-portal.tsx +218 -0
  29. package/src/lib/authentication/state.ts +12 -0
  30. package/src/lib/authentication/ui/AuthCard.tsx +1 -1
  31. package/src/lib/authentication/ui/EmailLinkCallbackUi.tsx +5 -5
  32. package/src/lib/authentication/ui/EmailLinkSentUi.tsx +3 -3
  33. package/src/lib/authentication/ui/EmailLinkSignInUi.tsx +4 -4
  34. package/src/lib/authentication/ui/EmailVerificationUi.tsx +4 -4
  35. package/src/lib/authentication/ui/ZudokuAuthUi.tsx +6 -6
  36. package/src/lib/components/AiAssistantMenuItems.tsx +1 -1
  37. package/src/lib/components/Autocomplete.tsx +2 -2
  38. package/src/lib/components/DeveloperHint.tsx +1 -1
  39. package/src/lib/components/Header.tsx +4 -4
  40. package/src/lib/components/LandingLayout.tsx +1 -1
  41. package/src/lib/components/Layout.tsx +1 -1
  42. package/src/lib/components/Main.tsx +1 -1
  43. package/src/lib/components/Mermaid.tsx +1 -1
  44. package/src/lib/components/MobileTopNavigation.tsx +6 -5
  45. package/src/lib/components/MultiSelect.tsx +1 -1
  46. package/src/lib/components/TopNavigation.tsx +1 -1
  47. package/src/lib/components/context/ZudokuContext.ts +1 -1
  48. package/src/lib/components/navigation/NavigationCategory.tsx +1 -1
  49. package/src/lib/components/navigation/NavigationFilterInput.tsx +1 -1
  50. package/src/lib/components/navigation/NavigationItem.tsx +2 -2
  51. package/src/lib/core/RouteGuard.tsx +13 -3
  52. package/src/lib/errors/ErrorMessage.tsx +2 -2
  53. package/src/lib/plugins/api-catalog/Catalog.tsx +1 -1
  54. package/src/lib/plugins/api-keys/CreateApiKey.tsx +4 -4
  55. package/src/lib/plugins/api-keys/CreateApiKeyDialog.tsx +1 -1
  56. package/src/lib/plugins/api-keys/SettingsApiKeys.tsx +3 -3
  57. package/src/lib/plugins/api-keys/settings/ApiKeyItem.tsx +5 -5
  58. package/src/lib/plugins/api-keys/settings/RevealApiKey.tsx +3 -3
  59. package/src/lib/plugins/markdown/MdxPage.tsx +3 -3
  60. package/src/lib/plugins/openapi/ApiHeader.tsx +1 -1
  61. package/src/lib/plugins/openapi/CollapsibleCode.tsx +2 -2
  62. package/src/lib/plugins/openapi/DownloadSchemaButton.tsx +3 -3
  63. package/src/lib/plugins/openapi/GeneratedExampleSidecarBox.tsx +2 -2
  64. package/src/lib/plugins/openapi/OperationListItem.tsx +2 -2
  65. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +2 -2
  66. package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +4 -4
  67. package/src/lib/plugins/openapi/SchemaInfo.tsx +1 -1
  68. package/src/lib/plugins/openapi/SchemaList.tsx +2 -2
  69. package/src/lib/plugins/openapi/Sidecar.tsx +4 -4
  70. package/src/lib/plugins/openapi/SidecarExamples.tsx +2 -2
  71. package/src/lib/plugins/openapi/components/EnumValues.tsx +1 -1
  72. package/src/lib/plugins/openapi/components/ResponseContent.tsx +3 -3
  73. package/src/lib/plugins/openapi/playground/BodyPanel.tsx +4 -4
  74. package/src/lib/plugins/openapi/playground/CollapsibleHeader.tsx +1 -1
  75. package/src/lib/plugins/openapi/playground/ExamplesDropdown.tsx +2 -2
  76. package/src/lib/plugins/openapi/playground/Headers.tsx +3 -3
  77. package/src/lib/plugins/openapi/playground/IdentityDialog.tsx +5 -5
  78. package/src/lib/plugins/openapi/playground/IdentitySelector.tsx +2 -2
  79. package/src/lib/plugins/openapi/playground/ParamsGrid.tsx +2 -2
  80. package/src/lib/plugins/openapi/playground/Playground.tsx +4 -4
  81. package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +3 -3
  82. package/src/lib/plugins/openapi/playground/QueryParams.tsx +2 -2
  83. package/src/lib/plugins/openapi/playground/RequestLoginDialog.tsx +4 -4
  84. package/src/lib/plugins/openapi/playground/request-panel/MultipartField.tsx +2 -2
  85. package/src/lib/plugins/openapi/playground/result-panel/AudioPlayer.tsx +1 -1
  86. package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +5 -5
  87. package/src/lib/plugins/openapi/playground/result-panel/ResultPanel.tsx +2 -2
  88. package/src/lib/plugins/openapi/schema/SchemaPropertyItem.tsx +1 -1
  89. package/src/lib/plugins/openapi/schema/SchemaView.tsx +2 -2
  90. package/src/lib/plugins/search-pagefind/IndexingDialog.tsx +2 -2
  91. package/src/lib/plugins/search-pagefind/PagefindSearch.tsx +4 -4
  92. package/src/lib/plugins/search-pagefind/ResultList.tsx +1 -1
  93. package/src/lib/ui/ActionButton.tsx +1 -1
  94. package/src/lib/ui/Command.tsx +1 -1
  95. package/src/lib/util/MdxComponents.tsx +2 -2
  96. package/src/vite/config.ts +4 -4
  97. package/src/vite/plugin-api-keys.ts +1 -1
  98. package/src/vite/plugin-api.ts +2 -2
  99. package/src/vite/plugin-auth.ts +1 -1
  100. package/src/vite/plugin-component.ts +5 -5
  101. package/src/vite/plugin-config.ts +1 -1
  102. package/src/vite/plugin-custom-pages.ts +1 -1
  103. package/src/vite/plugin-docs.ts +1 -1
  104. package/src/vite/plugin-mdx.ts +1 -1
  105. package/src/vite/plugin-modules.ts +2 -2
  106. package/src/vite/plugin-navigation.ts +1 -1
  107. package/src/vite/plugin-search.ts +2 -2
  108. package/src/vite/plugin.ts +1 -1
@@ -77,6 +77,8 @@ export const useAuth = () => {
77
77
  return {
78
78
  isAuthEnabled,
79
79
  ...authState,
80
+ isBackendAvailable: authState.isBackendAvailable,
81
+ authMode: authState.authMode,
80
82
 
81
83
  login: async (options?: AuthActionOptions) => {
82
84
  if (!isAuthEnabled) {
@@ -1,5 +1,5 @@
1
1
  import type { Clerk } from "@clerk/clerk-js";
2
- import type { ApitogoPlugin } from "@lukoweb/apitogo/plugins";
2
+ import type { ApitogoPlugin } from "@mehdad67/apitogo/plugins";
3
3
  import type { ClerkAuthenticationConfig } from "../../../config/config.js";
4
4
  import type {
5
5
  AuthActionContext,
@@ -0,0 +1,21 @@
1
+ /** Placeholder backend URL written by MCP before publish. */
2
+ export const DEV_PORTAL_PLACEHOLDER_API_URL =
3
+ "https://dp-example.azurecontainerapps.io";
4
+
5
+ export type DevPortalAuthMode = "live" | "preview";
6
+
7
+ export type EndUserMeResponse = {
8
+ userId: string;
9
+ displayName: string | null;
10
+ subscriptions: Array<{
11
+ planSku: string;
12
+ status: string;
13
+ entitlementState: string | null;
14
+ currentPeriodEnd: string | null;
15
+ }>;
16
+ };
17
+
18
+ export type DevPortalAuthStatusResponse = {
19
+ available: boolean;
20
+ oidcConfigured: boolean;
21
+ };
@@ -0,0 +1,107 @@
1
+ import type { DevPortalAuthenticationConfig } from "../../../config/config.js";
2
+ import {
3
+ DEV_PORTAL_PLACEHOLDER_API_URL,
4
+ type DevPortalAuthStatusResponse,
5
+ type EndUserMeResponse,
6
+ } from "./dev-portal-constants.js";
7
+
8
+ declare const VITE_APITOGO_DEV_PORTAL_API_URL: string | undefined;
9
+
10
+ export function resolveDevPortalApiBaseUrl(
11
+ config: Pick<DevPortalAuthenticationConfig, "apiBaseUrl">,
12
+ ): string | undefined {
13
+ const fromConfig = config.apiBaseUrl?.trim();
14
+ if (fromConfig) {
15
+ return fromConfig.replace(/\/+$/, "");
16
+ }
17
+
18
+ const fromEnv =
19
+ typeof VITE_APITOGO_DEV_PORTAL_API_URL === "string"
20
+ ? VITE_APITOGO_DEV_PORTAL_API_URL.trim()
21
+ : "";
22
+
23
+ return fromEnv ? fromEnv.replace(/\/+$/, "") : undefined;
24
+ }
25
+
26
+ export function isPlaceholderDevPortalApiUrl(
27
+ apiBaseUrl: string | undefined,
28
+ ): boolean {
29
+ if (!apiBaseUrl) {
30
+ return true;
31
+ }
32
+
33
+ return (
34
+ apiBaseUrl === DEV_PORTAL_PLACEHOLDER_API_URL ||
35
+ apiBaseUrl.includes("dp-example.azurecontainerapps.io")
36
+ );
37
+ }
38
+
39
+ export function toAbsoluteReturnUrl(pathOrUrl: string): string {
40
+ if (pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")) {
41
+ return pathOrUrl;
42
+ }
43
+
44
+ if (typeof window === "undefined") {
45
+ return pathOrUrl;
46
+ }
47
+
48
+ return new URL(pathOrUrl, window.location.origin).href;
49
+ }
50
+
51
+ export function buildDevPortalLoginUrl(
52
+ apiBaseUrl: string,
53
+ returnUrl: string,
54
+ ): string {
55
+ const url = new URL("/api/v1/auth/login", apiBaseUrl);
56
+ url.searchParams.set("returnUrl", toAbsoluteReturnUrl(returnUrl));
57
+ return url.toString();
58
+ }
59
+
60
+ export function buildDevPortalLogoutUrl(
61
+ apiBaseUrl: string,
62
+ returnUrl: string,
63
+ ): string {
64
+ const url = new URL("/api/v1/auth/logout", apiBaseUrl);
65
+ url.searchParams.set("returnUrl", toAbsoluteReturnUrl(returnUrl));
66
+ return url.toString();
67
+ }
68
+
69
+ export async function probeDevPortalBackend(
70
+ apiBaseUrl: string,
71
+ fetchImpl: typeof fetch = fetch,
72
+ ): Promise<boolean> {
73
+ const controller = new AbortController();
74
+ const timeout = setTimeout(() => controller.abort(), 8_000);
75
+
76
+ try {
77
+ const response = await fetchImpl(`${apiBaseUrl}/api/v1/auth/status`, {
78
+ method: "GET",
79
+ headers: { Accept: "application/json" },
80
+ credentials: "include",
81
+ signal: controller.signal,
82
+ });
83
+
84
+ if (!response.ok) {
85
+ return false;
86
+ }
87
+
88
+ const body = (await response.json()) as DevPortalAuthStatusResponse;
89
+ return body.available === true;
90
+ } catch {
91
+ return false;
92
+ } finally {
93
+ clearTimeout(timeout);
94
+ }
95
+ }
96
+
97
+ export function mapEndUserMeToProfile(
98
+ me: EndUserMeResponse,
99
+ ): import("../state.js").UserProfile {
100
+ return {
101
+ sub: me.userId,
102
+ email: undefined,
103
+ emailVerified: false,
104
+ name: me.displayName ?? undefined,
105
+ pictureUrl: undefined,
106
+ };
107
+ }
@@ -0,0 +1,218 @@
1
+ import type { DevPortalAuthenticationConfig } from "../../../config/config.js";
2
+ import type { ZudokuContext } from "../../core/ZudokuContext.js";
3
+ import type {
4
+ AuthActionContext,
5
+ AuthActionOptions,
6
+ AuthenticationPlugin,
7
+ AuthenticationProviderInitializer,
8
+ } from "../authentication.js";
9
+ import { CoreAuthenticationPlugin } from "../AuthenticationPlugin.js";
10
+ import { type UserProfile, useAuthState } from "../state.js";
11
+ import type {
12
+ DevPortalAuthMode,
13
+ EndUserMeResponse,
14
+ } from "./dev-portal-constants.js";
15
+ import {
16
+ buildDevPortalLoginUrl,
17
+ buildDevPortalLogoutUrl,
18
+ isPlaceholderDevPortalApiUrl,
19
+ mapEndUserMeToProfile,
20
+ probeDevPortalBackend,
21
+ resolveDevPortalApiBaseUrl,
22
+ } from "./dev-portal-utils.js";
23
+
24
+ export type DevPortalProviderData = {
25
+ type: "dev-portal";
26
+ apiBaseUrl: string;
27
+ authMode: DevPortalAuthMode;
28
+ };
29
+
30
+ declare module "../state.js" {
31
+ interface ProviderDataRegistry {
32
+ "dev-portal": DevPortalProviderData;
33
+ }
34
+ }
35
+
36
+ export class DevPortalAuthenticationProvider
37
+ extends CoreAuthenticationPlugin
38
+ implements AuthenticationPlugin
39
+ {
40
+ private readonly apiBaseUrl: string | undefined;
41
+ private readonly redirectToAfterSignIn: string | undefined;
42
+ private readonly redirectToAfterSignUp: string | undefined;
43
+ private readonly redirectToAfterSignOut: string;
44
+ private authMode: DevPortalAuthMode = "preview";
45
+ private backendAvailable = false;
46
+
47
+ constructor({
48
+ apiBaseUrl,
49
+ redirectToAfterSignIn,
50
+ redirectToAfterSignUp,
51
+ redirectToAfterSignOut = "/",
52
+ }: DevPortalAuthenticationConfig) {
53
+ super();
54
+ this.apiBaseUrl = resolveDevPortalApiBaseUrl({ apiBaseUrl });
55
+ this.redirectToAfterSignIn = redirectToAfterSignIn;
56
+ this.redirectToAfterSignUp = redirectToAfterSignUp;
57
+ this.redirectToAfterSignOut = redirectToAfterSignOut;
58
+ }
59
+
60
+ async initialize(_context: ZudokuContext): Promise<void> {
61
+ await this.syncBackendAvailability();
62
+ if (!this.backendAvailable) {
63
+ this.setPreviewState();
64
+ return;
65
+ }
66
+
67
+ await this.refreshUserProfile();
68
+ }
69
+
70
+ onPageLoad = async () => {
71
+ if (!this.backendAvailable) {
72
+ this.setPreviewState();
73
+ return;
74
+ }
75
+
76
+ await this.refreshUserProfile();
77
+ };
78
+
79
+ private async syncBackendAvailability(): Promise<void> {
80
+ if (!this.apiBaseUrl || isPlaceholderDevPortalApiUrl(this.apiBaseUrl)) {
81
+ this.backendAvailable = false;
82
+ this.authMode = "preview";
83
+ return;
84
+ }
85
+
86
+ this.backendAvailable = await probeDevPortalBackend(this.apiBaseUrl);
87
+ this.authMode = this.backendAvailable ? "live" : "preview";
88
+ }
89
+
90
+ private setPreviewState(): void {
91
+ useAuthState.setState({
92
+ isAuthenticated: false,
93
+ isPending: false,
94
+ profile: null,
95
+ providerData: this.apiBaseUrl
96
+ ? {
97
+ type: "dev-portal",
98
+ apiBaseUrl: this.apiBaseUrl,
99
+ authMode: "preview",
100
+ }
101
+ : null,
102
+ isBackendAvailable: false,
103
+ authMode: "preview",
104
+ });
105
+ }
106
+
107
+ private ensureLiveBackend(): string {
108
+ if (!this.apiBaseUrl || !this.backendAvailable) {
109
+ throw new Error("Dev portal authentication is not available in preview.");
110
+ }
111
+
112
+ return this.apiBaseUrl;
113
+ }
114
+
115
+ async refreshUserProfile(): Promise<boolean> {
116
+ if (!this.apiBaseUrl) {
117
+ this.setPreviewState();
118
+ return false;
119
+ }
120
+
121
+ if (isPlaceholderDevPortalApiUrl(this.apiBaseUrl)) {
122
+ this.setPreviewState();
123
+ return false;
124
+ }
125
+
126
+ await this.syncBackendAvailability();
127
+ if (!this.backendAvailable) {
128
+ this.setPreviewState();
129
+ return false;
130
+ }
131
+
132
+ try {
133
+ const response = await fetch(`${this.apiBaseUrl}/api/v1/auth/me`, {
134
+ method: "GET",
135
+ headers: { Accept: "application/json" },
136
+ credentials: "include",
137
+ });
138
+
139
+ if (response.status === 401) {
140
+ useAuthState.setState({
141
+ isAuthenticated: false,
142
+ isPending: false,
143
+ profile: null,
144
+ providerData: {
145
+ type: "dev-portal",
146
+ apiBaseUrl: this.apiBaseUrl,
147
+ authMode: "live",
148
+ },
149
+ isBackendAvailable: true,
150
+ authMode: "live",
151
+ });
152
+ return false;
153
+ }
154
+
155
+ if (!response.ok) {
156
+ this.setPreviewState();
157
+ return false;
158
+ }
159
+
160
+ const me = (await response.json()) as EndUserMeResponse;
161
+ const profile: UserProfile = mapEndUserMeToProfile(me);
162
+
163
+ useAuthState.getState().setLoggedIn({
164
+ profile,
165
+ providerData: {
166
+ type: "dev-portal",
167
+ apiBaseUrl: this.apiBaseUrl,
168
+ authMode: "live",
169
+ },
170
+ });
171
+ useAuthState.setState({
172
+ isBackendAvailable: true,
173
+ authMode: "live",
174
+ });
175
+ return true;
176
+ } catch {
177
+ this.setPreviewState();
178
+ return false;
179
+ }
180
+ }
181
+
182
+ async signIn(
183
+ _: AuthActionContext,
184
+ { redirectTo }: AuthActionOptions = {},
185
+ ): Promise<void> {
186
+ const apiBaseUrl = this.ensureLiveBackend();
187
+ const target =
188
+ redirectTo ?? this.redirectToAfterSignIn ?? window.location.href;
189
+ window.location.assign(buildDevPortalLoginUrl(apiBaseUrl, target));
190
+ }
191
+
192
+ async signUp(
193
+ _: AuthActionContext,
194
+ { redirectTo }: AuthActionOptions = {},
195
+ ): Promise<void> {
196
+ const apiBaseUrl = this.ensureLiveBackend();
197
+ const target =
198
+ redirectTo ?? this.redirectToAfterSignUp ?? window.location.href;
199
+ window.location.assign(buildDevPortalLoginUrl(apiBaseUrl, target));
200
+ }
201
+
202
+ async signOut(_: AuthActionContext): Promise<void> {
203
+ const apiBaseUrl = this.ensureLiveBackend();
204
+ window.location.assign(
205
+ buildDevPortalLogoutUrl(apiBaseUrl, this.redirectToAfterSignOut),
206
+ );
207
+ }
208
+
209
+ async signRequest(request: Request): Promise<Request> {
210
+ return request;
211
+ }
212
+ }
213
+
214
+ const devPortalAuth: AuthenticationProviderInitializer<
215
+ DevPortalAuthenticationConfig
216
+ > = (options) => new DevPortalAuthenticationProvider(options);
217
+
218
+ export default devPortalAuth;
@@ -21,11 +21,17 @@ export type ProviderData = [keyof ProviderDataRegistry] extends [never]
21
21
  ? unknown
22
22
  : ProviderDataRegistry[keyof ProviderDataRegistry];
23
23
 
24
+ export type AuthMode = "live" | "preview";
25
+
24
26
  export interface AuthState {
25
27
  isAuthenticated: boolean;
26
28
  isPending: boolean;
27
29
  profile: UserProfile | null;
28
30
  providerData: ProviderData | null;
31
+ /** False when dev-portal backend is unavailable (local preview). Defaults true. */
32
+ isBackendAvailable: boolean;
33
+ /** Live when backend auth is reachable; preview otherwise. */
34
+ authMode: AuthMode;
29
35
  setAuthenticationPending: () => void;
30
36
  setLoggedOut: () => void;
31
37
  setLoggedIn: (args: {
@@ -41,12 +47,16 @@ export const authState = create<AuthState>()(
41
47
  isPending: true,
42
48
  profile: null,
43
49
  providerData: null,
50
+ isBackendAvailable: true,
51
+ authMode: "live",
44
52
  setAuthenticationPending: () =>
45
53
  set(() => ({
46
54
  isAuthenticated: false,
47
55
  isPending: false,
48
56
  profile: null,
49
57
  providerData: null,
58
+ isBackendAvailable: true,
59
+ authMode: "live",
50
60
  })),
51
61
  setLoggedOut: () =>
52
62
  set(() => ({
@@ -54,6 +64,8 @@ export const authState = create<AuthState>()(
54
64
  isPending: false,
55
65
  profile: null,
56
66
  providerData: null,
67
+ isBackendAvailable: true,
68
+ authMode: "live",
57
69
  })),
58
70
  setLoggedIn: ({ profile, providerData }) =>
59
71
  set(() => ({
@@ -1,4 +1,4 @@
1
- import { Card } from "@lukoweb/apitogo/ui/Card.js";
1
+ import { Card } from "@mehdad67/apitogo/ui/Card.js";
2
2
  import createVariantComponent from "../../util/createVariantComponent.js";
3
3
 
4
4
  export const AuthCard = createVariantComponent(
@@ -1,17 +1,17 @@
1
- import { Spinner } from "@lukoweb/apitogo/components";
2
- import { ActionButton } from "@lukoweb/apitogo/ui/ActionButton.js";
1
+ import { Spinner } from "@mehdad67/apitogo/components";
2
+ import { ActionButton } from "@mehdad67/apitogo/ui/ActionButton.js";
3
3
  import {
4
4
  Alert,
5
5
  AlertDescription,
6
6
  AlertTitle,
7
- } from "@lukoweb/apitogo/ui/Alert.js";
7
+ } from "@mehdad67/apitogo/ui/Alert.js";
8
8
  import {
9
9
  CardContent,
10
10
  CardDescription,
11
11
  CardHeader,
12
12
  CardTitle,
13
- } from "@lukoweb/apitogo/ui/Card.js";
14
- import { Input } from "@lukoweb/apitogo/ui/Input.js";
13
+ } from "@mehdad67/apitogo/ui/Card.js";
14
+ import { Input } from "@mehdad67/apitogo/ui/Input.js";
15
15
  import { useMutation } from "@tanstack/react-query";
16
16
  import { useEffect, useRef, useState } from "react";
17
17
  import { useNavigate, useSearchParams } from "react-router";
@@ -2,14 +2,14 @@ import {
2
2
  Alert,
3
3
  AlertDescription,
4
4
  AlertTitle,
5
- } from "@lukoweb/apitogo/ui/Alert.js";
6
- import { Button } from "@lukoweb/apitogo/ui/Button.js";
5
+ } from "@mehdad67/apitogo/ui/Alert.js";
6
+ import { Button } from "@mehdad67/apitogo/ui/Button.js";
7
7
  import {
8
8
  CardContent,
9
9
  CardDescription,
10
10
  CardHeader,
11
11
  CardTitle,
12
- } from "@lukoweb/apitogo/ui/Card.js";
12
+ } from "@mehdad67/apitogo/ui/Card.js";
13
13
  import { useMutation } from "@tanstack/react-query";
14
14
  import { Mail, RefreshCw } from "lucide-react";
15
15
  import { useState } from "react";
@@ -1,16 +1,16 @@
1
- import { ActionButton } from "@lukoweb/apitogo/ui/ActionButton.js";
1
+ import { ActionButton } from "@mehdad67/apitogo/ui/ActionButton.js";
2
2
  import {
3
3
  Alert,
4
4
  AlertDescription,
5
5
  AlertTitle,
6
- } from "@lukoweb/apitogo/ui/Alert.js";
6
+ } from "@mehdad67/apitogo/ui/Alert.js";
7
7
  import {
8
8
  CardContent,
9
9
  CardDescription,
10
10
  CardHeader,
11
11
  CardTitle,
12
- } from "@lukoweb/apitogo/ui/Card.js";
13
- import { Input } from "@lukoweb/apitogo/ui/Input.js";
12
+ } from "@mehdad67/apitogo/ui/Card.js";
13
+ import { Input } from "@mehdad67/apitogo/ui/Input.js";
14
14
  import { useMutation } from "@tanstack/react-query";
15
15
  import { useForm } from "react-hook-form";
16
16
  import { Link, useNavigate, useSearchParams } from "react-router";
@@ -1,17 +1,17 @@
1
- import { ActionButton } from "@lukoweb/apitogo/ui/ActionButton.js";
1
+ import { ActionButton } from "@mehdad67/apitogo/ui/ActionButton.js";
2
2
  import {
3
3
  Alert,
4
4
  AlertDescription,
5
5
  AlertTitle,
6
- } from "@lukoweb/apitogo/ui/Alert.js";
7
- import { Button } from "@lukoweb/apitogo/ui/Button.js";
6
+ } from "@mehdad67/apitogo/ui/Alert.js";
7
+ import { Button } from "@mehdad67/apitogo/ui/Button.js";
8
8
  import {
9
9
  Card,
10
10
  CardContent,
11
11
  CardDescription,
12
12
  CardHeader,
13
13
  CardTitle,
14
- } from "@lukoweb/apitogo/ui/Card.js";
14
+ } from "@mehdad67/apitogo/ui/Card.js";
15
15
  import { useMutation, useQuery } from "@tanstack/react-query";
16
16
  import { CheckIcon, MailCheck, RefreshCw } from "lucide-react";
17
17
  import { Navigate, useSearchParams } from "react-router";
@@ -1,18 +1,18 @@
1
- import { ActionButton } from "@lukoweb/apitogo/ui/ActionButton.js";
1
+ import { ActionButton } from "@mehdad67/apitogo/ui/ActionButton.js";
2
2
  import {
3
3
  Alert,
4
4
  AlertDescription,
5
5
  AlertTitle,
6
- } from "@lukoweb/apitogo/ui/Alert.js";
7
- import { Button, type ButtonProps } from "@lukoweb/apitogo/ui/Button.js";
6
+ } from "@mehdad67/apitogo/ui/Alert.js";
7
+ import { Button, type ButtonProps } from "@mehdad67/apitogo/ui/Button.js";
8
8
  import {
9
9
  CardContent,
10
10
  CardDescription,
11
11
  CardHeader,
12
12
  CardTitle,
13
- } from "@lukoweb/apitogo/ui/Card.js";
14
- import { Input } from "@lukoweb/apitogo/ui/Input.js";
15
- import { Separator } from "@lukoweb/apitogo/ui/Separator.js";
13
+ } from "@mehdad67/apitogo/ui/Card.js";
14
+ import { Input } from "@mehdad67/apitogo/ui/Input.js";
15
+ import { Separator } from "@mehdad67/apitogo/ui/Separator.js";
16
16
  import { useMutation } from "@tanstack/react-query";
17
17
  import React from "react";
18
18
  import { useForm } from "react-hook-form";
@@ -1,4 +1,4 @@
1
- import { DropdownMenuItem } from "@lukoweb/apitogo/ui/DropdownMenu.js";
1
+ import { DropdownMenuItem } from "@mehdad67/apitogo/ui/DropdownMenu.js";
2
2
  import type { ReactNode } from "react";
3
3
  import type {
4
4
  AiAssistantCustom,
@@ -3,8 +3,8 @@ import {
3
3
  CommandInlineInput,
4
4
  CommandItem,
5
5
  CommandList,
6
- } from "@lukoweb/apitogo/ui/Command.js";
7
- import { Popover, PopoverContent } from "@lukoweb/apitogo/ui/Popover.js";
6
+ } from "@mehdad67/apitogo/ui/Command.js";
7
+ import { Popover, PopoverContent } from "@mehdad67/apitogo/ui/Popover.js";
8
8
  import { PopoverAnchor } from "@radix-ui/react-popover";
9
9
  import { useCommandState } from "cmdk";
10
10
  import { type KeyboardEvent, type Ref, useRef, useState } from "react";
@@ -2,7 +2,7 @@ import {
2
2
  Alert,
3
3
  AlertDescription,
4
4
  AlertTitle,
5
- } from "@lukoweb/apitogo/ui/Alert.js";
5
+ } from "@mehdad67/apitogo/ui/Alert.js";
6
6
  import { InfoIcon } from "lucide-react";
7
7
  import { type ReactNode, Suspense, lazy } from "react";
8
8
 
@@ -1,5 +1,5 @@
1
- import { Button } from "@lukoweb/apitogo/ui/Button.js";
2
- import { Skeleton } from "@lukoweb/apitogo/ui/Skeleton.js";
1
+ import { Button } from "@mehdad67/apitogo/ui/Button.js";
2
+ import { Skeleton } from "@mehdad67/apitogo/ui/Skeleton.js";
3
3
  import { LogOutIcon } from "lucide-react";
4
4
  import { lazy, memo, Suspense } from "react";
5
5
  import { Link } from "react-router";
@@ -73,9 +73,9 @@ const ProfileMenu = () => {
73
73
  const context = useZudoku();
74
74
  const profileItems = context.getProfileMenuItems();
75
75
  const auth = useAuth();
76
- const { isAuthEnabled, isAuthenticated, profile } = auth;
76
+ const { isAuthEnabled, isAuthenticated, profile, isBackendAvailable } = auth;
77
77
 
78
- if (!isAuthEnabled) return null;
78
+ if (!isAuthEnabled || !isBackendAvailable) return null;
79
79
 
80
80
  return (
81
81
  <ClientOnly fallback={<Skeleton className="rounded-sm h-5 w-24 mr-4" />}>
@@ -1,4 +1,4 @@
1
- import { TooltipProvider } from "@lukoweb/apitogo/ui/Tooltip.js";
1
+ import { TooltipProvider } from "@mehdad67/apitogo/ui/Tooltip.js";
2
2
  import type { ReactNode } from "react";
3
3
  import { Suspense } from "react";
4
4
  import { Outlet } from "react-router";
@@ -1,4 +1,4 @@
1
- import { TooltipProvider } from "@lukoweb/apitogo/ui/Tooltip.js";
1
+ import { TooltipProvider } from "@mehdad67/apitogo/ui/Tooltip.js";
2
2
  import { type ReactNode, Suspense, useEffect } from "react";
3
3
  import { Outlet } from "react-router";
4
4
  import { cn } from "../util/cn.js";
@@ -1,4 +1,4 @@
1
- import { Drawer, DrawerTrigger } from "@lukoweb/apitogo/ui/Drawer.js";
1
+ import { Drawer, DrawerTrigger } from "@mehdad67/apitogo/ui/Drawer.js";
2
2
  import { PanelLeftIcon } from "lucide-react";
3
3
  import { type PropsWithChildren, useState } from "react";
4
4
  import { useNavigation } from "react-router";
@@ -2,7 +2,7 @@ import {
2
2
  Alert,
3
3
  AlertDescription,
4
4
  AlertTitle,
5
- } from "@lukoweb/apitogo/ui/Alert.js";
5
+ } from "@mehdad67/apitogo/ui/Alert.js";
6
6
  import { useQuery } from "@tanstack/react-query";
7
7
  import type { MermaidConfig } from "mermaid";
8
8
  import type { ComponentProps } from "react";
@@ -1,6 +1,6 @@
1
- import { Button } from "@lukoweb/apitogo/ui/Button.js";
2
- import { Separator } from "@lukoweb/apitogo/ui/Separator.js";
3
- import { Skeleton } from "@lukoweb/apitogo/ui/Skeleton.js";
1
+ import { Button } from "@mehdad67/apitogo/ui/Button.js";
2
+ import { Separator } from "@mehdad67/apitogo/ui/Separator.js";
3
+ import { Skeleton } from "@mehdad67/apitogo/ui/Skeleton.js";
4
4
  import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
5
5
  import { deepEqual } from "fast-equals";
6
6
  import {
@@ -130,7 +130,8 @@ export const MobileTopNavigation = () => {
130
130
  getProfileMenuItems,
131
131
  } = context;
132
132
  const headerNavigation = header?.navigation ?? [];
133
- const { isAuthenticated, profile, isAuthEnabled } = authState;
133
+ const { isAuthenticated, profile, isAuthEnabled, isBackendAvailable } =
134
+ authState;
134
135
  const [drawerOpen, setDrawerOpen] = useState(false);
135
136
 
136
137
  const accountItems = getProfileMenuItems();
@@ -232,7 +233,7 @@ export const MobileTopNavigation = () => {
232
233
  </div>
233
234
  <div className="border-t shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.05)] px-4 pt-3 flex flex-col gap-2">
234
235
  <div className="flex items-center justify-between">
235
- {isAuthEnabled && (
236
+ {isAuthEnabled && isBackendAvailable && (
236
237
  <ClientOnly
237
238
  fallback={<Skeleton className="rounded-sm h-8 w-16" />}
238
239
  >
@@ -2,7 +2,7 @@ import {
2
2
  Popover,
3
3
  PopoverContent,
4
4
  PopoverTrigger,
5
- } from "@lukoweb/apitogo/ui/Popover.js";
5
+ } from "@mehdad67/apitogo/ui/Popover.js";
6
6
  import { CheckIcon, ChevronDownIcon } from "lucide-react";
7
7
  import { useState } from "react";
8
8
  import { cn } from "../util/cn.js";
@@ -1,4 +1,4 @@
1
- import { Separator } from "@lukoweb/apitogo/ui/Separator.js";
1
+ import { Separator } from "@mehdad67/apitogo/ui/Separator.js";
2
2
  import { cx } from "class-variance-authority";
3
3
  import { deepEqual } from "fast-equals";
4
4
  import { Suspense } from "react";
@@ -14,7 +14,7 @@ const useApitogoContext = () => {
14
14
 
15
15
  if (!context) {
16
16
  throw new Error(
17
- "useApitogo must be used within an ApitogoProvider (from @lukoweb/apitogo/components).",
17
+ "useApitogo must be used within an ApitogoProvider (from @mehdad67/apitogo/components).",
18
18
  );
19
19
  }
20
20