Derive the product's stable identity key — shared by its base series and
every variant series. Prefers the stamped cacheKey, falling back to the
URL then the title so scraped products without a stamped key still track.
Derive the series id for one of a product's variants: the product key joined
with the variant's discriminator (see variantDiscriminator).
Record the current USD price of each product (and its variants) into the
price-history store. Fire-and-forget: the caller wraps this in void from the
search flow, but the returned promise is also tracked internally so
flushPendingPriceHistory can wait for it — the MV3 popup can be closed
(destroying its JS context) the instant results are shown, before a sequential
multi-series write finishes.
Force all in-flight recordProductPrices writes to finish, bypassing nothing
(there's no debounce to skip) but giving the caller a promise to await before a
point where the popup could otherwise close mid-write — e.g. once a search
finishes. Also invoked as a visibilitychange backstop below.
Read all recorded price-history series for a product — its base row plus any variant rows — keyed by series id for direct lookup from the detail UI.
Collapse several price series into one synthetic mean-price series so a group (e.g. all of a product's variants) can be summarized by a single trend. At each recorded timestamp the mean is taken over every series that has started by then (forward-filling each series' last-known price), and consecutive-equal means are deduped so describeTrend's last two points reflect an actual move. Empty in ⇒ empty out.
Resolve the price series a results-table row should graph, shared by the
collapsed trend column and the expanded detail panel so the two never diverge.
A flattened variant row (identified by its stamped Product.priceSeriesKey)
graphs its own variant series; any other row graphs the mean of its displayed
variants (see buildAggregateSeries), falling back to the base product
series. Returns undefined when there aren't two points to draw a line.
Summarize the latest price move for a series from its last two points. Returns a flat, zero-delta trend when there are fewer than two points.