oidc-spa 8.1.15 → 8.2.0

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 (111) hide show
  1. package/core/BASE_URL.d.ts +4 -0
  2. package/core/BASE_URL.js +12 -0
  3. package/core/BASE_URL.js.map +1 -0
  4. package/core/createOidc.d.ts +14 -7
  5. package/core/createOidc.js +38 -3
  6. package/core/createOidc.js.map +1 -1
  7. package/core/earlyInit.d.ts +1 -0
  8. package/core/earlyInit.js +8 -8
  9. package/core/earlyInit.js.map +1 -1
  10. package/core/prShouldLoadApp.d.ts +4 -0
  11. package/core/prShouldLoadApp.js +13 -0
  12. package/core/prShouldLoadApp.js.map +1 -0
  13. package/esm/core/BASE_URL.d.ts +4 -0
  14. package/esm/core/BASE_URL.js +8 -0
  15. package/esm/core/BASE_URL.js.map +1 -0
  16. package/esm/core/createOidc.d.ts +14 -7
  17. package/esm/core/createOidc.js +38 -3
  18. package/esm/core/createOidc.js.map +1 -1
  19. package/esm/core/earlyInit.d.ts +1 -0
  20. package/esm/core/earlyInit.js +8 -8
  21. package/esm/core/earlyInit.js.map +1 -1
  22. package/esm/core/prShouldLoadApp.d.ts +4 -0
  23. package/esm/core/prShouldLoadApp.js +9 -0
  24. package/esm/core/prShouldLoadApp.js.map +1 -0
  25. package/esm/keycloak/keycloak-js/Keycloak.d.ts +1 -1
  26. package/esm/keycloak/keycloak-js/Keycloak.js +1 -1
  27. package/esm/keycloak/keycloak-js/Keycloak.js.map +1 -1
  28. package/esm/mock/oidc.d.ts +3 -1
  29. package/esm/mock/oidc.js +4 -2
  30. package/esm/mock/oidc.js.map +1 -1
  31. package/esm/react-spa/apiBuilder.d.ts +12 -0
  32. package/esm/react-spa/apiBuilder.js +26 -0
  33. package/esm/react-spa/apiBuilder.js.map +1 -0
  34. package/esm/react-spa/createOidcSpaApi.d.ts +8 -0
  35. package/esm/react-spa/createOidcSpaApi.js +387 -0
  36. package/esm/react-spa/createOidcSpaApi.js.map +1 -0
  37. package/esm/react-spa/index.d.ts +2 -0
  38. package/esm/react-spa/index.js +3 -0
  39. package/esm/react-spa/index.js.map +1 -0
  40. package/esm/react-spa/types.d.ts +279 -0
  41. package/esm/react-spa/types.js +2 -0
  42. package/esm/react-spa/types.js.map +1 -0
  43. package/esm/tanstack-start/react/apiBuilder.js.map +1 -1
  44. package/esm/tanstack-start/react/createOidcSpaApi.js +13 -9
  45. package/esm/tanstack-start/react/createOidcSpaApi.js.map +1 -1
  46. package/esm/tanstack-start/react/types.d.ts +5 -4
  47. package/keycloak/keycloak-js/Keycloak.d.ts +1 -1
  48. package/keycloak/keycloak-js/Keycloak.js +1 -1
  49. package/keycloak/keycloak-js/Keycloak.js.map +1 -1
  50. package/mock/oidc.d.ts +3 -1
  51. package/mock/oidc.js +4 -2
  52. package/mock/oidc.js.map +1 -1
  53. package/package.json +5 -1
  54. package/react-spa/apiBuilder.d.ts +12 -0
  55. package/react-spa/apiBuilder.js +29 -0
  56. package/react-spa/apiBuilder.js.map +1 -0
  57. package/react-spa/createOidcSpaApi.d.ts +8 -0
  58. package/react-spa/createOidcSpaApi.js +423 -0
  59. package/react-spa/createOidcSpaApi.js.map +1 -0
  60. package/react-spa/index.d.ts +2 -0
  61. package/react-spa/index.js +6 -0
  62. package/react-spa/index.js.map +1 -0
  63. package/react-spa/types.d.ts +279 -0
  64. package/react-spa/types.js +3 -0
  65. package/react-spa/types.js.map +1 -0
  66. package/src/angular.ts +1 -1
  67. package/src/core/BASE_URL.ts +9 -0
  68. package/src/core/createOidc.ts +64 -10
  69. package/src/core/earlyInit.ts +14 -11
  70. package/src/core/prShouldLoadApp.ts +11 -0
  71. package/src/keycloak/keycloak-js/Keycloak.ts +2 -2
  72. package/src/mock/oidc.ts +9 -3
  73. package/src/react-spa/apiBuilder.ts +70 -0
  74. package/src/react-spa/createOidcSpaApi.tsx +527 -0
  75. package/src/react-spa/index.ts +4 -0
  76. package/src/react-spa/types.tsx +308 -0
  77. package/src/tanstack-start/react/apiBuilder.ts +0 -1
  78. package/src/tanstack-start/react/createOidcSpaApi.tsx +24 -20
  79. package/src/tanstack-start/react/types.tsx +3 -4
  80. package/src/vite-plugin/handleClientEntrypoint.ts +5 -5
  81. package/src/vite-plugin/manageOptimizedDeps.ts +64 -0
  82. package/src/vite-plugin/projectType.ts +18 -0
  83. package/src/vite-plugin/vite-plugin.ts +40 -10
  84. package/vite-plugin/handleClientEntrypoint.d.ts +2 -0
  85. package/vite-plugin/handleClientEntrypoint.js +3 -4
  86. package/vite-plugin/handleClientEntrypoint.js.map +1 -1
  87. package/vite-plugin/manageOptimizedDeps.d.ts +6 -0
  88. package/vite-plugin/{excludeModuleExportFromOptimizedDeps.js → manageOptimizedDeps.js} +42 -7
  89. package/vite-plugin/manageOptimizedDeps.js.map +1 -0
  90. package/vite-plugin/projectType.d.ts +4 -0
  91. package/vite-plugin/projectType.js +15 -0
  92. package/vite-plugin/projectType.js.map +1 -0
  93. package/vite-plugin/{transformCreateFileRoute.js → transformTanstackRouterCreateFileRoute.js} +1 -1
  94. package/vite-plugin/transformTanstackRouterCreateFileRoute.js.map +1 -0
  95. package/vite-plugin/vite-plugin.d.ts +1 -1
  96. package/vite-plugin/vite-plugin.js +28 -8
  97. package/vite-plugin/vite-plugin.js.map +1 -1
  98. package/esm/tools/infer_import_meta_env_BASE_URL.d.ts +0 -1
  99. package/esm/tools/infer_import_meta_env_BASE_URL.js +0 -15
  100. package/esm/tools/infer_import_meta_env_BASE_URL.js.map +0 -1
  101. package/src/tools/infer_import_meta_env_BASE_URL.ts +0 -19
  102. package/src/vite-plugin/detectProjectType.ts +0 -20
  103. package/src/vite-plugin/excludeModuleExportFromOptimizedDeps.ts +0 -20
  104. package/vite-plugin/detectProjectType.d.ts +0 -10
  105. package/vite-plugin/detectProjectType.js +0 -15
  106. package/vite-plugin/detectProjectType.js.map +0 -1
  107. package/vite-plugin/excludeModuleExportFromOptimizedDeps.d.ts +0 -4
  108. package/vite-plugin/excludeModuleExportFromOptimizedDeps.js.map +0 -1
  109. package/vite-plugin/transformCreateFileRoute.js.map +0 -1
  110. /package/src/vite-plugin/{transformCreateFileRoute.ts → transformTanstackRouterCreateFileRoute.ts} +0 -0
  111. /package/vite-plugin/{transformCreateFileRoute.d.ts → transformTanstackRouterCreateFileRoute.d.ts} +0 -0
