Revert "fix: harden automatic mobile boards"
This reverts commit d0b9348b86.
This commit is contained in:
1 parent
92a2be2b65
commit
dba9a250ff
37 files changed
+137
-637
No files matched your search
@@ -4,17 +4,6 @@ import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
import { createRequestHandler } from "./request-handler";
|
||||
|
||||
const createDeferred = <T>() => {
|
||||
let resolve!: (value: T) => void;
|
||||
let reject!: (reason: unknown) => void;
|
||||
const promise = new Promise<T>((resolvePromise, rejectPromise) => {
|
||||
resolve = resolvePromise;
|
||||
reject = rejectPromise;
|
||||
});
|
||||
|
||||
return { promise, reject, resolve };
|
||||
};
|
||||
|
||||
describe("createRequestHandler", () => {
|
||||
test("keeps the standard result contract free of cache provenance", async () => {
|
||||
const requestHandler = createRequestHandler({
|
||||
@@ -58,47 +47,4 @@ describe("createRequestHandler", () => {
|
||||
"offline",
|
||||
);
|
||||
});
|
||||
|
||||
test("does not let a pre-invalidation request overwrite a newer cached result", async () => {
|
||||
const first = createDeferred<string>();
|
||||
const second = createDeferred<string>();
|
||||
const requestAsync = vi.fn().mockReturnValueOnce(first.promise).mockReturnValueOnce(second.promise);
|
||||
const requestHandler = createRequestHandler({ requestAsync });
|
||||
const handler = requestHandler.handler({ integrationId: "first" });
|
||||
|
||||
const oldRequest = handler.getDataWithProvenanceAsync();
|
||||
requestHandler.invalidateCache();
|
||||
const newRequest = handler.getDataWithProvenanceAsync();
|
||||
|
||||
second.resolve("new");
|
||||
await expect(newRequest).resolves.toMatchObject({ data: "new" });
|
||||
first.resolve("old");
|
||||
await expect(oldRequest).resolves.toMatchObject({ data: "old" });
|
||||
|
||||
await expect(handler.getDataWithProvenanceAsync()).resolves.toMatchObject({ data: "new" });
|
||||
expect(requestAsync).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test("does not let a pre-invalidation request clear a newer inflight request", async () => {
|
||||
const first = createDeferred<string>();
|
||||
const second = createDeferred<string>();
|
||||
const requestAsync = vi.fn().mockReturnValueOnce(first.promise).mockReturnValueOnce(second.promise);
|
||||
const requestHandler = createRequestHandler({ requestAsync });
|
||||
const handler = requestHandler.handler({ integrationId: "first" });
|
||||
|
||||
const oldRequest = handler.getDataWithProvenanceAsync();
|
||||
requestHandler.invalidateCache();
|
||||
const newRequest = handler.getDataWithProvenanceAsync();
|
||||
|
||||
first.resolve("old");
|
||||
await expect(oldRequest).resolves.toMatchObject({ data: "old" });
|
||||
const sharedNewRequest = handler.getDataWithProvenanceAsync();
|
||||
expect(requestAsync).toHaveBeenCalledTimes(2);
|
||||
|
||||
second.resolve("new");
|
||||
await expect(Promise.all([newRequest, sharedNewRequest])).resolves.toEqual([
|
||||
expect.objectContaining({ data: "new" }),
|
||||
expect.objectContaining({ data: "new" }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -22,11 +22,9 @@ export const createRequestHandler = <TData, TInput extends Record<string, unknow
|
||||
) => {
|
||||
const cache = new Map<string, CacheEntry<TData>>();
|
||||
const inflight = new Map<string, Promise<RequestResult<TData>>>();
|
||||
let cacheGeneration = 0;
|
||||
|
||||
return {
|
||||
invalidateCache: () => {
|
||||
cacheGeneration += 1;
|
||||
cache.clear();
|
||||
inflight.clear();
|
||||
},
|
||||
@@ -43,29 +41,21 @@ export const createRequestHandler = <TData, TInput extends Record<string, unknow
|
||||
const existing = inflight.get(key);
|
||||
if (existing) return existing;
|
||||
|
||||
const requestGeneration = cacheGeneration;
|
||||
let promise: Promise<RequestResult<TData>>;
|
||||
const isCurrentRequest = () => requestGeneration === cacheGeneration && inflight.get(key) === promise;
|
||||
|
||||
promise = options
|
||||
const promise = options
|
||||
.requestAsync(input)
|
||||
.then((data) => {
|
||||
const entry: CacheEntry<TData> = { data, timestamp: new Date(), expiresAt: Date.now() + ttl };
|
||||
|
||||
if (isCurrentRequest()) {
|
||||
if (cache.size >= MAX_CACHE_SIZE) evictExpired(cache);
|
||||
if (cache.size >= MAX_CACHE_SIZE) {
|
||||
const oldest = cache.keys().next().value;
|
||||
if (oldest) cache.delete(oldest);
|
||||
}
|
||||
cache.set(key, entry);
|
||||
inflight.delete(key);
|
||||
if (cache.size >= MAX_CACHE_SIZE) evictExpired(cache);
|
||||
if (cache.size >= MAX_CACHE_SIZE) {
|
||||
const oldest = cache.keys().next().value;
|
||||
if (oldest) cache.delete(oldest);
|
||||
}
|
||||
|
||||
const entry: CacheEntry<TData> = { data, timestamp: new Date(), expiresAt: Date.now() + ttl };
|
||||
cache.set(key, entry);
|
||||
inflight.delete(key);
|
||||
return { data: entry.data, timestamp: entry.timestamp, isStale: false };
|
||||
})
|
||||
.catch((err) => {
|
||||
if (isCurrentRequest()) inflight.delete(key);
|
||||
inflight.delete(key);
|
||||
if (options.fallbackToStaleOnError && cached) {
|
||||
return { data: cached.data, timestamp: cached.timestamp, isStale: true };
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user