Compare commits

..

1 Commits

Author SHA1 Message Date
57b38cafaf feat: replace flat context with tuple-keyed store
Context keys are now tuples instead of strings, inspired by
React Query's query keys. This prevents context collisions
when multiple instances of the same source type are registered.

Sources write to structured keys like
["aris.google-calendar", "nextEvent", { account: "work" }]
and consumers can query by prefix via context.find().

Co-authored-by: Ona <no-reply@ona.com>
2026-03-01 22:51:29 +00:00
193 changed files with 533 additions and 2540 deletions

View File

@@ -1,8 +1,8 @@
services:
expo:
name: Expo Dev Server
description: Expo development server for aelis-client
description: Expo development server for aris-client
triggeredBy:
- postDevcontainerStart
commands:
start: cd apps/aelis-client && ./scripts/run-dev-server.sh
start: cd apps/aris-client && ./scripts/run-dev-server.sh

View File

@@ -2,7 +2,7 @@
## Project
AELIS is an AI-powered personal assistant that aggregates data from various sources into a contextual feed. Monorepo with `packages/` (shared libraries) and `apps/` (applications).
ARIS is an AI-powered personal assistant that aggregates data from various sources into a contextual feed. Monorepo with `packages/` (shared libraries) and `apps/` (applications).
## Commands

View File

@@ -1,4 +1,4 @@
# aelis
# aris
To install dependencies:
@@ -8,14 +8,14 @@ bun install
## Packages
### @aelis/source-tfl
### @aris/source-tfl
TfL (Transport for London) feed source for tube, overground, and Elizabeth line alerts.
#### Testing
```bash
cd packages/aelis-source-tfl
cd packages/aris-source-tfl
bun run test
```

View File

