@lukoweb/apitogo 0.1.40 → 0.1.41

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/cli/cli.js CHANGED
@@ -4124,7 +4124,7 @@ import {
4124
4124
  // package.json
4125
4125
  var package_default = {
4126
4126
  name: "@lukoweb/apitogo",
4127
- version: "0.1.40",
4127
+ version: "0.1.41",
4128
4128
  type: "module",
4129
4129
  sideEffects: [
4130
4130
  "**/*.css",
@@ -6761,7 +6761,7 @@ var viteAuthPlugin = () => {
6761
6761
  async load(id) {
6762
6762
  if (id === resolvedVirtualModuleId4) {
6763
6763
  const config2 = getCurrentConfig();
6764
- if (!config2.authentication || !config2.__resolvedModules.userPanel.userManagement.enabled || config2.__meta.mode === "standalone") {
6764
+ if (!config2.authentication || config2.__meta.mode === "standalone") {
6765
6765
  return `export const configuredAuthProvider = undefined;`;
6766
6766
  }
6767
6767
  return [
@@ -5,6 +5,7 @@ export type AuthMode = "live" | "preview";
5
5
  export interface AuthState {
6
6
  isAuthenticated: boolean;
7
7
  isPending: boolean;
8
+ sessionChecked: boolean;
8
9
  profile: UserProfile | null;
9
10
  providerData: ProviderData | null;
10
11
  isBackendAvailable: boolean;
@@ -1,4 +1,4 @@
1
- import { type PropsWithChildren } from "react";
1
+ import type { PropsWithChildren } from "react";
2
2
  import type { ZudokuContext } from "../../core/ZudokuContext.js";
3
3
  export declare const ZudokuProvider: ({ children, context, }: PropsWithChildren<{
4
4
  context: ZudokuContext;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo",
3
- "version": "0.1.40",
3
+ "version": "0.1.41",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -106,6 +106,7 @@ export class DevPortalAuthenticationProvider
106
106
  useAuthState.setState({
107
107
  isAuthenticated: false,
108
108
  isPending: false,
109
+ sessionChecked: true,
109
110
  profile: null,
110
111
  providerData: this.apiBaseUrl
111
112
  ? {
@@ -181,6 +182,7 @@ export class DevPortalAuthenticationProvider
181
182
  useAuthState.setState({
182
183
  isAuthenticated: false,
183
184
  isPending: false,
185
+ sessionChecked: true,
184
186
  profile: null,
185
187
  providerData: {
186
188
  type: "dev-portal",
@@ -208,6 +210,7 @@ export class DevPortalAuthenticationProvider
208
210
  useAuthState.setState({
209
211
  isAuthenticated: true,
210
212
  isPending: false,
213
+ sessionChecked: true,
211
214
  profile,
212
215
  providerData: {
213
216
  type: "dev-portal",
@@ -26,6 +26,8 @@ export type AuthMode = "live" | "preview";
26
26
  export interface AuthState {
27
27
  isAuthenticated: boolean;
28
28
  isPending: boolean;
29
+ /** True after the auth provider has finished its first session probe on load. */
30
+ sessionChecked: boolean;
29
31
  profile: UserProfile | null;
30
32
  providerData: ProviderData | null;
31
33
  /** False when dev-portal backend is unavailable (local preview). Defaults true. */
@@ -57,6 +59,7 @@ function createAuthStateStore(): AuthStateStore {
57
59
  (set) => ({
58
60
  isAuthenticated: false,
59
61
  isPending: true,
62
+ sessionChecked: false,
60
63
  profile: null,
61
64
  providerData: null,
62
65
  isBackendAvailable: true,
@@ -65,6 +68,7 @@ function createAuthStateStore(): AuthStateStore {
65
68
  set(() => ({
66
69
  isAuthenticated: false,
67
70
  isPending: false,
71
+ sessionChecked: true,
68
72
  profile: null,
69
73
  providerData: null,
70
74
  isBackendAvailable: true,
@@ -74,6 +78,7 @@ function createAuthStateStore(): AuthStateStore {
74
78
  set(() => ({
75
79
  isAuthenticated: false,
76
80
  isPending: false,
81
+ sessionChecked: true,
77
82
  profile: null,
78
83
  providerData: null,
79
84
  isBackendAvailable: true,
@@ -83,6 +88,7 @@ function createAuthStateStore(): AuthStateStore {
83
88
  set(() => ({
84
89
  isAuthenticated: true,
85
90
  isPending: false,
91
+ sessionChecked: true,
86
92
  profile,
87
93
  providerData,
88
94
  })),
@@ -113,9 +119,11 @@ function createAuthStateStore(): AuthStateStore {
113
119
 
114
120
  // Cookie/session auth is refreshed from the backend on load. Do not
115
121
  // restore identity fields from localStorage or OIDC return looks logged out.
122
+ // Keep isPending true until the auth provider finishes /auth/me (or equivalent).
116
123
  return {
117
124
  ...currentState,
118
- isPending: false,
125
+ isPending: true,
126
+ sessionChecked: false,
119
127
  isAuthenticated: currentState.isAuthenticated,
120
128
  profile: currentState.profile,
121
129
  providerData: currentState.providerData,
@@ -7,7 +7,6 @@ import {
7
7
  type HeaderNavLinkItem,
8
8
  } from "../../config/validators/HeaderNavigationSchema.js";
9
9
  import { useAuth } from "../authentication/hook.js";
10
- import { shouldShowItem } from "./navigation/utils.js";
11
10
  import type { ZudokuContext } from "../core/ZudokuContext.js";
12
11
  import {
13
12
  NavigationMenu,
@@ -20,6 +19,7 @@ import {
20
19
  } from "../ui/NavigationMenu.js";
21
20
  import { cn } from "../util/cn.js";
22
21
  import { useZudoku } from "./context/ZudokuContext.js";
22
+ import { shouldShowItem } from "./navigation/utils.js";
23
23
 
24
24
  const NavLinkItem = ({ item }: { item: HeaderNavLinkItem }) => {
25
25
  const Icon = item.icon as LucideIcon | undefined;
@@ -160,10 +160,9 @@ export const HeaderNavigation = () => {
160
160
  return (
161
161
  <NavigationMenu className={viewportAlign}>
162
162
  <NavigationMenuList>
163
- {items.map((item, index) => (
164
- // biome-ignore lint/suspicious/noArrayIndexKey: Stable because it's statically defined in the config
163
+ {items.map((item) => (
165
164
  <HeaderNavItemComponent
166
- key={`${item.label}-${index}`}
165
+ key={"to" in item ? item.to : item.label}
167
166
  item={item}
168
167
  auth={auth}
169
168
  context={context}
@@ -6,6 +6,7 @@ import {
6
6
  type PropsWithChildren,
7
7
  useEffect,
8
8
  useMemo,
9
+ useRef,
9
10
  useState,
10
11
  } from "react";
11
12
  import { ErrorBoundary } from "react-error-boundary";
@@ -23,8 +24,6 @@ import { ViewportAnchorProvider } from "./context/ViewportAnchorContext.js";
23
24
  import { ApitogoProvider } from "./context/ZudokuProvider.js";
24
25
  import { PluginHeads } from "./PluginHeads.js";
25
26
 
26
- let apitogoContext: ZudokuContext | undefined;
27
-
28
27
  const ZudokuInner = memo(
29
28
  ({
30
29
  children,
@@ -53,6 +52,8 @@ const ZudokuInner = memo(
53
52
  const [didNavigate, setDidNavigate] = useState(false);
54
53
  const navigation = useNavigation();
55
54
  const queryClient = useQueryClient();
55
+ const apitogoContextRef = useRef<ZudokuContext | undefined>(undefined);
56
+ apitogoContextRef.current ??= new ZudokuContext(props, queryClient, env);
56
57
 
57
58
  useEffect(() => {
58
59
  if (didNavigate || !navigation.location) {
@@ -61,12 +62,10 @@ const ZudokuInner = memo(
61
62
  setDidNavigate(true);
62
63
  }, [didNavigate, navigation.location]);
63
64
 
64
- apitogoContext ??= new ZudokuContext(props, queryClient, env);
65
-
66
65
  return (
67
66
  <>
68
67
  <PluginHeads plugins={props.plugins ?? []} location={location} />
69
- <ApitogoProvider context={apitogoContext}>
68
+ <ApitogoProvider context={apitogoContextRef.current}>
70
69
  <RouterEventsEmitter />
71
70
  <SlotProvider slots={props.slots ?? props.UNSAFE_slotlets}>
72
71
  <MDXProvider components={mdxComponents}>
@@ -1,4 +1,4 @@
1
- import { use, type PropsWithChildren } from "react";
1
+ import type { PropsWithChildren } from "react";
2
2
  import type { ZudokuContext } from "../../core/ZudokuContext.js";
3
3
  import { ZudokuReactContext } from "./ZudokuReactContext.js";
4
4
 
@@ -6,8 +6,6 @@ const ZudokuProviderImpl = ({
6
6
  children,
7
7
  context,
8
8
  }: PropsWithChildren<{ context: ZudokuContext }>) => {
9
- if (context.initialize) use(context.initialize);
10
-
11
9
  return (
12
10
  <ZudokuReactContext.Provider value={context}>
13
11
  {children}
@@ -179,8 +179,10 @@ export const RouteGuard = () => {
179
179
  if (
180
180
  !auth.isBackendAvailable ||
181
181
  auth.isPending ||
182
+ !auth.sessionChecked ||
182
183
  !needsAuth ||
183
- auth.hasDistinctSignUpFlow
184
+ auth.hasDistinctSignUpFlow ||
185
+ !auth.isAuthEnabled
184
186
  ) {
185
187
  unifiedRedirectKeyRef.current = null;
186
188
  return;
@@ -195,7 +197,9 @@ export const RouteGuard = () => {
195
197
  }, [
196
198
  auth.isBackendAvailable,
197
199
  auth.isPending,
200
+ auth.sessionChecked,
198
201
  auth.hasDistinctSignUpFlow,
202
+ auth.isAuthEnabled,
199
203
  auth.login,
200
204
  needsAuth,
201
205
  redirectTo,
@@ -210,6 +214,11 @@ export const RouteGuard = () => {
210
214
  }
211
215
 
212
216
  if (isProtectedRoute && !auth.isAuthEnabled) {
217
+ // Dev SSR can reuse a stale context before auth plugins hydrate; client decides.
218
+ if (typeof window === "undefined") {
219
+ return <BypassRoute isProtectedRoute={isProtectedRoute} />;
220
+ }
221
+
213
222
  throw new ZudokuError("Authentication is not enabled", {
214
223
  title: "Authentication is not enabled",
215
224
  developerHint:
@@ -217,6 +226,10 @@ export const RouteGuard = () => {
217
226
  });
218
227
  }
219
228
 
229
+ if (needsToSignIn && !auth.sessionChecked && typeof window !== "undefined") {
230
+ return null;
231
+ }
232
+
220
233
  if (needsToSignIn && auth.isPending && typeof window !== "undefined") {
221
234
  return null;
222
235
  }
@@ -68,6 +68,7 @@ const StaticZudoku = ({
68
68
  useAuthState.setState({
69
69
  isAuthenticated: true,
70
70
  isPending: false,
71
+ sessionChecked: true,
71
72
  profile: {
72
73
  sub: "test-user",
73
74
  email: "test@example.com",
@@ -16,11 +16,7 @@ const viteAuthPlugin = (): Plugin => {
16
16
  if (id === resolvedVirtualModuleId) {
17
17
  const config = getCurrentConfig();
18
18
 
19
- if (
20
- !config.authentication ||
21
- !config.__resolvedModules.userPanel.userManagement.enabled ||
22
- config.__meta.mode === "standalone"
23
- ) {
19
+ if (!config.authentication || config.__meta.mode === "standalone") {
24
20
  return `export const configuredAuthProvider = undefined;`;
25
21
  }
26
22
  // TODO: Validate that the authConfig.type is a valid authentication provider