react-native-onyx
Version:
State management for React Native
205 lines (204 loc) • 7.43 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
/** Single source of truth for the set of registered collection keys */
let collectionKeySet = new Set();
/** Reverse lookup: member key → collection key for O(1) access */
const memberToCollectionKeyMap = new Map();
/** Forward lookup: collection key → set of member keys for O(collectionMembers) iteration */
const collectionToMembersMap = new Map();
/** Set of keys that should only be stored in RAM, not persisted to storage */
let ramOnlyKeySet = new Set();
/**
* Initializes the collection key set. Called once during Onyx.init().
*/
function setCollectionKeys(keys) {
collectionKeySet = keys;
}
/**
* Returns the set of all registered collection keys.
*/
function getCollectionKeys() {
return collectionKeySet;
}
/**
* Checks if the given key is a registered collection key (e.g. "report_").
*/
function isCollectionKey(key) {
return collectionKeySet.has(key);
}
/**
* Checks if the given key is a member of the specified collection key.
* e.g. isCollectionMemberKey("report_", "report_123") → true
*/
function isCollectionMemberKey(collectionKey, key) {
return key.startsWith(collectionKey) && key.length > collectionKey.length;
}
/**
* Checks if a given key is a collection member key (not just a collection key).
*/
function isCollectionMember(key) {
const collectionKey = getCollectionKey(key);
return !!collectionKey && key.length > collectionKey.length;
}
/**
* Checks if the provided key matches the config key — either an exact match
* or a collection prefix match.
*/
function isKeyMatch(configKey, key) {
return isCollectionKey(configKey) ? key.startsWith(configKey) : configKey === key;
}
/**
* Extracts the collection key from a collection member key.
*
* Uses a pre-computed Map for O(1) lookup. Falls back to string parsing
* for keys not yet in the map (e.g. before they're cached).
*
* Examples:
* - getCollectionKey("report_123") → "report_"
* - getCollectionKey("report_") → "report_"
* - getCollectionKey("sharedNVP_user_-1_something") → "sharedNVP_user_"
*/
function getCollectionKey(key) {
// Fast path: O(1) Map lookup for known member keys
const cached = memberToCollectionKeyMap.get(key);
if (cached !== undefined) {
return cached;
}
// If the key itself is a collection key, return it directly
if (isCollectionKey(key)) {
return key;
}
// Slow path: string parsing — use a `string` variable to avoid
// TypeScript narrowing `key` to `never` after the isCollectionKey guard.
const keyStr = key;
let lastUnderscoreIndex = keyStr.lastIndexOf('_');
while (lastUnderscoreIndex > 0) {
const possibleKey = keyStr.slice(0, lastUnderscoreIndex + 1);
if (isCollectionKey(possibleKey)) {
// Cache for future O(1) lookups
memberToCollectionKeyMap.set(key, possibleKey);
return possibleKey;
}
lastUnderscoreIndex = keyStr.lastIndexOf('_', lastUnderscoreIndex - 1);
}
return undefined;
}
/**
* Pre-computes and caches the member → collection key mapping for a given key.
* Called from OnyxCache.addKey() to ensure the Map stays populated.
*/
function registerMemberKey(key) {
const existingCollectionKey = memberToCollectionKeyMap.get(key);
if (existingCollectionKey !== undefined) {
// Already in reverse map — ensure forward map is also populated.
// getCollectionKey() can populate memberToCollectionKeyMap without
// updating collectionToMembersMap, so we must sync here.
let members = collectionToMembersMap.get(existingCollectionKey);
if (!members) {
members = new Set();
collectionToMembersMap.set(existingCollectionKey, members);
}
members.add(key);
return;
}
// Find the longest (most specific) matching collection key.
// e.g. for 'test_level_1', prefer 'test_level_' over 'test_'.
let matchedCollectionKey;
for (const collectionKey of collectionKeySet) {
if (isCollectionMemberKey(collectionKey, key)) {
if (!matchedCollectionKey || collectionKey.length > matchedCollectionKey.length) {
matchedCollectionKey = collectionKey;
}
}
}
if (matchedCollectionKey) {
memberToCollectionKeyMap.set(key, matchedCollectionKey);
// Also register in the forward lookup (collection → members)
let members = collectionToMembersMap.get(matchedCollectionKey);
if (!members) {
members = new Set();
collectionToMembersMap.set(matchedCollectionKey, members);
}
members.add(key);
}
}
/**
* Removes a member key from the reverse lookup map.
* Called when a key is dropped from cache.
*/
function deregisterMemberKey(key) {
const collectionKey = memberToCollectionKeyMap.get(key);
if (collectionKey) {
const members = collectionToMembersMap.get(collectionKey);
if (members) {
members.delete(key);
if (members.size === 0) {
collectionToMembersMap.delete(collectionKey);
}
}
}
memberToCollectionKeyMap.delete(key);
}
/**
* Returns the set of member keys for a given collection key.
* O(1) lookup using the forward index.
*/
function getMembersOfCollection(collectionKey) {
return collectionToMembersMap.get(collectionKey);
}
/**
* Splits a collection member key into the collection key part and the ID part.
*
* @param key - The collection member key to split
* @param collectionKey - Optional pre-resolved collection key for optimization
* @returns A tuple of [collectionKey, memberId]
* @throws If the key is not a valid collection member key
*/
function splitCollectionMemberKey(key, collectionKey) {
if (collectionKey && !isCollectionMemberKey(collectionKey, key)) {
throw new Error(`Invalid '${collectionKey}' collection key provided, it isn't compatible with '${key}' key.`);
}
if (!collectionKey) {
const resolvedKey = getCollectionKey(key);
if (!resolvedKey) {
throw new Error(`Invalid '${key}' key provided, only collection keys are allowed.`);
}
// eslint-disable-next-line no-param-reassign
collectionKey = resolvedKey;
}
return [collectionKey, key.slice(collectionKey.length)];
}
/**
* Initializes the RAM-only key set. Called once during Onyx.init().
*/
function setRamOnlyKeys(keys) {
ramOnlyKeySet = keys;
}
/**
* Checks if a given key is a RAM-only key, RAM-only collection key, or a RAM-only collection member.
*
* For example, given ramOnlyKeys: ["ramOnlyKey", "ramOnlyCollection_"]:
* - isRamOnlyKey("ramOnlyKey") → true
* - isRamOnlyKey("ramOnlyCollection_") → true
* - isRamOnlyKey("ramOnlyCollection_1") → true
* - isRamOnlyKey("someOtherKey") → false
*/
function isRamOnlyKey(key) {
var _a;
return ramOnlyKeySet.has(key) || ramOnlyKeySet.has((_a = getCollectionKey(key)) !== null && _a !== void 0 ? _a : '');
}
exports.default = {
setCollectionKeys,
getCollectionKeys,
isCollectionKey,
isCollectionMemberKey,
isCollectionMember,
isKeyMatch,
getCollectionKey,
registerMemberKey,
deregisterMemberKey,
getMembersOfCollection,
splitCollectionMemberKey,
setRamOnlyKeys,
isRamOnlyKey,
};