Partially implemented the Manage Time functionality
This commit is contained in:
61
src/features/preferences/request-cache.js
Normal file
61
src/features/preferences/request-cache.js
Normal file
@@ -0,0 +1,61 @@
|
||||
function createFormRequestCache({ getHours, getUserId }) {
|
||||
const entries = new Map();
|
||||
const pending = new Map();
|
||||
let generation = 0;
|
||||
let owner = "";
|
||||
const queries = new Set([
|
||||
"requestWeekDetails", "requestActiveServiceRequests", "requestServiceRequestExpand", "requestLookupTaskType",
|
||||
"requestActionTeamMembers", "requestActionFindLookup", "requestSrLookupPrimaryPillar", "requestSrLookupLanguage",
|
||||
"requestSrLookupTechPlatform", "requestSrLookupOpportunityBusiness", "requestLookupTypePlatform", "requestInternalSrServices"
|
||||
]);
|
||||
const writes = new Set(["requestMgmtAction", "requestPostInternalServiceRequest", "requestPostServiceRequest"]);
|
||||
function invalidate() { generation++; entries.clear(); pending.clear(); }
|
||||
function maxAge() {
|
||||
const hours = Number(getHours());
|
||||
return (Number.isFinite(hours) && hours > 0 ? Math.min(hours, 168) : 24) * 3600000;
|
||||
}
|
||||
function copy(value) { return JSON.parse(JSON.stringify(value)); }
|
||||
function stable(value) {
|
||||
if (Array.isArray(value)) return value.map(stable);
|
||||
if (value && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().map(key => [key, stable(value[key])]));
|
||||
return value;
|
||||
}
|
||||
async function send(message, transport) {
|
||||
const userId = String(getUserId() || "").trim().toLowerCase();
|
||||
if (userId !== owner) { invalidate(); owner = userId; }
|
||||
const operation = String(message.type || "").split(".").pop();
|
||||
const duration = maxAge();
|
||||
const request = queries.has(operation) || writes.has(operation) || operation === "requestResourceUser"
|
||||
? { ...message, cacheMaxAgeMs: duration } : message;
|
||||
if (operation === "invalidateProductsCache") invalidate();
|
||||
// Only explicitly known read operations are reusable. Writes and token generation always execute.
|
||||
if (!queries.has(operation) || !userId) {
|
||||
const result = await transport(request);
|
||||
if (result?.ok && writes.has(operation) && !(operation === "requestPostServiceRequest" && message.phase === "prepare")) invalidate();
|
||||
return result;
|
||||
}
|
||||
const key = JSON.stringify(stable(message));
|
||||
const hit = entries.get(key);
|
||||
if (hit && Date.now() >= hit.fetchedAt && Date.now() - hit.fetchedAt < duration) {
|
||||
return { ...copy(hit.result), debugRequests: [], fromCache: true, cachedAt: hit.fetchedAt };
|
||||
}
|
||||
entries.delete(key);
|
||||
if (pending.has(key)) {
|
||||
const result = await pending.get(key);
|
||||
return { ...copy(result), debugRequests: [] };
|
||||
}
|
||||
const version = generation;
|
||||
const promise = transport(request).then(result => {
|
||||
if (result?.ok && version === generation) {
|
||||
// Keep this per-page cache bounded and avoid persisting credentials or customer data to disk.
|
||||
if (entries.size >= 500) entries.delete(entries.keys().next().value);
|
||||
entries.set(key, { result: copy({ ...result, debugRequests: [] }), fetchedAt: Date.now() });
|
||||
}
|
||||
return result;
|
||||
});
|
||||
pending.set(key, promise);
|
||||
try { return await promise; }
|
||||
finally { if (pending.get(key) === promise) pending.delete(key); }
|
||||
}
|
||||
return { send, invalidate };
|
||||
}
|
||||
Reference in New Issue
Block a user