Skip to content

fix: removes the proxy and adds a polling mechanism for js-core package#44

Open
pandeymangg wants to merge 4 commits intomainfrom
fix/refactors-loadSurvey-function
Open

fix: removes the proxy and adds a polling mechanism for js-core package#44
pandeymangg wants to merge 4 commits intomainfrom
fix/refactors-loadSurvey-function

Conversation

@pandeymangg
Copy link
Copy Markdown
Contributor

Problem: The @formbricks/js wrapper SDK uses a Proxy to intercept all method calls and route them to the dynamically loaded formbricks.umd.cjs core. After loading the core via
<script> tag, it reads globalThis.formbricks — which is set by the UMD wrapper. If the UMD environment detection is fooled (e.g. another script on the page leaks exports or module
into the global scope), the assignment goes to module.exports instead of globalThis.formbricks, and initialization fails with "Failed to load Formbricks SDK".

Changes:

  • Replace Proxy with explicit method object (index.ts): Each SDK method (setup, track, setEmail, etc.) is now a direct function that delegates to setup() or
    callMethod(). This makes the API statically typed, debuggable (real function names in stack traces), and catches typos at compile time instead of silently queueing them.

  • Add polling fallback after script.onload (load-formbricks.ts): After the core script loads, if globalThis.formbricks isn't set immediately (UMD detection was fooled), polls
    every 10ms for up to 500ms before giving up. This pairs with the explicit globalThis assignment being added to js-core on the server side.

  • Store coreInstance locally (load-formbricks.ts): The loaded core SDK reference is captured once during setup rather than re-reading globalThis.formbricks on every method call.
    More reliable and avoids issues if the global is later overwritten.

  • Preserve method queueing: Methods called before setup() are still queued and flushed after initialization, same as before.

  • Add UMD fallback tests (load-formbricks.test.ts): Two new tests simulate the UMD detection failure scenario — one where the global appears after a delay (polling recovers), and
    one where it never appears (polling times out gracefully).

  • Add queue-test playground page (apps/playground/app/queue-test/page.tsx): Interactive page at /queue-test to manually verify method queueing. Call SDK methods before setup, then
    trigger setup and observe the queued calls flush.

  • Remove deprecated init method (types/formbricks.ts): The init method (deprecated in favor of setup) has been removed from the type interface.

@pandeymangg pandeymangg marked this pull request as draft March 31, 2026 06:15
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Walkthrough

This pull request refactors the formbricks SDK from a proxy-based dynamic dispatch pattern to explicit method implementations. The primary export object transitions from using a Proxy handler that routed calls to loadFormbricksToProxy to defining concrete methods that call callMethod. The SDK loading mechanism is updated with polling to verify globalThis.formbricks availability. The exported loadFormbricksToProxy function is replaced with setup and callMethod, and the method queuing system is restructured to enforce setup initialization. The deprecated init method is removed from the type interface, and all tests are updated to verify the new method delegation and error handling behavior.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: removing the Proxy-based implementation and adding a polling mechanism for the js-core package.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing the problem, changes, and rationale for each modification.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch fix/refactors-loadSurvey-function

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/js/src/lib/load-formbricks.test.ts (1)

8-19: ⚠️ Potential issue | 🟡 Minor

setNonce method missing from mockFormbricks.

The mockFormbricks object includes all methods except setNonce, which is part of the TFormbricks interface. If callMethod("setNonce", ...) is tested, this mock will be incomplete.

