Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-flags-initialize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@reflag/browser-sdk": patch
---

Avoid reporting `CLIENT_NOT_INITIALIZED` when a flag is evaluated from the initial flag state while client initialization is completing.
6 changes: 1 addition & 5 deletions packages/browser-sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,6 @@ function shouldShowToolbar(opts: InitOptions) {
*/
export class ReflagClient {
private state: State = "idle";
private initializationFinished = false;
private contextUpdateLoading = false;
private readonly publishableKey: string;
private context: ReflagContext;
Expand Down Expand Up @@ -740,7 +739,6 @@ export class ReflagClient {
"ms" +
(this.config.offline ? " (offline mode)" : ""),
);
this.initializationFinished = true;
this.setState("initialized");
}

Expand Down Expand Up @@ -1422,9 +1420,7 @@ export class ReflagClient {
getFlag(flagKey: string): Flag {
const f = this.getFlags()[flagKey];
const evaluatedBeforeInitialization =
!this.initializationFinished &&
!this.config.offline &&
!this.config.bootstrapped;
!this.flagsClient.hasFlagState() && !this.config.offline;

// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
Expand Down
7 changes: 7 additions & 0 deletions packages/browser-sdk/src/flag/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ export class FlagsClient {
private initialized = false;
private bootstrapped = false;
private initializationComplete = false;
private flagStateAvailable = false;

private rateLimiter: RateLimiter;
private readonly logger: Logger;
Expand Down Expand Up @@ -425,6 +426,10 @@ export class FlagsClient {
return this.fetchedFlags;
}

hasFlagState(): boolean {
return this.flagStateAvailable;
}

requestOptInFlags() {
this.optInFlagsRequested = true;

Expand Down Expand Up @@ -545,6 +550,8 @@ export class FlagsClient {
triggerEvent = true,
flagStateVersion?: number,
) {
// Mark the state available before notifying consumers from updateFlags().
this.flagStateAvailable = true;
// Create a new fetched flags object making sure to clone the flags
this.fetchedFlags = { ...fetchedFlags };
this.fetchedFlagStateVersion = flagStateVersion;
Expand Down
29 changes: 29 additions & 0 deletions packages/browser-sdk/test/usage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,35 @@ describe(`sends "check" events `, () => {
);
});

it("does not add initialization diagnostics once flag state is available", async () => {
const sendCheckEventSpy = vi.spyOn(
FlagsClient.prototype,
"sendCheckEvent",
);
const client = new ReflagClient({ publishableKey: KEY });
const evaluatedDuringInitialization = vi.fn();

client.on("flagsUpdated", () => {
expect(client.getState()).toBe("initializing");
expect(client.getFlag("flagA").isEnabled).toBe(true);
evaluatedDuringInitialization();
});

await client.initialize();

expect(evaluatedDuringInitialization).toHaveBeenCalledOnce();
expect(sendCheckEventSpy).toHaveBeenCalledWith(
expect.objectContaining({
action: "check-is-enabled",
key: "flagA",
value: true,
version: 1,
evaluationErrors: flagsResult.flagA.evaluationErrors,
}),
expect.any(Function),
);
});

it("does not add initialization diagnostics to bootstrapped evaluations", () => {
const sendCheckEventSpy = vi.spyOn(
FlagsClient.prototype,
Expand Down
Loading