Files
homarr/packages/request-handler/src/stock-price.ts
Jack Weller 91a69c162a feat: stock price widget (#2579)
* feat: added stock price widget

* fix: formatting

* fix: broken lock file

* fix: requested changes

* fix: added parsing schema

* fix: improve time range and interval inputs

* fix: only return required data

* fix: formatting

* fix: deepsource tests

* fix: moved all time frames into one location

* fix: formatting

* fix: requested changes

* fix: formatting

* fix: parse response data

* fix: update packages

* fix: typescript issues

* fix: formatting

* fix: broken lockfile

---------

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2025-03-20 17:49:19 +01:00

59 lines
1.5 KiB
TypeScript

import dayjs from "dayjs";
import { z } from "zod";
import { fetchWithTimeout } from "@homarr/common";
import { createCachedWidgetRequestHandler } from "./lib/cached-widget-request-handler";
export const fetchStockPriceHandler = createCachedWidgetRequestHandler({
queryKey: "fetchStockPriceResult",
widgetKind: "stockPrice",
async requestAsync(input: { stock: string; timeRange: string; timeInterval: string }) {
const response = await fetchWithTimeout(
`https://query1.finance.yahoo.com/v8/finance/chart/${input.stock}?range=${input.timeRange}&interval=${input.timeInterval}`,
);
const data = dataSchema.parse(await response.json());
if ("error" in data) {
throw new Error(data.error.description);
}
if (data.chart.result.length !== 1) {
throw new Error("Received multiple results");
}
if (!data.chart.result[0]) {
throw new Error("Received invalid data");
}
return data.chart.result[0];
},
cacheDuration: dayjs.duration(5, "minutes"),
});
const dataSchema = z
.object({
error: z.object({
description: z.string(),
}),
})
.or(
z.object({
chart: z.object({
result: z.array(
z.object({
indicators: z.object({
quote: z.array(
z.object({
close: z.array(z.number()),
}),
),
}),
meta: z.object({
symbol: z.string(),
shortName: z.string(),
}),
}),
),
}),
}),
);