You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

104 lines
2.5 KiB

import fs from "node:fs";
export type HarEntry = {
request: { url: string; method: string };
response: {
status: number;
content?: { mimeType?: string; text?: string };
};
};
export type HarFile = {
log?: { entries?: HarEntry[] };
};
export function readHarFile(harPath: string): HarFile {
const raw = fs.readFileSync(harPath, "utf8");
return JSON.parse(raw) as HarFile;
}
export function listHarEntries(har: HarFile): HarEntry[] {
return har.log?.entries ?? [];
}
export function parseHarResponseJson(entry: HarEntry): unknown | null {
const text = entry.response.content?.text;
if (!text?.trim()) {
return null;
}
try {
return JSON.parse(text) as unknown;
} catch {
return null;
}
}
const QUOTE_URL_PATTERN =
/axel\/quote|api\.mothership\.com|plugins\.mothership\.com|services\.mothership\.com/i;
const QUOTE_URL_HINT = /quote|rate|pricing|graphql/i;
export function isQuoteHarEntry(entry: HarEntry): boolean {
if (entry.response.status < 200 || entry.response.status >= 300) {
return false;
}
const mime = entry.response.content?.mimeType ?? "";
if (!/json/i.test(mime) && !entry.response.content?.text?.trim().startsWith("{")) {
return false;
}
const url = entry.request.url.toLowerCase();
if (QUOTE_URL_PATTERN.test(url)) {
return true;
}
return QUOTE_URL_HINT.test(url) && /mothership/i.test(url);
}
export function scoreQuoteBody(body: unknown): number {
if (body == null || typeof body !== "object") {
return 0;
}
let score = 0;
const text = JSON.stringify(body).toLowerCase();
if (text.includes("rates")) {
score += 3;
}
if (text.includes("standard")) {
score += 2;
}
if (text.includes("guaranteed")) {
score += 2;
}
if (text.includes("freight") || text.includes("price")) {
score += 1;
}
if (text.includes("place_id") || text.includes("placeid")) {
score += 1;
}
return score;
}
export function pickBestQuoteEntry(entries: HarEntry[]): {
entry: HarEntry;
body: unknown;
} | null {
let best: { entry: HarEntry; body: unknown; score: number } | null = null;
for (const entry of entries) {
if (!isQuoteHarEntry(entry)) {
continue;
}
const body = parseHarResponseJson(entry);
if (body == null) {
continue;
}
const score = scoreQuoteBody(body);
if (score <= 0) {
continue;
}
if (!best || score > best.score) {
best = { entry, body, score };
}
}
return best ? { entry: best.entry, body: best.body } : null;
}