diff --git a/packages/client/src/bsky/actor/actor.ts b/packages/client/src/actor/actor.ts similarity index 98% rename from packages/client/src/bsky/actor/actor.ts rename to packages/client/src/actor/actor.ts index 94cd3ae..5d41e11 100644 --- a/packages/client/src/bsky/actor/actor.ts +++ b/packages/client/src/actor/actor.ts @@ -2,7 +2,7 @@ import type { AppBskyActorDefs, AppBskyFeedGetAuthorFeed, } from '@tsky/lexicons'; -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; diff --git a/packages/client/src/bsky/actor/index.ts b/packages/client/src/actor/index.ts similarity index 100% rename from packages/client/src/bsky/actor/index.ts rename to packages/client/src/actor/index.ts diff --git a/packages/client/src/agent/agent.ts b/packages/client/src/agent/agent.ts new file mode 100644 index 0000000..f1e3c00 --- /dev/null +++ b/packages/client/src/agent/agent.ts @@ -0,0 +1,55 @@ +import { type CredentialManager, XRPC } from '@atcute/client'; +import type { Queries } from '@tsky/lexicons'; +import { Actor } from '~/actor'; +import { Feed } from '~/feed'; +import { List } from '~/list'; +import { StarterPack } from '~/starterpack'; +import { User } from '~/user'; +import { Video } from '~/video'; +import { Client } from './client'; + +export class Agent { + private client: Client; + + constructor(private handler: CredentialManager) { + // Initialize the client + const xrpc = new XRPC({ handler: this.handler }); + this.client = new Client(xrpc); + } + + get session() { + return this.handler.session; + } + + actor(identifier: string) { + return new Actor(this.client, identifier); + } + + list(uri: string) { + return new List(this.client, uri); + } + + get feed() { + return new Feed(this.client); + } + + get user() { + if (!this.session) { + throw new Error('There is no active session'); + } + + return new User(this.client, this.session.handle); + } + + get video() { + if (!this.session) { + throw new Error('There is no active session'); + } + + return new Video(this.client); + } + + get starterpack() { + return new StarterPack(this.client); + } +} diff --git a/packages/client/src/tsky/client.ts b/packages/client/src/agent/client.ts similarity index 100% rename from packages/client/src/tsky/client.ts rename to packages/client/src/agent/client.ts diff --git a/packages/client/src/agent/index.ts b/packages/client/src/agent/index.ts new file mode 100644 index 0000000..bf1c0a4 --- /dev/null +++ b/packages/client/src/agent/index.ts @@ -0,0 +1 @@ +export * from './agent'; diff --git a/packages/client/src/auth/auth.ts b/packages/client/src/auth/auth.ts deleted file mode 100644 index a1f969c..0000000 --- a/packages/client/src/auth/auth.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - type AtpSessionData, - CredentialManager, - type CredentialManagerOptions, -} from '@atcute/client'; - -export class Auth { - manager: CredentialManager; - sessions: Map = new Map(); - - constructor(options?: CredentialManagerOptions) { - this.manager = new CredentialManager( - options ?? { service: 'https://bsky.social' }, - ); - } - - async login(identifier: string, password: string) { - const session = await this.manager.login({ - identifier, - password, - }); - - this.sessions.set(session.did, session); - - return session; - } - - async switch(did: string) { - const session = this.sessions.get(did); - - if (!session) { - throw new Error('Session not found'); - } - - return await this.manager.resume(session); - } - - logout(did: string) { - this.sessions.delete(did); - } - - get currentSession() { - return this.manager.session; - } -} diff --git a/packages/client/src/auth/index.ts b/packages/client/src/auth/index.ts deleted file mode 100644 index 269586e..0000000 --- a/packages/client/src/auth/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './auth'; diff --git a/packages/client/src/bsky/bsky.ts b/packages/client/src/bsky/bsky.ts deleted file mode 100644 index 813677b..0000000 --- a/packages/client/src/bsky/bsky.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Feed } from '~/bsky/feed'; -import type { Client } from '~/tsky/client'; -import { Actor } from './actor'; -import { List } from './list'; - -export class Bsky { - constructor(private client: Client) {} - - actor(identifier: string) { - return new Actor(this.client, identifier); - } - - list(uri: string) { - return new List(this.client, uri); - } - - get feed() { - return new Feed(this.client); - } -} diff --git a/packages/client/src/bsky/index.ts b/packages/client/src/bsky/index.ts deleted file mode 100644 index a29617f..0000000 --- a/packages/client/src/bsky/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './bsky'; -export * from './feed'; diff --git a/packages/client/src/bsky/feed/feed.test.ts b/packages/client/src/feed/feed.test.ts similarity index 76% rename from packages/client/src/bsky/feed/feed.test.ts rename to packages/client/src/feed/feed.test.ts index 40f88bf..99a7480 100644 --- a/packages/client/src/bsky/feed/feed.test.ts +++ b/packages/client/src/feed/feed.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { Tsky } from '~/index'; +import { createAgent } from '~/tsky'; const TEST_CREDENTIALS = { alice: { @@ -14,21 +14,13 @@ const TEST_CREDENTIALS = { }, }; -async function getAliceTsky() { - const tsky = new Tsky(); - - await tsky.auth.login( - TEST_CREDENTIALS.alice.handle, - TEST_CREDENTIALS.alice.password, - ); - - return tsky; -} - describe('feed', () => { it('.getFeed()', async () => { - const tsky = await getAliceTsky(); - const paginator = await tsky.bsky.feed.get({ + const agent = await createAgent({ + identifier: TEST_CREDENTIALS.alice.handle, + password: TEST_CREDENTIALS.alice.password, + }); + const paginator = await agent.feed.get({ // "Birds! 🦉" custom feed // - https://bsky.app/profile/daryllmarie.bsky.social/feed/aaagllxbcbsje feed: 'at://did:plc:ffkgesg3jsv2j7aagkzrtcvt/app.bsky.feed.generator/aaagllxbcbsje', diff --git a/packages/client/src/bsky/feed/feed.ts b/packages/client/src/feed/feed.ts similarity index 96% rename from packages/client/src/bsky/feed/feed.ts rename to packages/client/src/feed/feed.ts index 7d0aacb..8da6749 100644 --- a/packages/client/src/bsky/feed/feed.ts +++ b/packages/client/src/feed/feed.ts @@ -2,7 +2,7 @@ import type { AppBskyFeedGetFeed, AppBskyFeedSendInteractions, } from '@tsky/lexicons'; -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; import { FeedGenerator } from './generator'; diff --git a/packages/client/src/bsky/feed/generator.ts b/packages/client/src/feed/generator.ts similarity index 97% rename from packages/client/src/bsky/feed/generator.ts rename to packages/client/src/feed/generator.ts index f2d5064..2c05d59 100644 --- a/packages/client/src/bsky/feed/generator.ts +++ b/packages/client/src/feed/generator.ts @@ -3,7 +3,7 @@ import type { AppBskyFeedGetFeedGenerators, AppBskyFeedGetFeedSkeleton, } from '@tsky/lexicons'; -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; diff --git a/packages/client/src/bsky/feed/index.ts b/packages/client/src/feed/index.ts similarity index 100% rename from packages/client/src/bsky/feed/index.ts rename to packages/client/src/feed/index.ts diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 9450186..2a1ee5f 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,2 +1 @@ -export * from './bsky'; -export * from './tsky'; +export * from '~/tsky'; diff --git a/packages/client/src/bsky/list/index.ts b/packages/client/src/list/index.ts similarity index 100% rename from packages/client/src/bsky/list/index.ts rename to packages/client/src/list/index.ts diff --git a/packages/client/src/bsky/list/list.ts b/packages/client/src/list/list.ts similarity index 95% rename from packages/client/src/bsky/list/list.ts rename to packages/client/src/list/list.ts index 313b403..e036d6c 100644 --- a/packages/client/src/bsky/list/list.ts +++ b/packages/client/src/list/list.ts @@ -1,4 +1,4 @@ -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; diff --git a/packages/client/src/bsky/post/index.ts b/packages/client/src/post/index.ts similarity index 100% rename from packages/client/src/bsky/post/index.ts rename to packages/client/src/post/index.ts diff --git a/packages/client/src/bsky/post/post.ts b/packages/client/src/post/post.ts similarity index 98% rename from packages/client/src/bsky/post/post.ts rename to packages/client/src/post/post.ts index 41112a6..5db0b25 100644 --- a/packages/client/src/bsky/post/post.ts +++ b/packages/client/src/post/post.ts @@ -5,7 +5,7 @@ import type { AppBskyFeedGetRepostedBy, AppBskyFeedSearchPosts, } from '@tsky/lexicons'; -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; diff --git a/packages/client/src/starterpack/starterpack.ts b/packages/client/src/starterpack/starterpack.ts index dc2e559..8b35a0b 100644 --- a/packages/client/src/starterpack/starterpack.ts +++ b/packages/client/src/starterpack/starterpack.ts @@ -2,7 +2,7 @@ import type { AppBskyGraphGetStarterPack, AppBskyGraphGetStarterPacks, } from '@tsky/lexicons'; -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; diff --git a/packages/client/src/tsky/tsky.ts b/packages/client/src/tsky/tsky.ts index a19fa76..39c9855 100644 --- a/packages/client/src/tsky/tsky.ts +++ b/packages/client/src/tsky/tsky.ts @@ -1,46 +1,20 @@ -import { XRPC } from '@atcute/client'; -import type { Queries } from '@tsky/lexicons'; -import { Auth } from '~/auth'; -import { Bsky } from '~/bsky'; -import { StarterPack } from '~/starterpack'; -import { User } from '~/user'; -import { Video } from '~/video'; -import { Client } from './client'; - -export class Tsky { - auth: Auth; - private client: Client; - - constructor() { - // Initialize the auth manager - this.auth = new Auth(); - - // Initialize the client - const xrpc = new XRPC({ handler: this.auth.manager }); - this.client = new Client(xrpc); - } - - get user() { - if (!this.auth.currentSession) { - throw new Error('There is no active session'); - } - - return new User(this.client, this.auth.currentSession.handle); - } - - get bsky() { - return new Bsky(this.client); - } - - get video() { - if (!this.auth.currentSession) { - throw new Error('There is no active session'); - } - - return new Video(this.client); - } - - get starterpack() { - return new StarterPack(this.client); - } +import { + CredentialManager, + type CredentialManagerOptions, +} from '@atcute/client'; +import { Agent } from '~/agent'; +import type { CreateAgentOptions } from './types'; + +export async function createAgent( + credentials: CreateAgentOptions, + options?: CredentialManagerOptions, +) { + const manager = new CredentialManager( + options ?? { service: 'https://bsky.social' }, + ); + + if ('session' in credentials) manager.resume(credentials.session); + else await manager.login(credentials); + + return new Agent(manager); } diff --git a/packages/client/src/tsky/types.ts b/packages/client/src/tsky/types.ts new file mode 100644 index 0000000..01c0118 --- /dev/null +++ b/packages/client/src/tsky/types.ts @@ -0,0 +1,10 @@ +import type { AtpSessionData } from '@atcute/client'; + +export type CreateAgentOptions = + | { + identifier: string; + password: string; + } + | { + session: AtpSessionData; + }; diff --git a/packages/client/src/user/index.ts b/packages/client/src/user/index.ts index 99e2809..99e5101 100644 --- a/packages/client/src/user/index.ts +++ b/packages/client/src/user/index.ts @@ -1,5 +1,5 @@ import type { AppBskyFeedGetTimeline } from '@tsky/lexicons'; -import { Actor } from '~/bsky/actor'; +import { Actor } from '~/actor'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; import { Mute } from './mute'; diff --git a/packages/client/src/user/mute/mute.ts b/packages/client/src/user/mute/mute.ts index a6c65d8..6738591 100644 --- a/packages/client/src/user/mute/mute.ts +++ b/packages/client/src/user/mute/mute.ts @@ -1,4 +1,4 @@ -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; export class Mute { diff --git a/packages/client/src/user/muted/muted.ts b/packages/client/src/user/muted/muted.ts index 4c81219..2a3f755 100644 --- a/packages/client/src/user/muted/muted.ts +++ b/packages/client/src/user/muted/muted.ts @@ -1,4 +1,4 @@ -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; diff --git a/packages/client/src/user/preferences/preferences.test.ts b/packages/client/src/user/preferences/preferences.test.ts index d6a82df..2fb286a 100644 --- a/packages/client/src/user/preferences/preferences.test.ts +++ b/packages/client/src/user/preferences/preferences.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { Tsky } from '~/index'; +import { createAgent } from '~/index'; const TEST_CREDENTIALS = { alice: { @@ -14,36 +14,31 @@ const TEST_CREDENTIALS = { }, }; -async function getAliceTsky() { - const tsky = new Tsky(); - - await tsky.auth.login( - TEST_CREDENTIALS.alice.handle, - TEST_CREDENTIALS.alice.password, - ); - - return tsky; -} - describe('preferences', () => { it('.get()', async () => { - const tsky = await getAliceTsky(); - const preferences = await tsky.user.preferences.get(); + const agent = await createAgent({ + identifier: TEST_CREDENTIALS.alice.handle, + password: TEST_CREDENTIALS.alice.password, + }); + const preferences = await agent.user.preferences.get(); expect(preferences).toBeDefined(); }); it('.set()', async () => { - const tsky = await getAliceTsky(); + const agent = await createAgent({ + identifier: TEST_CREDENTIALS.alice.handle, + password: TEST_CREDENTIALS.alice.password, + }); const payload = { $type: 'app.bsky.actor.defs.adultContentPref', enabled: false, }; - await tsky.user.preferences.set([payload]); + await agent.user.preferences.set([payload]); - const preferences = await tsky.user.preferences.get(); + const preferences = await agent.user.preferences.get(); expect(preferences).toBeDefined(); diff --git a/packages/client/src/user/preferences/preferences.ts b/packages/client/src/user/preferences/preferences.ts index 652614b..c66b88a 100644 --- a/packages/client/src/user/preferences/preferences.ts +++ b/packages/client/src/user/preferences/preferences.ts @@ -1,5 +1,5 @@ import type { AppBskyActorPutPreferences } from '@tsky/lexicons'; -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; export class Preferences { diff --git a/packages/client/src/user/profile.test.ts b/packages/client/src/user/profile.test.ts index 3d24b53..19c618b 100644 --- a/packages/client/src/user/profile.test.ts +++ b/packages/client/src/user/profile.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { Tsky } from '~/index'; +import { createAgent } from '~/index'; const TEST_CREDENTIALS = { alice: { @@ -15,29 +15,25 @@ const TEST_CREDENTIALS = { }; describe('profile', async () => { - const tsky = new Tsky(); - - await tsky.auth.login( - TEST_CREDENTIALS.alice.handle, - TEST_CREDENTIALS.alice.password, - ); - it("Getting alice's profile", async () => { - const profile = await tsky.user.profile(); + const agent = await createAgent({ + identifier: TEST_CREDENTIALS.alice.handle, + password: TEST_CREDENTIALS.alice.password, + }); + + const profile = await agent.user.profile(); expect(profile).toBeDefined(); expect(profile.handle).toBe(TEST_CREDENTIALS.alice.handle); }); - it("Switching to bob's profile", async () => { - await tsky.auth.login( - TEST_CREDENTIALS.bob.handle, - TEST_CREDENTIALS.bob.password, - ); - }); - it("Getting bob's profile", async () => { - const profile = await tsky.user.profile(); + const agent = await createAgent({ + identifier: TEST_CREDENTIALS.bob.handle, + password: TEST_CREDENTIALS.bob.password, + }); + + const profile = await agent.user.profile(); expect(profile).toBeDefined(); expect(profile.handle).toBe(TEST_CREDENTIALS.bob.handle); diff --git a/packages/client/src/user/suggestion/suggestion.ts b/packages/client/src/user/suggestion/suggestion.ts index c6af88c..5aa10b8 100644 --- a/packages/client/src/user/suggestion/suggestion.ts +++ b/packages/client/src/user/suggestion/suggestion.ts @@ -1,4 +1,4 @@ -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; diff --git a/packages/client/src/user/unmute/unmute.ts b/packages/client/src/user/unmute/unmute.ts index c100a66..be4791a 100644 --- a/packages/client/src/user/unmute/unmute.ts +++ b/packages/client/src/user/unmute/unmute.ts @@ -1,4 +1,4 @@ -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; export class Unmute { diff --git a/packages/client/src/video/video.ts b/packages/client/src/video/video.ts index 96af382..a36ad64 100644 --- a/packages/client/src/video/video.ts +++ b/packages/client/src/video/video.ts @@ -1,5 +1,5 @@ import type { AppBskyVideoDefs, AppBskyVideoUploadVideo } from '@tsky/lexicons'; -import type { Client } from '~/tsky/client'; +import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; export class Video {