mirror of
https://github.com/kennethnym/aris.git
synced 2026-06-13 19:11:18 +01:00
chore: rename aelis to freya (#122)
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "@aelis/feed-enhancers",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"scripts": {
|
||||
"test": "bun test src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aelis/core": "workspace:*",
|
||||
"@aelis/source-caldav": "workspace:*",
|
||||
"@aelis/source-google-calendar": "workspace:*",
|
||||
"@aelis/source-tfl": "workspace:*",
|
||||
"@aelis/source-weatherkit": "workspace:*"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { ContextKey } from "@aelis/core"
|
||||
|
||||
import { contextKey } from "@aelis/core"
|
||||
|
||||
export interface NextEvent {
|
||||
title: string
|
||||
startTime: Date
|
||||
endTime: Date
|
||||
minutesUntilStart: number
|
||||
location: string | null
|
||||
}
|
||||
|
||||
export const NextEventKey: ContextKey<NextEvent> = contextKey("aelis.google-calendar", "nextEvent")
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@aelis/components",
|
||||
"name": "@freya/components",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -1,6 +1,6 @@
|
||||
# @aelis/core
|
||||
# @freya/core
|
||||
|
||||
Core orchestration layer for AELIS feed reconciliation.
|
||||
Core orchestration layer for FREYA feed reconciliation.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -63,7 +63,7 @@ A source may:
|
||||
Each package exports typed context keys for type-safe access:
|
||||
|
||||
```ts
|
||||
import { contextKey, type ContextKey } from "@aelis/core"
|
||||
import { contextKey, type ContextKey } from "@freya/core"
|
||||
|
||||
interface Location {
|
||||
lat: number
|
||||
@@ -78,7 +78,7 @@ export const LocationKey: ContextKey<Location> = contextKey("location")
|
||||
### Define a Context-Only Source
|
||||
|
||||
```ts
|
||||
import type { FeedSource } from "@aelis/core"
|
||||
import type { FeedSource } from "@freya/core"
|
||||
|
||||
const locationSource: FeedSource = {
|
||||
id: "location",
|
||||
@@ -104,8 +104,8 @@ const locationSource: FeedSource = {
|
||||
### Define a Source with Dependencies
|
||||
|
||||
```ts
|
||||
import type { FeedSource, FeedItem } from "@aelis/core"
|
||||
import { contextValue } from "@aelis/core"
|
||||
import type { FeedSource, FeedItem } from "@freya/core"
|
||||
import { contextValue } from "@freya/core"
|
||||
|
||||
type WeatherItem = FeedItem<"weather", { temp: number; condition: string }>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@aelis/core",
|
||||
"name": "@freya/core",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -5,7 +5,7 @@ import type { StandardSchemaV1 } from "@standard-schema/spec"
|
||||
*
|
||||
* Action IDs use descriptive verb-noun kebab-case (e.g., "update-location", "play-track").
|
||||
* Combined with the source's reverse-domain ID, they form a globally unique identifier:
|
||||
* `<sourceId>/<actionId>` (e.g., "aelis.location/update-location").
|
||||
* `<sourceId>/<actionId>` (e.g., "freya.location/update-location").
|
||||
*/
|
||||
export class UnknownActionError extends Error {
|
||||
readonly actionId: string
|
||||
@@ -12,8 +12,8 @@ interface NextEvent {
|
||||
title: string
|
||||
}
|
||||
|
||||
const WeatherKey: ContextKey<Weather> = contextKey("aelis.weather", "current")
|
||||
const NextEventKey: ContextKey<NextEvent> = contextKey("aelis.google-calendar", "nextEvent")
|
||||
const WeatherKey: ContextKey<Weather> = contextKey("freya.weather", "current")
|
||||
const NextEventKey: ContextKey<NextEvent> = contextKey("freya.google-calendar", "nextEvent")
|
||||
|
||||
describe("Context", () => {
|
||||
describe("get", () => {
|
||||
@@ -66,10 +66,10 @@ describe("Context", () => {
|
||||
})
|
||||
|
||||
test("prefix match returns multiple instances", () => {
|
||||
const workKey = contextKey<NextEvent>("aelis.google-calendar", "nextEvent", {
|
||||
const workKey = contextKey<NextEvent>("freya.google-calendar", "nextEvent", {
|
||||
account: "work",
|
||||
})
|
||||
const personalKey = contextKey<NextEvent>("aelis.google-calendar", "nextEvent", {
|
||||
const personalKey = contextKey<NextEvent>("freya.google-calendar", "nextEvent", {
|
||||
account: "personal",
|
||||
})
|
||||
|
||||
@@ -79,7 +79,7 @@ describe("Context", () => {
|
||||
[personalKey, { title: "Dentist" }],
|
||||
])
|
||||
|
||||
const prefix = contextKey<NextEvent>("aelis.google-calendar", "nextEvent")
|
||||
const prefix = contextKey<NextEvent>("freya.google-calendar", "nextEvent")
|
||||
const results = ctx.find(prefix)
|
||||
|
||||
expect(results).toHaveLength(2)
|
||||
@@ -88,8 +88,8 @@ describe("Context", () => {
|
||||
})
|
||||
|
||||
test("prefix match includes exact match and longer keys", () => {
|
||||
const baseKey = contextKey<NextEvent>("aelis.google-calendar", "nextEvent")
|
||||
const instanceKey = contextKey<NextEvent>("aelis.google-calendar", "nextEvent", {
|
||||
const baseKey = contextKey<NextEvent>("freya.google-calendar", "nextEvent")
|
||||
const instanceKey = contextKey<NextEvent>("freya.google-calendar", "nextEvent", {
|
||||
account: "work",
|
||||
})
|
||||
|
||||
@@ -104,8 +104,8 @@ describe("Context", () => {
|
||||
})
|
||||
|
||||
test("does not match keys that share a string prefix but differ at segment boundary", () => {
|
||||
const keyA = contextKey<string>("aelis.calendar", "next")
|
||||
const keyB = contextKey<string>("aelis.calendar", "nextEvent")
|
||||
const keyA = contextKey<string>("freya.calendar", "next")
|
||||
const keyB = contextKey<string>("freya.calendar", "nextEvent")
|
||||
|
||||
const ctx = new Context()
|
||||
ctx.set([
|
||||
@@ -137,20 +137,20 @@ describe("Context", () => {
|
||||
test("single-segment prefix matches all keys starting with that segment", () => {
|
||||
const ctx = new Context()
|
||||
ctx.set([
|
||||
[contextKey("aelis.weather", "current"), { temperature: 20 }],
|
||||
[contextKey("aelis.weather", "forecast"), { high: 25 }],
|
||||
[contextKey("aelis.calendar", "nextEvent"), { title: "Meeting" }],
|
||||
[contextKey("freya.weather", "current"), { temperature: 20 }],
|
||||
[contextKey("freya.weather", "forecast"), { high: 25 }],
|
||||
[contextKey("freya.calendar", "nextEvent"), { title: "Meeting" }],
|
||||
])
|
||||
|
||||
const results = ctx.find(contextKey("aelis.weather"))
|
||||
const results = ctx.find(contextKey("freya.weather"))
|
||||
expect(results).toHaveLength(2)
|
||||
})
|
||||
|
||||
test("does not match shorter keys", () => {
|
||||
const ctx = new Context()
|
||||
ctx.set([[contextKey("aelis.weather"), "short"]])
|
||||
ctx.set([[contextKey("freya.weather"), "short"]])
|
||||
|
||||
const results = ctx.find(contextKey("aelis.weather", "current"))
|
||||
const results = ctx.find(contextKey("freya.weather", "current"))
|
||||
expect(results).toHaveLength(0)
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Tuple-keyed context system inspired by React Query's query keys.
|
||||
*
|
||||
* Context keys are arrays that form a hierarchy. Sources write to specific
|
||||
* keys (e.g., ["aelis.google-calendar", "nextEvent", { account: "work" }])
|
||||
* keys (e.g., ["freya.google-calendar", "nextEvent", { account: "work" }])
|
||||
* and consumers can query by exact match or prefix match to get all values
|
||||
* of a given type across source instances.
|
||||
*/
|
||||
@@ -17,7 +17,7 @@ import type { FeedItem } from "./feed"
|
||||
* const data = await fetchWeather(location)
|
||||
* return [{
|
||||
* id: `weather-${Date.now()}`,
|
||||
* sourceId: "aelis.weather",
|
||||
* sourceId: "freya.weather",
|
||||
* type: this.type,
|
||||
* timestamp: context.time,
|
||||
* data: { temp: data.temperature },
|
||||
@@ -29,13 +29,13 @@ type WeatherItem = FeedItem<"weather", { temp: number }>
|
||||
type CalendarItem = FeedItem<"calendar", { title: string }>
|
||||
|
||||
function weatherItem(id: string, temp: number): WeatherItem {
|
||||
return { id, sourceId: "aelis.weather", type: "weather", timestamp: new Date(), data: { temp } }
|
||||
return { id, sourceId: "freya.weather", type: "weather", timestamp: new Date(), data: { temp } }
|
||||
}
|
||||
|
||||
function calendarItem(id: string, title: string): CalendarItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.calendar",
|
||||
sourceId: "freya.calendar",
|
||||
type: "calendar",
|
||||
timestamp: new Date(),
|
||||
data: { title },
|
||||
@@ -48,7 +48,7 @@ function calendarItem(id: string, title: string): CalendarItem {
|
||||
|
||||
function createWeatherSource(items: WeatherItem[]) {
|
||||
return {
|
||||
id: "aelis.weather",
|
||||
id: "freya.weather",
|
||||
...noActions,
|
||||
async fetchContext() {
|
||||
return null
|
||||
@@ -61,7 +61,7 @@ function createWeatherSource(items: WeatherItem[]) {
|
||||
|
||||
function createCalendarSource(items: CalendarItem[]) {
|
||||
return {
|
||||
id: "aelis.calendar",
|
||||
id: "freya.calendar",
|
||||
...noActions,
|
||||
async fetchContext() {
|
||||
return null
|
||||
@@ -486,7 +486,7 @@ describe("FeedPostProcessor", () => {
|
||||
let triggerUpdate: ((entries: readonly ContextEntry[]) => void) | null = null
|
||||
|
||||
const source: FeedSource = {
|
||||
id: "aelis.reactive",
|
||||
id: "freya.reactive",
|
||||
...noActions,
|
||||
async fetchContext() {
|
||||
return null
|
||||
@@ -528,7 +528,7 @@ describe("FeedPostProcessor", () => {
|
||||
let triggerItemsUpdate: ((items: FeedItem[]) => void) | null = null
|
||||
|
||||
const source: FeedSource = {
|
||||
id: "aelis.reactive",
|
||||
id: "freya.reactive",
|
||||
...noActions,
|
||||
async fetchContext() {
|
||||
return null
|
||||
@@ -9,7 +9,7 @@ import type { FeedItem } from "./feed"
|
||||
* it depends on, and the graph ensures dependencies are resolved before
|
||||
* dependents run.
|
||||
*
|
||||
* Source IDs use reverse domain notation. Built-in sources use `aelis.<name>`,
|
||||
* Source IDs use reverse domain notation. Built-in sources use `freya.<name>`,
|
||||
* third parties use their own domain (e.g., `com.spotify`).
|
||||
*
|
||||
* Every method maps to a protocol operation for remote source support:
|
||||
@@ -24,7 +24,7 @@ import type { FeedItem } from "./feed"
|
||||
* @example
|
||||
* ```ts
|
||||
* const locationSource: FeedSource = {
|
||||
* id: "aelis.location",
|
||||
* id: "freya.location",
|
||||
* async listActions() { return { "update-location": { id: "update-location" } } },
|
||||
* async executeAction(actionId) { throw new UnknownActionError(actionId) },
|
||||
* async fetchContext() { ... },
|
||||
@@ -18,7 +18,7 @@ describe("FeedItem slots", () => {
|
||||
test("FeedItem with unfilled slots", () => {
|
||||
const item: FeedItem<"weather", { temp: number }> = {
|
||||
id: "weather-1",
|
||||
sourceId: "aelis.weather",
|
||||
sourceId: "freya.weather",
|
||||
type: "weather",
|
||||
timestamp: new Date(),
|
||||
data: { temp: 18 },
|
||||
@@ -43,7 +43,7 @@ describe("FeedItem slots", () => {
|
||||
test("FeedItem with filled slots", () => {
|
||||
const item: FeedItem<"weather", { temp: number }> = {
|
||||
id: "weather-1",
|
||||
sourceId: "aelis.weather",
|
||||
sourceId: "freya.weather",
|
||||
type: "weather",
|
||||
timestamp: new Date(),
|
||||
data: { temp: 18 },
|
||||
@@ -48,7 +48,7 @@ export interface Slot {
|
||||
*
|
||||
* const item: WeatherItem = {
|
||||
* id: "weather-123",
|
||||
* sourceId: "aelis.weatherkit",
|
||||
* sourceId: "freya.weatherkit",
|
||||
* type: "weather",
|
||||
* timestamp: new Date(),
|
||||
* data: { temp: 18, condition: "cloudy" },
|
||||
17
packages/freya-feed-enhancers/package.json
Normal file
17
packages/freya-feed-enhancers/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@freya/feed-enhancers",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"scripts": {
|
||||
"test": "bun test src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@freya/core": "workspace:*",
|
||||
"@freya/source-caldav": "workspace:*",
|
||||
"@freya/source-google-calendar": "workspace:*",
|
||||
"@freya/source-tfl": "workspace:*",
|
||||
"@freya/source-weatherkit": "workspace:*"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { FeedItem, FeedItemSignals } from "@aelis/core"
|
||||
import type { FeedItem, FeedItemSignals } from "@freya/core"
|
||||
|
||||
import { Context, TimeRelevance } from "@aelis/core"
|
||||
import { CalDavFeedItemType } from "@aelis/source-caldav"
|
||||
import { CalendarFeedItemType } from "@aelis/source-google-calendar"
|
||||
import { TflFeedItemType } from "@aelis/source-tfl"
|
||||
import { WeatherFeedItemType } from "@aelis/source-weatherkit"
|
||||
import { Context, TimeRelevance } from "@freya/core"
|
||||
import { CalDavFeedItemType } from "@freya/source-caldav"
|
||||
import { CalendarFeedItemType } from "@freya/source-google-calendar"
|
||||
import { TflFeedItemType } from "@freya/source-tfl"
|
||||
import { WeatherFeedItemType } from "@freya/source-weatherkit"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
import {
|
||||
@@ -40,7 +40,7 @@ function saturday(hour: number, minute = 0): Date {
|
||||
function weatherCurrent(id = "w-current"): FeedItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.weather",
|
||||
sourceId: "freya.weather",
|
||||
type: WeatherFeedItemType.Current,
|
||||
timestamp: new Date(),
|
||||
data: { temperature: 18, precipitationIntensity: 0 },
|
||||
@@ -50,7 +50,7 @@ function weatherCurrent(id = "w-current"): FeedItem {
|
||||
function weatherCurrentRainy(id = "w-current-rain"): FeedItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.weather",
|
||||
sourceId: "freya.weather",
|
||||
type: WeatherFeedItemType.Current,
|
||||
timestamp: new Date(),
|
||||
data: { temperature: 12, precipitationIntensity: 2.5 },
|
||||
@@ -60,7 +60,7 @@ function weatherCurrentRainy(id = "w-current-rain"): FeedItem {
|
||||
function weatherCurrentExtreme(id = "w-current-extreme"): FeedItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.weather",
|
||||
sourceId: "freya.weather",
|
||||
type: WeatherFeedItemType.Current,
|
||||
timestamp: new Date(),
|
||||
data: { temperature: -5, precipitationIntensity: 0 },
|
||||
@@ -70,7 +70,7 @@ function weatherCurrentExtreme(id = "w-current-extreme"): FeedItem {
|
||||
function weatherHourly(id = "w-hourly"): FeedItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.weather",
|
||||
sourceId: "freya.weather",
|
||||
type: WeatherFeedItemType.Hourly,
|
||||
timestamp: new Date(),
|
||||
data: { forecastTime: new Date(), temperature: 20 },
|
||||
@@ -80,7 +80,7 @@ function weatherHourly(id = "w-hourly"): FeedItem {
|
||||
function weatherDaily(id = "w-daily"): FeedItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.weather",
|
||||
sourceId: "freya.weather",
|
||||
type: WeatherFeedItemType.Daily,
|
||||
timestamp: new Date(),
|
||||
data: { forecastDate: new Date() },
|
||||
@@ -90,7 +90,7 @@ function weatherDaily(id = "w-daily"): FeedItem {
|
||||
function weatherAlert(id = "w-alert", urgency = 0.9): FeedItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.weather",
|
||||
sourceId: "freya.weather",
|
||||
type: WeatherFeedItemType.Alert,
|
||||
timestamp: new Date(),
|
||||
data: { severity: "extreme" },
|
||||
@@ -105,7 +105,7 @@ function calendarEvent(
|
||||
): FeedItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.google-calendar",
|
||||
sourceId: "freya.google-calendar",
|
||||
type: CalendarFeedItemType.Event,
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
@@ -127,7 +127,7 @@ function calendarEvent(
|
||||
function calendarAllDay(id: string): FeedItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.google-calendar",
|
||||
sourceId: "freya.google-calendar",
|
||||
type: CalendarFeedItemType.AllDay,
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
@@ -153,7 +153,7 @@ function caldavEvent(
|
||||
): FeedItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.caldav",
|
||||
sourceId: "freya.caldav",
|
||||
type: CalDavFeedItemType.Event,
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
@@ -179,7 +179,7 @@ function caldavEvent(
|
||||
function tflAlert(id = "tfl-1", urgency = 0.8): FeedItem {
|
||||
return {
|
||||
id,
|
||||
sourceId: "aelis.tfl",
|
||||
sourceId: "freya.tfl",
|
||||
type: TflFeedItemType.Alert,
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { Context, FeedEnhancement, FeedItem, FeedPostProcessor } from "@aelis/core"
|
||||
import type { CalDavEventData } from "@aelis/source-caldav"
|
||||
import type { CalendarEventData } from "@aelis/source-google-calendar"
|
||||
import type { CurrentWeatherData } from "@aelis/source-weatherkit"
|
||||
import type { Context, FeedEnhancement, FeedItem, FeedPostProcessor } from "@freya/core"
|
||||
import type { CalDavEventData } from "@freya/source-caldav"
|
||||
import type { CalendarEventData } from "@freya/source-google-calendar"
|
||||
import type { CurrentWeatherData } from "@freya/source-weatherkit"
|
||||
|
||||
import { TimeRelevance } from "@aelis/core"
|
||||
import { CalDavFeedItemType } from "@aelis/source-caldav"
|
||||
import { CalendarFeedItemType } from "@aelis/source-google-calendar"
|
||||
import { TflFeedItemType } from "@aelis/source-tfl"
|
||||
import { WeatherFeedItemType } from "@aelis/source-weatherkit"
|
||||
import { TimeRelevance } from "@freya/core"
|
||||
import { CalDavFeedItemType } from "@freya/source-caldav"
|
||||
import { CalendarFeedItemType } from "@freya/source-google-calendar"
|
||||
import { TflFeedItemType } from "@freya/source-tfl"
|
||||
import { WeatherFeedItemType } from "@freya/source-weatherkit"
|
||||
|
||||
export const TimePeriod = {
|
||||
Morning: "morning",
|
||||
@@ -1,11 +1,11 @@
|
||||
# @aelis/source-caldav
|
||||
# @freya/source-caldav
|
||||
|
||||
A FeedSource that fetches calendar events from any CalDAV server.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { CalDavSource } from "@aelis/source-caldav"
|
||||
import { CalDavSource } from "@freya/source-caldav"
|
||||
|
||||
// Basic auth (Nextcloud, Radicale, Baikal, iCloud, etc.)
|
||||
const source = new CalDavSource({
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@aelis/source-caldav",
|
||||
"name": "@freya/source-caldav",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -9,8 +9,8 @@
|
||||
"test:live": "bun run scripts/test-live.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aelis/components": "workspace:*",
|
||||
"@aelis/core": "workspace:*",
|
||||
"@freya/components": "workspace:*",
|
||||
"@freya/core": "workspace:*",
|
||||
"ical.js": "^2.1.0",
|
||||
"tsdav": "^2.1.7"
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
* Writes feed items (with slots) to scripts/.cache/feed-items.json for inspection.
|
||||
*/
|
||||
|
||||
import { Context } from "@aelis/core"
|
||||
import { Context } from "@freya/core"
|
||||
import { mkdirSync, writeFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ContextEntry } from "@aelis/core"
|
||||
import type { ContextEntry } from "@freya/core"
|
||||
|
||||
import { Context, TimeRelevance } from "@aelis/core"
|
||||
import { Context, TimeRelevance } from "@freya/core"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { readFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
@@ -76,7 +76,7 @@ describe("CalDavSource", () => {
|
||||
test("has correct id", () => {
|
||||
const client = new MockDAVClient([], {})
|
||||
const source = createSource(client)
|
||||
expect(source.id).toBe("aelis.caldav")
|
||||
expect(source.id).toBe("freya.caldav")
|
||||
})
|
||||
|
||||
test("returns empty array when no calendars exist", async () => {
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ActionDefinition, ContextEntry, FeedItemSignals, FeedSource, Slot } from "@aelis/core"
|
||||
import type { ActionDefinition, ContextEntry, FeedItemSignals, FeedSource, Slot } from "@freya/core"
|
||||
|
||||
import { Context, TimeRelevance, UnknownActionError } from "@aelis/core"
|
||||
import { Context, TimeRelevance, UnknownActionError } from "@freya/core"
|
||||
import { DAVClient } from "tsdav"
|
||||
|
||||
import type { CalDavDAVClient, CalDavEventData, CalDavFeedItem } from "./types.ts"
|
||||
@@ -71,7 +71,7 @@ const DEFAULT_LOOK_AHEAD_DAYS = 0
|
||||
* ```
|
||||
*/
|
||||
export class CalDavSource implements FeedSource<CalDavFeedItem> {
|
||||
readonly id = "aelis.caldav"
|
||||
readonly id = "freya.caldav"
|
||||
|
||||
private options: CalDavSourceOptions | null
|
||||
private readonly lookAheadDays: number
|
||||
@@ -180,7 +180,7 @@ export class CalDavSource implements FeedSource<CalDavFeedItem> {
|
||||
const allEvents: CalDavEventData[] = []
|
||||
for (const result of results) {
|
||||
if (result.status === "rejected") {
|
||||
console.warn("[aelis.caldav] Failed to fetch calendar:", result.reason)
|
||||
console.warn("[freya.caldav] Failed to fetch calendar:", result.reason)
|
||||
continue
|
||||
}
|
||||
const { objects, calendarName } = result.value
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ContextKey } from "@aelis/core"
|
||||
import type { ContextKey } from "@freya/core"
|
||||
|
||||
import { contextKey } from "@aelis/core"
|
||||
import { contextKey } from "@freya/core"
|
||||
|
||||
import type { CalDavEventData } from "./types.ts"
|
||||
|
||||
@@ -21,4 +21,4 @@ export interface CalendarContext {
|
||||
todayEventCount: number
|
||||
}
|
||||
|
||||
export const CalDavCalendarKey: ContextKey<CalendarContext> = contextKey("aelis.caldav", "calendar")
|
||||
export const CalDavCalendarKey: ContextKey<CalendarContext> = contextKey("freya.caldav", "calendar")
|
||||
@@ -125,7 +125,7 @@ export function parseICalEvents(
|
||||
while (next) {
|
||||
if (++iterations > MAX_RECURRENCE_ITERATIONS) {
|
||||
console.warn(
|
||||
`[aelis.caldav] Recurrence expansion for "${masterEvent.uid}" hit iteration limit (${MAX_RECURRENCE_ITERATIONS}), stopping`,
|
||||
`[freya.caldav] Recurrence expansion for "${masterEvent.uid}" hit iteration limit (${MAX_RECURRENCE_ITERATIONS}), stopping`,
|
||||
)
|
||||
break
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/** @jsxImportSource @nym.sh/jrx */
|
||||
|
||||
import type { FeedItemRenderer } from "@aelis/core"
|
||||
import type { FeedItemRenderer } from "@freya/core"
|
||||
|
||||
import { FeedCard, SansSerifText, SerifText } from "@aelis/components"
|
||||
import { FeedCard, SansSerifText, SerifText } from "@freya/components"
|
||||
|
||||
import type { CalDavEventData } from "./types.ts"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FeedItem } from "@aelis/core"
|
||||
import type { FeedItem } from "@freya/core"
|
||||
|
||||
// -- Event status --
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@aelis/source-google-calendar",
|
||||
"name": "@freya/source-google-calendar",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -8,7 +8,7 @@
|
||||
"test": "bun test ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@aelis/core": "workspace:*",
|
||||
"@freya/core": "workspace:*",
|
||||
"arktype": "^2.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { ContextKey } from "@freya/core"
|
||||
|
||||
import { contextKey } from "@freya/core"
|
||||
|
||||
export interface NextEvent {
|
||||
title: string
|
||||
startTime: Date
|
||||
endTime: Date
|
||||
minutesUntilStart: number
|
||||
location: string | null
|
||||
}
|
||||
|
||||
export const NextEventKey: ContextKey<NextEvent> = contextKey("freya.google-calendar", "nextEvent")
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FeedItem } from "@aelis/core"
|
||||
import type { FeedItem } from "@freya/core"
|
||||
|
||||
import type { CalendarEventData } from "./types"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Context, TimeRelevance } from "@aelis/core"
|
||||
import { Context, TimeRelevance } from "@freya/core"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
import type { ApiCalendarEvent, GoogleCalendarClient, ListEventsOptions } from "./types"
|
||||
@@ -45,7 +45,7 @@ describe("GoogleCalendarSource", () => {
|
||||
describe("constructor", () => {
|
||||
test("has correct id", () => {
|
||||
const source = new GoogleCalendarSource({ client: defaultMockClient() })
|
||||
expect(source.id).toBe("aelis.google-calendar")
|
||||
expect(source.id).toBe("freya.google-calendar")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ActionDefinition, ContextEntry, FeedItemSignals, FeedSource } from "@aelis/core"
|
||||
import type { ActionDefinition, ContextEntry, FeedItemSignals, FeedSource } from "@freya/core"
|
||||
|
||||
import { Context, TimeRelevance, UnknownActionError } from "@aelis/core"
|
||||
import { Context, TimeRelevance, UnknownActionError } from "@freya/core"
|
||||
|
||||
import type {
|
||||
ApiCalendarEvent,
|
||||
@@ -65,7 +65,7 @@ const URGENCY_ALL_DAY = 0.4
|
||||
* ```
|
||||
*/
|
||||
export class GoogleCalendarSource implements FeedSource<CalendarFeedItem> {
|
||||
readonly id = "aelis.google-calendar"
|
||||
readonly id = "freya.google-calendar"
|
||||
|
||||
private readonly client: GoogleCalendarClient
|
||||
private readonly calendarIds: string[] | undefined
|
||||
@@ -1,6 +1,6 @@
|
||||
# @aelis/source-location
|
||||
# @freya/source-location
|
||||
|
||||
A FeedSource that provides location context to the AELIS feed graph.
|
||||
A FeedSource that provides location context to the FREYA feed graph.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -9,14 +9,14 @@ This source accepts external location pushes and does not query location itself.
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
bun add @aelis/source-location
|
||||
bun add @freya/source-location
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { LocationSource, LocationKey, type Location } from "@aelis/source-location"
|
||||
import { contextValue } from "@aelis/core"
|
||||
import { LocationSource, LocationKey, type Location } from "@freya/source-location"
|
||||
import { contextValue } from "@freya/core"
|
||||
|
||||
// Create source with default history size (1)
|
||||
const locationSource = new LocationSource()
|
||||
@@ -42,8 +42,8 @@ locationSource.locationHistory // readonly Location[]
|
||||
### With FeedController
|
||||
|
||||
```ts
|
||||
import { FeedController } from "@aelis/core"
|
||||
import { LocationSource } from "@aelis/source-location"
|
||||
import { FeedController } from "@freya/core"
|
||||
import { LocationSource } from "@freya/source-location"
|
||||
|
||||
const locationSource = new LocationSource()
|
||||
|
||||
@@ -63,8 +63,8 @@ locationSource.pushLocation({
|
||||
### Reading Location in Downstream Sources
|
||||
|
||||
```ts
|
||||
import { contextValue, type FeedSource } from "@aelis/core"
|
||||
import { LocationKey } from "@aelis/source-location"
|
||||
import { contextValue, type FeedSource } from "@freya/core"
|
||||
import { LocationKey } from "@freya/source-location"
|
||||
|
||||
const weatherSource: FeedSource = {
|
||||
id: "weather",
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@aelis/source-location",
|
||||
"name": "@freya/source-location",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -8,7 +8,7 @@
|
||||
"test": "bun test src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aelis/core": "workspace:*",
|
||||
"@freya/core": "workspace:*",
|
||||
"arktype": "^2.1.0"
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ describe("LocationSource", () => {
|
||||
describe("FeedSource interface", () => {
|
||||
test("has correct id", () => {
|
||||
const source = new LocationSource()
|
||||
expect(source.id).toBe("aelis.location")
|
||||
expect(source.id).toBe("freya.location")
|
||||
})
|
||||
|
||||
test("fetchItems always returns empty array", async () => {
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { ActionDefinition, ContextEntry, FeedSource } from "@aelis/core"
|
||||
import type { ActionDefinition, ContextEntry, FeedSource } from "@freya/core"
|
||||
|
||||
import { Context, UnknownActionError, contextKey, type ContextKey } from "@aelis/core"
|
||||
import { Context, UnknownActionError, contextKey, type ContextKey } from "@freya/core"
|
||||
import { type } from "arktype"
|
||||
|
||||
import { Location, type LocationSourceOptions } from "./types.ts"
|
||||
|
||||
export const LocationKey: ContextKey<Location> = contextKey("aelis.location", "location")
|
||||
export const LocationKey: ContextKey<Location> = contextKey("freya.location", "location")
|
||||
|
||||
/**
|
||||
* A FeedSource that provides location context.
|
||||
@@ -16,7 +16,7 @@ export const LocationKey: ContextKey<Location> = contextKey("aelis.location", "l
|
||||
* Does not produce feed items - always returns empty array from `fetchItems`.
|
||||
*/
|
||||
export class LocationSource implements FeedSource {
|
||||
readonly id = "aelis.location"
|
||||
readonly id = "freya.location"
|
||||
|
||||
private readonly historySize: number
|
||||
private locations: Location[] = []
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@aelis/source-tfl",
|
||||
"name": "@freya/source-tfl",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -9,9 +9,9 @@
|
||||
"fetch-fixtures": "bun run scripts/fetch-fixtures.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aelis/components": "workspace:*",
|
||||
"@aelis/core": "workspace:*",
|
||||
"@aelis/source-location": "workspace:*",
|
||||
"@freya/components": "workspace:*",
|
||||
"@freya/core": "workspace:*",
|
||||
"@freya/source-location": "workspace:*",
|
||||
"arktype": "^2.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -20,7 +20,7 @@ function makeAlert(overrides: Partial<TflAlertData> = {}): TflAlertData {
|
||||
function makeItem(alerts: TflAlertData[]): TflStatusFeedItem {
|
||||
return {
|
||||
id: "tfl-status",
|
||||
sourceId: "aelis.tfl",
|
||||
sourceId: "freya.tfl",
|
||||
type: "tfl-status",
|
||||
timestamp: new Date("2026-01-15T12:00:00Z"),
|
||||
data: { alerts },
|
||||
@@ -1,7 +1,7 @@
|
||||
/** @jsxImportSource @nym.sh/jrx */
|
||||
import type { FeedItemRenderer } from "@aelis/core"
|
||||
import type { FeedItemRenderer } from "@freya/core"
|
||||
|
||||
import { FeedCard, SansSerifText } from "@aelis/components"
|
||||
import { FeedCard, SansSerifText } from "@freya/components"
|
||||
|
||||
import type { TflAlertData, TflStatusData } from "./types.ts"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Context } from "@aelis/core"
|
||||
import { LocationKey, type Location } from "@aelis/source-location"
|
||||
import { Context } from "@freya/core"
|
||||
import { LocationKey, type Location } from "@freya/source-location"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
import type {
|
||||
@@ -93,12 +93,12 @@ describe("TflSource", () => {
|
||||
describe("interface", () => {
|
||||
test("has correct id", () => {
|
||||
const source = new TflSource({ client: api })
|
||||
expect(source.id).toBe("aelis.tfl")
|
||||
expect(source.id).toBe("freya.tfl")
|
||||
})
|
||||
|
||||
test("depends on location", () => {
|
||||
const source = new TflSource({ client: api })
|
||||
expect(source.dependencies).toEqual(["aelis.location"])
|
||||
expect(source.dependencies).toEqual(["freya.location"])
|
||||
})
|
||||
|
||||
test("implements fetchItems", () => {
|
||||
@@ -192,7 +192,7 @@ describe("TflSource", () => {
|
||||
const item = items[0]!
|
||||
expect(item.id).toBe("tfl-status")
|
||||
expect(item.type).toBe("tfl-status")
|
||||
expect(item.sourceId).toBe("aelis.tfl")
|
||||
expect(item.sourceId).toBe("freya.tfl")
|
||||
expect(item.signals).toBeDefined()
|
||||
expect(typeof item.signals!.urgency).toBe("number")
|
||||
expect(item.timestamp).toBeInstanceOf(Date)
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ActionDefinition, ContextEntry, FeedItemSignals, FeedSource } from "@aelis/core"
|
||||
import type { ActionDefinition, ContextEntry, FeedItemSignals, FeedSource } from "@freya/core"
|
||||
|
||||
import { Context, TimeRelevance, UnknownActionError } from "@aelis/core"
|
||||
import { LocationKey } from "@aelis/source-location"
|
||||
import { Context, TimeRelevance, UnknownActionError } from "@freya/core"
|
||||
import { LocationKey } from "@freya/source-location"
|
||||
import { type } from "arktype"
|
||||
|
||||
import type {
|
||||
@@ -73,8 +73,8 @@ export class TflSource implements FeedSource<TflStatusFeedItem> {
|
||||
"elizabeth",
|
||||
]
|
||||
|
||||
readonly id = "aelis.tfl"
|
||||
readonly dependencies = ["aelis.location"]
|
||||
readonly id = "freya.tfl"
|
||||
readonly dependencies = ["freya.location"]
|
||||
|
||||
private readonly client: ITflApi
|
||||
private lines: TflLineId[]
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FeedItem } from "@aelis/core"
|
||||
import type { FeedItem } from "@freya/core"
|
||||
|
||||
import type { TflLineId } from "./tfl-api.ts"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# @aelis/source-weatherkit
|
||||
# @freya/source-weatherkit
|
||||
|
||||
Weather feed source using Apple WeatherKit API.
|
||||
|
||||
@@ -7,7 +7,7 @@ Weather feed source using Apple WeatherKit API.
|
||||
### Basic Setup
|
||||
|
||||
```ts
|
||||
import { WeatherSource, Units } from "@aelis/source-weatherkit"
|
||||
import { WeatherSource, Units } from "@freya/source-weatherkit"
|
||||
|
||||
const weatherSource = new WeatherSource({
|
||||
credentials: {
|
||||
@@ -23,8 +23,8 @@ const weatherSource = new WeatherSource({
|
||||
### With Feed Source Graph
|
||||
|
||||
```ts
|
||||
import { LocationSource } from "@aelis/source-location"
|
||||
import { WeatherSource } from "@aelis/source-weatherkit"
|
||||
import { LocationSource } from "@freya/source-location"
|
||||
import { WeatherSource } from "@freya/source-weatherkit"
|
||||
|
||||
const locationSource = new LocationSource()
|
||||
const weatherSource = new WeatherSource({ credentials })
|
||||
@@ -38,8 +38,8 @@ const sources = [locationSource, weatherSource]
|
||||
Downstream sources can access weather data:
|
||||
|
||||
```ts
|
||||
import { contextValue } from "@aelis/core"
|
||||
import { WeatherKey } from "@aelis/source-weatherkit"
|
||||
import { contextValue } from "@freya/core"
|
||||
import { WeatherKey } from "@freya/source-weatherkit"
|
||||
|
||||
async function fetchContext(context: Context) {
|
||||
const weather = contextValue(context, WeatherKey)
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@aelis/source-weatherkit",
|
||||
"name": "@freya/source-weatherkit",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -8,8 +8,8 @@
|
||||
"test": "bun test ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@aelis/core": "workspace:*",
|
||||
"@aelis/source-location": "workspace:*",
|
||||
"@freya/core": "workspace:*",
|
||||
"@freya/source-location": "workspace:*",
|
||||
"arktype": "^2.1.0"
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,11 @@
|
||||
* then prints the raw API response and processed feed items.
|
||||
* Caches credentials locally and writes response JSON to a file.
|
||||
*
|
||||
* Usage: bun packages/aelis-source-weatherkit/scripts/query.ts
|
||||
* Usage: bun packages/freya-source-weatherkit/scripts/query.ts
|
||||
*/
|
||||
|
||||
import { Context } from "@aelis/core"
|
||||
import { LocationKey } from "@aelis/source-location"
|
||||
import { Context } from "@freya/core"
|
||||
import { LocationKey } from "@freya/source-location"
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
import { createInterface } from "node:readline/promises"
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FeedItem } from "@aelis/core"
|
||||
import type { FeedItem } from "@freya/core"
|
||||
|
||||
import type { Certainty, ConditionCode, PrecipitationType, Severity, Urgency } from "./weatherkit"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ContextKey } from "@aelis/core"
|
||||
import type { ContextKey } from "@freya/core"
|
||||
|
||||
import { contextKey } from "@aelis/core"
|
||||
import { contextKey } from "@freya/core"
|
||||
|
||||
import type { ConditionCode } from "./weatherkit"
|
||||
|
||||
@@ -24,4 +24,4 @@ export interface Weather {
|
||||
daylight: boolean
|
||||
}
|
||||
|
||||
export const WeatherKey: ContextKey<Weather> = contextKey("aelis.weather", "weather")
|
||||
export const WeatherKey: ContextKey<Weather> = contextKey("freya.weather", "weather")
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { FeedSource } from "@aelis/core"
|
||||
import type { FeedSource } from "@freya/core"
|
||||
|
||||
import { Context } from "@aelis/core"
|
||||
import { LocationKey } from "@aelis/source-location"
|
||||
import { Context } from "@freya/core"
|
||||
import { LocationKey } from "@freya/source-location"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
import type {
|
||||
@@ -41,12 +41,12 @@ describe("WeatherSource", () => {
|
||||
describe("properties", () => {
|
||||
test("has correct id", () => {
|
||||
const source = new WeatherSource({ credentials: mockCredentials })
|
||||
expect(source.id).toBe("aelis.weather")
|
||||
expect(source.id).toBe("freya.weather")
|
||||
})
|
||||
|
||||
test("depends on location", () => {
|
||||
const source = new WeatherSource({ credentials: mockCredentials })
|
||||
expect(source.dependencies).toEqual(["aelis.location"])
|
||||
expect(source.dependencies).toEqual(["freya.location"])
|
||||
})
|
||||
|
||||
test("throws error if neither client nor credentials provided", () => {
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ActionDefinition, ContextEntry, FeedItemSignals, FeedSource } from "@aelis/core"
|
||||
import type { ActionDefinition, ContextEntry, FeedItemSignals, FeedSource } from "@freya/core"
|
||||
|
||||
import { Context, TimeRelevance, UnknownActionError } from "@aelis/core"
|
||||
import { LocationKey } from "@aelis/source-location"
|
||||
import { Context, TimeRelevance, UnknownActionError } from "@freya/core"
|
||||
import { LocationKey } from "@freya/source-location"
|
||||
|
||||
import {
|
||||
WeatherFeedItemType,
|
||||
@@ -99,8 +99,8 @@ const MODERATE_CONDITIONS = new Set<ConditionCode>([
|
||||
* ```
|
||||
*/
|
||||
export class WeatherSource implements FeedSource<WeatherFeedItem> {
|
||||
readonly id = "aelis.weather"
|
||||
readonly dependencies = ["aelis.location"]
|
||||
readonly id = "freya.weather"
|
||||
readonly dependencies = ["freya.location"]
|
||||
|
||||
private readonly client: WeatherKitClient
|
||||
private readonly hourlyLimit: number
|
||||
Reference in New Issue
Block a user