@@ -1,5 +1,5 @@
{
"name": "@aelis/backend",
"name": "@aris/backend",
"version": "0.0.0",
"type": "module",
"main": "src/server.ts",
@@ -9,10 +9,10 @@
"test": "bun test src/"
},
"dependencies": {
"@aelis/core": "workspace:*",
"@aelis/source-location": "workspace:*",
"@aelis/source-tfl": "workspace:*",
"@aelis/source-weatherkit": "workspace:*",
"@aris/core": "workspace:*",
"@aris/source-location": "workspace:*",
"@aris/source-tfl": "workspace:*",
"@aris/source-weatherkit": "workspace:*",
"arktype": "^2.1.29",
"better-auth": "^1",
"hono": "^4",

View File

@@ -1,4 +1,4 @@
import type { ActionDefinition, ContextEntry, FeedItem, FeedSource } from "@aelis/core"
import type { ActionDefinition, ContextEntry, FeedItem, FeedSource } from "@aris/core"
import { describe, expect, test } from "bun:test"
import { Hono } from "hono"

View File

@@ -45,7 +45,7 @@ async function handleUpdateLocation(c: Context<Env>) {
const user = c.get("user")!
const sessionManager = c.get("sessionManager")
const session = sessionManager.getOrCreate(user.id)
await session.engine.executeAction("aelis.location", "update-location", {
await session.engine.executeAction("aris.location", "update-location", {
lat: result.lat,
lng: result.lng,
accuracy: result.accuracy,

View File

@@ -1,4 +1,4 @@
import { LocationSource } from "@aelis/source-location"
import { LocationSource } from "@aris/source-location"
import { Hono } from "hono"
import { registerAuthHandlers } from "./auth/http.ts"

View File

@@ -1,4 +1,4 @@
import type { FeedSource } from "@aelis/core"
import type { FeedSource } from "@aris/core"
export interface FeedSourceProvider {
feedSourceForUser(userId: string): FeedSource

View File

@@ -1,6 +1,6 @@
import type { WeatherKitClient, WeatherKitResponse } from "@aelis/source-weatherkit"
import type { WeatherKitClient, WeatherKitResponse } from "@aris/source-weatherkit"
import { LocationSource } from "@aelis/source-location"
import { LocationSource } from "@aris/source-location"
import { describe, expect, mock, test } from "bun:test"
import { WeatherSourceProvider } from "../weather/provider.ts"
@@ -44,8 +44,8 @@ describe("UserSessionManager", () => {
const session1 = manager.getOrCreate("user-1")
const session2 = manager.getOrCreate("user-2")
const source1 = session1.getSource<LocationSource>("aelis.location")
const source2 = session2.getSource<LocationSource>("aelis.location")
const source1 = session1.getSource<LocationSource>("aris.location")
const source2 = session2.getSource<LocationSource>("aris.location")
expect(source1).not.toBe(source2)
})
@@ -81,7 +81,7 @@ describe("UserSessionManager", () => {
const session = manager.getOrCreate("user-1")
expect(session.getSource("aelis.weather")).toBeDefined()
expect(session.getSource("aris.weather")).toBeDefined()
})
test("accepts mixed providers", () => {
@@ -90,8 +90,8 @@ describe("UserSessionManager", () => {
const session = manager.getOrCreate("user-1")
expect(session.getSource("aelis.location")).toBeDefined()
expect(session.getSource("aelis.weather")).toBeDefined()
expect(session.getSource("aris.location")).toBeDefined()
expect(session.getSource("aris.weather")).toBeDefined()
})
test("refresh returns feed result through session", async () => {
@@ -110,14 +110,14 @@ describe("UserSessionManager", () => {
const manager = new UserSessionManager([() => new LocationSource()])
const session = manager.getOrCreate("user-1")
await session.engine.executeAction("aelis.location", "update-location", {
await session.engine.executeAction("aris.location", "update-location", {
lat: 51.5074,
lng: -0.1278,
accuracy: 10,
timestamp: new Date(),
})
const source = session.getSource<LocationSource>("aelis.location")
const source = session.getSource<LocationSource>("aris.location")
expect(source?.lastLocation?.lat).toBe(51.5074)
})
@@ -128,7 +128,7 @@ describe("UserSessionManager", () => {
const session = manager.getOrCreate("user-1")
session.engine.subscribe(callback)
await session.engine.executeAction("aelis.location", "update-location", {
await session.engine.executeAction("aris.location", "update-location", {
lat: 51.5074,
lng: -0.1278,
accuracy: 10,
@@ -152,7 +152,7 @@ describe("UserSessionManager", () => {
// Create new session and push location — old callback should not fire
const session2 = manager.getOrCreate("user-1")
await session2.engine.executeAction("aelis.location", "update-location", {
await session2.engine.executeAction("aris.location", "update-location", {
lat: 51.5074,
lng: -0.1278,
accuracy: 10,

View File

@@ -1,6 +1,6 @@
import type { ActionDefinition, ContextEntry, FeedSource } from "@aelis/core"
import type { ActionDefinition, ContextEntry, FeedSource } from "@aris/core"
import { LocationSource } from "@aelis/source-location"
import { LocationSource } from "@aris/source-location"
import { describe, expect, test } from "bun:test"
import { UserSession } from "./user-session.ts"
@@ -36,7 +36,7 @@ describe("UserSession", () => {
const location = new LocationSource()
const session = new UserSession([location])
const result = session.getSource<LocationSource>("aelis.location")
const result = session.getSource<LocationSource>("aris.location")
expect(result).toBe(location)
})
@@ -59,7 +59,7 @@ describe("UserSession", () => {
const location = new LocationSource()
const session = new UserSession([location])
await session.engine.executeAction("aelis.location", "update-location", {
await session.engine.executeAction("aris.location", "update-location", {
lat: 51.5,
lng: -0.1,
accuracy: 10,

View File

@@ -1,4 +1,4 @@
import { FeedEngine, type FeedSource } from "@aelis/core"
import { FeedEngine, type FeedSource } from "@aris/core"
export class UserSession {
readonly engine: FeedEngine

View File

@@ -1,4 +1,4 @@
import { TflSource, type ITflApi } from "@aelis/source-tfl"
import { TflSource, type ITflApi } from "@aris/source-tfl"
import type { FeedSourceProvider } from "../session/feed-source-provider.ts"

View File

@@ -1,4 +1,4 @@
import { WeatherSource, type WeatherSourceOptions } from "@aelis/source-weatherkit"
import { WeatherSource, type WeatherSourceOptions } from "@aris/source-weatherkit"
import type { FeedSourceProvider } from "../session/feed-source-provider.ts"

View File

@@ -1,11 +1,11 @@
{
"expo": {
"name": "Aelis",
"slug": "aelis-client",
"name": "Aris",
"slug": "aris-client",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "aelis",
"scheme": "aris",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"ios": {
@@ -15,7 +15,7 @@
},
"ITSAppUsesNonExemptEncryption": false
},
"bundleIdentifier": "sh.nym.aelis"
"bundleIdentifier": "sh.nym.aris"
},
"android": {
"adaptiveIcon": {
@@ -26,7 +26,7 @@
},
"edgeToEdgeEnabled": true,
"predictiveBackGestureEnabled": false,
"package": "sh.nym.aelis"
"package": "sh.nym.aris"
},
"web": {
"output": "static",

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 384 KiB

After

Width:  |  Height:  |  Size: 384 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,5 +1,5 @@
{
"name": "aelis-client",
"name": "aris-client",
"version": "1.0.0",
"private": true,
"main": "expo-router/entry",

109
bun.lock
View File

@@ -13,14 +13,14 @@
"typescript": "^5",
},
},
"apps/aelis-backend": {
"name": "@aelis/backend",
"apps/aris-backend": {
"name": "@aris/backend",
"version": "0.0.0",
"dependencies": {
"@aelis/core": "workspace:*",
"@aelis/source-location": "workspace:*",
"@aelis/source-tfl": "workspace:*",
"@aelis/source-weatherkit": "workspace:*",
"@aris/core": "workspace:*",
"@aris/source-location": "workspace:*",
"@aris/source-tfl": "workspace:*",
"@aris/source-weatherkit": "workspace:*",
"arktype": "^2.1.29",
"better-auth": "^1",
"hono": "^4",
@@ -30,8 +30,8 @@
"@types/pg": "^8",
},
},
"apps/aelis-client": {
"name": "aelis-client",
"apps/aris-client": {
"name": "aris-client",
"version": "1.0.0",
"dependencies": {
"@expo-google-fonts/inter": "^0.4.2",
@@ -74,72 +74,61 @@
"typescript": "~5.9.2",
},
},
"packages/aelis-core": {
"name": "@aelis/core",
"packages/aris-core": {
"name": "@aris/core",
"version": "0.0.0",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
},
},
"packages/aelis-data-source-weatherkit": {
"name": "@aelis/data-source-weatherkit",
"packages/aris-data-source-weatherkit": {
"name": "@aris/data-source-weatherkit",
"version": "0.0.0",
"dependencies": {
"@aelis/core": "workspace:*",
"@aris/core": "workspace:*",
"arktype": "^2.1.0",
},
},
"packages/aelis-feed-enhancers": {
"name": "@aelis/feed-enhancers",
"packages/aris-source-caldav": {
"name": "@aris/source-caldav",
"version": "0.0.0",
"dependencies": {
"@aelis/core": "workspace:*",
"@aelis/source-caldav": "workspace:*",
"@aelis/source-google-calendar": "workspace:*",
"@aelis/source-tfl": "workspace:*",
"@aelis/source-weatherkit": "workspace:*",
},
},
"packages/aelis-source-caldav": {
"name": "@aelis/source-caldav",
"version": "0.0.0",
"dependencies": {
"@aelis/core": "workspace:*",
"@aris/core": "workspace:*",
"ical.js": "^2.1.0",
"tsdav": "^2.1.7",
},
},
"packages/aelis-source-google-calendar": {
"name": "@aelis/source-google-calendar",
"packages/aris-source-google-calendar": {
"name": "@aris/source-google-calendar",
"version": "0.0.0",
"dependencies": {
"@aelis/core": "workspace:*",
"@aris/core": "workspace:*",
"arktype": "^2.1.0",
},
},
"packages/aelis-source-location": {
"name": "@aelis/source-location",
"packages/aris-source-location": {
"name": "@aris/source-location",
"version": "0.0.0",
"dependencies": {
"@aelis/core": "workspace:*",
"@aris/core": "workspace:*",
"arktype": "^2.1.0",
},
},
"packages/aelis-source-tfl": {
"name": "@aelis/source-tfl",
"packages/aris-source-tfl": {
"name": "@aris/source-tfl",
"version": "0.0.0",
"dependencies": {
"@aelis/core": "workspace:*",
"@aelis/source-location": "workspace:*",
"@aris/core": "workspace:*",
"@aris/source-location": "workspace:*",
"arktype": "^2.1.0",
},
},
"packages/aelis-source-weatherkit": {
"name": "@aelis/source-weatherkit",
"packages/aris-source-weatherkit": {
"name": "@aris/source-weatherkit",
"version": "0.0.0",
"dependencies": {
"@aelis/core": "workspace:*",
"@aelis/source-location": "workspace:*",
"@aris/core": "workspace:*",
"@aris/source-location": "workspace:*",
"arktype": "^2.1.0",
},
},
@@ -147,26 +136,24 @@
"packages": {
"@0no-co/graphql.web": ["@0no-co/graphql.web@1.2.0", "", { "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" }, "optionalPeers": ["graphql"] }, "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw=="],
"@aelis/backend": ["@aelis/backend@workspace:apps/aelis-backend"],
"@aelis/core": ["@aelis/core@workspace:packages/aelis-core"],
"@aelis/data-source-weatherkit": ["@aelis/data-source-weatherkit@workspace:packages/aelis-data-source-weatherkit"],
"@aelis/feed-enhancers": ["@aelis/feed-enhancers@workspace:packages/aelis-feed-enhancers"],
"@aelis/source-caldav": ["@aelis/source-caldav@workspace:packages/aelis-source-caldav"],
"@aelis/source-google-calendar": ["@aelis/source-google-calendar@workspace:packages/aelis-source-google-calendar"],
"@aelis/source-location": ["@aelis/source-location@workspace:packages/aelis-source-location"],
"@aelis/source-tfl": ["@aelis/source-tfl@workspace:packages/aelis-source-tfl"],
"@aelis/source-weatherkit": ["@aelis/source-weatherkit@workspace:packages/aelis-source-weatherkit"],
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
"@aris/backend": ["@aris/backend@workspace:apps/aris-backend"],
"@aris/core": ["@aris/core@workspace:packages/aris-core"],
"@aris/data-source-weatherkit": ["@aris/data-source-weatherkit@workspace:packages/aris-data-source-weatherkit"],
"@aris/source-caldav": ["@aris/source-caldav@workspace:packages/aris-source-caldav"],
"@aris/source-google-calendar": ["@aris/source-google-calendar@workspace:packages/aris-source-google-calendar"],
"@aris/source-location": ["@aris/source-location@workspace:packages/aris-source-location"],
"@aris/source-tfl": ["@aris/source-tfl@workspace:packages/aris-source-tfl"],
"@aris/source-weatherkit": ["@aris/source-weatherkit@workspace:packages/aris-source-weatherkit"],
"@ark/schema": ["@ark/schema@0.56.0", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA=="],
"@ark/util": ["@ark/util@0.56.0", "", {}, "sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA=="],
@@ -823,8 +810,6 @@
"acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="],
"aelis-client": ["aelis-client@workspace:apps/aelis-client"],
"agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
"ajv": ["ajv@8.11.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg=="],
@@ -851,6 +836,8 @@
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
"aris-client": ["aris-client@workspace:apps/aris-client"],
"arkregex": ["arkregex@0.0.5", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw=="],
"arktype": ["arktype@2.1.29", "", { "dependencies": { "@ark/schema": "0.56.0", "@ark/util": "0.56.0", "arkregex": "0.0.5" } }, "sha512-jyfKk4xIOzvYNayqnD8ZJQqOwcrTOUbIU4293yrzAjA3O1dWh61j71ArMQ6tS/u4pD7vabSPe7nG3RCyoXW6RQ=="],

Some files were not shown because too many files have changed in this diff Show More