Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TS migration] Prepare remaining files for Onyx.js migration #507

Merged
merged 18 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions lib/Onyx.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type BaseConnectOptions = {
statePropertyName?: string;
withOnyxInstance?: Component;
initWithStoredValues?: boolean;
displayName?: string;
};

type TryGetCachedValueMapping<TKey extends OnyxKey> = {
Expand Down
36 changes: 17 additions & 19 deletions lib/OnyxCache.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
import {deepEqual} from 'fast-equals';
import bindAll from 'lodash/bindAll';
import type {Key, Value} from './storage/providers/types';
import utils from './utils';

type StorageMap = Record<Key, Value>;
import type {OnyxKey, OnyxValue} from './types';

/**
* In memory cache providing data by reference
* Encapsulates Onyx cache related functionality
*/
class OnyxCache {
/** Cache of all the storage keys available in persistent storage */
private storageKeys: Set<Key>;
storageKeys: Set<OnyxKey>;

/** Unique list of keys maintained in access order (most recent at the end) */
private recentKeys: Set<Key>;
private recentKeys: Set<OnyxKey>;

/** A map of cached values */
private storageMap: StorageMap;
private storageMap: Record<OnyxKey, OnyxValue>;

/**
* Captured pending tasks for already running storage methods
* Using a map yields better performance on operations such a delete
*/
private pendingPromises: Map<string, Promise<unknown>>;
private pendingPromises: Map<string, Promise<OnyxValue | OnyxKey[]>>;

/** Maximum size of the keys store din cache */
private maxRecentKeysSize = 0;
Expand Down Expand Up @@ -54,38 +52,38 @@ class OnyxCache {
}

/** Get all the storage keys */
getAllKeys(): Set<Key> {
getAllKeys(): Set<OnyxKey> {
return this.storageKeys;
}

/**
* Get a cached value from storage
* @param [shouldReindexCache] – This is an LRU cache, and by default accessing a value will make it become last in line to be evicted. This flag can be used to skip that and just access the value directly without side-effects.
*/
getValue(key: Key, shouldReindexCache = true): Value {
getValue(key: OnyxKey, shouldReindexCache = true): OnyxValue {
if (shouldReindexCache) {
this.addToAccessedKeys(key);
}
return this.storageMap[key];
}

/** Check whether cache has data for the given key */
hasCacheForKey(key: Key): boolean {
hasCacheForKey(key: OnyxKey): boolean {
return this.storageMap[key] !== undefined;
}

/** Saves a key in the storage keys list
* Serves to keep the result of `getAllKeys` up to date
*/
addKey(key: Key): void {
addKey(key: OnyxKey): void {
this.storageKeys.add(key);
}

/**
* Set's a key value in cache
* Adds the key to the storage keys list as well
*/
set(key: Key, value: Value): Value {
set(key: OnyxKey, value: OnyxValue): OnyxValue {
this.addKey(key);
this.addToAccessedKeys(key);
this.storageMap[key] = value;
Expand All @@ -94,7 +92,7 @@ class OnyxCache {
}

/** Forget the cached value for the given key */
drop(key: Key): void {
drop(key: OnyxKey): void {
delete this.storageMap[key];
this.storageKeys.delete(key);
this.recentKeys.delete(key);
Expand All @@ -104,7 +102,7 @@ class OnyxCache {
* Deep merge data to cache, any non existing keys will be created
* @param data - a map of (cache) key - values
*/
merge(data: StorageMap): void {
merge(data: Record<OnyxKey, OnyxValue>): void {
if (typeof data !== 'object' || Array.isArray(data)) {
throw new Error('data passed to cache.merge() must be an Object of onyx key/value pairs');
}
Expand All @@ -128,7 +126,7 @@ class OnyxCache {
*
* @param keys - an array of keys
*/
setAllKeys(keys: Key[]) {
setAllKeys(keys: OnyxKey[]) {
this.storageKeys = new Set(keys);
}

Expand All @@ -146,7 +144,7 @@ class OnyxCache {
* provided from this function
* @param taskName - unique name given for the task
*/
getTaskPromise(taskName: string): Promise<unknown> | undefined {
getTaskPromise(taskName: string): Promise<OnyxValue | OnyxKey[]> | undefined {
return this.pendingPromises.get(taskName);
}

Expand All @@ -155,7 +153,7 @@ class OnyxCache {
* hook up to the promise if it's still pending
* @param taskName - unique name for the task
*/
captureTask(taskName: string, promise: Promise<unknown>): Promise<unknown> {
captureTask(taskName: string, promise: Promise<OnyxValue>): Promise<OnyxValue> {
const returnPromise = promise.finally(() => {
this.pendingPromises.delete(taskName);
});
Expand All @@ -166,7 +164,7 @@ class OnyxCache {
}

/** Adds a key to the top of the recently accessed keys */
private addToAccessedKeys(key: Key): void {
addToAccessedKeys(key: OnyxKey): void {
this.recentKeys.delete(key);
this.recentKeys.add(key);
}
Expand Down Expand Up @@ -198,7 +196,7 @@ class OnyxCache {
}

/** Check if the value has changed */
hasValueChanged(key: Key, value: Value): boolean {
hasValueChanged(key: OnyxKey, value: OnyxValue): boolean {
return !deepEqual(this.storageMap[key], value);
}
}
Expand Down
9 changes: 3 additions & 6 deletions lib/PerformanceUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import lodashTransform from 'lodash/transform';
import {deepEqual} from 'fast-equals';
import type {OnyxKey} from './types';
import type {ConnectOptions} from './Onyx';

type UnknownObject = Record<string, unknown>;

Expand All @@ -10,11 +12,6 @@ type LogParams = {
newValue?: unknown;
};

type Mapping = Record<string, unknown> & {
key: string;
displayName: string;
};

let debugSetState = false;

function setShouldDebugSetState(debug: boolean) {
Expand Down Expand Up @@ -44,7 +41,7 @@ function diffObject<TObject extends UnknownObject, TBase extends UnknownObject>(
/**
* Provide insights into why a setState() call occurred by diffing the before and after values.
*/
function logSetStateCall(mapping: Mapping, previousValue: unknown, newValue: unknown, caller: string, keyThatChanged: string) {
function logSetStateCall<TKey extends OnyxKey>(mapping: ConnectOptions<TKey>, previousValue: unknown, newValue: unknown, caller: string, keyThatChanged?: string) {
if (!debugSetState) {
return;
}
Expand Down
5 changes: 3 additions & 2 deletions lib/storage/WebStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
* when using LocalStorage APIs in the browser. These events are great because multiple tabs can listen for when
* data changes and then stay up-to-date with everything happening in Onyx.
*/
import type {OnyxKey} from '../types';
import Storage from './providers/IDBKeyVal';
import type {KeyList, Key} from './providers/types';
import type {KeyList} from './providers/types';
import type StorageProvider from './providers/types';

const SYNC_ONYX = 'SYNC_ONYX';

/**
* Raise an event thorough `localStorage` to let other tabs know a value changed
*/
function raiseStorageSyncEvent(onyxKey: Key) {
function raiseStorageSyncEvent(onyxKey: OnyxKey) {
global.localStorage.setItem(SYNC_ONYX, onyxKey);
global.localStorage.removeItem(SYNC_ONYX);
}
Expand Down
8 changes: 4 additions & 4 deletions lib/storage/__mocks__/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type {OnyxKey, OnyxValue} from '../../types';
import utils from '../../utils';
import type {Key, KeyValuePairList, Value} from '../providers/types';
import type {KeyValuePairList} from '../providers/types';
import type StorageProvider from '../providers/types';

let storageMapInternal: Record<Key, Value> = {};
let storageMapInternal: Record<OnyxKey, OnyxValue> = {};

const set = jest.fn((key, value) => {
storageMapInternal[key] = value;
Expand Down Expand Up @@ -34,8 +35,7 @@ const idbKeyvalMock: StorageProvider = {
multiMerge(pairs) {
pairs.forEach(([key, value]) => {
const existingValue = storageMapInternal[key];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const newValue = utils.fastMerge(existingValue as any, value);
const newValue = utils.fastMerge(existingValue!, value!);
blazejkustra marked this conversation as resolved.
Show resolved Hide resolved

set(key, newValue);
});
Expand Down
7 changes: 3 additions & 4 deletions lib/storage/providers/IDBKeyVal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {UseStore} from 'idb-keyval';
import {set, keys, getMany, setMany, get, clear, del, delMany, createStore, promisifyRequest} from 'idb-keyval';
import utils from '../../utils';
import type StorageProvider from './types';
import type {Value} from './types';
import type {OnyxValue} from '../../types';

// We don't want to initialize the store while the JS bundle loads as idb-keyval will try to use global.indexedDB
// which might not be available in certain environments that load the bundle (e.g. electron main process).
Expand All @@ -21,13 +21,12 @@ const provider: StorageProvider = {
getCustomStore()('readwrite', (store) => {
// Note: we are using the manual store transaction here, to fit the read and update
// of the items in one transaction to achieve best performance.
const getValues = Promise.all(pairs.map(([key]) => promisifyRequest<Value>(store.get(key))));
const getValues = Promise.all(pairs.map(([key]) => promisifyRequest<OnyxValue>(store.get(key))));

return getValues.then((values) => {
const upsertMany = pairs.map(([key, value], index) => {
const prev = values[index];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const newValue = utils.fastMerge(prev as any, value);
const newValue = utils.fastMerge(prev!, value!);
blazejkustra marked this conversation as resolved.
Show resolved Hide resolved
return promisifyRequest(store.put(newValue, key));
});
return Promise.all(upsertMany);
Expand Down
19 changes: 9 additions & 10 deletions lib/storage/providers/types.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import type {BatchQueryResult, QueryResult} from 'react-native-quick-sqlite';
import type {OnyxKey, OnyxValue} from '../../types';

type Key = string;
type Value = IDBValidKey;
type KeyValuePair = [Key, Value];
type KeyList = Key[];
type KeyValuePair = [OnyxKey, OnyxValue];
type KeyList = OnyxKey[];
type KeyValuePairList = KeyValuePair[];

type OnStorageKeyChanged = (key: Key, value: Value | null) => void;
type OnStorageKeyChanged = (key: OnyxKey, value: OnyxValue | null) => void;

type StorageProvider = {
/**
* Gets the value of a given key or return `null` if it's not available in storage
*/
getItem: (key: Key) => Promise<Value | null>;
getItem: (key: OnyxKey) => Promise<OnyxValue | null>;

/**
* Get multiple key-value pairs for the given array of keys in a batch
Expand All @@ -22,7 +21,7 @@ type StorageProvider = {
/**
* Sets the value for a given key. The only requirement is that the value should be serializable to JSON string
*/
setItem: (key: Key, value: Value) => Promise<QueryResult | void>;
setItem: (key: OnyxKey, value: OnyxValue) => Promise<QueryResult | void>;

/**
* Stores multiple key-value pairs in a batch
Expand All @@ -39,7 +38,7 @@ type StorageProvider = {
* @param changes - the delta for a specific key
* @param modifiedData - the pre-merged data from `Onyx.applyMerge`
*/
mergeItem: (key: Key, changes: Value, modifiedData: Value) => Promise<BatchQueryResult | void>;
mergeItem: (key: OnyxKey, changes: OnyxValue, modifiedData: OnyxValue) => Promise<BatchQueryResult | void>;

/**
* Returns all keys available in storage
Expand All @@ -49,7 +48,7 @@ type StorageProvider = {
/**
* Removes given key and its value from storage
*/
removeItem: (key: Key) => Promise<QueryResult | void>;
removeItem: (key: OnyxKey) => Promise<QueryResult | void>;

/**
* Removes given keys and their values from storage
Expand Down Expand Up @@ -78,4 +77,4 @@ type StorageProvider = {
};

export default StorageProvider;
export type {Value, Key, KeyList, KeyValuePairList};
export type {KeyList, KeyValuePair, KeyValuePairList};
10 changes: 6 additions & 4 deletions lib/types.d.ts → lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Merge} from 'type-fest';
import {BuiltIns} from 'type-fest/source/internal';
import type {Merge} from 'type-fest';
import type {BuiltIns} from 'type-fest/source/internal';

/**
* Represents a deeply nested record. It maps keys to values,
Expand Down Expand Up @@ -76,6 +76,7 @@ type TypeOptions = Merge<
* }
* ```
*/
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface CustomTypeOptions {}

/**
Expand All @@ -102,7 +103,7 @@ type OnyxKey = Key | CollectionKey;
/**
* Represents a Onyx value that can be either a single entry or a collection of entries, depending on the `TKey` provided.
*/
type OnyxValue<TKey extends OnyxKey> = TKey extends CollectionKeyBase ? OnyxCollection<KeyValueMapping[TKey]> : OnyxEntry<KeyValueMapping[TKey]>;
type OnyxValue<TKey extends OnyxKey = OnyxKey> = TKey extends CollectionKeyBase ? OnyxCollection<KeyValueMapping[TKey]> : OnyxEntry<KeyValueMapping[TKey]>;

blazejkustra marked this conversation as resolved.
Show resolved Hide resolved
/**
* Represents a mapping of Onyx keys to values, where keys are either normal or collection Onyx keys
Expand Down Expand Up @@ -193,6 +194,7 @@ type ExtractOnyxCollectionValue<TOnyxCollection> = TOnyxCollection extends NonNu

type NonTransformableTypes =
| BuiltIns
// eslint-disable-next-line @typescript-eslint/no-explicit-any
| ((...args: any[]) => unknown)
| Map<unknown, unknown>
| Set<unknown>
Expand Down Expand Up @@ -234,7 +236,7 @@ type NullishObjectDeep<ObjectType extends object> = {
*/
type WithOnyxInstanceState<TOnyxProps> = (TOnyxProps & {loading: boolean}) | undefined;

export {
export type {
CollectionKey,
CollectionKeyBase,
CustomTypeOptions,
Expand Down
Loading
Loading