Skip to content

Commit 80c7220

Browse files
committed
feat: cleanup context types
1 parent c097336 commit 80c7220

7 files changed

Lines changed: 90 additions & 63 deletions

File tree

packages/browser-sdk/src/client.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
} from "./flag/flags";
1919
import { ToolbarPosition } from "./ui/types";
2020
import { API_BASE_URL, APP_BASE_URL, SSE_REALTIME_BASE_URL } from "./config";
21-
import { ReflagContext } from "./context";
21+
import { ReflagContext, ReflagDeprecatedContext } from "./context";
2222
import { HookArgs, HooksManager, State } from "./hooksManager";
2323
import { HttpClient } from "./httpClient";
2424
import { Logger, loggerWithPrefix, quietConsoleLogger } from "./logger";
@@ -198,7 +198,7 @@ export type FlagDefinitions = Readonly<Array<string>>;
198198
/**
199199
* ReflagClient initialization options.
200200
*/
201-
export type InitOptions = ReflagContext & {
201+
export type InitOptions = ReflagDeprecatedContext & {
202202
/**
203203
* Publishable key for authentication
204204
*/
@@ -436,11 +436,7 @@ export class ReflagClient {
436436

437437
this.flagsClient = new FlagsClient(
438438
this.httpClient,
439-
{
440-
user: this.context.user,
441-
company: this.context.company,
442-
other: { ...this.context.otherContext, ...this.context.other },
443-
},
439+
this.context,
444440
this.logger,
445441
isBootstrapped(opts)
446442
? {
@@ -677,7 +673,7 @@ export class ReflagClient {
677673
*
678674
* @param context The context to update.
679675
*/
680-
async updateContext({ otherContext, ...context }: ReflagContext) {
676+
async updateContext({ otherContext, ...context }: ReflagDeprecatedContext) {
681677
const userIdChanged =
682678
context.user?.id && context.user.id !== this.context.user?.id;
683679
const newContext = {

packages/browser-sdk/src/context.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,12 @@ export interface ReflagContext {
5858
* Context which is not related to a user or a company.
5959
*/
6060
other?: Record<string, string | number | undefined>;
61+
}
6162

63+
export interface ReflagDeprecatedContext extends ReflagContext {
6264
/**
6365
* Context which is not related to a user or a company.
64-
* @deprecated Use `other` instead
66+
* @deprecated Use `other` instead, this property will be removed in the next major version
6567
*/
6668
otherContext?: Record<string, string | number | undefined>;
6769
}

packages/browser-sdk/src/flag/flagCache.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,12 @@ function validateCacheData(cacheDataInput: any) {
157157
return cacheData;
158158
}
159159

160-
// Simple object check.
161-
export function isObject(item: any): boolean {
162-
return item && typeof item === "object" && !Array.isArray(item);
160+
/**
161+
* Check if the given item is an object.
162+
*
163+
* @param item - The item to check.
164+
* @returns `true` if the item is an object, `false` otherwise.
165+
**/
166+
export function isObject(item: any): item is Record<string, any> {
167+
return (item && typeof item === "object" && !Array.isArray(item)) || false;
163168
}

packages/browser-sdk/src/flag/flags.ts

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -184,33 +184,6 @@ const overridesCookieKey = `__reflag_overrides`;
184184

185185
type OverridesFlags = Record<string, boolean | null>;
186186

187-
function setOverridesCache(overrides: OverridesFlags) {
188-
try {
189-
Cookies.set(overridesCookieKey, JSON.stringify(overrides), {
190-
expires: 30, // 30 days
191-
sameSite: "strict",
192-
});
193-
} catch {
194-
// Cookie setting failed, overrides won't persist
195-
}
196-
}
197-
198-
function getOverridesCache(): OverridesFlags {
199-
try {
200-
const cookieValue = Cookies.get(overridesCookieKey);
201-
if (cookieValue) {
202-
const cachedOverrides = JSON.parse(cookieValue);
203-
if (isObject(cachedOverrides)) {
204-
return cachedOverrides;
205-
}
206-
}
207-
} catch {
208-
// Ignore cookie parsing errors
209-
}
210-
211-
return {};
212-
}
213-
214187
type FlagsClientOptions = Partial<Config> & {
215188
bootstrappedFlags?: FetchedFlags;
216189
fallbackFlags?: Record<string, FallbackFlagOverride> | string[];
@@ -263,7 +236,7 @@ export class FlagsClient {
263236
this.fallbackFlags = this.setupFallbackFlags(fallbackFlags);
264237

265238
try {
266-
const storedFlagOverrides = getOverridesCache();
239+
const storedFlagOverrides = this.getOverridesCache();
267240
for (const key in storedFlagOverrides) {
268241
this.flagOverrides[key] = storedFlagOverrides[key];
269242
}
@@ -312,11 +285,7 @@ export class FlagsClient {
312285
}
313286

314287
async setContext(context: ReflagContext) {
315-
this.context = {
316-
user: context.user,
317-
company: context.company,
318-
other: { ...context.otherContext, ...context.other },
319-
};
288+
this.context = context;
320289
this.setFetchedFlags((await this.maybeFetchFlags()) || {});
321290
}
322291

@@ -330,7 +299,7 @@ export class FlagsClient {
330299
} else {
331300
this.flagOverrides[key] = isEnabled;
332301
}
333-
setOverridesCache(this.flagOverrides);
302+
this.setOverridesCache(this.flagOverrides);
334303

335304
this.flags = this.mergeFlags(this.fetchedFlags, this.flagOverrides);
336305
this.triggerFlagsUpdated();
@@ -430,6 +399,31 @@ export class FlagsClient {
430399
}
431400
}
432401

402+
private setOverridesCache(overrides: OverridesFlags) {
403+
try {
404+
Cookies.set(overridesCookieKey, JSON.stringify(overrides), {
405+
expires: 7, // 1 week
406+
sameSite: "strict",
407+
});
408+
} catch {
409+
this.logger.warn(
410+
"storing flag overrides in cookies failed, overrides won't persist",
411+
);
412+
}
413+
}
414+
415+
private getOverridesCache(): OverridesFlags {
416+
try {
417+
const overridesCookie = Cookies.get(overridesCookieKey);
418+
const overrides = JSON.parse(overridesCookie || "{}");
419+
if (!isObject(overrides)) throw new Error("invalid overrides");
420+
return overrides;
421+
} catch {
422+
this.logger.warn("getting flag overrides from cookies failed");
423+
return {};
424+
}
425+
}
426+
433427
private async maybeFetchFlags(): Promise<FetchedFlags | undefined> {
434428
if (this.config.offline) {
435429
return;

packages/react-sdk/dev/nextjs-flag-demo/components/Providers.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ export const Providers = ({ publishableKey, children }: Props) => {
1212
return (
1313
<ReflagProvider
1414
publishableKey={publishableKey}
15-
company={{ id: "demo-company", name: "Demo Company" }}
16-
user={{
17-
id: "demo-user",
18-
email: "demo-user@example.com",
19-
"optin-huddles": "true",
15+
context={{
16+
company: { id: "demo-company", name: "Demo Company" },
17+
user: {
18+
id: "demo-user",
19+
email: "demo-user@example.com",
20+
"optin-huddles": "true",
21+
},
2022
}}
2123
fallbackFlags={["fallback-feature"]}
2224
>

packages/react-sdk/dev/plain/app.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,11 @@ export function App() {
314314
return (
315315
<ReflagProvider
316316
publishableKey={publishableKey}
317-
company={initialCompany}
318-
user={initialUser}
319-
other={initialOtherContext}
317+
context={{
318+
user: initialUser,
319+
company: initialCompany,
320+
other: initialOtherContext,
321+
}}
320322
apiBaseUrl={apiBaseUrl}
321323
>
322324
{!publishableKey && (

packages/react-sdk/src/index.tsx

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,29 +205,55 @@ export function ReflagClientProvider({
205205
/**
206206
* Props for the ReflagProvider.
207207
*/
208-
export type ReflagProps = InitOptions & ReflagPropsBase;
208+
export type ReflagProps = Omit<InitOptions, keyof ReflagContext> &
209+
ReflagPropsBase & {
210+
/**
211+
* The context to use for the ReflagClient containing user, company, and other context.
212+
*/
213+
context: ReflagContext;
214+
215+
/**
216+
* Company related context. If you provide `id` Reflag will enrich the evaluation context with
217+
* company attributes on Reflag servers.
218+
* @deprecated Use `context` instead, this property will be removed in the next major version
219+
*/
220+
company?: CompanyContext;
221+
222+
/**
223+
* User related context. If you provide `id` Reflag will enrich the evaluation context with
224+
* user attributes on Reflag servers.
225+
* @deprecated Use `context` instead, this property will be removed in the next major version
226+
*/
227+
user?: UserContext;
228+
229+
/**
230+
* Context which is not related to a user or a company.
231+
* @deprecated Use `context` instead, this property will be removed in the next major version
232+
*/
233+
otherContext?: Record<string, string | number | undefined>;
234+
};
209235

210236
/**
211237
* Provider for the ReflagClient.
212238
*/
213239
export function ReflagProvider({
214240
children,
241+
context,
215242
user,
216243
company,
217-
other,
218244
otherContext,
219245
loadingComponent,
220246
debug,
221247
...config
222248
}: ReflagProps) {
223-
const context = useMemo(
224-
() => ({ user, company, other: { ...otherContext, ...other } }),
225-
[user, company, other, otherContext],
249+
const resolvedContext = useMemo(
250+
() => ({ user, company, other: otherContext, ...context }),
251+
[user, company, otherContext, context],
226252
);
227253
const client = useReflagClient(
228254
{
229255
...config,
230-
...context,
256+
...resolvedContext,
231257
},
232258
debug,
233259
);
@@ -242,8 +268,8 @@ export function ReflagProvider({
242268

243269
// Update the context if it changes
244270
useEffect(() => {
245-
void client.updateContext(context);
246-
}, [client, context]);
271+
void client.updateContext(resolvedContext);
272+
}, [client, resolvedContext]);
247273

248274
return (
249275
<ReflagClientProvider client={client} loadingComponent={loadingComponent}>
@@ -257,7 +283,7 @@ export function ReflagProvider({
257283
*/
258284
export type ReflagBootstrappedProps = Omit<
259285
InitOptionsBootstrapped,
260-
"bootstrappedFlags" | keyof ReflagContext
286+
"user" | "company" | "otherContext" | "bootstrappedFlags"
261287
> &
262288
ReflagPropsBase & {
263289
/**

0 commit comments

Comments
 (0)