gloomberb 0.7.3 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/chart/chart-renderer.ts +109 -32
- package/src/components/chart/chart-types.ts +18 -0
- package/src/components/chart/chart.test.ts +152 -5
- package/src/components/chart/comparison-chart-renderer.test.ts +14 -30
- package/src/components/chart/comparison-chart-renderer.ts +50 -11
- package/src/components/chart/comparison-stock-chart.tsx +45 -8
- package/src/components/chart/market-session.ts +187 -0
- package/src/components/chart/native/chart-rasterizer.ts +65 -2
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +96 -0
- package/src/components/chart/stock-chart.tsx +36 -8
- package/src/components/chart/time-axis-label.tsx +56 -2
- package/src/components/command-bar/command-bar.test.tsx +1 -1
- package/src/components/command-bar/command-bar.tsx +7 -1
- package/src/components/command-bar/list-model.ts +8 -4
- package/src/components/command-bar/view-model.test.ts +20 -0
- package/src/components/command-bar/view-model.ts +15 -3
- package/src/components/ticker-list-table.tsx +4 -15
- package/src/core/state/session-persistence.ts +25 -0
- package/src/market-data/coordinator-subscriptions.test.ts +102 -18
- package/src/market-data/coordinator.test.ts +122 -2
- package/src/market-data/coordinator.ts +192 -55
- package/src/market-data/hooks.test.tsx +31 -0
- package/src/market-data/hooks.tsx +1 -0
- package/src/plugins/builtin/ai/screener-pane.tsx +3 -0
- package/src/plugins/builtin/chat-controller.ts +8 -7
- package/src/plugins/builtin/chat.test.tsx +55 -0
- package/src/plugins/builtin/chat.tsx +20 -6
- package/src/plugins/builtin/market-movers/index.tsx +72 -40
- package/src/plugins/builtin/news-wire/breaking-pane.test.tsx +156 -0
- package/src/plugins/builtin/news-wire/breaking-pane.tsx +0 -90
- package/src/plugins/builtin/news-wire/index.tsx +0 -2
- package/src/plugins/builtin/portfolio-list/metrics.ts +2 -2
- package/src/plugins/builtin/portfolio-list/pane.tsx +26 -9
- package/src/plugins/builtin/portfolio-list.test.tsx +29 -7
- package/src/plugins/builtin/sectors/index.tsx +30 -9
- package/src/plugins/builtin/ticker-detail/pane.tsx +18 -1
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +4 -1
- package/src/plugins/builtin/world-indices/index.tsx +58 -19
- package/src/sources/gloomberb-cloud.test.ts +55 -2
- package/src/sources/gloomberb-cloud.ts +180 -29
- package/src/sources/provider-router.test.ts +567 -10
- package/src/sources/provider-router.ts +323 -93
- package/src/sources/yahoo-finance.test.ts +37 -0
- package/src/sources/yahoo-finance.ts +48 -2
- package/src/state/app-bootstrap.test.ts +50 -23
- package/src/state/app-bootstrap.ts +26 -10
- package/src/state/session-persistence.test.ts +5 -3
- package/src/state/use-inline-tickers.ts +3 -0
- package/src/state/use-quote-streaming.ts +11 -0
- package/src/types/data-provider.ts +5 -1
- package/src/types/financials.ts +1 -0
- package/src/utils/api-client.test.ts +120 -0
- package/src/utils/api-client.ts +66 -19
- package/src/utils/exchanges.test.ts +16 -0
- package/src/utils/exchanges.ts +33 -0
- package/src/utils/options.ts +1 -1
- package/src/utils/price-history.test.ts +28 -1
- package/src/utils/price-history.ts +16 -0
- package/src/utils/quote-freshness.test.ts +150 -0
- package/src/utils/quote-freshness.ts +42 -1
- package/src/utils/quote-resolution.ts +2 -0
- package/src/utils/quote-time.test.ts +34 -0
- package/src/utils/quote-time.ts +18 -5
- package/src/version.ts +1 -1
- package/src/plugins/builtin/news-wire/digest-store.ts +0 -44
package/src/utils/quote-time.ts
CHANGED
|
@@ -11,20 +11,33 @@ export function formatQuoteAge(lastUpdated: number | undefined, now = Date.now()
|
|
|
11
11
|
const clamped = clampQuoteTimestamp(lastUpdated, now);
|
|
12
12
|
if (clamped == null) return "—";
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const ageMs = Math.max(0, now - clamped);
|
|
15
|
+
if (ageMs < 1000) return `${Math.floor(ageMs)}ms`;
|
|
16
|
+
|
|
17
|
+
const ageSeconds = ageMs / 1000;
|
|
15
18
|
if (ageSeconds < 60) return `${Math.floor(ageSeconds)}s`;
|
|
16
19
|
if (ageSeconds < 3600) return `${Math.floor(ageSeconds / 60)}m`;
|
|
17
20
|
if (ageSeconds < 86400) return `${Math.floor(ageSeconds / 3600)}h`;
|
|
18
21
|
return `${Math.floor(ageSeconds / 86400)}d`;
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
export function resolveQuoteAgeTimestamp(
|
|
25
|
+
quote: Pick<Quote, "lastUpdated" | "receivedAt"> | null | undefined,
|
|
26
|
+
now = Date.now(),
|
|
27
|
+
): number | null {
|
|
28
|
+
if (!quote) return null;
|
|
29
|
+
const receivedAt = clampQuoteTimestamp(quote.receivedAt, now);
|
|
30
|
+
return receivedAt ?? clampQuoteTimestamp(quote.lastUpdated, now);
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
export function formatQuoteAgeWithSource(
|
|
22
|
-
quote: Pick<Quote, "lastUpdated" | "dataSource"> | null | undefined,
|
|
34
|
+
quote: Pick<Quote, "lastUpdated" | "receivedAt" | "dataSource"> | null | undefined,
|
|
23
35
|
now = Date.now(),
|
|
24
36
|
): string {
|
|
25
37
|
if (!quote) return "—";
|
|
26
38
|
|
|
27
|
-
const
|
|
39
|
+
const timestamp = resolveQuoteAgeTimestamp(quote, now);
|
|
40
|
+
const age = timestamp == null ? "—" : formatQuoteAge(timestamp, now);
|
|
28
41
|
if (age === "—") return age;
|
|
29
42
|
|
|
30
43
|
const prefix = quote.dataSource === "delayed" ? "◷" : "";
|
|
@@ -32,12 +45,12 @@ export function formatQuoteAgeWithSource(
|
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
export function getMostRecentQuoteUpdate(
|
|
35
|
-
quotes: Iterable<Pick<Quote, "lastUpdated"> | null | undefined>,
|
|
48
|
+
quotes: Iterable<Pick<Quote, "lastUpdated" | "receivedAt"> | null | undefined>,
|
|
36
49
|
now = Date.now(),
|
|
37
50
|
): number | null {
|
|
38
51
|
let latest: number | null = null;
|
|
39
52
|
for (const quote of quotes) {
|
|
40
|
-
const timestamp =
|
|
53
|
+
const timestamp = resolveQuoteAgeTimestamp(quote, now);
|
|
41
54
|
if (timestamp != null && (latest == null || timestamp > latest)) {
|
|
42
55
|
latest = timestamp;
|
|
43
56
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.7.
|
|
1
|
+
export const VERSION = "0.7.4";
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { PluginPersistence } from "../../../types/plugin";
|
|
2
|
-
|
|
3
|
-
const CACHE_POLICY = {
|
|
4
|
-
staleMs: 7 * 86_400_000, // 7 days
|
|
5
|
-
expireMs: 30 * 86_400_000, // 30 days
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const cache = new Map<string, string>();
|
|
9
|
-
const inFlight = new Set<string>();
|
|
10
|
-
let persist: PluginPersistence | null = null;
|
|
11
|
-
|
|
12
|
-
export function setDigestPersistence(p: PluginPersistence): void {
|
|
13
|
-
persist = p;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function getDigest(articleId: string): string | null {
|
|
17
|
-
const cached = cache.get(articleId);
|
|
18
|
-
if (cached !== undefined) return cached;
|
|
19
|
-
|
|
20
|
-
if (!persist) return null;
|
|
21
|
-
const res = persist.getResource<string>("ai-digest", articleId, { allowExpired: false });
|
|
22
|
-
if (res) {
|
|
23
|
-
cache.set(articleId, res.value);
|
|
24
|
-
return res.value;
|
|
25
|
-
}
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function setDigest(articleId: string, digest: string): void {
|
|
30
|
-
cache.set(articleId, digest);
|
|
31
|
-
persist?.setResource("ai-digest", articleId, digest, { cachePolicy: CACHE_POLICY });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function isDigestInFlight(articleId: string): boolean {
|
|
35
|
-
return inFlight.has(articleId);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function markDigestInFlight(articleId: string): void {
|
|
39
|
-
inFlight.add(articleId);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function clearDigestInFlight(articleId: string): void {
|
|
43
|
-
inFlight.delete(articleId);
|
|
44
|
-
}
|