@equinor/fusion-query
Version:
Reactive data fetching and caching library with observable streams and comprehensive event system
106 lines • 4.55 kB
JavaScript
import { castDraft } from 'immer';
import { createReducer } from '@equinor/fusion-observable';
/**
* Sort function for ordering cache records based on their update time.
* @param a First QueryCacheRecord for comparison.
* @param b Second QueryCacheRecord for comparison.
* @returns A number indicating the sort order.
*/
const sortCache = (a, b) => {
return (b.updated ?? 0) - (a.updated ?? 0);
};
/**
* Creates a reducer for managing the state of a query cache.
* @param actions An object containing action creators for the cache.
* @param initial The initial state data of the query cache.
* @returns A reducer function tailored for the query cache state.
*/
export default function (actions, initial = {}) {
return createReducer(initial, (builder) => builder
.addCase(actions.set, (state, action) => {
const { key, record } = action.payload;
state[key] = castDraft(record);
})
// Handles the 'set' action to update or add a cache record.
.addCase(actions.insert, (state, action) => {
const { key, entry } = action.payload;
const record = state[key];
if (record) {
// If the record exists, update the timestamp and increment the update count.
record.updated = Date.now();
record.updates ??= 0;
record.updates++;
record.value = entry.value;
record.transaction = entry.transaction;
// reset the mutated timestamp
record.mutated = undefined;
}
else {
// If the record does not exist, create a new one with the current timestamp.
const created = Date.now();
state[key] = castDraft({
...entry,
created,
updated: created, // Set `updated` to the creation time.
});
}
})
// Handles the 'remove' action to delete a cache record.
.addCase(actions.remove, (state, action) => {
delete state[action.payload];
})
// Handles the 'invalidate' action to invalidate a cache record.
.addCase(actions.invalidate, (state, action) => {
const invalidKey = action.payload ? [action.payload] : Object.keys(state);
for (const key of invalidKey) {
const entry = state[key];
if (entry) {
// reset the updated timestamp
entry.updated = undefined;
}
}
})
// Handles the 'mutate' action to modify a cache record's value and metadata.
.addCase(actions.mutate, (state, action) => {
const { key, value, updated } = action.payload;
const record = state[key];
if (record) {
// Update the record with the new value and metadata.
record.value = value;
record.updated = updated;
record.mutated = Date.now();
record.updates ??= 0;
record.updates++;
}
})
// Handles the 'trim' action to limit the number of cache records and remove invalid ones.
.addCase(actions.trim, (state, action) => {
const { payload } = action;
const sortFn = payload.sort ?? sortCache; // Use custom sort function if provided.
const currentKeys = Object.keys(state);
const validKeys = Object.entries(state)
// Filter out invalid entries based on the provided validation function.
.filter(([_, value]) => !payload.validate || payload.validate(value))
// Sort the remaining entries using the provided sort function.
.sort((a, b) => sortFn(a[1], b[1]))
// Limit the number of entries based on the specified size or default to MAX_SAFE_INTEGER.
.slice(0, payload.size ?? Number.MAX_SAFE_INTEGER)
// Map the entries to their keys.
.map(([key]) => key);
// Remove keys that are not in the list of valid keys.
if (currentKeys.length !== validKeys.length) {
for (const key of currentKeys) {
const validKeyIndex = validKeys.indexOf(key);
if (validKeyIndex !== -1) {
// If the key is valid, remove it from the search list.
validKeys.splice(validKeyIndex, 1);
}
else {
// If the key is not valid, remove it from the state.
delete state[key];
}
}
}
}));
}
//# sourceMappingURL=create-reducer.js.map