Skip to content

Commit

Permalink
chore: publish version 1.7.0 (#283)
Browse files Browse the repository at this point in the history
* Revert "feat: changing storageProvider type to sync, use mmkv for storage in RN (#274)"

This reverts commit 815bfdb.

* Revert "chore: rm cacheWarming from expo snippet"

This reverts commit ef40ecd.

* chore: publish version 1.7.0
  • Loading branch information
daniel-statsig authored Aug 12, 2024
1 parent ef40ecd commit 29c8b07
Show file tree
Hide file tree
Showing 50 changed files with 424 additions and 272 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "statsig",
"version": "1.6.0",
"version": "1.7.0",
"license": "MIT",
"scripts": {
"preinstall": "npx only-allow pnpm",
Expand Down Expand Up @@ -156,7 +156,6 @@
"react-dom": "18.2.0",
"react-native": "0.73.2",
"react-native-device-info": "^10.11.0",
"react-native-mmkv": "2.12.2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
Expand Down
4 changes: 2 additions & 2 deletions packages/angular-bindings/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"private": true,
"name": "@statsig/angular-bindings",
"version": "1.6.0",
"version": "1.7.0",
"dependencies": {},
"peerDependencies": {
"@angular/core": "^17.1.0",
"@statsig/client-core": "1.6.0"
"@statsig/client-core": "1.7.0"
},
"sideEffects": false
}
2 changes: 1 addition & 1 deletion packages/client-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@statsig/client-core",
"version": "1.6.0",
"version": "1.7.0",
"dependencies": {},
"type": "commonjs",
"main": "./src/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/client-core/src/DataAdapterCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export abstract class DataAdapterCore {
}

