Skip to content

Commit a94a281

Browse files
fix: resolve GPT shim auto-init race by using explicit activation
The server-injected inline script now calls __tsjs_installGptShim() directly instead of relying on a module-scope auto-init check that raced against the enable flag. This ensures the shim installs regardless of script injection order. Also fixes the vacuous disabled-gating test that reset state before asserting, making it always pass.
1 parent 4359cb4 commit a94a281

3 files changed

Lines changed: 41 additions & 21 deletions

File tree

crates/common/src/integrations/gpt.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,14 @@ impl IntegrationHeadInjector for GptIntegration {
426426
}
427427

428428
fn head_inserts(&self, _ctx: &IntegrationHtmlContext<'_>) -> Vec<String> {
429-
vec!["<script>window.__tsjs_gpt_enabled=true;</script>".to_string()]
429+
// Set the enable flag and explicitly call the activation function
430+
// registered by the GPT shim module. The unified bundle's <script> tag
431+
// is emitted before this inline script, so `__tsjs_installGptShim` is
432+
// guaranteed to exist when this executes.
433+
vec![
434+
"<script>window.__tsjs_gpt_enabled=true;window.__tsjs_installGptShim&&window.__tsjs_installGptShim();</script>"
435+
.to_string(),
436+
]
430437
}
431438
}
432439

@@ -905,8 +912,9 @@ mod tests {
905912

906913
assert_eq!(inserts.len(), 1, "should emit exactly one head insert");
907914
assert_eq!(
908-
inserts[0], "<script>window.__tsjs_gpt_enabled=true;</script>",
909-
"should set __tsjs_gpt_enabled flag for the client-side GPT shim"
915+
inserts[0],
916+
"<script>window.__tsjs_gpt_enabled=true;window.__tsjs_installGptShim&&window.__tsjs_installGptShim();</script>",
917+
"should set the enable flag and call the GPT shim activation function"
910918
);
911919
}
912920

crates/js/lib/src/integrations/gpt/index.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,14 @@ export function installGptShim(): boolean {
161161
return true;
162162
}
163163

164-
// Self-initialise on import when the server-side GPT integration is enabled.
165-
// The trusted server injects `window.__tsjs_gpt_enabled = true` via an inline
166-
// script (IntegrationHeadInjector) so the shim stays dormant when the GPT proxy
167-
// routes are not registered.
168-
if (typeof window !== 'undefined' && (window as Record<string, unknown>).__tsjs_gpt_enabled) {
169-
installGptShim();
164+
// Register the activation function on `window` so the server-injected inline
165+
// script can call it explicitly. The server emits:
166+
// <script>window.__tsjs_gpt_enabled=true;
167+
// window.__tsjs_installGptShim&&window.__tsjs_installGptShim();</script>
168+
// Because that inline <script> runs *after* the unified bundle has evaluated,
169+
// the function is guaranteed to be available by the time the inline script
170+
// executes. This avoids a race where the module-scope auto-init would check
171+
// `__tsjs_gpt_enabled` before the flag is set.
172+
if (typeof window !== 'undefined') {
173+
(window as Record<string, unknown>).__tsjs_installGptShim = installGptShim;
170174
}

crates/js/lib/test/integrations/gpt/index.test.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -180,31 +180,39 @@ describe('GPT shim – runtime gating', () => {
180180
resetGuardState();
181181
delete (window as GatedWindow).googletag;
182182
delete (window as GatedWindow).__tsjs_gpt_enabled;
183+
delete (window as Record<string, unknown>).__tsjs_installGptShim;
183184
});
184185

185-
it('installs the shim when __tsjs_gpt_enabled is set', async () => {
186-
win.__tsjs_gpt_enabled = true;
187-
186+
it('installs the shim when activation function is called (simulates server inline script)', async () => {
188187
const { installGptShim } = await import('../../../src/integrations/gpt/index');
189188

190-
// Explicitly call since the dynamic import may have already cached.
189+
// Simulate what the server-injected inline script does:
190+
// set the flag then call the activation function.
191+
win.__tsjs_gpt_enabled = true;
191192
installGptShim();
192193

193194
expect(isGuardInstalled()).toBe(true);
194195
expect(win.googletag).toBeDefined();
195196
});
196197

197-
it('does not install the shim when __tsjs_gpt_enabled is absent', async () => {
198-
// No flag set — shim should stay dormant.
198+
it('registers __tsjs_installGptShim on window after import', async () => {
199+
vi.resetModules();
199200
await import('../../../src/integrations/gpt/index');
200201

201-
// Reset guard to verify the auto-init did NOT install.
202-
resetGuardState();
203-
delete win.googletag;
202+
expect(typeof (window as Record<string, unknown>).__tsjs_installGptShim).toBe('function');
203+
});
204+
205+
it('does not install the shim when only imported (no explicit activation)', async () => {
206+
// Reset modules so the next dynamic import re-evaluates the module.
207+
vi.resetModules();
208+
209+
// Import a fresh copy — the module should register the activation
210+
// function on `window` but NOT call `installGptShim()` on its own.
211+
await import('../../../src/integrations/gpt/index');
204212

205-
// Manually verify: calling installGptShim without the flag should still
206-
// work (it's a direct call), but the *auto-init path* would not have run.
207-
// The key assertion is that the guard is not installed after reset.
213+
// Assert immediately — the guard must not be installed because the
214+
// module only registers `__tsjs_installGptShim`, it does not auto-init.
208215
expect(isGuardInstalled()).toBe(false);
216+
expect(win.googletag).toBeUndefined();
209217
});
210218
});

0 commit comments

Comments
 (0)