Compare commits

..

5 Commits

Author SHA1 Message Date
6520926aca fix: use PascalCase for FeedItemType members
Rename camelCase members to PascalCase in WeatherFeedItemType
and CalendarFeedItemType to match TflFeedItemType and
CalDavFeedItemType conventions.

Co-authored-by: Ona <no-reply@ona.com>
2026-03-01 22:06:20 +00:00
4c9ac2c61a feat(tfl): export TflFeedItemType const (#47)
Replace hardcoded "tfl-alert" string with a
TflFeedItemType const object, matching the pattern
used by google-calendar and weatherkit packages.

Co-authored-by: Ona <no-reply@ona.com>
2026-03-01 18:43:27 +00:00
be3fc41a00 refactor(google-calendar): remove redundant type aliases (#48)
The *TypeType re-exports are unnecessary since
consumers can use import type to get the type.

Co-authored-by: Ona <no-reply@ona.com>
2026-03-01 18:43:08 +00:00
2e9c600e93 refactor(weatherkit): remove redundant type aliases (#49)
The *TypeType re-exports are unnecessary since
consumers can use import type to get the type.

Co-authored-by: Ona <no-reply@ona.com>
2026-03-01 18:42:58 +00:00
d616fd52d3 feat(caldav): export CalDavFeedItemType const (#46)
Replace hardcoded "caldav-event" string with a
CalDavFeedItemType const object, matching the pattern
used by google-calendar and weatherkit packages.

Co-authored-by: Ona <no-reply@ona.com>
2026-03-01 18:42:40 +00:00
16 changed files with 77 additions and 62 deletions

View File

@@ -39,7 +39,7 @@ describe("WeatherKitDataSource", () => {
credentials: mockCredentials,
})
expect(dataSource.type).toBe(WeatherFeedItemType.current)
expect(dataSource.type).toBe(WeatherFeedItemType.Current)
})
test("throws error if neither client nor credentials provided", () => {
@@ -130,9 +130,9 @@ describe("query() with mocked client", () => {
const items = await dataSource.query(context)
expect(items.length).toBeGreaterThan(0)
expect(items.some((i) => i.type === WeatherFeedItemType.current)).toBe(true)
expect(items.some((i) => i.type === WeatherFeedItemType.hourly)).toBe(true)
expect(items.some((i) => i.type === WeatherFeedItemType.daily)).toBe(true)
expect(items.some((i) => i.type === WeatherFeedItemType.Current)).toBe(true)
expect(items.some((i) => i.type === WeatherFeedItemType.Hourly)).toBe(true)
expect(items.some((i) => i.type === WeatherFeedItemType.Daily)).toBe(true)
})
test("applies hourly and daily limits", async () => {
@@ -145,8 +145,8 @@ describe("query() with mocked client", () => {
const items = await dataSource.query(context)
const hourlyItems = items.filter((i) => i.type === WeatherFeedItemType.hourly)
const dailyItems = items.filter((i) => i.type === WeatherFeedItemType.daily)
const hourlyItems = items.filter((i) => i.type === WeatherFeedItemType.Hourly)
const dailyItems = items.filter((i) => i.type === WeatherFeedItemType.Daily)
expect(hourlyItems.length).toBe(3)
expect(dailyItems.length).toBe(2)
@@ -176,8 +176,8 @@ describe("query() with mocked client", () => {
units: Units.imperial,
})
const metricCurrent = metricItems.find((i) => i.type === WeatherFeedItemType.current)
const imperialCurrent = imperialItems.find((i) => i.type === WeatherFeedItemType.current)
const metricCurrent = metricItems.find((i) => i.type === WeatherFeedItemType.Current)
const imperialCurrent = imperialItems.find((i) => i.type === WeatherFeedItemType.Current)
expect(metricCurrent).toBeDefined()
expect(imperialCurrent).toBeDefined()
@@ -203,7 +203,7 @@ describe("query() with mocked client", () => {
expect(item.signals!.timeRelevance).toBeDefined()
}
const currentItem = items.find((i) => i.type === WeatherFeedItemType.current)
const currentItem = items.find((i) => i.type === WeatherFeedItemType.Current)
expect(currentItem).toBeDefined()
expect(currentItem!.signals!.urgency).toBeGreaterThanOrEqual(0.5)
})

View File

@@ -44,7 +44,7 @@ export class WeatherKitDataSource implements DataSource<WeatherFeedItem, Weather
private readonly DEFAULT_HOURLY_LIMIT = 12
private readonly DEFAULT_DAILY_LIMIT = 7
readonly type = WeatherFeedItemType.current
readonly type = WeatherFeedItemType.Current
private readonly client: WeatherKitClient
private readonly hourlyLimit: number
private readonly dailyLimit: number
@@ -228,7 +228,7 @@ function createCurrentWeatherFeedItem(
return {
id: `weather-current-${timestamp.getTime()}`,
type: WeatherFeedItemType.current,
type: WeatherFeedItemType.Current,
timestamp,
data: {
conditionCode: current.conditionCode,
@@ -262,7 +262,7 @@ function createHourlyWeatherFeedItem(
return {
id: `weather-hourly-${timestamp.getTime()}-${index}`,
type: WeatherFeedItemType.hourly,
type: WeatherFeedItemType.Hourly,
timestamp,
data: {
forecastTime: new Date(hourly.forecastStart),
@@ -296,7 +296,7 @@ function createDailyWeatherFeedItem(
return {
id: `weather-daily-${timestamp.getTime()}-${index}`,
type: WeatherFeedItemType.daily,
type: WeatherFeedItemType.Daily,
timestamp,
data: {
forecastDate: new Date(daily.forecastStart),
@@ -323,7 +323,7 @@ function createWeatherAlertFeedItem(alert: WeatherAlert, timestamp: Date): Weath
return {
id: `weather-alert-${alert.id}`,
type: WeatherFeedItemType.alert,
type: WeatherFeedItemType.Alert,
timestamp,
data: {
alertId: alert.id,

View File

@@ -3,10 +3,10 @@ import type { FeedItem } from "@aris/core"
import type { Certainty, ConditionCode, PrecipitationType, Severity, Urgency } from "./weatherkit"
export const WeatherFeedItemType = {
current: "weather-current",
hourly: "weather-hourly",
daily: "weather-daily",
alert: "weather-alert",
Current: "weather-current",
Hourly: "weather-hourly",
Daily: "weather-daily",
Alert: "weather-alert",
} as const
export type WeatherFeedItemType = (typeof WeatherFeedItemType)[keyof typeof WeatherFeedItemType]
@@ -28,7 +28,7 @@ export type CurrentWeatherData = {
}
export interface CurrentWeatherFeedItem extends FeedItem<
typeof WeatherFeedItemType.current,
typeof WeatherFeedItemType.Current,
CurrentWeatherData
> {}
@@ -49,7 +49,7 @@ export type HourlyWeatherData = {
}
export interface HourlyWeatherFeedItem extends FeedItem<
typeof WeatherFeedItemType.hourly,
typeof WeatherFeedItemType.Hourly,
HourlyWeatherData
> {}
@@ -68,7 +68,7 @@ export type DailyWeatherData = {
}
export interface DailyWeatherFeedItem extends FeedItem<
typeof WeatherFeedItemType.daily,
typeof WeatherFeedItemType.Daily,
DailyWeatherData
> {}
@@ -86,7 +86,7 @@ export type WeatherAlertData = {
}
export interface WeatherAlertFeedItem extends FeedItem<
typeof WeatherFeedItemType.alert,
typeof WeatherFeedItemType.Alert,
WeatherAlertData
> {}

View File

@@ -4,10 +4,10 @@ import { TimeRelevance, UnknownActionError } from "@aris/core"
import { DAVClient } from "tsdav"
import type { CalDavDAVClient, CalDavEventData, CalDavFeedItem } from "./types.ts"
import { CalDavEventStatus } from "./types.ts"
import { CalDavCalendarKey, type CalendarContext } from "./calendar-context.ts"
import { parseICalEvents } from "./ical-parser.ts"
import { CalDavEventStatus, CalDavFeedItemType } from "./types.ts"
// -- Source options --
@@ -340,7 +340,7 @@ export function computeSignals(
function createFeedItem(event: CalDavEventData, now: Date, timeZone?: string): CalDavFeedItem {
return {
id: `caldav-event-${event.uid}${event.recurrenceId ? `-${event.recurrenceId}` : ""}`,
type: "caldav-event",
type: CalDavFeedItemType.Event,
timestamp: now,
data: event,
signals: computeSignals(event, now, timeZone),

View File

@@ -5,6 +5,7 @@ export {
AttendeeRole,
AttendeeStatus,
CalDavEventStatus,
CalDavFeedItemType,
type CalDavAlarm,
type CalDavAttendee,
type CalDavDAVCalendar,

View File

@@ -64,9 +64,17 @@ export interface CalDavEventData extends Record<string, unknown> {
recurrenceId: string | null
}
// -- Feed item type --
export const CalDavFeedItemType = {
Event: "caldav-event",
} as const
export type CalDavFeedItemType = (typeof CalDavFeedItemType)[keyof typeof CalDavFeedItemType]
// -- Feed item --
export type CalDavFeedItem = FeedItem<"caldav-event", CalDavEventData>
export type CalDavFeedItem = FeedItem<typeof CalDavFeedItemType.Event, CalDavEventData>
// -- DAV client interface --

View File

@@ -3,19 +3,19 @@ import type { FeedItem } from "@aris/core"
import type { CalendarEventData } from "./types"
export const CalendarFeedItemType = {
event: "calendar-event",
allDay: "calendar-all-day",
Event: "calendar-event",
AllDay: "calendar-all-day",
} as const
export type CalendarFeedItemType = (typeof CalendarFeedItemType)[keyof typeof CalendarFeedItemType]
export interface CalendarEventFeedItem extends FeedItem<
typeof CalendarFeedItemType.event,
typeof CalendarFeedItemType.Event,
CalendarEventData
> {}
export interface CalendarAllDayFeedItem extends FeedItem<
typeof CalendarFeedItemType.allDay,
typeof CalendarFeedItemType.AllDay,
CalendarEventData
> {}

View File

@@ -69,7 +69,7 @@ describe("GoogleCalendarSource", () => {
const source = new GoogleCalendarSource({ client: defaultMockClient() })
const items = await source.fetchItems(createContext())
const timedItems = items.filter((i) => i.type === CalendarFeedItemType.event)
const timedItems = items.filter((i) => i.type === CalendarFeedItemType.Event)
expect(timedItems.length).toBe(4)
})
@@ -77,7 +77,7 @@ describe("GoogleCalendarSource", () => {
const source = new GoogleCalendarSource({ client: defaultMockClient() })
const items = await source.fetchItems(createContext())
const allDayItems = items.filter((i) => i.type === CalendarFeedItemType.allDay)
const allDayItems = items.filter((i) => i.type === CalendarFeedItemType.AllDay)
expect(allDayItems.length).toBe(1)
})

View File

@@ -209,7 +209,7 @@ function createFeedItem(
nowMs: number,
lookaheadMs: number,
): CalendarFeedItem {
const itemType = event.isAllDay ? CalendarFeedItemType.allDay : CalendarFeedItemType.event
const itemType = event.isAllDay ? CalendarFeedItemType.AllDay : CalendarFeedItemType.Event
return {
id: `calendar-${event.calendarId}-${event.eventId}`,

View File

@@ -1,7 +1,6 @@
export { NextEventKey, type NextEvent } from "./calendar-context"
export {
CalendarFeedItemType,
type CalendarFeedItemType as CalendarFeedItemTypeType,
type CalendarAllDayFeedItem,
type CalendarEventFeedItem,
type CalendarFeedItem,
@@ -10,7 +9,6 @@ export { DefaultGoogleCalendarClient } from "./google-calendar-api"
export { GoogleCalendarSource, type GoogleCalendarSourceOptions } from "./google-calendar-source"
export {
EventStatus,
type EventStatus as EventStatusType,
type ApiCalendarEvent,
type ApiEventDateTime,
type CalendarEventData,

View File

@@ -1,12 +1,13 @@
export { TflSource } from "./tfl-source.ts"
export { TflApi } from "./tfl-api.ts"
export type { TflLineId } from "./tfl-api.ts"
export type {
ITflApi,
StationLocation,
TflAlertData,
TflAlertFeedItem,
TflAlertSeverity,
TflLineStatus,
TflSourceOptions,
export {
TflFeedItemType,
type ITflApi,
type StationLocation,
type TflAlertData,
type TflAlertFeedItem,
type TflAlertSeverity,
type TflLineStatus,
type TflSourceOptions,
} from "./types.ts"

View File

@@ -15,6 +15,7 @@ import type {
} from "./types.ts"
import { TflApi, lineId } from "./tfl-api.ts"
import { TflFeedItemType } from "./types.ts"
const setLinesInput = lineId.array()
@@ -150,7 +151,7 @@ export class TflSource implements FeedSource<TflAlertFeedItem> {
return {
id: `tfl-alert-${status.lineId}-${status.severity}`,
type: "tfl-alert",
type: TflFeedItemType.Alert,
timestamp: context.time,
data,
signals,

View File

@@ -20,7 +20,13 @@ export interface TflAlertData extends Record<string, unknown> {
closestStationDistance: number | null
}
export type TflAlertFeedItem = FeedItem<"tfl-alert", TflAlertData>
export const TflFeedItemType = {
Alert: "tfl-alert",
} as const
export type TflFeedItemType = (typeof TflFeedItemType)[keyof typeof TflFeedItemType]
export type TflAlertFeedItem = FeedItem<typeof TflFeedItemType.Alert, TflAlertData>
export interface TflSourceOptions {
apiKey?: string

View File

@@ -3,10 +3,10 @@ import type { FeedItem } from "@aris/core"
import type { Certainty, ConditionCode, PrecipitationType, Severity, Urgency } from "./weatherkit"
export const WeatherFeedItemType = {
current: "weather-current",
hourly: "weather-hourly",
daily: "weather-daily",
alert: "weather-alert",
Current: "weather-current",
Hourly: "weather-hourly",
Daily: "weather-daily",
Alert: "weather-alert",
} as const
export type WeatherFeedItemType = (typeof WeatherFeedItemType)[keyof typeof WeatherFeedItemType]
@@ -28,7 +28,7 @@ export type CurrentWeatherData = {
}
export interface CurrentWeatherFeedItem extends FeedItem<
typeof WeatherFeedItemType.current,
typeof WeatherFeedItemType.Current,
CurrentWeatherData
> {}
@@ -49,7 +49,7 @@ export type HourlyWeatherData = {
}
export interface HourlyWeatherFeedItem extends FeedItem<
typeof WeatherFeedItemType.hourly,
typeof WeatherFeedItemType.Hourly,
HourlyWeatherData
> {}
@@ -68,7 +68,7 @@ export type DailyWeatherData = {
}
export interface DailyWeatherFeedItem extends FeedItem<
typeof WeatherFeedItemType.daily,
typeof WeatherFeedItemType.Daily,
DailyWeatherData
> {}
@@ -86,7 +86,7 @@ export type WeatherAlertData = {
}
export interface WeatherAlertFeedItem extends FeedItem<
typeof WeatherFeedItemType.alert,
typeof WeatherFeedItemType.Alert,
WeatherAlertData
> {}

View File

@@ -110,9 +110,9 @@ describe("WeatherSource", () => {
const items = await source.fetchItems(context)
expect(items.length).toBeGreaterThan(0)
expect(items.some((i) => i.type === WeatherFeedItemType.current)).toBe(true)
expect(items.some((i) => i.type === WeatherFeedItemType.hourly)).toBe(true)
expect(items.some((i) => i.type === WeatherFeedItemType.daily)).toBe(true)
expect(items.some((i) => i.type === WeatherFeedItemType.Current)).toBe(true)
expect(items.some((i) => i.type === WeatherFeedItemType.Hourly)).toBe(true)
expect(items.some((i) => i.type === WeatherFeedItemType.Daily)).toBe(true)
})
test("applies hourly and daily limits", async () => {
@@ -125,8 +125,8 @@ describe("WeatherSource", () => {
const items = await source.fetchItems(context)
const hourlyItems = items.filter((i) => i.type === WeatherFeedItemType.hourly)
const dailyItems = items.filter((i) => i.type === WeatherFeedItemType.daily)
const hourlyItems = items.filter((i) => i.type === WeatherFeedItemType.Hourly)
const dailyItems = items.filter((i) => i.type === WeatherFeedItemType.Daily)
expect(hourlyItems.length).toBe(3)
expect(dailyItems.length).toBe(2)
@@ -158,7 +158,7 @@ describe("WeatherSource", () => {
expect(item.signals!.timeRelevance).toBeDefined()
}
const currentItem = items.find((i) => i.type === WeatherFeedItemType.current)
const currentItem = items.find((i) => i.type === WeatherFeedItemType.Current)
expect(currentItem).toBeDefined()
expect(currentItem!.signals!.urgency).toBeGreaterThanOrEqual(0.5)
})

View File

@@ -291,7 +291,7 @@ function createCurrentWeatherFeedItem(
return {
id: `weather-current-${timestamp.getTime()}`,
type: WeatherFeedItemType.current,
type: WeatherFeedItemType.Current,
timestamp,
data: {
conditionCode: current.conditionCode,
@@ -325,7 +325,7 @@ function createHourlyWeatherFeedItem(
return {
id: `weather-hourly-${timestamp.getTime()}-${index}`,
type: WeatherFeedItemType.hourly,
type: WeatherFeedItemType.Hourly,
timestamp,
data: {
forecastTime: new Date(hourly.forecastStart),
@@ -359,7 +359,7 @@ function createDailyWeatherFeedItem(
return {
id: `weather-daily-${timestamp.getTime()}-${index}`,
type: WeatherFeedItemType.daily,
type: WeatherFeedItemType.Daily,
timestamp,
data: {
forecastDate: new Date(daily.forecastStart),
@@ -386,7 +386,7 @@ function createWeatherAlertFeedItem(alert: WeatherAlert, timestamp: Date): Weath
return {
id: `weather-alert-${alert.id}`,
type: WeatherFeedItemType.alert,
type: WeatherFeedItemType.Alert,
timestamp,
data: {
alertId: alert.id,