68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
/** Cache only semantic inputs. Scope/context stay in the source-range identity; generation is scheduling.
|
|
* This pure wire-format helper is shared by the runtime and presentation.
|
|
*/
|
|
export function cacheConfiguration(
|
|
identity: string,
|
|
scope: string,
|
|
_generation: string,
|
|
options: Record<string, unknown>,
|
|
inputs: readonly string[],
|
|
) {
|
|
return JSON.stringify({
|
|
processor: identity,
|
|
scope,
|
|
options: Object.fromEntries([...inputs].sort().map((key) => [key, options[key]])),
|
|
});
|
|
}
|
|
|
|
/** Keep transaction reads bounded, without one serial RPC per historical job.
|
|
* Relationship order still decides which reusable result wins. */
|
|
export async function findCachedJob<T>(
|
|
history: readonly T[],
|
|
read: (reference: T) => Promise<{ key: string; status: string }>,
|
|
key: string,
|
|
retry: boolean,
|
|
) {
|
|
let retries = 0;
|
|
for (let offset = 0; offset < history.length; offset += 32) {
|
|
const batch = history.slice(offset, offset + 32);
|
|
const records = await Promise.all(batch.map(read));
|
|
for (let index = 0; index < batch.length; index++) {
|
|
const data = records[index];
|
|
if (data.key !== key) continue;
|
|
retries++;
|
|
if (data.status === "queued" || (!retry && data.status === "ready")) return { reference: batch[index], retries };
|
|
}
|
|
}
|
|
return { reference: undefined, retries };
|
|
}
|
|
|
|
/** Compare persisted semantic configurations without scheduling choices. The
|
|
* original wire format retained generation; removing it preserves old results
|
|
* when an author switches between preparation and intent-driven generation. */
|
|
export function sameCacheConfiguration(a: string, b: string) {
|
|
if (a === b) return true;
|
|
try {
|
|
const semantic = (raw: string) => {
|
|
const { processor, scope, options } = JSON.parse(raw);
|
|
if (
|
|
typeof processor !== "string" ||
|
|
typeof scope !== "string" ||
|
|
!options ||
|
|
typeof options !== "object" ||
|
|
Array.isArray(options)
|
|
)
|
|
return null;
|
|
return JSON.stringify({
|
|
processor,
|
|
scope,
|
|
options: Object.fromEntries(Object.entries(options).sort(([a], [b]) => a.localeCompare(b))),
|
|
});
|
|
};
|
|
const left = semantic(a);
|
|
return left !== null && left === semantic(b);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|