mirror of
https://github.com/kennethnym/aris.git
synced 2026-02-02 05:01:17 +00:00
Introduces FeedEngine that consumes FeedSource instances and manages the dependency graph for context flow and item collection. - Validates dependency graph (missing deps, circular references) - Topologically sorts sources for execution order - Runs fetchContext() in dependency order, accumulating context - Runs fetchItems() on all sources with final context - Supports reactive updates via onContextUpdate/onItemsUpdate - Graceful error handling (continues after source failures) Marks DataSource, ContextProvider, ContextBridge, Reconciler, and FeedController as deprecated in favor of FeedSource + FeedEngine. Co-authored-by: Ona <no-reply@ona.com>
@aris/core
Core orchestration layer for ARIS feed reconciliation.
Overview
flowchart TB
subgraph Sources["Feed Sources (Graph)"]
LS[Location Source]
WS[Weather Source]
TS[TFL Source]
CS[Calendar Source]
end
LS --> WS
LS --> TS
subgraph Controller["FeedController"]
direction TB
C1[Holds context]
C2[Manages source graph]
C3[Reconciles on update]
C4[Notifies subscribers]
end
Sources --> Controller
Controller --> Sub[Subscribers]
Concepts
FeedSource
A unified interface for sources that provide context and/or feed items. Sources form a dependency graph.
interface FeedSource<TItem extends FeedItem = FeedItem> {
readonly id: string
readonly dependencies?: readonly string[]
// Context production (optional)
onContextUpdate?(
callback: (update: Partial<Context>) => void,
getContext: () => Context,
): () => void
fetchContext?(context: Context): Promise<Partial<Context>>
// Feed item production (optional)
onItemsUpdate?(callback: (items: TItem[]) => void, getContext: () => Context): () => void
fetchItems?(context: Context): Promise<TItem[]>
}
A source may:
- Provide context for other sources (implement
fetchContext/onContextUpdate) - Produce feed items (implement
fetchItems/onItemsUpdate) - Both
Context Keys
Each package exports typed context keys for type-safe access:
import { contextKey, type ContextKey } from "@aris/core"
interface Location {
lat: number
lng: number
}
export const LocationKey: ContextKey<Location> = contextKey("location")
Usage
Define a Context-Only Source
import type { FeedSource } from "@aris/core"
const locationSource: FeedSource = {
id: "location",
onContextUpdate(callback, _getContext) {
const watchId = navigator.geolocation.watchPosition((pos) => {
callback({
[LocationKey]: { lat: pos.coords.latitude, lng: pos.coords.longitude },
})
})
return () => navigator.geolocation.clearWatch(watchId)
},
async fetchContext() {
const pos = await getCurrentPosition()
return {
[LocationKey]: { lat: pos.coords.latitude, lng: pos.coords.longitude },
}
},
}
Define a Source with Dependencies
import type { FeedSource, FeedItem } from "@aris/core"
import { contextValue } from "@aris/core"
type WeatherItem = FeedItem<"weather", { temp: number; condition: string }>
const weatherSource: FeedSource<WeatherItem> = {
id: "weather",
dependencies: ["location"],
async fetchContext(context) {
const location = contextValue(context, LocationKey)
if (!location) return {}
const weather = await fetchWeatherApi(location)
return { [WeatherKey]: weather }
},
async fetchItems(context) {
const weather = contextValue(context, WeatherKey)
if (!weather) return []
return [
{
id: `weather-${Date.now()}`,
type: "weather",
priority: 0.5,
timestamp: new Date(),
data: { temp: weather.temp, condition: weather.condition },
},
]
},
}
Graph Behavior
The source graph:
- Validates all dependencies exist
- Detects circular dependencies
- Topologically sorts sources
On refresh:
fetchContextruns in dependency orderfetchItemsruns on all sources- Combined items returned to subscribers
On reactive update:
- Source pushes context update via
onContextUpdatecallback - Dependent sources re-run
fetchContext - Affected sources re-run
fetchItems - Subscribers notified
API
Context
| Export | Description |
|---|---|
ContextKey<T> |
Branded type for type-safe context keys |
contextKey<T>(key) |
Creates a typed context key |
contextValue(context, key) |
Type-safe context value accessor |
Context |
Time + arbitrary key-value bag |
Feed
| Export | Description |
|---|---|
FeedSource<TItem> |
Unified source interface |
FeedItem<TType, TData> |
Single item in the feed |
Legacy (deprecated)
| Export | Description |
|---|---|
DataSource<TItem, TConfig> |
Use FeedSource instead |
ContextProvider<T> |
Use FeedSource instead |
ContextBridge |
Use source graph instead |