@@ -48,6 +48,48 @@ const DEFAULT_CONFIG: LsConfig = {
4848 debug : false ,
4949} ;
5050
51+ // ============================================================================
52+ // Config Ready Gating
53+ // ============================================================================
54+
55+ /**
56+ * Promise that resolves when config is ready (from localStorage or CustomEvent).
57+ * First fetch waits on this to ensure correct config is used.
58+ */
59+ let resolveConfigReady : ( ( ) => void ) | null = null ;
60+ const configReady = new Promise < void > ( ( resolve ) => {
61+ resolveConfigReady = resolve ;
62+ } ) ;
63+
64+ /**
65+ * Resolve the configReady promise (idempotent - only resolves once).
66+ */
67+ function tryResolveConfigReady ( ) : void {
68+ if ( resolveConfigReady ) {
69+ resolveConfigReady ( ) ;
70+ resolveConfigReady = null ;
71+ }
72+ }
73+
74+ /**
75+ * Wait for config to be ready with timeout.
76+ * Returns immediately if config already loaded.
77+ * After timeout, marks config as ready to avoid repeated delays on subsequent fetches.
78+ * @param timeoutMs Max time to wait (default 50ms)
79+ */
80+ async function ensureConfigReady ( timeoutMs = 50 ) : Promise < void > {
81+ if ( ! resolveConfigReady ) {
82+ // Already resolved
83+ return ;
84+ }
85+ await Promise . race ( [
86+ configReady ,
87+ new Promise < void > ( ( resolve ) => setTimeout ( resolve , timeoutMs ) ) ,
88+ ] ) ;
89+ // After timeout (or config arrived), mark as ready so subsequent fetches don't wait
90+ tryResolveConfigReady ( ) ;
91+ }
92+
5193/**
5294 * localStorage key - must match storage.ts LOCAL_STORAGE_KEY
5395 */
@@ -197,13 +239,6 @@ async function interceptedFetch(
197239 nativeFetch : typeof fetch ,
198240 ...args : Parameters < typeof fetch >
199241) : Promise < Response > {
200- const cfg = getConfig ( ) ;
201-
202- // Skip if disabled
203- if ( ! cfg . enabled ) {
204- return nativeFetch ( ...args ) ;
205- }
206-
207242 // Extract URL/method BEFORE fetching (handles string, URL, Request)
208243 // This avoids "Body has already been consumed" error when args[0] is a Request
209244 const [ input , init ] = args ;
@@ -223,11 +258,21 @@ async function interceptedFetch(
223258
224259 const url = new URL ( urlString , location . href ) ;
225260
226- // Early return for non-matching requests (before fetching)
261+ // Early return for non-matching requests - no config wait needed
227262 if ( ! isConversationRequest ( method , url ) ) {
228263 return nativeFetch ( ...args ) ;
229264 }
230265
266+ // Wait for config only for ChatGPT API requests (max 50ms on first request)
267+ await ensureConfigReady ( ) ;
268+
269+ const cfg = getConfig ( ) ;
270+
271+ // Skip if disabled
272+ if ( ! cfg . enabled ) {
273+ return nativeFetch ( ...args ) ;
274+ }
275+
231276 // Fetch and process matching requests
232277 const res = await nativeFetch ( ...args ) ;
233278
@@ -355,6 +400,9 @@ function setupConfigListener(): void {
355400 debug : config . debug ?? DEFAULT_CONFIG . debug ,
356401 } ;
357402 log ( 'Config updated:' , window . __LS_CONFIG__ ) ;
403+
404+ // Signal that config is ready (unblocks first fetch)
405+ tryResolveConfigReady ( ) ;
358406 }
359407 } ) as EventListener ) ;
360408}
@@ -369,6 +417,14 @@ function setupConfigListener(): void {
369417 window . __LS_DEBUG__ = false ;
370418 }
371419
420+ // Check localStorage first - if already synced by page-inject, resolve immediately
421+ const stored = loadFromLocalStorage ( ) ;
422+ if ( stored ) {
423+ window . __LS_CONFIG__ = stored ;
424+ window . __LS_DEBUG__ = stored . debug ;
425+ tryResolveConfigReady ( ) ;
426+ }
427+
372428 setupConfigListener ( ) ;
373429 patchFetch ( ) ;
374430
0 commit comments