private _loadFromCache(cacheKey: string): DataAdapterResult | null {
const cache = Storage._getItem?.(cacheKey);
const cache = Storage._getItemSync?.(cacheKey);
if (cache == null) {
return null;
}
Expand Down
10 changes: 4 additions & 6 deletions packages/client-core/src/EventLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,22 +261,20 @@ export class EventLogger {

const storageKey = this._getStorageKey();

try {
_setObjectInStorage(storageKey, events);
} catch {
_setObjectInStorage(storageKey, events).catch(() => {
Log.warn('Unable to save failed logs to storage');
}
});
}

private _retryFailedLogs() {
const storageKey = this._getStorageKey();
(async () => {
const events = _getObjectFromStorage<EventQueue>(storageKey);
const events = await _getObjectFromStorage<EventQueue>(storageKey);
if (!events) {
return;
}

Storage._removeItem(storageKey);
await Storage._removeItem(storageKey);
await this._sendEvents(events);
})().catch(() => {
Log.warn('Failed to flush stored logs');
Expand Down
30 changes: 16 additions & 14 deletions packages/client-core/src/SessionID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@ export type StatsigSession = {

const MAX_SESSION_IDLE_TIME = 30 * 60 * 1000; // 30 minutes
const MAX_SESSION_AGE = 4 * 60 * 60 * 1000; // 4 hours
const PROMISE_MAP: Record<string, StatsigSession> = {};
const PROMISE_MAP: Record<string, Promise<StatsigSession>> = {};

export const SessionID = {
get: (sdkKey: string): string => {
return StatsigSession.get(sdkKey).data.sessionID;
get: async (sdkKey: string): Promise<string> => {
return StatsigSession.get(sdkKey).then((x) => x.data.sessionID);
},
};

export const StatsigSession = {
get: (sdkKey: string): StatsigSession => {
get: async (sdkKey: string): Promise<StatsigSession> => {
if (PROMISE_MAP[sdkKey] == null) {
PROMISE_MAP[sdkKey] = _loadSession(sdkKey);
}

const session = PROMISE_MAP[sdkKey];
const session = await PROMISE_MAP[sdkKey];
return _bumpSession(session);
},

Expand All @@ -43,8 +43,8 @@ export const StatsigSession = {
},
};

function _loadSession(sdkKey: string): StatsigSession {
let data = _loadFromStorage(sdkKey);
async function _loadSession(sdkKey: string): Promise<StatsigSession> {
let data = await _loadFromStorage(sdkKey);
const now = Date.now();
if (!data) {
data = {
Expand All @@ -60,16 +60,19 @@ function _loadSession(sdkKey: string): StatsigSession {
};
}

function _overrideSessionId(override: string, sdkKey: string): StatsigSession {
function _overrideSessionId(
override: string,
sdkKey: string,
): Promise<StatsigSession> {
const now = Date.now();
return {
return Promise.resolve({
data: {
sessionID: override,
startTime: now,
lastUpdate: now,
},
sdkKey,
};
});
}

function _bumpSession(session: StatsigSession): StatsigSession {
Expand Down Expand Up @@ -125,11 +128,10 @@ function _getSessionIDStorageKey(sdkKey: string): string {

function _persistToStorage(session: SessionData, sdkKey: string) {
const storageKey = _getSessionIDStorageKey(sdkKey);
try {
_setObjectInStorage(storageKey, session);
} catch (e) {

_setObjectInStorage(storageKey, session).catch(() => {
Log.warn('Failed to save SessionID');
}
});
}

function _loadFromStorage(sdkKey: string) {
Expand Down
28 changes: 15 additions & 13 deletions packages/client-core/src/StableID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,27 @@ import { Log } from './Log';
import { _getObjectFromStorage, _setObjectInStorage } from './StorageProvider';
import { getUUID } from './UUID';

const PROMISE_MAP: Record<string, string> = {};
const PROMISE_MAP: Record<string, Promise<string>> = {};

export const StableID = {
get: (sdkKey: string): string => {
get: async (sdkKey: string): Promise<string> => {
if (PROMISE_MAP[sdkKey] == null) {
let stableID = _loadFromStorage(sdkKey);
if (stableID == null) {
stableID = getUUID();
_persistToStorage(stableID, sdkKey);
}
PROMISE_MAP[sdkKey] = stableID;
PROMISE_MAP[sdkKey] = _loadFromStorage(sdkKey).then((stableID) => {
if (stableID != null) {
return stableID;
}

const newStableID = getUUID();
_persistToStorage(newStableID, sdkKey);
return newStableID;
});
}

return PROMISE_MAP[sdkKey];
},

setOverride: (override: string, sdkKey: string): void => {
PROMISE_MAP[sdkKey] = override;
PROMISE_MAP[sdkKey] = Promise.resolve(override);
_persistToStorage(override, sdkKey);
},
};
Expand All @@ -31,11 +34,10 @@ function _getStableIDStorageKey(sdkKey: string): string {

function _persistToStorage(stableID: string, sdkKey: string) {
const storageKey = _getStableIDStorageKey(sdkKey);
try {
_setObjectInStorage(storageKey, stableID);
} catch (e) {

_setObjectInStorage(storageKey, stableID).catch(() => {
Log.warn('Failed to save StableID');
}
});
}

function _loadFromStorage(sdkKey: string) {
Expand Down
2 changes: 1 addition & 1 deletion packages/client-core/src/StatsigMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const SDK_VERSION = '1.6.0';
export const SDK_VERSION = '1.7.0';

export type StatsigMetadata = {
readonly [key: string]: string | undefined;
Expand Down
67 changes: 45 additions & 22 deletions packages/client-core/src/StorageProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { _getWindowSafe } from './SafeJs';

export type StorageProvider = {
_getProviderName: () => string;
_getItem: (key: string) => string | null;
_setItem: (key: string, value: string) => void;
_removeItem: (key: string) => void;
_getAllKeys: () => readonly string[];
_getItem: (key: string) => Promise<string | null>;
_setItem: (key: string, value: string) => Promise<void>;
_removeItem: (key: string) => Promise<void>;
_getAllKeys: () => Promise<readonly string[]>;

_getItemSync?: (key: string) => string | null;
};

type StorageProviderManagment = {
Expand All @@ -16,17 +18,22 @@ type StorageProviderManagment = {

const inMemoryStore: Record<string, string> = {};

const _resolve = <T>(input?: unknown) => Promise.resolve<T>(input as T);

const _inMemoryProvider: StorageProvider = {
_getProviderName: () => 'InMemory',
_getItem: (key: string): string | null =>
_getItemSync: (key: string): string | null =>
inMemoryStore[key] ? inMemoryStore[key] : null,
_setItem: (key: string, value: string): void => {
inMemoryStore[key] = value;
},
_removeItem: (key: string): void => {
delete inMemoryStore[key];
},
_getAllKeys: (): readonly string[] => Object.keys(inMemoryStore),
_getItem: (key: string): Promise<string | null> =>
_resolve(inMemoryStore[key] ? inMemoryStore[key] : null),
_setItem: (key: string, value: string): Promise<void> => (
(inMemoryStore[key] = value), _resolve()
),
_removeItem: (key: string): Promise<void> => (
delete inMemoryStore[key], _resolve()
),
_getAllKeys: (): Promise<readonly string[]> =>
_resolve(Object.keys(inMemoryStore)),
};

let _localStorageProvider: StorageProvider | null = null;
Expand All @@ -39,11 +46,18 @@ try {
) {
_localStorageProvider = {
_getProviderName: () => 'LocalStorage',
_getItem: (key: string): string | null => win.localStorage.getItem(key),
_setItem: (key: string, value: string): void =>
win.localStorage.setItem(key, value),
_removeItem: (key: string): void => win.localStorage.removeItem(key),
_getAllKeys: (): string[] => Object.keys(win.localStorage),
_getItemSync: (key: string): string | null =>
win.localStorage.getItem(key),
_getItem: (key: string): Promise<string | null> =>
_resolve(win.localStorage.getItem(key)),
_setItem: (key: string, value: string): Promise<void> => (
win.localStorage.setItem(key, value), _resolve()
),
_removeItem: (key: string): Promise<void> => (
win.localStorage.removeItem(key), _resolve()
),
_getAllKeys: (): Promise<string[]> =>
_resolve(Object.keys(win.localStorage)),
};
}
} catch (error) {
Expand All @@ -68,7 +82,13 @@ function _inMemoryBreaker<T>(get: () => T) {
export const Storage: StorageProvider & StorageProviderManagment = {
_getProviderName: () => _current._getProviderName(),

_getItem: (key: string) => _inMemoryBreaker(() => _current._getItem(key)),
_getItem: async (key: string) =>
_inMemoryBreaker(() => _current._getItem(key)),

_getItemSync: (key: string) =>
_inMemoryBreaker(() =>
_current._getItemSync ? _current._getItemSync(key) : null,
),

_setItem: (key: string, value: string) => _current._setItem(key, value),
_removeItem: (key: string) => _current._removeItem(key),
Expand All @@ -89,11 +109,14 @@ export const Storage: StorageProvider & StorageProviderManagment = {
},
};

export function _getObjectFromStorage<T>(key: string): T | null {
const value = Storage._getItem(key);
export async function _getObjectFromStorage<T>(key: string): Promise<T | null> {
const value = await Storage._getItem(key);
return JSON.parse(value ?? 'null') as T | null;
}

export function _setObjectInStorage(key: string, obj: unknown): void {
Storage._setItem(key, JSON.stringify(obj));
export async function _setObjectInStorage(
key: string,
obj: unknown,
): Promise<void> {
await Storage._setItem(key, JSON.stringify(obj));
}
25 changes: 18 additions & 7 deletions packages/client-core/src/__tests__/SessionID.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
CreateTestPromise,
MockLocalStorage,
MockRemoteServerEvalClient,
TestPromise,
anyUUID,
} from 'statsig-test-helpers';

Expand All @@ -19,7 +21,7 @@ async function getSessionIDIgnoringInMemoryCache(
let result: string | null = null;
await jest.isolateModulesAsync(async () => {
const s = (await import('../SessionID')).SessionID;
result = s.get(sdkKey);
result = await s.get(sdkKey);
});

return result ?? 'error';
Expand Down Expand Up @@ -255,33 +257,42 @@ describe('SessionID', () => {
describe('when multiple calls to session id', () => {
const data: Record<string, string> = {};
let calls = 0;
let firstReqPromise: TestPromise<string | null>;
let secondReqPromise: TestPromise<string | null>;

beforeAll(async () => {
storageMock.clear();
firstReqPromise = CreateTestPromise<string | null>();
secondReqPromise = CreateTestPromise<string | null>();

const inMemoryStore = {};

Storage._setProvider({
_getProviderName: () => 'JestStorage',
_getAllKeys: () => Object.keys(inMemoryStore),
_getItem: (_key: string) => {
_getAllKeys: () => Promise.resolve(Object.keys(inMemoryStore)),
_getItem: async (_key: string) => {
if (calls++ === 0) {
return 'first';
return firstReqPromise;
}
return 'second';
return secondReqPromise;
},
_setItem: (key: string, value: string) => {
data[key] = value;
return Promise.resolve();
},
_removeItem: (key: string) => {
delete data[key];
return Promise.resolve();
},
});
});

it('gets the same session id', async () => {
const sessionID = SessionID.get(SDK_KEY);
const sessionID2 = SessionID.get(SDK_KEY);
const sessionID = SessionID.get(SDK_KEY).catch(() => 'error');
const sessionID2 = SessionID.get(SDK_KEY).catch(() => 'error');

secondReqPromise.resolve(null);
firstReqPromise.resolve(null);

const val1 = await sessionID;
const val2 = await sessionID2;
Expand Down
Loading

0 comments on commit 29c8b07

Please sign in to comment.