@@ -25,12 +25,6 @@ interface RuntimeConfig {
2525 redirectUri ?: string ;
2626}
2727
28- let runtimeConfig : RuntimeConfig = { } ;
29- if ( ! import . meta. env . DEV ) {
30- const response = await fetch ( '/runtime.json' ) ;
31- runtimeConfig = await response . json ( ) ;
32- }
33-
3428// Helper function to get config value, preferring env vars in development mode
3529// and filtering out placeholder values from both runtime and env sources
3630const getConfigValue = ( runtimeValue : string | undefined , envValue : string | undefined ) : string | undefined => {
@@ -50,16 +44,98 @@ const getConfigValue = (runtimeValue: string | undefined, envValue: string | und
5044 return filteredRuntimeValue || filteredEnvValue ;
5145} ;
5246
47+ // Load runtime configuration asynchronously
48+ const loadRuntimeConfig = async ( ) : Promise < RuntimeConfig > => {
49+ if ( import . meta. env . DEV ) {
50+ // In development mode, skip fetching runtime.json
51+ return { } ;
52+ }
53+
54+ try {
55+ const response = await fetch ( '/runtime.json' ) ;
56+ if ( ! response . ok ) {
57+ console . warn ( `Failed to fetch runtime.json: ${ response . status } ${ response . statusText } ` ) ;
58+ return { } ;
59+ }
60+ return await response . json ( ) ;
61+ } catch ( error ) {
62+ console . error ( 'Error loading runtime configuration:' , error ) ;
63+ // Return empty config to fallback to environment variables
64+ return { } ;
65+ }
66+ } ;
67+
68+ // Initialize runtime config with promise-based guard to prevent race conditions
69+ let runtimeConfig : RuntimeConfig = { } ;
70+ let configInitializationPromise : Promise < void > | null = null ;
71+
72+ const initializeConfig = async ( ) : Promise < void > => {
73+ // If already initializing or initialized, return the existing promise
74+ if ( configInitializationPromise ) {
75+ return configInitializationPromise ;
76+ }
77+
78+ // Create and store the initialization promise
79+ // Wrap in try-catch to ensure the promise never rejects
80+ configInitializationPromise = ( async ( ) => {
81+ try {
82+ runtimeConfig = await loadRuntimeConfig ( ) ;
83+ } catch ( error ) {
84+ // This should never happen since loadRuntimeConfig has its own error handling,
85+ // but we catch it here as a safety measure to ensure the promise always resolves
86+ console . error ( 'Unexpected error during config initialization:' , error ) ;
87+ runtimeConfig = { } ;
88+ }
89+ } ) ( ) ;
90+
91+ return configInitializationPromise ;
92+ } ;
93+
94+ // Start initialization immediately
95+ const configReady = initializeConfig ( ) ;
96+
97+ /**
98+ * Ensures the configuration is fully loaded before proceeding.
99+ *
100+ * IMPORTANT: You must call this function and await it before accessing config values
101+ * in production mode to ensure runtime.json has been loaded.
102+ *
103+ * @example
104+ * ```tsx
105+ * await ensureConfigReady();
106+ * const endpoint = config.applicationID; // Now safe to access
107+ * ```
108+ */
109+ export const ensureConfigReady = ( ) : Promise < void > => configReady ;
110+
53111const config = {
54- applicationID : getConfigValue ( runtimeConfig . applicationID , import . meta. env . VITE_REACT_APP_AUTH_APP_ID ) || '' ,
55- applicationsEndpoint : getConfigValue ( runtimeConfig . applicationsEndpoint , import . meta. env . VITE_REACT_APPLICATIONS_ENDPOINT ) || '' ,
56- flowEndpoint : getConfigValue ( runtimeConfig . flowEndpoint , import . meta. env . VITE_REACT_APP_SERVER_FLOW_ENDPOINT ) || '' ,
57- authorizationEndpoint : getConfigValue ( runtimeConfig . authorizationEndpoint , import . meta. env . VITE_REACT_APP_SERVER_AUTHORIZATION_ENDPOINT ) || '' ,
58- tokenEndpoint : getConfigValue ( runtimeConfig . tokenEndpoint , import . meta. env . VITE_REACT_APP_SERVER_TOKEN_ENDPOINT ) || '' ,
59- clientId : import . meta. env . VITE_REACT_APP_CLIENT_ID || '' ,
60- clientSecret : import . meta. env . VITE_REACT_APP_CLIENT_SECRET || '' ,
61- redirectUri : getConfigValue ( runtimeConfig . redirectUri , import . meta. env . VITE_REACT_APP_REDIRECT_URI ) || '' ,
62- scope : import . meta. env . VITE_REACT_APP_SCOPE || ''
112+ get applicationID ( ) {
113+ return getConfigValue ( runtimeConfig . applicationID , import . meta. env . VITE_REACT_APP_AUTH_APP_ID ) || '' ;
114+ } ,
115+ get applicationsEndpoint ( ) {
116+ return getConfigValue ( runtimeConfig . applicationsEndpoint , import . meta. env . VITE_REACT_APPLICATIONS_ENDPOINT ) || '' ;
117+ } ,
118+ get flowEndpoint ( ) {
119+ return getConfigValue ( runtimeConfig . flowEndpoint , import . meta. env . VITE_REACT_APP_SERVER_FLOW_ENDPOINT ) || '' ;
120+ } ,
121+ get authorizationEndpoint ( ) {
122+ return getConfigValue ( runtimeConfig . authorizationEndpoint , import . meta. env . VITE_REACT_APP_SERVER_AUTHORIZATION_ENDPOINT ) || '' ;
123+ } ,
124+ get tokenEndpoint ( ) {
125+ return getConfigValue ( runtimeConfig . tokenEndpoint , import . meta. env . VITE_REACT_APP_SERVER_TOKEN_ENDPOINT ) || '' ;
126+ } ,
127+ get clientId ( ) {
128+ return import . meta. env . VITE_REACT_APP_CLIENT_ID || '' ;
129+ } ,
130+ get clientSecret ( ) {
131+ return import . meta. env . VITE_REACT_APP_CLIENT_SECRET || '' ;
132+ } ,
133+ get redirectUri ( ) {
134+ return getConfigValue ( runtimeConfig . redirectUri , import . meta. env . VITE_REACT_APP_REDIRECT_URI ) || '' ;
135+ } ,
136+ get scope ( ) {
137+ return import . meta. env . VITE_REACT_APP_SCOPE || '' ;
138+ }
63139} ;
64140
65141export default config ;
0 commit comments