This version of Antigravity is no longer supported. Please upgrade to receive the latest features.
This commit is contained in:
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
|
||||
import {
|
||||
buildCityMarketScanCacheKey,
|
||||
deriveCityMarketScanView,
|
||||
normalizeCityMarketScanPayload,
|
||||
resolveCityMarketScanSnapshot,
|
||||
writeCachedCityMarketScan,
|
||||
} from "@/components/dashboard/scan-terminal/market-scan-state";
|
||||
@@ -74,6 +75,35 @@ export function runTests() {
|
||||
assert.equal((cachedSnapshot.payload as unknown as { label: string }).label, "cached");
|
||||
}
|
||||
|
||||
storage.clear();
|
||||
writeCachedCityMarketScan(cacheKey, {
|
||||
market_scan: {
|
||||
available: true,
|
||||
market_price: 0.41,
|
||||
selected_slug: "highest-temperature-in-nyc-on-april-30-2026-60-61f",
|
||||
},
|
||||
selected_date: "2026-04-30",
|
||||
} as unknown as MarketScan);
|
||||
const wrappedCachedSnapshot = resolveCityMarketScanSnapshot({
|
||||
detail: cityDetail(),
|
||||
detailCityName: "Test City",
|
||||
enabled: true,
|
||||
});
|
||||
assert.equal(wrappedCachedSnapshot.action, "success");
|
||||
if (wrappedCachedSnapshot.action === "success") {
|
||||
assert.equal(wrappedCachedSnapshot.payload.available, true);
|
||||
assert.equal(wrappedCachedSnapshot.payload.market_price, 0.41);
|
||||
assert.equal(wrappedCachedSnapshot.payload.selected_date, "2026-04-30");
|
||||
}
|
||||
|
||||
const wrappedResponse = normalizeCityMarketScanPayload({
|
||||
market_scan: { available: true, yes_buy: 0.42 } as MarketScan,
|
||||
selected_date: "2026-04-30",
|
||||
});
|
||||
assert.equal(wrappedResponse?.available, true);
|
||||
assert.equal(wrappedResponse?.yes_buy, 0.42);
|
||||
assert.equal(wrappedResponse?.selected_date, "2026-04-30");
|
||||
|
||||
storage.clear();
|
||||
assert.equal(
|
||||
resolveCityMarketScanSnapshot({
|
||||
|
||||
@@ -7,11 +7,19 @@ import type { RemoteData } from "@/components/dashboard/scan-terminal/scan-termi
|
||||
import type { CityDetail, MarketScan } from "@/lib/dashboard-types";
|
||||
import { normalizeCityKey } from "./decision-utils";
|
||||
|
||||
const CITY_MARKET_SCAN_CACHE_PREFIX = "polyWeather_cityMarketScan_v3";
|
||||
const CITY_MARKET_SCAN_CACHE_PREFIX = "polyWeather_cityMarketScan_v4";
|
||||
const CITY_MARKET_SCAN_CACHE_TTL_MS = 10 * 60 * 1000;
|
||||
|
||||
export type CityMarketScanStatus = "idle" | "loading" | "ready" | "failed";
|
||||
|
||||
export type CityMarketScanApiPayload =
|
||||
| MarketScan
|
||||
| {
|
||||
fetched_at?: string | null;
|
||||
market_scan?: MarketScan | null;
|
||||
selected_date?: string | null;
|
||||
};
|
||||
|
||||
export type CityMarketScanSnapshot =
|
||||
| { action: "reset"; cacheKey?: string }
|
||||
| { action: "success"; cacheKey: string; payload: MarketScan; shouldWriteCache?: boolean }
|
||||
@@ -32,13 +40,30 @@ export function buildCityMarketScanCacheKey({
|
||||
}
|
||||
|
||||
export function readCachedCityMarketScan(cacheKey: string) {
|
||||
return readCachedPayload<MarketScan>(cacheKey, CITY_MARKET_SCAN_CACHE_TTL_MS);
|
||||
return normalizeCityMarketScanPayload(
|
||||
readCachedPayload<CityMarketScanApiPayload>(cacheKey, CITY_MARKET_SCAN_CACHE_TTL_MS),
|
||||
);
|
||||
}
|
||||
|
||||
export function writeCachedCityMarketScan(cacheKey: string, payload: MarketScan) {
|
||||
writeCachedPayload(cacheKey, payload);
|
||||
}
|
||||
|
||||
export function normalizeCityMarketScanPayload(
|
||||
payload: CityMarketScanApiPayload | null | undefined,
|
||||
): MarketScan | null {
|
||||
if (!payload || typeof payload !== "object") return null;
|
||||
if ("market_scan" in payload) {
|
||||
const scan = payload.market_scan;
|
||||
if (!scan || typeof scan !== "object") return null;
|
||||
return {
|
||||
...scan,
|
||||
selected_date: scan.selected_date ?? payload.selected_date ?? null,
|
||||
};
|
||||
}
|
||||
return payload as MarketScan;
|
||||
}
|
||||
|
||||
export function resolveCityMarketScanSnapshot({
|
||||
detail,
|
||||
detailCityName,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { extractStreamingAirportRead } from "@/components/dashboard/scan-terminal/ai-city-stream";
|
||||
import {
|
||||
normalizeCityMarketScanPayload,
|
||||
type CityMarketScanApiPayload,
|
||||
} from "@/components/dashboard/scan-terminal/market-scan-state";
|
||||
import type { AiCityForecastPayload } from "@/components/dashboard/scan-terminal/types";
|
||||
import {
|
||||
buildBrowserBackendHeaders,
|
||||
@@ -292,7 +296,7 @@ async function getMarketScan(city: string, options: MarketScanQueryOptions = {})
|
||||
if (options.marketSlug) params.set("market_slug", options.marketSlug);
|
||||
if (options.lite != null) params.set("lite", String(options.lite));
|
||||
const headers = await buildBrowserBackendHeaders({ Accept: "application/json" });
|
||||
return readJsonOrThrow<MarketScan>(
|
||||
const payload = await readJsonOrThrow<CityMarketScanApiPayload>(
|
||||
`/api/city/${encodeURIComponent(city)}/market-scan?${params.toString()}`,
|
||||
{
|
||||
cache: "no-store",
|
||||
@@ -300,6 +304,12 @@ async function getMarketScan(city: string, options: MarketScanQueryOptions = {})
|
||||
signal: options.signal,
|
||||
},
|
||||
);
|
||||
return (
|
||||
normalizeCityMarketScanPayload(payload) ?? {
|
||||
available: false,
|
||||
reason: "No market scan payload returned.",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function runQueuedAiCityReadTask<T>(task: () => Promise<T>, onQueued?: () => void) {
|
||||
|
||||
Reference in New Issue
Block a user