fix(amazon-shopping): verify prime and delivery filters
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { ProductFilters, ProductSearchResult, SearchProductsResponse } from "./types.js";
|
||||
import { extractWidthInches, formatWidthInches } from "./product-metrics.js";
|
||||
|
||||
export interface ResponseInput {
|
||||
query: string;
|
||||
@@ -33,25 +34,87 @@ function formatFilters(filters: ProductFilters): string {
|
||||
filters.minRating !== undefined ? `rating ${filters.ratingComparison ?? "gte"} ${filters.minRating}` : "",
|
||||
filters.minReviews !== undefined ? `reviews ${filters.reviewCountComparison ?? "gte"} ${filters.minReviews}` : "",
|
||||
filters.maxPrice !== undefined ? `price <= $${filters.maxPrice}` : "",
|
||||
filters.maxUnitPrice !== undefined ? `unit price <= $${filters.maxUnitPrice}` : ""
|
||||
filters.maxUnitPrice !== undefined ? `unit price <= $${filters.maxUnitPrice}` : "",
|
||||
filters.minWidthInches !== undefined ? `width ${filters.widthComparison ?? "gte"} ${filters.minWidthInches} inches` : "",
|
||||
filters.requirePrime ? "Prime delivery" : "",
|
||||
filters.requireFreeDelivery ? "free delivery" : "",
|
||||
filters.deliveryBy ? `delivery by ${filters.deliveryBy}` : "",
|
||||
filters.sortBy === "price" ? "sort by price" : ""
|
||||
].filter(Boolean);
|
||||
return parts.length > 0 ? parts.join(", ") : "none";
|
||||
}
|
||||
|
||||
function formatProduct(product: ProductSearchResult, index: number): string {
|
||||
const specs = product.specs.slice(0, 3).map((spec) => `${spec.name}: ${spec.value}`).join("; ");
|
||||
const lines = [
|
||||
`${index}. ${product.title}`,
|
||||
` Link: ${product.url}`,
|
||||
` Price: ${product.price?.display ?? "unknown"}${product.unitPrice ? ` (${product.unitPrice.display})` : ""}`,
|
||||
` Rating: ${product.rating ?? "unknown"} stars; reviews: ${product.reviewCount ?? "unknown"}`,
|
||||
` Delivery: ${product.delivery?.display ?? "unknown"}`,
|
||||
specs ? ` Specs: ${specs}` : "",
|
||||
product.bullets[0] ? ` Notes: ${product.bullets.slice(0, 2).join(" ")}` : "",
|
||||
product.missingFields.length > 0 ? ` Missing: ${product.missingFields.join(", ")}` : "",
|
||||
product.isSponsored ? " Sponsored: yes" : ""
|
||||
].filter(Boolean);
|
||||
return lines.join("\n");
|
||||
function escapeCell(value: string): string {
|
||||
return value.replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
function marker(passes: boolean | undefined, enabled: boolean): string {
|
||||
if (!enabled) {
|
||||
return "";
|
||||
}
|
||||
return passes ? " OK" : " NO";
|
||||
}
|
||||
|
||||
function widthCell(product: ProductSearchResult, filters: ProductFilters): string {
|
||||
const width = extractWidthInches(product);
|
||||
const passes = width !== undefined && (filters.widthComparison === "gt" ? width > (filters.minWidthInches ?? 0) : width >= (filters.minWidthInches ?? 0));
|
||||
return `${formatWidthInches(width)}${marker(passes, filters.minWidthInches !== undefined)}`;
|
||||
}
|
||||
|
||||
function primeCell(product: ProductSearchResult, filters: ProductFilters): string {
|
||||
if (product.delivery?.prime) {
|
||||
return `Prime${marker(true, Boolean(filters.requirePrime))}`;
|
||||
}
|
||||
return `not verified${marker(false, Boolean(filters.requirePrime))}`;
|
||||
}
|
||||
|
||||
function deliveryCell(product: ProductSearchResult, filters: ProductFilters): string {
|
||||
const display = product.delivery?.display ?? "unknown";
|
||||
if (!filters.deliveryBy) {
|
||||
return display;
|
||||
}
|
||||
const normalized = display.toLowerCase();
|
||||
const passes = filters.deliveryBy === "today"
|
||||
? /\btoday\b|same[- ]day/.test(normalized)
|
||||
: filters.deliveryBy === "tomorrow" || filters.deliveryBy === "overnight"
|
||||
? /\btomorrow\b|overnight|next[- ]day|one[- ]day/.test(normalized)
|
||||
: normalized.includes(filters.deliveryBy.toLowerCase());
|
||||
return `${display}${marker(passes, true)}`;
|
||||
}
|
||||
|
||||
function resultTable(products: ProductSearchResult[], filters: ProductFilters): string[] {
|
||||
const rows = [
|
||||
"| # | Product | Price | Rating | Reviews | Width | Prime | Delivery | Link |",
|
||||
"|---|---|---:|---:|---:|---:|---|---|---|",
|
||||
...products.map((product, index) => [
|
||||
String(index + 1),
|
||||
escapeCell(product.title),
|
||||
product.price?.display ?? "unknown",
|
||||
`${product.rating ?? "unknown"} stars`,
|
||||
product.reviewCount?.toLocaleString("en-US") ?? "unknown",
|
||||
widthCell(product, filters),
|
||||
primeCell(product, filters),
|
||||
escapeCell(deliveryCell(product, filters)),
|
||||
`[Amazon](${product.url})`
|
||||
].join(" | "))
|
||||
.map((row) => `| ${row} |`)
|
||||
];
|
||||
return rows;
|
||||
}
|
||||
|
||||
function metadataLines(products: ProductSearchResult[]): string[] {
|
||||
const lines: string[] = [];
|
||||
for (const product of products) {
|
||||
const notes = [
|
||||
product.missingFields.length > 0 ? `missing ${product.missingFields.join(", ")}` : "",
|
||||
product.isSponsored ? "sponsored" : "",
|
||||
product.extractionNotes.length > 0 ? product.extractionNotes.join("; ") : ""
|
||||
].filter(Boolean);
|
||||
if (notes.length > 0) {
|
||||
lines.push(`- ${product.title}: ${notes.join("; ")}`);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
export function createMarkdownReport(response: SearchProductsResponse): string {
|
||||
@@ -63,7 +126,12 @@ export function createMarkdownReport(response: SearchProductsResponse): string {
|
||||
`Results returned: ${response.results.length} (filtered out: ${response.filteredOutCount})`,
|
||||
response.warnings.length > 0 ? `Warnings: ${response.warnings.join("; ")}` : "",
|
||||
"",
|
||||
...response.results.map((product, index) => formatProduct(product, index + 1))
|
||||
"## Best Matches",
|
||||
"",
|
||||
response.results.length > 0 ? "" : "No products matched all requested filters.",
|
||||
...resultTable(response.results, response.filters),
|
||||
"",
|
||||
...metadataLines(response.results)
|
||||
].filter((line) => line !== "");
|
||||
return `${lines.join("\n")}\n`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user