@@ -0,0 +1,308 @@
1
+ import type { ReactNode, ComponentType } from "react";
2
+ import type { Oidc as Oidc_core, OidcInitializationError } from "../core";
3
+ import type { OidcMetadata } from "../core/OidcMetadata";
4
+
5
+ export type UseOidc<DecodedIdToken> = {
6
+ (params?: { assert?: undefined }): UseOidc.Oidc<DecodedIdToken>;
7
+ (params: { assert: "user logged in" }): UseOidc.Oidc.LoggedIn<DecodedIdToken>;
8
+ (params: { assert: "user not logged in" }): UseOidc.Oidc.NotLoggedIn;
9
+ };
10
+
11
+ export namespace UseOidc {
12
+ export type WithAutoLogin<DecodedIdToken> = (params?: {
13
+ assert: "user logged in";
14
+ }) => Oidc.LoggedIn<DecodedIdToken>;
15
+
16
+ export type Oidc<DecodedIdToken> =
17
+ | (Oidc.NotLoggedIn & {
18
+ decodedIdToken?: never;
19
+ logout?: never;
20
+ renewTokens?: never;
21
+ goToAuthServer?: never;
22
+ backFromAuthServer?: never;
23
+ isNewBrowserSession?: never;
24
+ })
25
+ | (Oidc.LoggedIn<DecodedIdToken> & {
26
+ login?: never;
27
+ initializationError?: never;
28
+ });
29
+
30
+ export namespace Oidc {
31
+ type Common = {
32
+ issuerUri: string;
33
+ clientId: string;
34
+ };
35
+
36
+ export type NotLoggedIn = Common & {
37
+ login: (params?: {
38
+ extraQueryParams?: Record<string, string | undefined>;
39
+ redirectUrl?: string;
40
+ transformUrlBeforeRedirect?: (url: string) => string;
41
+ }) => Promise<never>;
42
+ autoLogoutState: {
43
+ shouldDisplayWarning: false;
44
+ };
45
+ isUserLoggedIn: false;
46
+ initializationError: OidcInitializationError | undefined;
47
+ };
48
+
49
+ export type LoggedIn<DecodedIdToken> = Common & {
50
+ isUserLoggedIn: true;
51
+ decodedIdToken: DecodedIdToken;
52
+ logout: Oidc_core.LoggedIn["logout"];
53
+ renewTokens: Oidc_core.LoggedIn["renewTokens"];
54
+ goToAuthServer: Oidc_core.LoggedIn["goToAuthServer"];
55
+ backFromAuthServer: Oidc_core.LoggedIn["backFromAuthServer"];
56
+ isNewBrowserSession: boolean;
57
+ autoLogoutState:
58
+ | {
59
+ shouldDisplayWarning: true;
60
+ secondsLeftBeforeAutoLogout: number;
61
+ }
62
+ | {
63
+ shouldDisplayWarning: false;
64
+ };
65
+ };
66
+ }
67
+ }
68
+
69
+ export type GetOidc<DecodedIdToken> = {
70
+ (params?: { assert?: undefined }): Promise<GetOidc.Oidc<DecodedIdToken>>;
71
+ (params: { assert: "user logged in" }): Promise<GetOidc.Oidc.LoggedIn<DecodedIdToken>>;
72
+ (params: { assert: "user not logged in" }): Promise<GetOidc.Oidc.NotLoggedIn>;
73
+ };
74
+
75
+ export namespace GetOidc {
76
+ export type WithAutoLogin<DecodedIdToken> = (params?: {
77
+ assert: "user logged in";
78
+ }) => Promise<Oidc.LoggedIn<DecodedIdToken>>;
79
+
80
+ export type Oidc<DecodedIdToken> =
81
+ | (Oidc.NotLoggedIn & {
82
+ getAccessToken?: never;
83
+ getDecodedIdToken?: never;
84
+ logout?: never;
85
+ renewTokens?: never;
86
+ goToAuthServer?: never;
87
+ backFromAuthServer?: never;
88
+ isNewBrowserSession?: never;
89
+ subscribeToAutoLogoutState?: never;
90
+ })
91
+ | (Oidc.LoggedIn<DecodedIdToken> & {
92
+ initializationError?: never;
93
+ login?: never;
94
+ });
95
+
96
+ export namespace Oidc {
97
+ type Common = {
98
+ issuerUri: string;
99
+ clientId: string;
100
+ };
101
+
102
+ export type NotLoggedIn = Common & {
103
+ isUserLoggedIn: false;
104
+ initializationError: OidcInitializationError | undefined;
105
+ login: Oidc_core.NotLoggedIn["login"];
106
+ };
107
+
108
+ export type LoggedIn<DecodedIdToken> = Common & {
109
+ isUserLoggedIn: true;
110
+ getAccessToken: () => Promise<string>;
111
+ getDecodedIdToken: () => DecodedIdToken;
112
+ logout: Oidc_core.LoggedIn["logout"];
113
+ renewTokens: Oidc_core.LoggedIn["renewTokens"];
114
+ goToAuthServer: Oidc_core.LoggedIn["goToAuthServer"];
115
+ backFromAuthServer: Oidc_core.LoggedIn["backFromAuthServer"];
116
+ isNewBrowserSession: boolean;
117
+ subscribeToAutoLogoutState: (
118
+ next: (
119
+ autoLogoutState:
120
+ | {
121
+ shouldDisplayWarning: true;
122
+ secondsLeftBeforeAutoLogout: number;
123
+ }
124
+ | {
125
+ shouldDisplayWarning: false;
126
+ }
127
+ ) => void
128
+ ) => { unsubscribeFromAutoLogoutState: () => void };
129
+ };
130
+ }
131
+ }
132
+
133
+ export type ParamsOfBootstrap<AutoLogin, DecodedIdToken> =
134
+ | ParamsOfBootstrap.Real<AutoLogin>
135
+ | ParamsOfBootstrap.Mock<AutoLogin, DecodedIdToken>;
136
+
137
+ export namespace ParamsOfBootstrap {
138
+ export type Real<AutoLogin> = {
139
+ implementation: "real";
140
+
141
+ /**
142
+ * See: https://docs.oidc-spa.dev/v/v8/providers-configuration/provider-configuration
143
+ */
144
+ issuerUri: string;
145
+ /**
146
+ * See: https://docs.oidc-spa.dev/v/v8/providers-configuration/provider-configuration
147
+ */
148
+ clientId: string;
149
+
150
+ /**
151
+ * Default: 45 second.
152
+ * It defines how long before the auto logout we should start
153
+ * displaying an overlay message to the user alerting them
154
+ * like: "Are you still there? You'll be disconnected in 45...44..."
155
+ * NOTE: This parameter is only UI related! It does not defines
156
+ * after how much time of inactivity the user should be auto logged out.
157
+ * This is a server policy (that can be overwrote by idleSessionLifetimeInSeconds)
158
+ * See: https://docs.oidc-spa.dev/v/v8/auto-logout
159
+ */
160
+ startCountdownSecondsBeforeAutoLogout?: number;
161
+ /**
162
+ * This parameter defines after how many seconds of inactivity the user should be
163
+ * logged out automatically.
164
+ *
165
+ * WARNING: It should be configured on the identity server side
166
+ * as it's the authoritative source for security policies and not the client.
167
+ * If you don't provide this parameter it will be inferred from the refresh token expiration time.
168
+ * Some provider however don't issue a refresh token or do not correctly set the
169
+ * expiration time. This parameter enable you to hard code the value to compensate
170
+ * the shortcoming of your auth server.
171
+ * */
172
+ idleSessionLifetimeInSeconds?: number;
173
+
174
+ /**
175
+ * The scopes being requested from the OIDC/OAuth2 provider (default: `["profile"]`
176
+ * (the scope "openid" is added automatically as it's mandatory)
177
+ **/
178
+ scopes?: string[];
179
+
180
+ /**
181
+ * Transform the url (authorization endpoint) before redirecting to the login pages.
182
+ *
183
+ * The isSilent parameter is true when the redirect is initiated in the background iframe for silent signin.
184
+ * This can be used to omit ui related query parameters (like `ui_locales`).
185
+ */
186
+ transformUrlBeforeRedirect?: (params: { authorizationUrl: string; isSilent: boolean }) => string;
187
+
188
+ /**
189
+ * Extra query params to be added to the authorization endpoint url before redirecting or silent signing in.
190
+ * You can provide a function that returns those extra query params, it will be called
191
+ * when login() is called.
192
+ *
193
+ * Example: extraQueryParams: ()=> ({ ui_locales: "fr" })
194
+ *
195
+ * This parameter can also be passed to login() directly.
196
+ */
197
+ extraQueryParams?:
198
+ | Record<string, string | undefined>
199
+ | ((params: { isSilent: boolean; url: string }) => Record<string, string | undefined>);
200
+ /**
201
+ * Extra body params to be added to the /token POST request.
202
+ *
203
+ * It will be used when for the initial request, whenever the token is getting refreshed and if you call `renewTokens()`.
204
+ * You can also provide this parameter directly to the `renewTokens()` method.
205
+ *
206
+ * It can be either a string to string record or a function that returns a string to string record.
207
+ *
208
+ * Example: extraTokenParams: ()=> ({ selectedCustomer: "xxx" })
209
+ * extraTokenParams: { selectedCustomer: "xxx" }
210
+ */
211
+ extraTokenParams?:
212
+ | Record<string, string | undefined>
213
+ | (() => Record<string, string | undefined>);
214
+
215
+ /**
216
+ * Default: false
217
+ *
218
+ * See: https://docs.oidc-spa.dev/v/v8/resources/iframe-related-issues
219
+ */
220
+ noIframe?: boolean;
221
+
222
+ debugLogs?: boolean;
223
+
224
+ /**
225
+ * WARNING: This option exists solely as a workaround
226
+ * for limitations in the Google OAuth API.
227
+ * See: https://docs.oidc-spa.dev/providers-configuration/google-oauth
228
+ *
229
+ * Do not use this for other providers.
230
+ * If you think you need a client secret in a SPA, you are likely
231
+ * trying to use a confidential (private) client in the browser,
232
+ * which is insecure and not supported.
233
+ */
234
+ __unsafe_clientSecret?: string;
235
+
236
+ /**
237
+ * This option should only be used as a last resort.
238
+ *
239
+ * If your OIDC provider is correctly configured, this should not be necessary.
240
+ *
241
+ * The metadata is normally retrieved automatically from:
242
+ * `${issuerUri}/.well-known/openid-configuration`
243
+ *
244
+ * Use this only if that endpoint is not accessible (e.g. due to missing CORS headers
245
+ * or non-standard deployments), and you cannot fix the server-side configuration.
246
+ */
247
+ __metadata?: Partial<OidcMetadata>;
248
+
249
+ /**
250
+ * WARNING: Setting this to true is a workaround for provider
251
+ * like Google OAuth that don't support JWT access token.
252
+ * Use at your own risk, this is a hack.
253
+ */
254
+ __unsafe_useIdTokenAsAccessToken?: boolean;
255
+
256
+ /**
257
+ * Let's you override the params passed to
258
+ * (if you weren't able to provide it)
259
+ */
260
+ BASE_URL?: string;
261
+ } & (AutoLogin extends true ? {} : {});
262
+
263
+ export type Mock<AutoLogin, DecodedIdToken> = {
264
+ implementation: "mock";
265
+ issuerUri_mock?: string;
266
+ clientId_mock?: string;
267
+ decodedIdToken_mock?: DecodedIdToken;
268
+
269
+ /**
270
+ * Let's you override the params passed to
271
+ * (if you weren't able to provide it)
272
+ */
273
+ BASE_URL?: string;
274
+ } & (AutoLogin extends true
275
+ ? {
276
+ isUserInitiallyLoggedIn?: true;
277
+ }
278
+ : {
279
+ isUserInitiallyLoggedIn: boolean;
280
+ });
281
+ }
282
+
283
+ export type OidcSpaApi<AutoLogin, DecodedIdToken> = {
284
+ bootstrapOidc: (params: ParamsOfBootstrap<AutoLogin, DecodedIdToken>) => void;
285
+ useOidc: AutoLogin extends true ? UseOidc.WithAutoLogin<DecodedIdToken> : UseOidc<DecodedIdToken>;
286
+ getOidc: AutoLogin extends true ? GetOidc.WithAutoLogin<DecodedIdToken> : GetOidc<DecodedIdToken>;
287
+ } & (AutoLogin extends true
288
+ ? {
289
+ OidcInitializationErrorGate: (props: {
290
+ errorComponent: ComponentType<{
291
+ oidcInitializationError: OidcInitializationError;
292
+ }>;
293
+ children: ReactNode;
294
+ }) => ReactNode;
295
+ }
296
+ : {
297
+ enforceLogin: (loaderContext: {
298
+ request?: { url?: string };
299
+ cause?: "preload" | string;
300
+ location?: {
301
+ href?: string;
302
+ };
303
+ }) => Promise<void | never>;
304
+
305
+ withLoginEnforced: <Props extends Record<string, unknown>>(
306
+ component: ComponentType<Props>
307
+ ) => (props: Props) => ReactNode;
308
+ });
@@ -61,7 +61,6 @@ export type OidcSpaApiBuilder<
61
61
  ExcludedMethod | "withAccessTokenValidation"
