Skip to content

Commit ecd01ec

Browse files
committed
fix flags sdk
1 parent 555a3ca commit ecd01ec

File tree

2 files changed

+50
-3
lines changed

2 files changed

+50
-3
lines changed

packages/sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@databuddy/sdk",
3-
"version": "2.3.22",
3+
"version": "2.3.23",
44
"description": "Official Databuddy Analytics SDK",
55
"main": "./dist/core/index.mjs",
66
"types": "./dist/core/index.d.ts",

packages/sdk/src/core/flags/flags-manager.ts

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ import type {
2020
UserContext,
2121
} from "./types";
2222

23+
/** Storage key for anonymous ID (shared with tracker) */
24+
const ANONYMOUS_ID_KEY = "did";
25+
2326
/**
2427
* Core flags manager - lightweight API client with best practices:
2528
* - Stale-while-revalidate caching
2629
* - Request batching (multiple flags → single request)
2730
* - Request deduplication
2831
* - Visibility API awareness (pause when hidden)
2932
* - Optimistic storage hydration
33+
* - Automatic anonymous ID for gradual rollouts
3034
*/
3135
export class CoreFlagsManager implements FlagsManager {
3236
private config: FlagsConfig;
@@ -70,11 +74,54 @@ export class CoreFlagsManager implements FlagsManager {
7074
this.initialize();
7175
}
7276

77+
/**
78+
* Get or create anonymous ID for deterministic rollouts.
79+
* Uses the same storage key as the tracker ("did") for consistency.
80+
*/
81+
private getOrCreateAnonymousId(): string | null {
82+
if (typeof localStorage === "undefined") {
83+
return null;
84+
}
85+
86+
try {
87+
let id = localStorage.getItem(ANONYMOUS_ID_KEY);
88+
if (id) {
89+
return id;
90+
}
91+
92+
id = `anon_${crypto.randomUUID()}`;
93+
localStorage.setItem(ANONYMOUS_ID_KEY, id);
94+
return id;
95+
} catch {
96+
// localStorage might be blocked (private browsing, etc.)
97+
return null;
98+
}
99+
}
100+
101+
/**
102+
* Ensure user context has an identifier for deterministic flag evaluation.
103+
* If no userId/email provided, inject anonymous ID as userId.
104+
*/
105+
private ensureUserIdentity(user?: UserContext): UserContext | undefined {
106+
// If user already has identification, use as-is
107+
if (user?.userId || user?.email) {
108+
return user;
109+
}
110+
111+
const anonymousId = this.getOrCreateAnonymousId();
112+
if (!anonymousId) {
113+
return user;
114+
}
115+
116+
// Inject anonymous ID as userId for deterministic rollouts
117+
return { ...user, userId: anonymousId };
118+
}
119+
73120
private withDefaults(config: FlagsConfig): FlagsConfig {
74121
return {
75122
clientId: config.clientId,
76123
apiUrl: config.apiUrl ?? "https://api.databuddy.cc",
77-
user: config.user,
124+
user: this.ensureUserIdentity(config.user),
78125
disabled: config.disabled ?? false,
79126
debug: config.debug ?? false,
80127
skipStorage: config.skipStorage ?? false,
@@ -420,7 +467,7 @@ export class CoreFlagsManager implements FlagsManager {
420467
* Update user context and refresh flags
421468
*/
422469
updateUser(user: UserContext): void {
423-
this.config = { ...this.config, user };
470+
this.config = { ...this.config, user: this.ensureUserIdentity(user) };
424471

425472
// Recreate batcher with new user params
426473
this.batcher?.destroy();

0 commit comments

Comments
 (0)