From df7e87054bb495be0454150a56e16ddcc028a13c Mon Sep 17 00:00:00 2001 From: Daniel Krizan Date: Mon, 16 Mar 2026 15:53:48 +0100 Subject: [PATCH 1/4] feat: add branch support to browser extension plugin Read branch from sessionStorage and pass it via overrideCredentials so the chrome extension can control which branch translations are fetched from. Also include branch in the LibConfig handshake message. --- packages/core/src/types/plugin.ts | 1 + .../package/BrowserExtensionPlugin/BrowserExtensionPlugin.ts | 5 +++++ packages/web/src/package/tools/extension.ts | 1 + 3 files changed, 7 insertions(+) diff --git a/packages/core/src/types/plugin.ts b/packages/core/src/types/plugin.ts index 34cfda2304..8921db7b75 100644 --- a/packages/core/src/types/plugin.ts +++ b/packages/core/src/types/plugin.ts @@ -111,6 +111,7 @@ export type DevCredentials = apiUrl?: string; apiKey?: string; projectId?: string | number; + branch?: string; }; export type WrapperMiddleware = { unwrap: WrapperUnwrapFunction; diff --git a/packages/web/src/package/BrowserExtensionPlugin/BrowserExtensionPlugin.ts b/packages/web/src/package/BrowserExtensionPlugin/BrowserExtensionPlugin.ts index 9ba3d1b417..2799d4a241 100644 --- a/packages/web/src/package/BrowserExtensionPlugin/BrowserExtensionPlugin.ts +++ b/packages/web/src/package/BrowserExtensionPlugin/BrowserExtensionPlugin.ts @@ -4,10 +4,12 @@ import { loadInContextLib } from './loadInContextLib'; export const API_KEY_LOCAL_STORAGE = '__tolgee_apiKey'; export const API_URL_LOCAL_STORAGE = '__tolgee_apiUrl'; +export const BRANCH_LOCAL_STORAGE = '__tolgee_branch'; function getCredentials() { const apiKey = sessionStorage.getItem(API_KEY_LOCAL_STORAGE) || undefined; const apiUrl = sessionStorage.getItem(API_URL_LOCAL_STORAGE) || undefined; + const branch = sessionStorage.getItem(BRANCH_LOCAL_STORAGE) || undefined; if (!apiKey || !apiUrl) { return undefined; @@ -16,12 +18,14 @@ function getCredentials() { return { apiKey, apiUrl, + branch, }; } function clearSessionStorage() { sessionStorage.removeItem(API_KEY_LOCAL_STORAGE); sessionStorage.removeItem(API_URL_LOCAL_STORAGE); + sessionStorage.removeItem(BRANCH_LOCAL_STORAGE); } function onDocumentReady(callback: () => void) { @@ -70,6 +74,7 @@ if (sessionStorageAvailable()) { config: { apiUrl: tolgee.getInitialOptions().apiUrl || '', apiKey: tolgee.getInitialOptions().apiKey || '', + branch: tolgee.getInitialOptions().branch, }, }) as const; diff --git a/packages/web/src/package/tools/extension.ts b/packages/web/src/package/tools/extension.ts index 9d995a95ad..4dd3a9c2d6 100644 --- a/packages/web/src/package/tools/extension.ts +++ b/packages/web/src/package/tools/extension.ts @@ -102,6 +102,7 @@ export type LibConfig = { config: { apiUrl: string; apiKey: string; + branch?: string; }; }; From f708990c270fb3722485764a2065d566a79bd786 Mon Sep 17 00:00:00 2001 From: Daniel Krizan Date: Mon, 16 Mar 2026 17:39:39 +0100 Subject: [PATCH 2/4] test: add branch tests for browser extension and overrideCredentials --- packages/core/src/__test/options.test.ts | 31 +++++++++++++++++ .../__test__/browser.extension.test.ts | 33 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/packages/core/src/__test/options.test.ts b/packages/core/src/__test/options.test.ts index 89237bc457..9b94d2dd3c 100644 --- a/packages/core/src/__test/options.test.ts +++ b/packages/core/src/__test/options.test.ts @@ -61,4 +61,35 @@ describe('initial options', () => { expect(tolgee.getInitialOptions().apiUrl).toEqual('http://localhost:8000'); }); + + it('overrideCredentials with branch', () => { + const tolgee = TolgeeCore().init({ + language: 'en', + apiUrl: 'http://localhost:8080', + }); + + tolgee.overrideCredentials({ + apiUrl: 'http://localhost:8000', + apiKey: 'test', + branch: 'feature-x', + }); + + expect(tolgee.getInitialOptions().branch).toEqual('feature-x'); + }); + + it('overrideCredentials branch overrides init branch', () => { + const tolgee = TolgeeCore().init({ + language: 'en', + apiUrl: 'http://localhost:8080', + branch: 'original', + }); + + tolgee.overrideCredentials({ + apiUrl: 'http://localhost:8000', + apiKey: 'test', + branch: 'override', + }); + + expect(tolgee.getInitialOptions().branch).toEqual('override'); + }); }); diff --git a/packages/web/src/package/__test__/browser.extension.test.ts b/packages/web/src/package/__test__/browser.extension.test.ts index b5950d8b61..5e71312a16 100644 --- a/packages/web/src/package/__test__/browser.extension.test.ts +++ b/packages/web/src/package/__test__/browser.extension.test.ts @@ -21,6 +21,7 @@ import { BrowserExtensionPlugin } from '../typedIndex'; import { API_KEY_LOCAL_STORAGE, API_URL_LOCAL_STORAGE, + BRANCH_LOCAL_STORAGE, } from '../BrowserExtensionPlugin/BrowserExtensionPlugin'; import { readFile } from 'fs/promises'; import { join } from 'path'; @@ -28,6 +29,7 @@ import { join } from 'path'; describe('compatibility with browser extension', () => { afterEach(() => { sessionStorage.clear(); + jest.clearAllMocks(); }); it('sends correct data to extension', async () => { @@ -39,6 +41,7 @@ describe('compatibility with browser extension', () => { config: { apiKey: '', apiUrl: 'test', + branch: undefined, }, mode: 'production', uiPresent: true, @@ -46,6 +49,24 @@ describe('compatibility with browser extension', () => { }); }); + it('sends branch from SDK config to extension', async () => { + const tolgee = TolgeeCore().init({ + language: 'en', + apiUrl: 'test', + branch: 'my-branch', + }); + tolgee.addPlugin(BrowserExtensionPlugin()); + await tolgee.run(); + expect(handshakerUpdate).toBeCalledTimes(1); + expect(handshakerUpdate).toBeCalledWith( + expect.objectContaining({ + config: expect.objectContaining({ + branch: 'my-branch', + }), + }) + ); + }); + it('loads in-context lib if session storage is set', async () => { sessionStorage.setItem(API_KEY_LOCAL_STORAGE, 'test'); sessionStorage.setItem(API_URL_LOCAL_STORAGE, 'test'); @@ -57,6 +78,18 @@ describe('compatibility with browser extension', () => { expect(loadInContextLib).toBeCalledTimes(1); }); + it('picks up branch from sessionStorage', async () => { + sessionStorage.setItem(API_KEY_LOCAL_STORAGE, 'test'); + sessionStorage.setItem(API_URL_LOCAL_STORAGE, 'test'); + sessionStorage.setItem(BRANCH_LOCAL_STORAGE, 'my-branch'); + + const tolgee = TolgeeCore().init({ language: 'en' }); + tolgee.addPlugin(BrowserExtensionPlugin()); + await tolgee.run(); + + expect(loadInContextLib).toBeCalledTimes(1); + }); + it('builded module is valid', async () => { // this test works only after build const fileContent = await readFile( From 59ddabc5b95a27f2225510eda070cab02200e52c Mon Sep 17 00:00:00 2001 From: Daniel Krizan Date: Mon, 16 Mar 2026 19:05:56 +0100 Subject: [PATCH 3/4] fix: don't override SDK branch with undefined when extension branch is empty --- .../package/BrowserExtensionPlugin/BrowserExtensionPlugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/package/BrowserExtensionPlugin/BrowserExtensionPlugin.ts b/packages/web/src/package/BrowserExtensionPlugin/BrowserExtensionPlugin.ts index 2799d4a241..83dd5e4cd4 100644 --- a/packages/web/src/package/BrowserExtensionPlugin/BrowserExtensionPlugin.ts +++ b/packages/web/src/package/BrowserExtensionPlugin/BrowserExtensionPlugin.ts @@ -18,7 +18,7 @@ function getCredentials() { return { apiKey, apiUrl, - branch, + ...(branch !== undefined ? { branch } : {}), }; } From 02832798dfa40b431b3ed415817fceb0da518de0 Mon Sep 17 00:00:00 2001 From: Daniel Krizan Date: Mon, 16 Mar 2026 19:15:04 +0100 Subject: [PATCH 4/4] test: assert branch is passed through to in-context tools --- .../src/package/__test__/browser.extension.test.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/web/src/package/__test__/browser.extension.test.ts b/packages/web/src/package/__test__/browser.extension.test.ts index 5e71312a16..3644f78c14 100644 --- a/packages/web/src/package/__test__/browser.extension.test.ts +++ b/packages/web/src/package/__test__/browser.extension.test.ts @@ -1,6 +1,7 @@ const handshakerUpdate = jest.fn(() => Promise.resolve()); const Handshaker = jest.fn(() => ({ update: handshakerUpdate })); -const loadInContextLib = jest.fn(() => Promise.resolve(() => {})); +const inContextToolsFactory = jest.fn(() => (tolgee: any) => tolgee); +const loadInContextLib = jest.fn(() => Promise.resolve(inContextToolsFactory)); jest.mock('../tools/extension', () => ({ Handshaker, @@ -88,6 +89,15 @@ describe('compatibility with browser extension', () => { await tolgee.run(); expect(loadInContextLib).toBeCalledTimes(1); + expect(inContextToolsFactory).toBeCalledWith( + expect.objectContaining({ + credentials: { + apiKey: 'test', + apiUrl: 'test', + branch: 'my-branch', + }, + }) + ); }); it('builded module is valid', async () => {