62
62
  >;
63
63
  };
64
-
65
64
  finalize: () => OidcSpaApi<AutoLogin, DecodedIdToken, AccessTokenClaims>;
66
65
  },
67
66
  ExcludedMethod
@@ -1,4 +1,11 @@
1
- import { useState, useEffect, type ReactNode, createContext, useContext } from "react";
1
+ import {
2
+ useState,
3
+ useEffect,
4
+ type ReactNode,
5
+ createContext,
6
+ useContext,
7
+ type ComponentType
8
+ } from "react";
2
9
  import type {
3
10
  CreateValidateAndGetAccessTokenClaims,
4
11
  OidcSpaApi,
@@ -13,7 +20,6 @@ import { OidcInitializationError } from "../../core/OidcInitializationError";
13
20
  import { Deferred } from "../../tools/Deferred";
14
21
  import { isBrowser } from "../../tools/isBrowser";
15
22
  import { assert, type Equals, is } from "../../tools/tsafe/assert";
16
- import { infer_import_meta_env_BASE_URL } from "../../tools/infer_import_meta_env_BASE_URL";
17
23
  import { createObjectThatThrowsIfAccessed } from "../../tools/createObjectThatThrowsIfAccessed";
18
24
  import { createStatefulEvt } from "../../tools/StatefulEvt";
19
25
  import { id } from "../../tools/tsafe/id";
@@ -610,7 +616,6 @@ export function createOidcSpaApi<
610
616
  const { createMockOidc } = await import("../../mock/oidc");
611
617
 
612
618
  const oidcCore = await createMockOidc({
613
- homeUrl: infer_import_meta_env_BASE_URL(),
614
619
  // NOTE: The `as false` is lying here, it's just to preserve some level of type-safety.
615
620
  autoLogin: autoLogin as false,
616
621
  // NOTE: Same here, the nullish coalescing is lying.
@@ -641,15 +646,12 @@ export function createOidcSpaApi<
641
646
  {
642
647
  const { createOidc } = await prModuleCore;
643
648
 
644
- const homeUrl = infer_import_meta_env_BASE_URL();
645
-
646
649
  let oidcCoreOrInitializationError:
647
650
  | Oidc_core<DecodedIdToken>
648
651
  | OidcInitializationError;
649
652
 
650
653
  try {
651
654
  oidcCoreOrInitializationError = await createOidc({
652
- homeUrl,
653
655
  autoLogin,
654
656
  decodedIdTokenSchema,
655
657
  issuerUri: paramsOfBootstrap.issuerUri,
@@ -735,12 +737,11 @@ export function createOidcSpaApi<
735
737
  enforceLogin.__isOidcSpaEnforceLogin = true;
736
738
 
737
739
  function OidcInitializationGate(props: {
738
- renderFallback: (props: {
739
- initializationError: OidcInitializationError | undefined;
740
- }) => ReactNode;
740
+ errorComponent?: ComponentType<{ oidcInitializationError: OidcInitializationError }>;
741
+ pendingComponent?: ComponentType<{}>;
741
742
  children: ReactNode;
742
743
  }): ReactNode {
743
- const { renderFallback, children } = props;
744
+ const { errorComponent: ErrorComponent, pendingComponent: PendingComponent, children } = props;
744
745
 
745
746
  const [oidcCoreOrInitializationError, setOidcCoreOrInitializationError] = useState<
746
747
  Oidc_core<DecodedIdToken> | OidcInitializationError | undefined
@@ -761,18 +762,21 @@ export function createOidcSpaApi<
761
762
  };
762
763
  }, []);
763
764
 
764
- if (
765
- oidcCoreOrInitializationError === undefined ||
766
- oidcCoreOrInitializationError instanceof OidcInitializationError
767
- ) {
768
- return renderFallback({ initializationError: oidcCoreOrInitializationError });
765
+ if (oidcCoreOrInitializationError === undefined) {
766
+ if (PendingComponent === undefined) {
767
+ return null;
768
+ }
769
+ return <PendingComponent />;
769
770
  }
770
771
 
771
- return (
772
- <context_isFreeOfSsrHydrationConcern.Provider value={true}>
773
- {children}
774
- </context_isFreeOfSsrHydrationConcern.Provider>
775
- );
772
+ if (oidcCoreOrInitializationError instanceof OidcInitializationError) {
773
+ if (ErrorComponent === undefined) {
774
+ throw oidcCoreOrInitializationError;
775
+ }
776
+ return <ErrorComponent oidcInitializationError={oidcCoreOrInitializationError} />;
777
+ }
778
+
779
+ return children;
776
780
  }
777
781
 
778
782
  const prValidateAndGetAccessTokenClaims =
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from "react";
1
+ import type { ReactNode, ComponentType } from "react";
2
2
  import type { Oidc as Oidc_core, OidcInitializationError } from "../../core";
3
3
  import type { FunctionMiddlewareAfterServer, RequestMiddlewareAfterServer } from "@tanstack/react-start";
4
4
  import type { GetterOrDirectValue } from "../../tools/GetterOrDirectValue";
@@ -390,9 +390,8 @@ export type OidcSpaApi<AutoLogin, DecodedIdToken, AccessTokenClaims> = {
390
390
  (AutoLogin extends true
391
391
  ? {
392
392
  OidcInitializationGate: (props: {
393
- renderFallback: (props: {
394
- initializationError: OidcInitializationError | undefined;
395
- }) => ReactNode;
393
+ errorComponent?: ComponentType<{ oidcInitializationError: OidcInitializationError }>;
394
+ pendingComponent?: ComponentType<{}>;
396
395
  children: ReactNode;
397
396
  }) => ReactNode;
398
397
  }
@@ -1,7 +1,6 @@
1
1
  import type { OidcSpaVitePluginParams } from "./vite-plugin";
2
2
  import type { ResolvedConfig } from "vite";
3
3
  import type { PluginContext } from "rollup";
4
- import { detectProjectType, ProjectType } from "./detectProjectType";
5
4
  import { existsSync } from "node:fs";
6
5
  import { promises as fs } from "node:fs";
7
6
  import * as path from "node:path";
@@ -9,6 +8,7 @@ import { fileURLToPath } from "node:url";
9
8
  import { normalizePath } from "vite";
10
9
  import { assert } from "../tools/tsafe/assert";
11
10
  import type { Equals } from "../tools/tsafe/Equals";
11
+ import type { ProjectType } from "./projectType";
12
12
 
13
13
  type EntryResolution = {
14
14
  absolutePath: string;
@@ -32,10 +32,9 @@ const TANSTACK_ENTRY_CANDIDATES = ["client.tsx", "client.ts", "client.jsx", "cli
32
32
  export function createLoadHandleEntrypoint(params: {
33
33
  oidcSpaVitePluginParams: OidcSpaVitePluginParams;
34
34
  resolvedConfig: ResolvedConfig;
35
+ projectType: ProjectType;
35
36
  }) {
36
- const { oidcSpaVitePluginParams, resolvedConfig } = params;
37
-
38
- const projectType = detectProjectType({ resolvedConfig });
37
+ const { oidcSpaVitePluginParams, resolvedConfig, projectType } = params;
39
38
 
40
39
  const entryResolution = resolveEntryForProject({
41
40
  config: resolvedConfig,
@@ -82,7 +81,8 @@ export function createLoadHandleEntrypoint(params: {
82
81
  ` freezeFetch: ${freezeFetch},`,
83
82
  ` freezeXMLHttpRequest: ${freezeXMLHttpRequest},`,
84
83
  ` freezeWebSocket: ${freezeWebSocket},`,
85
- ` isPostLoginRedirectManual: ${projectType === "tanstack-start"}`,
84
+ ` isPostLoginRedirectManual: ${projectType === "tanstack-start"},`,
85
+ ` BASE_URL: "${resolvedConfig.base}"`,
86
86
  `});`,
87
87
  ``,
88
88
  `if(shouldLoadApp){`,
@@ -0,0 +1,64 @@
1
+ import type { UserConfig } from "vite";
2
+ import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath_cjs";
3
+ import * as fs from "node:fs";
4
+ import { join as pathJoin } from "path";
5
+ import type { ProjectType } from "./projectType";
6
+ import { assert } from "../tools/tsafe/assert";
7
+
8
+ export function manageOptimizedDeps(params: {
9
+ userConfig: UserConfig;
10
+ projectType: ProjectType;
11
+ }): UserConfig {
12
+ const { userConfig, projectType } = params;
13
+
14
+ if (projectType === "other") {
15
+ return userConfig;
16
+ }
17
+
18
+ const packageJsonParsed = JSON.parse(
19
+ fs.readFileSync(pathJoin(getThisCodebaseRootDirPath(), "package.json")).toString("utf8")
20
+ ) as { name: string; exports: Record<string, { module?: string }> };
21
+
22
+ const moduleNames = Object.entries(packageJsonParsed.exports)
23
+ .filter(([, value]) => value.module !== undefined)
24
+ .map(([key]) => key.replace(/^\./, packageJsonParsed.name));
25
+
26
+ switch (projectType) {
27
+ case "tanstack-start":
28
+ {
29
+ ((userConfig.optimizeDeps ??= {}).exclude ??= []).push(...moduleNames);
30
+ }
31
+ break;
32
+ case "react-router-framework":
33
+ {
34
+ const moduleNames_include = [
35
+ "oidc-spa/react-spa",
36
+ "oidc-spa/entrypoint",
37
+ "oidc-spa/keycloak"
38
+ ];
39
+
40
+ for (const moduleName of moduleNames_include) {
41
+ assert(moduleNames.includes(moduleName));
42
+ }
43
+
44
+ const isZodInstalled = (() => {
45
+ try {
46
+ require.resolve(`zod/package.json`);
47
+ } catch {
48
+ return false;
49
+ }
50
+
51
+ return true;
52
+ })();
53
+
54
+ if (isZodInstalled) {
55
+ moduleNames_include.push("zod");
56
+ }
57
+
58
+ ((userConfig.optimizeDeps ??= {}).include ??= []).push(...moduleNames_include);
59
+ }
60
+ break;
61
+ }
62
+
63
+ return userConfig;
64
+ }
@@ -0,0 +1,18 @@
1
+ export type ProjectType = "tanstack-start" | "react-router-framework" | "other";
2
+
3
+ export function getProjectType(params: { pluginNames: string[] }) {
4
+ const pluginNames = new Set(params.pluginNames);
5
+
6
+ if (pluginNames.has("tanstack-react-start:config")) {
7
+ return "tanstack-start";
8
+ }
9
+
10
+ if (
11
+ pluginNames.has("react-router") ||
12
+ Array.from(pluginNames).some(pluginName => pluginName.startsWith("react-router:"))
13
+ ) {
14
+ return "react-router-framework";
15
+ }
16
+
17
+ return "other";
18
+ }
@@ -1,12 +1,16 @@
1
- import type { Plugin } from "vite";
1
+ import type { Plugin, TransformResult } from "vite";
2
2
  import { assert } from "../tools/tsafe/assert";
3
3
  import type { Param0 } from "../tools/tsafe/Param0";
4
4
  import type { oidcEarlyInit } from "../entrypoint";
5
5
  import { createLoadHandleEntrypoint } from "./handleClientEntrypoint";
6
- import { excludeModuleExportFromOptimizedDeps } from "./excludeModuleExportFromOptimizedDeps";
7
- import { transformCreateFileRoute } from "./transformCreateFileRoute";
6
+ import { manageOptimizedDeps } from "./manageOptimizedDeps";
7
+ import { transformCreateFileRoute } from "./transformTanstackRouterCreateFileRoute";
8
+ import { getProjectType, type ProjectType } from "./projectType";
8
9
 
9
- export type OidcSpaVitePluginParams = Omit<Param0<typeof oidcEarlyInit>, "isPostLoginRedirectManual">;
10
+ export type OidcSpaVitePluginParams = Omit<
11
+ Param0<typeof oidcEarlyInit>,
12
+ "isPostLoginRedirectManual" | "BASE_URL"
13
+ >;
10
14
 
11
15
  export function oidcSpa(
12
16
  params: OidcSpaVitePluginParams = {
@@ -17,24 +21,50 @@ export function oidcSpa(
17
21
  ) {
18
22
  let loadHandleEntrypoint: ReturnType<typeof createLoadHandleEntrypoint> | undefined = undefined;
19
23
 
24
+ let projectType: ProjectType | undefined = undefined;
25
+
20
26
  const plugin: Plugin = {
21
27
  name: "oidc-spa",
22
28
  enforce: "pre",
23
29
  config(userConfig) {
24
- userConfig = excludeModuleExportFromOptimizedDeps({ userConfig });
30
+ const projectType = getProjectType({
31
+ pluginNames:
32
+ userConfig.plugins
33
+ ?.flat()
34
+ .filter(plugin => plugin instanceof Object)
35
+ .filter(plugin => "name" in plugin)
36
+ .map(plugin => plugin.name) ?? []
37
+ });
38
+
39
+ userConfig = manageOptimizedDeps({ userConfig, projectType });
25
40
  return userConfig;
26
41
  },
27
42
  configResolved(resolvedConfig) {
43
+ projectType = getProjectType({
44
+ pluginNames: resolvedConfig.plugins.map(({ name }) => name)
45
+ });
46
+
28
47
  loadHandleEntrypoint = createLoadHandleEntrypoint({
29
48
  oidcSpaVitePluginParams: params,
30
- resolvedConfig
49
+ resolvedConfig,
50
+ projectType
31
51
  });
32
52
  },
33
53
  transform(code, id) {
34
- const transformed = transformCreateFileRoute({
35
- code,
36
- id
37
- });
54
+ let transformed: TransformResult | null = null;
55
+
56
+ assert(projectType !== undefined);
57
+
58
+ tanstack_start_specific_transformations: {
59
+ if (projectType !== "tanstack-start") {
60
+ break tanstack_start_specific_transformations;
61
+ }
62
+
63
+ transformed = transformCreateFileRoute({
64
+ code,
65
+ id
66
+ });
67
+ }
38
68
 
39
69
  return transformed;
40
70
  },
@@ -1,9 +1,11 @@
1
1
  import type { OidcSpaVitePluginParams } from "./vite-plugin";
2
2
  import type { ResolvedConfig } from "vite";
3
3
  import type { PluginContext } from "rollup";
4
+ import type { ProjectType } from "./projectType";
4
5
  export declare function createLoadHandleEntrypoint(params: {
5
6
  oidcSpaVitePluginParams: OidcSpaVitePluginParams;
6
7
  resolvedConfig: ResolvedConfig;
8
+ projectType: ProjectType;
7
9
  }): (params: {
8
10
  id: string;
9
11
  pluginContext: PluginContext;
@@ -34,7 +34,6 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.createLoadHandleEntrypoint = createLoadHandleEntrypoint;
37
- const detectProjectType_1 = require("./detectProjectType");
38
37
  const node_fs_1 = require("node:fs");
39
38
  const node_fs_2 = require("node:fs");
40
39
  const path = __importStar(require("node:path"));
@@ -51,8 +50,7 @@ const REACT_ROUTER_ENTRY_CANDIDATES = [
51
50
  ];
52
51
  const TANSTACK_ENTRY_CANDIDATES = ["client.tsx", "client.ts", "client.jsx", "client.js"];
53
52
  function createLoadHandleEntrypoint(params) {
54
- const { oidcSpaVitePluginParams, resolvedConfig } = params;
55
- const projectType = (0, detectProjectType_1.detectProjectType)({ resolvedConfig });
53
+ const { oidcSpaVitePluginParams, resolvedConfig, projectType } = params;
56
54
  const entryResolution = resolveEntryForProject({
57
55
  config: resolvedConfig,
58
56
  projectType
@@ -82,7 +80,8 @@ function createLoadHandleEntrypoint(params) {
82
80
  ` freezeFetch: ${freezeFetch},`,
83
81
  ` freezeXMLHttpRequest: ${freezeXMLHttpRequest},`,
84
82
  ` freezeWebSocket: ${freezeWebSocket},`,
85
- ` isPostLoginRedirectManual: ${projectType === "tanstack-start"}`,
83
+ ` isPostLoginRedirectManual: ${projectType === "tanstack-start"},`,
84
+ ` BASE_URL: "${resolvedConfig.base}"`,
86
85
  `});`,
87
86
  ``,
88
87
  `if(shouldLoadApp){`,