From 037589cf4fd98344a5e8f4d92b20ea72e07f0d90 Mon Sep 17 00:00:00 2001 From: kenneth Date: Sun, 18 Jan 2026 20:23:54 +0000 Subject: [PATCH] refactor(core): rename getCurrentValue to fetchCurrentValue Also use Promise.allSettled in ContextBridge.refresh() to handle provider errors gracefully. Co-authored-by: Ona --- packages/aris-core/README.md | 2 +- packages/aris-core/src/context-bridge.ts | 10 +++++-- packages/aris-core/src/context-provider.ts | 6 ++--- packages/aris-core/src/integration.test.ts | 2 +- .../aris-data-source-tfl/src/data-source.ts | 26 ++++++++++++++----- .../src/integration.test.ts | 12 +++++---- packages/aris-data-source-tfl/src/tfl-api.ts | 1 + packages/aris-data-source-tfl/src/types.ts | 1 + 8 files changed, 41 insertions(+), 19 deletions(-) diff --git a/packages/aris-core/README.md b/packages/aris-core/README.md index f0f3716..d933964 100644 --- a/packages/aris-core/README.md +++ b/packages/aris-core/README.md @@ -107,7 +107,7 @@ class LocationProvider implements ContextProvider { return () => navigator.geolocation.clearWatch(watchId) } - async getCurrentValue(): Promise { + async fetchCurrentValue(): Promise { const pos = await new Promise((resolve, reject) => { navigator.geolocation.getCurrentPosition(resolve, reject) }) diff --git a/packages/aris-core/src/context-bridge.ts b/packages/aris-core/src/context-bridge.ts index 58fd37f..469cdfe 100644 --- a/packages/aris-core/src/context-bridge.ts +++ b/packages/aris-core/src/context-bridge.ts @@ -55,15 +55,21 @@ export class ContextBridge { /** * Gathers current values from all providers and pushes to controller. * Use for manual refresh when user pulls to refresh. + * Errors from individual providers are silently ignored. */ async refresh(): Promise { const updates: Partial = {} const entries = Array.from(this.providers.entries()) - const values = await Promise.all(entries.map(([_, provider]) => provider.getCurrentValue())) + const results = await Promise.allSettled( + entries.map(([_, provider]) => provider.fetchCurrentValue()), + ) entries.forEach(([key], i) => { - updates[key] = values[i] + const result = results[i] + if (result?.status === "fulfilled") { + updates[key] = result.value + } }) this.controller.pushContextUpdate(updates) diff --git a/packages/aris-core/src/context-provider.ts b/packages/aris-core/src/context-provider.ts index 55baf01..1dd5c25 100644 --- a/packages/aris-core/src/context-provider.ts +++ b/packages/aris-core/src/context-provider.ts @@ -16,7 +16,7 @@ * return () => navigator.geolocation.clearWatch(watchId) * } * - * async getCurrentValue(): Promise { + * async fetchCurrentValue(): Promise { * const pos = await getCurrentPosition() * return { lat: pos.coords.latitude, lng: pos.coords.longitude, accuracy: pos.coords.accuracy } * } @@ -30,6 +30,6 @@ export interface ContextProvider { /** Subscribe to value changes. Returns cleanup function. */ onUpdate(callback: (value: T) => void): () => void - /** Get current value on-demand (used for manual refresh). */ - getCurrentValue(): Promise + /** Fetch current value on-demand (used for manual refresh). */ + fetchCurrentValue(): Promise } diff --git a/packages/aris-core/src/integration.test.ts b/packages/aris-core/src/integration.test.ts index bfdd4b7..ea02e7f 100644 --- a/packages/aris-core/src/integration.test.ts +++ b/packages/aris-core/src/integration.test.ts @@ -111,7 +111,7 @@ function createLocationProvider(): SimulatedLocationProvider { callback = null } }, - async getCurrentValue() { + async fetchCurrentValue() { return currentLocation }, simulateUpdate(location: Location) { diff --git a/packages/aris-data-source-tfl/src/data-source.ts b/packages/aris-data-source-tfl/src/data-source.ts index ff4e8bd..ed299c3 100644 --- a/packages/aris-data-source-tfl/src/data-source.ts +++ b/packages/aris-data-source-tfl/src/data-source.ts @@ -1,5 +1,5 @@ import type { Context, DataSource } from "@aris/core" -import { TflApi, type ITflApi } from "./tfl-api.ts" + import type { StationLocation, TflAlertData, @@ -10,6 +10,8 @@ import type { TflLineId, } from "./types.ts" +import { TflApi, type ITflApi } from "./tfl-api.ts" + const SEVERITY_PRIORITY: Record = { closure: 100, "major-delays": 80, @@ -22,7 +24,10 @@ function haversineDistance(lat1: number, lng1: number, lat2: number, lng2: numbe const dLng = ((lng2 - lng1) * Math.PI) / 180 const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + - Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLng / 2) * Math.sin(dLng / 2) + Math.cos((lat1 * Math.PI) / 180) * + Math.cos((lat2 * Math.PI) / 180) * + Math.sin(dLng / 2) * + Math.sin(dLng / 2) const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) return R * c } @@ -62,13 +67,20 @@ export class TflDataSource implements DataSource { - const [statuses, stations] = await Promise.all([this.api.fetchLineStatuses(config.lines), this.api.fetchStations()]) + const [statuses, stations] = await Promise.all([ + this.api.fetchLineStatuses(config.lines), + this.api.fetchStations(), + ]) const items: TflAlertFeedItem[] = statuses.map((status) => { - const closestStationDistance = - context.location ? - findClosestStationDistance(status.lineId, stations, context.location.lat, context.location.lng) - : null + const closestStationDistance = context.location + ? findClosestStationDistance( + status.lineId, + stations, + context.location.lat, + context.location.lng, + ) + : null const data: TflAlertData = { line: status.lineId, diff --git a/packages/aris-data-source-tfl/src/integration.test.ts b/packages/aris-data-source-tfl/src/integration.test.ts index 668c672..94896a5 100644 --- a/packages/aris-data-source-tfl/src/integration.test.ts +++ b/packages/aris-data-source-tfl/src/integration.test.ts @@ -1,11 +1,12 @@ +import type { Context } from "@aris/core" + import { describe, expect, test } from "bun:test" -import type { Context } from "@aris/core" -import { TflDataSource } from "./data-source.ts" import type { ITflApi, TflLineStatus } from "./tfl-api.ts" import type { StationLocation, TflLineId } from "./types.ts" import fixtures from "../fixtures/tfl-responses.json" +import { TflDataSource } from "./data-source.ts" // Mock API that returns fixture data class FixtureTflApi implements ITflApi { @@ -109,9 +110,10 @@ describe("TfL Feed Items (using fixture data)", () => { expect(typeof item.data.lineName).toBe("string") expect(["minor-delays", "major-delays", "closure"]).toContain(item.data.severity) expect(typeof item.data.description).toBe("string") - expect(item.data.closestStationDistance === null || typeof item.data.closestStationDistance === "number").toBe( - true, - ) + expect( + item.data.closestStationDistance === null || + typeof item.data.closestStationDistance === "number", + ).toBe(true) } }) diff --git a/packages/aris-data-source-tfl/src/tfl-api.ts b/packages/aris-data-source-tfl/src/tfl-api.ts index 13c1d4d..70aec52 100644 --- a/packages/aris-data-source-tfl/src/tfl-api.ts +++ b/packages/aris-data-source-tfl/src/tfl-api.ts @@ -1,4 +1,5 @@ import { type } from "arktype" + import type { StationLocation, TflAlertSeverity } from "./types.ts" const TFL_API_BASE = "https://api.tfl.gov.uk" diff --git a/packages/aris-data-source-tfl/src/types.ts b/packages/aris-data-source-tfl/src/types.ts index 7b497ee..36cf098 100644 --- a/packages/aris-data-source-tfl/src/types.ts +++ b/packages/aris-data-source-tfl/src/types.ts @@ -1,4 +1,5 @@ import type { FeedItem } from "@aris/core" + import type { TflLineId } from "./tfl-api.ts" export type { TflLineId } from "./tfl-api.ts"