Skip to content

Commit 39960a3

Browse files
committed
implement Next.js SDK support
1 parent c27ab66 commit 39960a3

File tree

12 files changed

+1628
-945
lines changed

12 files changed

+1628
-945
lines changed

package-lock.json

Lines changed: 928 additions & 933 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,15 @@
1313
},
1414
"./react": {
1515
"import": "./dist/react.es.js",
16-
"types": "./dist/react.d.ts"
16+
"types": "./dist/react/index.d.ts"
17+
},
18+
"./next": {
19+
"import": "./dist/next.es.js",
20+
"types": "./dist/next/index.d.ts"
21+
},
22+
"./next/server": {
23+
"import": "./dist/next-server.es.js",
24+
"types": "./dist/next/server.d.ts"
1725
},
1826
"./toolbar.css": "./dist/toolbar.css"
1927
},
@@ -46,34 +54,31 @@
4654
"author": "",
4755
"license": "Apache-2.0",
4856
"peerDependencies": {
49-
"unleash-proxy-client": "^3.0.0",
50-
"@unleash/proxy-client-react": "^5.0.1"
57+
"unleash-proxy-client": "^3.0.0"
5158
},
5259
"peerDependenciesMeta": {
53-
"react": {
54-
"optional": true
55-
},
5660
"@unleash/proxy-client-react": {
5761
"optional": true
5862
}
5963
},
6064
"devDependencies": {
61-
"@biomejs/biome": "^2.3.10",
65+
"@biomejs/biome": "^2.3.12",
6266
"@eslint/js": "^9.39.2",
6367
"@testing-library/dom": "^10.4.1",
6468
"@testing-library/jest-dom": "^6.9.1",
6569
"@testing-library/react": "^16.3.0",
6670
"@types/eslint": "^9.6.1",
6771
"@types/node": "^20.10.5",
68-
"@types/react": "^18.2.45",
72+
"@types/react": "^18.3.1 || ^19.2.3",
73+
"@types/react-dom": "^18.3.1 || ^19.2.3",
6974
"@unleash/proxy-client-react": "^5.0.1",
7075
"@vitest/coverage-v8": "^4.0.15",
7176
"cssnano": "^7.1.2",
7277
"eslint": "^9.39.2",
7378
"jsdom": "^27.3.0",
7479
"postcss": "^8.5.6",
75-
"react": "^18.2.0",
76-
"react-dom": "^18.3.1",
80+
"react": "^18.3.1 || ^19.2.3",
81+
"react-dom": "^18.3.1 || ^19.2.3",
7782
"terser": "^5.44.1",
7883
"typescript": "^5.3.3",
7984
"typescript-eslint": "^8.49.0",

src/__tests__/state.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,4 +652,46 @@ describe('ToolbarStateManager', () => {
652652
expect(freshState.contextOverrides.userId).toBeUndefined();
653653
});
654654
});
655+
656+
describe('cookie sync', () => {
657+
beforeEach(() => {
658+
// Clear any existing cookies
659+
document.cookie = 'unleash-toolbar-state=; path=/; max-age=0';
660+
});
661+
662+
it('should not sync to cookies by default', () => {
663+
const manager = new ToolbarStateManager('local', 'test-toolbar-state', false);
664+
manager.recordEvaluation('flag', 'flag', true, true, {});
665+
manager.setFlagOverride('flag', { type: 'flag', value: false });
666+
667+
// Cookie should not be set
668+
expect(document.cookie).not.toContain('unleash-toolbar-state');
669+
});
670+
671+
it('should sync to cookies when enabled', () => {
672+
const manager = new ToolbarStateManager('local', 'test-toolbar-state', false);
673+
manager.enableCookieSync();
674+
675+
manager.recordEvaluation('flag', 'flag', true, true, {});
676+
manager.setFlagOverride('flag', { type: 'flag', value: false });
677+
678+
// Cookie should be set
679+
expect(document.cookie).toContain('unleash-toolbar-state');
680+
});
681+
682+
it('should clear cookie when clearing storage with sync enabled', () => {
683+
const manager = new ToolbarStateManager('local', 'test-toolbar-state', false);
684+
manager.enableCookieSync();
685+
686+
manager.recordEvaluation('flag', 'flag', true, true, {});
687+
expect(document.cookie).toContain('unleash-toolbar-state');
688+
689+
manager.clearPersistence();
690+
691+
// Cookie should be cleared
692+
const cookies = document.cookie.split(';').map(c => c.trim());
693+
const toolbarCookie = cookies.find(c => c.startsWith('unleash-toolbar-state='));
694+
expect(toolbarCookie).toBeUndefined();
695+
});
696+
});
655697
});

src/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ export class UnleashToolbar implements UnleashToolbarInstance {
8787
* Initialize the Unleash Toolbar with a client
8888
* This is the main entry point - handles both toolbar creation and client wrapping
8989
* Returns the wrapped client directly for immediate use
90+
*
91+
* @param client - The Unleash client to wrap
92+
* @param options - Toolbar configuration options
9093
*/
9194
export function initUnleashToolbar(
9295
client: UnleashClient,
@@ -95,8 +98,15 @@ export function initUnleashToolbar(
9598
const storageMode = options.storageMode || 'local';
9699
const storageKey = options.storageKey || 'unleash-toolbar-state';
97100
const sortAlphabetically = options.sortAlphabetically || false;
101+
const enableCookieSync = options.enableCookieSync || false;
98102

99103
const stateManager = new ToolbarStateManager(storageMode, storageKey, sortAlphabetically);
104+
105+
// Enable cookie sync if requested (for Next.js SSR)
106+
if (enableCookieSync) {
107+
stateManager.enableCookieSync();
108+
}
109+
100110
const wrappedClient = wrapUnleashClient(client, stateManager);
101111
const toolbar = new UnleashToolbar(stateManager, wrappedClient, options);
102112

0 commit comments

Comments
 (0)