🔧 Add missing setNonce mock
 const mockFormbricks = {
   setup: vi.fn(),
   init: vi.fn(),
   track: vi.fn(),
   setEmail: vi.fn(),
   setAttribute: vi.fn(),
   setAttributes: vi.fn(),
   setLanguage: vi.fn(),
   setUserId: vi.fn(),
+  setNonce: vi.fn(),
   logout: vi.fn(),
   registerRouteChange: vi.fn(),
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/js/src/lib/load-formbricks.test.ts` around lines 8 - 19, The test
mock object mockFormbricks is missing the setNonce method required by the
TFormbricks interface, so add a setNonce: vi.fn() entry to mockFormbricks (used
by tests that call callMethod("setNonce", ...)) to make the mock complete and
avoid undefined method errors when testing setNonce behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/js/src/index.ts`:
- Around line 14-25: Add a backwards-compatible init method to the exported
formbricks object so tests and callers can call formbricks.init(initConfig);
specifically, add an init: (initConfig) => callMethod("init", initConfig) entry
alongside the existing methods (same pattern as setEmail, setup, track, etc.)
and mark it as deprecated in comments if desired; this ensures
callMethod("init", ...) is invoked when formbricks.init is called and prevents
the runtime test failures.

In `@packages/js/src/lib/load-formbricks.test.ts`:
- Around line 396-403: The current cleanup in the concurrent setup test uses
mockSetTimeoutImmediate(), Promise.race([firstSetup, ...]) and swapping
globalThis.setTimeout back to originalSetTimeout which can leave timers in an
inconsistent state; replace that pattern by using the test runner's fake timers
and deterministically advancing them (e.g., call vi.useFakeTimers() at start of
the test, kick off firstSetup, then await vi.advanceTimersByTimeAsync(...) to
force the pending timeout to run, await firstSetup, and finally call
vi.useRealTimers() to restore normal timer behavior), ensuring you remove the
Promise.race and direct swapping of originalSetTimeout and reference the same
symbols (mockSetTimeoutImmediate, firstSetup, originalSetTimeout,
globalThis.setTimeout) when editing the test.

In `@packages/js/src/lib/load-formbricks.ts`:
- Around line 146-148: coreInstance is assigned before awaiting
coreInstance.setup(...), so if setup() throws the instance remains set and
callMethod may operate on an uninitialized core; wrap the setup call in a
try/catch (or assign coreInstance only after successful setup) so that on
failure you clear/reset coreInstance (e.g., set to undefined/null) and rethrow
the error, and only call processQueue() after setup succeeds; update the logic
around coreInstance, setup, processQueue, and any error paths to ensure
coreInstance is not left assigned when setup fails.
- Around line 9-69: The loadFormbricksSDK function can append duplicate <script>
tags on repeated calls after a failure; fix it by adding a module-scoped
sentinel (e.g., let formbricksScriptAppended = false) or checking for an
existing script element by src before creating/appending a new element, use that
sentinel/check in loadFormbricksSDK to return early if already appended, and
ensure the sentinel is cleared or updated appropriately on permanent
failures/success so subsequent legitimate retries behave correctly; reference
the loadFormbricksSDK function and the created script element handling
(script.src, document.head.appendChild) when implementing the guard.

---

Outside diff comments:
In `@packages/js/src/lib/load-formbricks.test.ts`:
- Around line 8-19: The test mock object mockFormbricks is missing the setNonce
method required by the TFormbricks interface, so add a setNonce: vi.fn() entry
to mockFormbricks (used by tests that call callMethod("setNonce", ...)) to make
the mock complete and avoid undefined method errors when testing setNonce
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9ad5cccc-bf84-4ea5-90d7-eb8b3913600c

📥 Commits

Reviewing files that changed from the base of the PR and between 529fea9 and e465f46.

📒 Files selected for processing (5)
  • packages/js/src/index.test.ts
  • packages/js/src/index.ts
  • packages/js/src/lib/load-formbricks.test.ts
  • packages/js/src/lib/load-formbricks.ts
  • packages/js/src/types/formbricks.ts
💤 Files with no reviewable changes (1)
  • packages/js/src/types/formbricks.ts

@pandeymangg pandeymangg marked this pull request as ready for review March 31, 2026 06:31
@sonarqubecloud
Copy link
Copy Markdown

@mattinannt mattinannt removed their request for review April 3, 2026 07:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant