From 4cf1bcd927a729c87767e637033fb901fec26894 Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Sat, 8 Feb 2025 17:20:30 +0530 Subject: [PATCH 01/16] refactor(client): shifted search logic --- packages/client/src/agent/agent.ts | 6 +++- packages/client/src/post/post.ts | 18 ----------- packages/client/src/search/index.ts | 1 + packages/client/src/search/search.ts | 45 ++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 packages/client/src/search/index.ts create mode 100644 packages/client/src/search/search.ts diff --git a/packages/client/src/agent/agent.ts b/packages/client/src/agent/agent.ts index f1e3c00..3298760 100644 --- a/packages/client/src/agent/agent.ts +++ b/packages/client/src/agent/agent.ts @@ -3,7 +3,7 @@ import type { Queries } from '@tsky/lexicons'; import { Actor } from '~/actor'; import { Feed } from '~/feed'; import { List } from '~/list'; -import { StarterPack } from '~/starterpack'; +import { Search } from '~/search'; import { User } from '~/user'; import { Video } from '~/video'; import { Client } from './client'; @@ -33,6 +33,10 @@ export class Agent { return new Feed(this.client); } + get search() { + return new Search(this.client); + } + get user() { if (!this.session) { throw new Error('There is no active session'); diff --git a/packages/client/src/post/post.ts b/packages/client/src/post/post.ts index 5db0b25..31b36a4 100644 --- a/packages/client/src/post/post.ts +++ b/packages/client/src/post/post.ts @@ -72,24 +72,6 @@ export class Post { }); } - /** - * Find posts matching search criteria, returning views of those posts. - */ - static search( - client: Client, - params: AppBskyFeedSearchPosts.Params, - options: RPCOptions = {}, - ) { - return Paginator.init(async (cursor) => { - const res = await client.get('app.bsky.feed.searchPosts', { - params: { cursor, ...params }, - ...options, - }); - - return res.data; - }); - } - /** * Gets post views for a specified list of posts (by AT-URI). This is sometimes referred to as 'hydrating' a 'feed skeleton'. */ diff --git a/packages/client/src/search/index.ts b/packages/client/src/search/index.ts new file mode 100644 index 0000000..5a2bdeb --- /dev/null +++ b/packages/client/src/search/index.ts @@ -0,0 +1 @@ +export * from './search'; diff --git a/packages/client/src/search/search.ts b/packages/client/src/search/search.ts new file mode 100644 index 0000000..bf1e346 --- /dev/null +++ b/packages/client/src/search/search.ts @@ -0,0 +1,45 @@ +import type { + AppBskyFeedSearchPosts, + AppBskyGraphSearchStarterPacks, +} from '@tsky/lexicons'; +import type { Client } from '~/agent/client'; +import type { RPCOptions } from '~/types'; +import { Paginator } from '~/utils'; + +export class Search { + constructor(private client: Client) {} + + /** + * Find posts matching search criteria, returning views of those posts. + */ + posts(params: AppBskyFeedSearchPosts.Params, options: RPCOptions = {}) { + return Paginator.init(async (cursor) => { + const res = await this.client.get('app.bsky.feed.searchPosts', { + params: { cursor, ...params }, + ...options, + }); + + return res.data; + }); + } + + /** + * Search for starter packs. + */ + starterpacks( + params: AppBskyGraphSearchStarterPacks.Params, + options?: RPCOptions, + ) { + return Paginator.init(async (cursor) => { + const res = await this.client.get('app.bsky.graph.searchStarterPacks', { + params: { + cursor, + ...params, + }, + ...options, + }); + + return res.data; + }); + } +} From 64b10eceef0e81f6c9b1f98a6b6269c5d9b4582a Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Sat, 8 Feb 2025 17:21:09 +0530 Subject: [PATCH 02/16] refactor(client): improved starterpacks api --- packages/client/src/agent/agent.ts | 42 ++++++++++++- packages/client/src/post/post.ts | 6 +- .../client/src/starterpack/starterpack.ts | 59 ------------------- 3 files changed, 43 insertions(+), 64 deletions(-) diff --git a/packages/client/src/agent/agent.ts b/packages/client/src/agent/agent.ts index 3298760..fe3bb69 100644 --- a/packages/client/src/agent/agent.ts +++ b/packages/client/src/agent/agent.ts @@ -1,9 +1,14 @@ import { type CredentialManager, XRPC } from '@atcute/client'; -import type { Queries } from '@tsky/lexicons'; +import type { + AppBskyGraphGetStarterPack, + AppBskyGraphGetStarterPacks, + Queries, +} from '@tsky/lexicons'; import { Actor } from '~/actor'; import { Feed } from '~/feed'; import { List } from '~/list'; import { Search } from '~/search'; +import type { RPCOptions } from '~/types'; import { User } from '~/user'; import { Video } from '~/video'; import { Client } from './client'; @@ -53,7 +58,38 @@ export class Agent { return new Video(this.client); } - get starterpack() { - return new StarterPack(this.client); + /** + * Gets a view of a starter pack. + */ + startpacks( + uri: string, + options?: RPCOptions, + ): Promise; + /** + * Get views for a list of starter packs. + */ + startpacks( + uris: string[], + options?: RPCOptions, + ): Promise; + + async startpacks(uris: string | string[], options: RPCOptions = {}) { + if (Array.isArray(uris)) { + const res = await this.client.get('app.bsky.graph.getStarterPacks', { + params: { + uris, + }, + ...options, + }); + + return res.data.starterPacks; + } + + const res = await this.client.get('app.bsky.graph.getStarterPack', { + params: { starterPack: uris }, + ...options, + }); + + return res.data; } } diff --git a/packages/client/src/post/post.ts b/packages/client/src/post/post.ts index 31b36a4..f7a9a06 100644 --- a/packages/client/src/post/post.ts +++ b/packages/client/src/post/post.ts @@ -3,14 +3,16 @@ import type { AppBskyFeedGetPostThread, AppBskyFeedGetQuotes, AppBskyFeedGetRepostedBy, - AppBskyFeedSearchPosts, } from '@tsky/lexicons'; import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; export class Post { - constructor(private client: Client) {} + constructor( + private client: Client, + private uri: string, + ) {} /** * Get posts in a thread. Does not require auth, but additional metadata and filtering will be applied for authed requests. diff --git a/packages/client/src/starterpack/starterpack.ts b/packages/client/src/starterpack/starterpack.ts index 8b35a0b..459ce5f 100644 --- a/packages/client/src/starterpack/starterpack.ts +++ b/packages/client/src/starterpack/starterpack.ts @@ -1,64 +1,5 @@ -import type { - AppBskyGraphGetStarterPack, - AppBskyGraphGetStarterPacks, -} from '@tsky/lexicons'; import type { Client } from '~/agent/client'; -import type { RPCOptions } from '~/types'; -import { Paginator } from '~/utils'; export class StarterPack { constructor(private client: Client) {} - - /** - * Gets a view of a starter pack. - */ - view( - uri: string, - options: RPCOptions, - ): Promise; - /** - * Get views for a list of starter packs. - */ - view( - uris: string[], - options: RPCOptions, - ): Promise; - - async view(uris: string | string[], options: RPCOptions) { - if (Array.isArray(uris)) { - const res = await this.client.get('app.bsky.graph.getStarterPacks', { - params: { - uris, - }, - ...options, - }); - - return res.data.starterPacks; - } - - const res = await this.client.get('app.bsky.graph.getStarterPack', { - params: { starterPack: uris }, - ...options, - }); - - return res.data; - } - - /** - * Search for starter packs. - */ - search(query: string, limit?: number, options?: RPCOptions) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.graph.searchStarterPacks', { - params: { - cursor, - q: query, - limit, - }, - ...options, - }); - - return res.data; - }); - } } From 373d900b1f9a31c5fdb925fdf2e174b30e65703c Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Sat, 8 Feb 2025 18:10:39 +0530 Subject: [PATCH 03/16] chore(client): minor changes --- packages/client/src/agent/agent.ts | 2 +- packages/client/src/post/post.ts | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/client/src/agent/agent.ts b/packages/client/src/agent/agent.ts index fe3bb69..de89978 100644 --- a/packages/client/src/agent/agent.ts +++ b/packages/client/src/agent/agent.ts @@ -14,7 +14,7 @@ import { Video } from '~/video'; import { Client } from './client'; export class Agent { - private client: Client; + client: Client; constructor(private handler: CredentialManager) { // Initialize the client diff --git a/packages/client/src/post/post.ts b/packages/client/src/post/post.ts index f7a9a06..617c49f 100644 --- a/packages/client/src/post/post.ts +++ b/packages/client/src/post/post.ts @@ -18,11 +18,11 @@ export class Post { * Get posts in a thread. Does not require auth, but additional metadata and filtering will be applied for authed requests. */ async threads( - params: AppBskyFeedGetPostThread.Params, + params: Omit = {}, options: RPCOptions = {}, ) { const res = await this.client.get('app.bsky.feed.getPostThread', { - params, + params: { uri: this.uri, ...params }, ...options, }); @@ -32,10 +32,13 @@ export class Post { /** * Get like records which reference a subject (by AT-URI and CID). */ - likes(params: AppBskyFeedGetLikes.Params, options: RPCOptions = {}) { + likes( + params: Omit = {}, + options: RPCOptions = {}, + ) { return Paginator.init(async (cursor) => { const res = await this.client.get('app.bsky.feed.getLikes', { - params: { cursor, ...params }, + params: { cursor, uri: this.uri, ...params }, ...options, }); @@ -46,10 +49,13 @@ export class Post { /** * Get a list of quotes for a given post. */ - quotes(params: AppBskyFeedGetQuotes.Params, options: RPCOptions = {}) { + quotes( + params: Omit = {}, + options: RPCOptions = {}, + ) { return Paginator.init(async (cursor) => { const res = await this.client.get('app.bsky.feed.getQuotes', { - params: { cursor, ...params }, + params: { cursor, uri: this.uri, ...params }, ...options, }); @@ -61,12 +67,12 @@ export class Post { * Get a list of reposts for a given post. */ repostedBy( - params: AppBskyFeedGetRepostedBy.Params, + params: Omit = {}, options: RPCOptions = {}, ) { return Paginator.init(async (cursor) => { const res = await this.client.get('app.bsky.feed.getRepostedBy', { - params: { cursor, ...params }, + params: { cursor, uri: this.uri, ...params }, ...options, }); From 3e0f2ef94bf341dabe746cf1886fc29bfe2ae6f1 Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Wed, 12 Feb 2025 08:38:06 +0530 Subject: [PATCH 04/16] feat: wip linking all classes --- packages/client/src/actor/actor.ts | 115 ++++++++++++++---- packages/client/src/agent/agent.ts | 18 ++- packages/client/src/agent/client.ts | 97 ++++++++++++++- packages/client/src/list/list.ts | 9 +- packages/client/src/post/post.ts | 51 +++++++- packages/client/src/search/search.ts | 14 ++- .../client/src/starterpack/starterpack.ts | 43 ++++++- packages/client/src/types.ts | 11 ++ packages/client/src/user/index.ts | 2 +- packages/client/src/utils/index.ts | 1 + packages/client/src/utils/parse.ts | 15 +++ 11 files changed, 332 insertions(+), 44 deletions(-) create mode 100644 packages/client/src/utils/parse.ts diff --git a/packages/client/src/actor/actor.ts b/packages/client/src/actor/actor.ts index 5d41e11..2c1bd13 100644 --- a/packages/client/src/actor/actor.ts +++ b/packages/client/src/actor/actor.ts @@ -1,29 +1,23 @@ import type { AppBskyActorDefs, AppBskyFeedGetAuthorFeed, + AppBskyGraphDefs, + At, + ComAtprotoLabelDefs, + ComAtprotoRepoStrongRef, } from '@tsky/lexicons'; import type { Client } from '~/agent/client'; +import { List } from '~/list'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; export class Actor { client: Client; - identifier: string; + did: At.DID; - constructor(client: Client, identifier: string) { + constructor(client: Client, did: At.DID) { this.client = client; - this.identifier = identifier; - } - - /** - * Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth. - */ - async profile(): Promise { - const res = await this.client.get('app.bsky.actor.getProfile', { - params: { actor: this.identifier }, - }); - - return res.data; + this.did = did; } /** @@ -32,7 +26,7 @@ export class Actor { starterPacks(limit?: number, options: RPCOptions = {}) { return Paginator.init(async (cursor) => { const res = await this.client.get('app.bsky.graph.getActorStarterPacks', { - params: { cursor, actor: this.identifier, limit }, + params: { cursor, actor: this.did, limit }, ...options, }); @@ -48,13 +42,19 @@ export class Actor { const res = await this.client.get('app.bsky.graph.getFollowers', { params: { cursor, - actor: this.identifier, + actor: this.did, limit, }, ...options, }); - return res.data; + return { + ...res.data, + subject: new ActorProfile(this.client, res.data.subject), + followers: res.data.followers.map( + (follower) => new ActorProfile(this.client, follower), + ), + }; }); } @@ -66,13 +66,19 @@ export class Actor { const res = await this.client.get('app.bsky.graph.getFollows', { params: { cursor, - actor: this.identifier, + actor: this.did, limit, }, ...options, }); - return res.data; + return { + ...res.data, + subject: new ActorProfile(this.client, res.data.subject), + follows: res.data.follows.map( + (follow) => new ActorProfile(this.client, follow), + ), + }; }); } @@ -84,13 +90,17 @@ export class Actor { const res = await this.client.get('app.bsky.graph.getLists', { params: { cursor, - actor: this.identifier, + actor: this.did, limit, }, ...options, }); - return res.data; + return { + ...res.data, + // TODO: Solve this + // lists: res.data.lists.map((list) => new List(this.client, list)), + }; }); } @@ -100,13 +110,18 @@ export class Actor { async relationships(others?: string[], options?: RPCOptions) { const res = await this.client.get('app.bsky.graph.getRelationships', { params: { - actor: this.identifier, + actor: this.did, others, }, ...options, }); - return res.data; + return { + ...res.data, + actor: res.data.actor + ? new Actor(this.client, res.data.actor) + : undefined, + }; } /** @@ -115,7 +130,7 @@ export class Actor { feeds(limit?: number, options?: RPCOptions) { return Paginator.init(async (cursor) => { const res = await this.client.get('app.bsky.feed.getActorFeeds', { - params: { cursor, actor: this.identifier, limit }, + params: { cursor, actor: this.did, limit }, ...options, }); @@ -132,7 +147,7 @@ export class Actor { ) { return Paginator.init(async (cursor) => { const res = await this.client.get('app.bsky.feed.getAuthorFeed', { - params: { cursor, ...params, actor: this.identifier }, + params: { cursor, ...params, actor: this.did }, ...options, }); @@ -140,3 +155,53 @@ export class Actor { }); } } + +export class BasicActorProfile + extends Actor + implements AppBskyActorDefs.ProfileViewBasic +{ + // @ts-expect-error - We added this property with Object.assign + handle: string; + associated?: AppBskyActorDefs.ProfileAssociated; + avatar?: string; + createdAt?: string; + displayName?: string; + labels?: ComAtprotoLabelDefs.Label[]; + viewer?: AppBskyActorDefs.ViewerState; + $type?: string; + + constructor(client: Client, actor: AppBskyActorDefs.ProfileViewBasic) { + super(client, actor.did); + Object.assign(this, actor); + } +} + +export class ActorProfile + extends BasicActorProfile + implements AppBskyActorDefs.ProfileView +{ + description?: string; + indexedAt?: string; + + constructor(client: Client, actor: AppBskyActorDefs.ProfileViewDetailed) { + super(client, actor); + Object.assign(this, actor); + } +} + +export class DetailedActorProfile + extends ActorProfile + implements AppBskyActorDefs.ProfileViewDetailed +{ + banner?: string; + followersCount?: number; + followsCount?: number; + joinedViaStarterPack?: AppBskyGraphDefs.StarterPackViewBasic; + pinnedPost?: ComAtprotoRepoStrongRef.Main; + postsCount?: number; + + constructor(client: Client, actor: AppBskyActorDefs.ProfileViewDetailed) { + super(client, actor); + Object.assign(this, actor); + } +} diff --git a/packages/client/src/agent/agent.ts b/packages/client/src/agent/agent.ts index de89978..8b2007c 100644 --- a/packages/client/src/agent/agent.ts +++ b/packages/client/src/agent/agent.ts @@ -2,9 +2,10 @@ import { type CredentialManager, XRPC } from '@atcute/client'; import type { AppBskyGraphGetStarterPack, AppBskyGraphGetStarterPacks, + At, Queries, } from '@tsky/lexicons'; -import { Actor } from '~/actor'; +import { DetailedActorProfile } from '~/actor'; import { Feed } from '~/feed'; import { List } from '~/list'; import { Search } from '~/search'; @@ -19,15 +20,22 @@ export class Agent { constructor(private handler: CredentialManager) { // Initialize the client const xrpc = new XRPC({ handler: this.handler }); - this.client = new Client(xrpc); + this.client = new Client(xrpc, this.handler); } get session() { return this.handler.session; } - actor(identifier: string) { - return new Actor(this.client, identifier); + /** + * Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth. + */ + async actor(identifier: At.DID | At.Handle): Promise { + const res = await this.client.get('app.bsky.actor.getProfile', { + params: { actor: identifier }, + }); + + return new DetailedActorProfile(this.client, res.data); } list(uri: string) { @@ -47,7 +55,7 @@ export class Agent { throw new Error('There is no active session'); } - return new User(this.client, this.session.handle); + return new User(this.client, this.session.did); } get video() { diff --git a/packages/client/src/agent/client.ts b/packages/client/src/agent/client.ts index 34a017c..3ee3b3d 100644 --- a/packages/client/src/agent/client.ts +++ b/packages/client/src/agent/client.ts @@ -1,24 +1,31 @@ import type { + CredentialManager, RPCOptions, XRPC, XRPCRequestOptions, XRPCResponse, } from '@atcute/client'; -import type { Procedures, Queries } from '@tsky/lexicons'; +import type { At, Procedures, Queries } from '@tsky/lexicons'; +import { parseAtUri } from '~/utils'; +import type { RPCOptions as GenericReqOptions, StrongRef } from '../types'; // From @atcute/client type OutputOf = T extends { - // biome-ignore lint/suspicious/noExplicitAny: - output: any; + output: unknown; } ? T['output'] : never; +const NO_SESSION_ERROR = + 'No session found. Please login to perform this action.'; + export class Client { xrpc: XRPC; + crenditials: CredentialManager; - constructor(xrpc: XRPC) { + constructor(xrpc: XRPC, crenditials: CredentialManager) { this.xrpc = xrpc; + this.crenditials = crenditials; } /** @@ -53,4 +60,86 @@ export class Client { async request(options: XRPCRequestOptions): Promise { return this.xrpc.request(options); } + + /** + * Create a record. + * @param nsid The collection's NSID. + * @param record The record to create. + * @param rkey The rkey to use. + * @returns The record's AT URI and CID. + */ + async createRecord( + nsid: K, + record: Omit, '$type' | 'createdAt'>, + rkey?: string, + ): Promise { + if (!this.crenditials.session) throw new Error(NO_SESSION_ERROR); + const response = await this.call( + 'com.atproto.repo.createRecord' as keyof P, + { + data: { + collection: nsid, + record: { + $type: nsid, + createdAt: new Date().toISOString(), + ...record, + }, + repo: this.crenditials.session.did, + ...(rkey ? { rkey } : {}), + }, + } as unknown as RPCOptions, + ); + return response.data; + } + + /** + * Put a record in place of an existing record. + * @param nsid The collection's NSID. + * @param record The record to put. + * @param rkey The rkey to use. + * @returns The record's AT URI and CID. + */ + async putRecord( + nsid: string, + record: object, + rkey: string, + ): Promise { + if (!this.crenditials.session) throw new Error(NO_SESSION_ERROR); + const response = await this.call( + 'com.atproto.repo.putRecord' as keyof P, + { + data: { + collection: nsid, + record: { + $type: nsid, + createdAt: new Date().toISOString(), + ...record, + }, + repo: this.crenditials.session.did, + rkey, + }, + } as unknown as RPCOptions, + ); + return response.data; + } + + /** + * Delete a record. + * @param uri The record's AT URI. + */ + async deleteRecord( + uri: At.Uri, + options: GenericReqOptions = {}, + ): Promise { + const { host: repo, collection, rkey } = parseAtUri(uri); + if (repo !== this.crenditials.session?.did) + throw new Error('Can only delete own record.'); + await this.call( + 'com.atproto.repo.deleteRecord' as keyof P, + { + data: { collection, repo, rkey }, + ...options, + } as unknown as RPCOptions, + ); + } } diff --git a/packages/client/src/list/list.ts b/packages/client/src/list/list.ts index e036d6c..062fd1f 100644 --- a/packages/client/src/list/list.ts +++ b/packages/client/src/list/list.ts @@ -1,3 +1,4 @@ +import { ActorProfile } from '~/actor'; import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; @@ -22,7 +23,13 @@ export class List { ...options, }); - return res.data; + return { + ...res.data, + items: res.data.items.map((item) => ({ + ...item, + subject: new ActorProfile(this.client, item.subject), + })), + }; }); } diff --git a/packages/client/src/post/post.ts b/packages/client/src/post/post.ts index 617c49f..1357867 100644 --- a/packages/client/src/post/post.ts +++ b/packages/client/src/post/post.ts @@ -1,18 +1,61 @@ import type { + AppBskyEmbedExternal, + AppBskyEmbedImages, + AppBskyEmbedRecord, + AppBskyEmbedRecordWithMedia, + AppBskyEmbedVideo, + AppBskyFeedDefs, AppBskyFeedGetLikes, AppBskyFeedGetPostThread, AppBskyFeedGetQuotes, AppBskyFeedGetRepostedBy, + ComAtprotoLabelDefs, + Typed, } from '@tsky/lexicons'; +import { BasicActorProfile } from '~/actor'; import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; -import { Paginator } from '~/utils'; +import { Paginator, parseAtUri } from '~/utils'; + +export class Post implements AppBskyFeedDefs.PostView { + uri: string; + author: BasicActorProfile; + cid: string; + indexedAt: string; + record: unknown; + embed?: + | Typed + | Typed + | Typed + | Typed + | Typed; + labels?: ComAtprotoLabelDefs.Label[]; + likeCount?: number; + quoteCount?: number; + replyCount?: number; + repostCount?: number; + threadgate?: AppBskyFeedDefs.ThreadgateView; + viewer?: AppBskyFeedDefs.ViewerState; + $type?: string; -export class Post { constructor( private client: Client, - private uri: string, - ) {} + payload: AppBskyFeedDefs.PostView, + ) { + Object.assign(this, payload); + this.author = new BasicActorProfile(this.client, payload.author); + } + + isOfCurrentUser() { + const { host: repo } = parseAtUri(this.uri); + return repo !== this.client.crenditials.session?.did; + } + + remove(options: RPCOptions = {}) { + this.client.deleteRecord(this.uri, options); + } + + // TODO: method for liking, unliking, reposting, un-reposting, quoting, etc. /** * Get posts in a thread. Does not require auth, but additional metadata and filtering will be applied for authed requests. diff --git a/packages/client/src/search/search.ts b/packages/client/src/search/search.ts index bf1e346..a3c1b65 100644 --- a/packages/client/src/search/search.ts +++ b/packages/client/src/search/search.ts @@ -3,6 +3,8 @@ import type { AppBskyGraphSearchStarterPacks, } from '@tsky/lexicons'; import type { Client } from '~/agent/client'; +import { Post } from '~/post'; +import { BasicStarterPack } from '~/starterpack'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; @@ -19,7 +21,10 @@ export class Search { ...options, }); - return res.data; + return { + ...res.data, + posts: res.data.posts.map((post) => new Post(this.client, post)), + }; }); } @@ -39,7 +44,12 @@ export class Search { ...options, }); - return res.data; + return { + ...res.data, + starterPacks: res.data.starterPacks.map( + (starterPack) => new BasicStarterPack(this.client, starterPack), + ), + }; }); } } diff --git a/packages/client/src/starterpack/starterpack.ts b/packages/client/src/starterpack/starterpack.ts index 459ce5f..6785ccc 100644 --- a/packages/client/src/starterpack/starterpack.ts +++ b/packages/client/src/starterpack/starterpack.ts @@ -1,5 +1,44 @@ +import type { + AppBskyActorDefs, + AppBskyFeedDefs, + AppBskyGraphDefs, + ComAtprotoLabelDefs, +} from '@tsky/lexicons'; +import { BasicActorProfile } from '~/actor'; import type { Client } from '~/agent/client'; -export class StarterPack { - constructor(private client: Client) {} +class Starterpack { + cid: string; + creator: AppBskyActorDefs.ProfileViewBasic; + indexedAt: string; + record: unknown; + uri: string; + joinedAllTimeCount?: number | undefined; + joinedWeekCount?: number | undefined; + labels?: ComAtprotoLabelDefs.Label[] | undefined; + $type?: string | undefined; + + constructor( + private client: Client, + payload: AppBskyGraphDefs.StarterPackView, + ) { + Object.assign(this, payload); + this.creator = new BasicActorProfile(this.client, payload.creator); + } +} + +export class BasicStarterPack + extends Starterpack + implements AppBskyGraphDefs.StarterPackViewBasic +{ + listItemCount?: number | undefined; +} + +export class StarterPack + extends Starterpack + implements AppBskyGraphDefs.StarterPackView +{ + feeds?: AppBskyFeedDefs.GeneratorView[]; + list?: AppBskyGraphDefs.ListViewBasic; + listItemsSample?: AppBskyGraphDefs.ListItemView[]; } diff --git a/packages/client/src/types.ts b/packages/client/src/types.ts index 5819b72..5c54345 100644 --- a/packages/client/src/types.ts +++ b/packages/client/src/types.ts @@ -1 +1,12 @@ export type RPCOptions = { signal?: AbortSignal; headers?: HeadersInit }; + +/** + * A reference to a record. + */ +export interface StrongRef { + /** The record's AT URI. */ + uri: string; + + /** The record's CID. */ + cid: string; +} diff --git a/packages/client/src/user/index.ts b/packages/client/src/user/index.ts index 99e5101..c55caf6 100644 --- a/packages/client/src/user/index.ts +++ b/packages/client/src/user/index.ts @@ -39,7 +39,7 @@ export class User extends Actor { likes(limit?: number, options: RPCOptions = {}) { return Paginator.init(async (cursor) => { const res = await this.client.get('app.bsky.feed.getActorLikes', { - params: { cursor, actor: this.identifier, limit }, + params: { cursor, actor: this.did, limit }, ...options, }); diff --git a/packages/client/src/utils/index.ts b/packages/client/src/utils/index.ts index 544dc7f..98b9548 100644 --- a/packages/client/src/utils/index.ts +++ b/packages/client/src/utils/index.ts @@ -1 +1,2 @@ export * from './paginator'; +export * from './parse'; diff --git a/packages/client/src/utils/parse.ts b/packages/client/src/utils/parse.ts new file mode 100644 index 0000000..dc49221 --- /dev/null +++ b/packages/client/src/utils/parse.ts @@ -0,0 +1,15 @@ +const ATP_URI_REGEX = + // proto- --did-------------- --name---------------- --path---- --query-- --hash-- + /^(at:\/\/)?((?:did:[a-z0-9:%-]+)|(?:[a-z0-9][a-z0-9.:-]*))(\/[^?#\s]*)?(\?[^#\s]+)?(#[^\s]+)?$/i; + +export function parseAtUri(uri: string): { + host: string; + collection: string; + rkey: string; +} { + const match = uri.match(ATP_URI_REGEX); + if (!match) throw new Error(`Invalid AT URI: ${uri}`); + const [, _proto, host, pathname] = match; + const [collection = '', rkey = ''] = pathname.split('/').filter(Boolean); + return { host, collection, rkey }; +} From 55a5f37e5824358a68ef9e2bc0651bc87809c1c9 Mon Sep 17 00:00:00 2001 From: Aditya Mathur Date: Sat, 15 Feb 2025 09:57:17 +0530 Subject: [PATCH 05/16] fix: updated agent to have instance without auth --- packages/client/src/tsky/tsky.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/client/src/tsky/tsky.ts b/packages/client/src/tsky/tsky.ts index cebabfc..60a6ba9 100644 --- a/packages/client/src/tsky/tsky.ts +++ b/packages/client/src/tsky/tsky.ts @@ -6,17 +6,19 @@ import { Agent } from '~/agent'; import type { CreateAgentOptions } from './types'; export async function createAgent( - credentials: CreateAgentOptions, + credentials?: CreateAgentOptions, options?: CredentialManagerOptions, ) { const manager = new CredentialManager( options ?? { service: 'https://bsky.social' }, ); - if ('session' in credentials) { - await manager.resume(credentials.session); - } else { - await manager.login(credentials); + if (credentials) { + if ('session' in credentials) { + await manager.resume(credentials.session); + } else { + await manager.login(credentials); + } } return new Agent(manager); From d5d3815ae86358dd6191b7e5db9106f9362460f9 Mon Sep 17 00:00:00 2001 From: Aditya Mathur Date: Sat, 15 Feb 2025 11:10:47 +0530 Subject: [PATCH 06/16] feat: added rimraf --- package.json | 7 ++-- packages/lex-cli/package.json | 2 +- packages/lexicons/package.json | 2 +- pnpm-lock.yaml | 58 ++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index fec2f64..f0a988b 100644 --- a/package.json +++ b/package.json @@ -27,9 +27,12 @@ }, "devDependencies": { "@biomejs/biome": "^1.9.4", - "nano-staged": "^0.8.0" + "nano-staged": "^0.8.0", + "rimraf": "^6.0.1" }, "nano-staged": { - "*.{js,ts,cjs,mjs,json}": ["biome check --write --"] + "*.{js,ts,cjs,mjs,json}": [ + "biome check --write --" + ] } } diff --git a/packages/lex-cli/package.json b/packages/lex-cli/package.json index bfdac8c..dda3016 100644 --- a/packages/lex-cli/package.json +++ b/packages/lex-cli/package.json @@ -12,7 +12,7 @@ "bin": "./dist/index.js", "scripts": { "build": "tsc", - "clean": "rm -rf dist", + "clean": "rimraf dist", "prepare": "pnpm run clean && pnpm run build" }, "dependencies": { diff --git a/packages/lexicons/package.json b/packages/lexicons/package.json index 2e8adef..c7e382b 100644 --- a/packages/lexicons/package.json +++ b/packages/lexicons/package.json @@ -19,7 +19,7 @@ "scripts": { "build": "tsx ./scripts/generate-types.ts && tsc", "check-version-change": "tsx ./scripts/check-version-change.ts && tsc", - "clean": "rm -rf dist && rm -rf lexicons", + "clean": "rimraf dist && rimraf lexicons", "prepare": "pnpm run clean && pnpm run build" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b42c11..ec776f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: nano-staged: specifier: ^0.8.0 version: 0.8.0 + rimraf: + specifier: ^6.0.1 + version: 6.0.1 docs: devDependencies: @@ -2251,6 +2254,11 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true + glob@11.0.1: + resolution: {integrity: sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==} + engines: {node: 20 || >=22} + hasBin: true + globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -2411,6 +2419,10 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@4.0.3: + resolution: {integrity: sha512-oSwM7q8PTHQWuZAlp995iPpPJ4Vkl7qT0ZRD+9duL9j2oBy6KcTfyxc8mEuHJYC+z/kbps80aJLkaNzTOrf/kw==} + engines: {node: 20 || >=22} + jose@5.9.6: resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} @@ -2457,6 +2469,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.0.2: + resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -2554,6 +2570,10 @@ packages: minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -2701,6 +2721,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + path-to-regexp@0.1.12: resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} @@ -2926,6 +2950,11 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true + rimraf@6.0.1: + resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} + engines: {node: 20 || >=22} + hasBin: true + roarr@7.21.1: resolution: {integrity: sha512-3niqt5bXFY1InKU8HKWqqYTYjtrBaxBMnXELXCXUYgtNYGUtZM5rB46HIC430AyacL95iEniGf7RgqsesykLmQ==} engines: {node: '>=18.0'} @@ -6195,6 +6224,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@11.0.1: + dependencies: + foreground-child: 3.3.0 + jackspeak: 4.0.3 + minimatch: 10.0.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + globals@11.12.0: {} globals@15.12.0: {} @@ -6379,6 +6417,10 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jackspeak@4.0.3: + dependencies: + '@isaacs/cliui': 8.0.2 + jose@5.9.6: {} js-tokens@4.0.0: {} @@ -6414,6 +6456,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.0.2: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -6511,6 +6555,10 @@ snapshots: minimalistic-crypto-utils@1.0.1: {} + minimatch@10.0.1: + dependencies: + brace-expansion: 2.0.1 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -6623,6 +6671,11 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-scurry@2.0.0: + dependencies: + lru-cache: 11.0.2 + minipass: 7.1.2 + path-to-regexp@0.1.12: {} pathe@2.0.2: {} @@ -6857,6 +6910,11 @@ snapshots: dependencies: glob: 10.4.5 + rimraf@6.0.1: + dependencies: + glob: 11.0.1 + package-json-from-dist: 1.0.1 + roarr@7.21.1: dependencies: fast-printf: 1.6.10 From 5165db2c0a83d23db9739a93c340e92a58d119d6 Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Thu, 20 Feb 2025 09:11:45 +0530 Subject: [PATCH 07/16] chore: minor changes in profile function --- packages/client/src/actor/actor.ts | 23 +++++++++++++++++++++++ packages/client/src/agent/agent.ts | 10 +++------- packages/client/src/user/index.ts | 4 ++-- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/client/src/actor/actor.ts b/packages/client/src/actor/actor.ts index 2c1bd13..cb72128 100644 --- a/packages/client/src/actor/actor.ts +++ b/packages/client/src/actor/actor.ts @@ -156,6 +156,29 @@ export class Actor { } } +// TODO: we can give this a better name +export class ActorWithProfileFunction extends Actor { + async profile() { + const data = await this.client + .get('app.bsky.actor.getProfile', { + params: { actor: this.did }, + }) + .then((res) => res.data); + + if ( + data.viewer?.knownFollowers?.followers && + data.viewer?.knownFollowers?.followers.length > 0 + ) { + data.viewer.knownFollowers.followers = + data.viewer.knownFollowers.followers.map( + (follower) => new BasicActorProfile(this.client, follower), + ); + } + + return data; + } +} + export class BasicActorProfile extends Actor implements AppBskyActorDefs.ProfileViewBasic diff --git a/packages/client/src/agent/agent.ts b/packages/client/src/agent/agent.ts index 8b2007c..c9e8772 100644 --- a/packages/client/src/agent/agent.ts +++ b/packages/client/src/agent/agent.ts @@ -5,7 +5,7 @@ import type { At, Queries, } from '@tsky/lexicons'; -import { DetailedActorProfile } from '~/actor'; +import { ActorWithProfileFunction } from '~/actor'; import { Feed } from '~/feed'; import { List } from '~/list'; import { Search } from '~/search'; @@ -30,12 +30,8 @@ export class Agent { /** * Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth. */ - async actor(identifier: At.DID | At.Handle): Promise { - const res = await this.client.get('app.bsky.actor.getProfile', { - params: { actor: identifier }, - }); - - return new DetailedActorProfile(this.client, res.data); + async actor(identifier: At.DID): Promise { + return new ActorWithProfileFunction(this.client, identifier); } list(uri: string) { diff --git a/packages/client/src/user/index.ts b/packages/client/src/user/index.ts index c55caf6..c47a4c0 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 '~/actor'; +import { ActorWithProfileFunction } from '~/actor'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; import { Mute } from './mute'; @@ -8,7 +8,7 @@ import { Preferences } from './preferences'; import { Suggestion } from './suggestion'; import { Unmute } from './unmute'; -export class User extends Actor { +export class User extends ActorWithProfileFunction { get preferences() { return new Preferences(this.client); } From 3ccc17a45d14c3b0ac3846f9632c2471a0e2b002 Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Mon, 3 Mar 2025 09:19:48 +0530 Subject: [PATCH 08/16] refactor: video job status refresh logic --- packages/client/src/video/video.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/client/src/video/video.ts b/packages/client/src/video/video.ts index a36ad64..849a5d4 100644 --- a/packages/client/src/video/video.ts +++ b/packages/client/src/video/video.ts @@ -72,16 +72,18 @@ class JobStatus { * Update status details for a video processing job. */ async refresh(options?: RPCOptions) { - const res = await this.client.get('app.bsky.video.getJobStatus', { - params: { jobId: this.jobId }, - ...options, - }); + const res = await this.client + .get('app.bsky.video.getJobStatus', { + params: { jobId: this.jobId }, + ...options, + }) + .then((res) => res.data.jobStatus); - this.state = res.data.jobStatus.state; + this.state = res.state; - this.progress = res.data.jobStatus.progress; - this.blob = res.data.jobStatus.blob; - this.error = res.data.jobStatus.error; - this.message = res.data.jobStatus.message; + this.progress = res.progress; + this.blob = res.blob; + this.error = res.error; + this.message = res.message; } } From 27646d1eb9c05f66f7c01ce7bfddbbed53b84d53 Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Mon, 3 Mar 2025 12:54:45 +0530 Subject: [PATCH 09/16] wip(client): connected the classes --- packages/client/src/actor/actor.ts | 26 ++++--- packages/client/src/agent/agent.ts | 56 +++++++++++++-- packages/client/src/feed/feed.ts | 71 +++++++------------ packages/client/src/feed/generator.ts | 35 +++++++++ packages/client/src/list/list.ts | 67 ++++++++++++++++- packages/client/src/post/post.ts | 6 +- .../client/src/starterpack/starterpack.ts | 29 ++++++-- packages/client/src/user/index.ts | 37 ++++++---- packages/client/src/user/muted/muted.ts | 46 +++++++----- .../client/src/user/suggestion/suggestion.ts | 65 +++++++++++------ 10 files changed, 319 insertions(+), 119 deletions(-) diff --git a/packages/client/src/actor/actor.ts b/packages/client/src/actor/actor.ts index cb72128..669a6ec 100644 --- a/packages/client/src/actor/actor.ts +++ b/packages/client/src/actor/actor.ts @@ -7,7 +7,8 @@ import type { ComAtprotoRepoStrongRef, } from '@tsky/lexicons'; import type { Client } from '~/agent/client'; -import { List } from '~/list'; +import { FeedGeneratorView } from '~/feed/generator'; +import { ListView } from '~/list'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; @@ -98,8 +99,7 @@ export class Actor { return { ...res.data, - // TODO: Solve this - // lists: res.data.lists.map((list) => new List(this.client, list)), + lists: res.data.lists.map((list) => new ListView(this.client, list)), }; }); } @@ -129,12 +129,19 @@ export class Actor { */ feeds(limit?: number, options?: RPCOptions) { return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getActorFeeds', { - params: { cursor, actor: this.did, limit }, - ...options, - }); + const res = await this.client + .get('app.bsky.feed.getActorFeeds', { + params: { cursor, actor: this.did, limit }, + ...options, + }) + .then((res) => res.data); - return res.data; + return { + ...res, + feeds: res.feeds.map( + (feed) => new FeedGeneratorView(this.client, feed), + ), + }; }); } @@ -183,8 +190,7 @@ export class BasicActorProfile extends Actor implements AppBskyActorDefs.ProfileViewBasic { - // @ts-expect-error - We added this property with Object.assign - handle: string; + handle!: string; associated?: AppBskyActorDefs.ProfileAssociated; avatar?: string; createdAt?: string; diff --git a/packages/client/src/agent/agent.ts b/packages/client/src/agent/agent.ts index c9e8772..c6f9be1 100644 --- a/packages/client/src/agent/agent.ts +++ b/packages/client/src/agent/agent.ts @@ -1,16 +1,19 @@ import { type CredentialManager, XRPC } from '@atcute/client'; import type { + AppBskyFeedGetFeed, + AppBskyFeedSendInteractions, AppBskyGraphGetStarterPack, AppBskyGraphGetStarterPacks, At, Queries, } from '@tsky/lexicons'; import { ActorWithProfileFunction } from '~/actor'; -import { Feed } from '~/feed'; -import { List } from '~/list'; +import { FeedViewPost } from '~/feed'; +import { Post } from '~/post'; import { Search } from '~/search'; import type { RPCOptions } from '~/types'; import { User } from '~/user'; +import { Paginator } from '~/utils'; import { Video } from '~/video'; import { Client } from './client'; @@ -34,12 +37,42 @@ export class Agent { return new ActorWithProfileFunction(this.client, identifier); } - list(uri: string) { - return new List(this.client, uri); + /** + * Get a hydrated feed from an actor's selected feed generator. Implemented by App View. + */ + async feed( + params: AppBskyFeedGetFeed.Params, + options?: AppBskyFeedGetFeed.Input, + ): Promise> { + return Paginator.init(async (cursor) => { + const res = await this.client.get('app.bsky.feed.getFeed', { + ...(options ?? {}), + params: { + cursor, + ...params, + }, + }); + + return { + ...res.data, + feed: res.data.feed.map((item) => new FeedViewPost(this.client, item)), + }; + }); } - get feed() { - return new Feed(this.client); + /** + * Send information about interactions with feed items back to the feed generator that served them. + */ + async sendInteractions( + interactions: AppBskyFeedSendInteractions.Input['interactions'], + options: RPCOptions = {}, + ) { + const res = await this.client.call('app.bsky.feed.sendInteractions', { + data: { interactions }, + ...options, + }); + + return res.data; } get search() { @@ -62,6 +95,17 @@ export class Agent { return new Video(this.client); } + async posts(uris: string[], options?: RPCOptions) { + const data = await this.client + .get('app.bsky.feed.getPosts', { + params: { uris }, + ...options, + }) + .then((res) => res.data); + + return data.posts.map((post) => new Post(this.client, post)); + } + /** * Gets a view of a starter pack. */ diff --git a/packages/client/src/feed/feed.ts b/packages/client/src/feed/feed.ts index 8da6749..44cd262 100644 --- a/packages/client/src/feed/feed.ts +++ b/packages/client/src/feed/feed.ts @@ -1,51 +1,34 @@ -import type { - AppBskyFeedGetFeed, - AppBskyFeedSendInteractions, -} from '@tsky/lexicons'; +import type { AppBskyFeedDefs, Typed } from '@tsky/lexicons'; +import { BasicActorProfile } from '~/actor'; import type { Client } from '~/agent/client'; -import type { RPCOptions } from '~/types'; -import { Paginator } from '~/utils'; -import { FeedGenerator } from './generator'; +import { Post } from '~/post'; -export class Feed { - constructor(private client: Client) {} +export class FeedViewPost implements AppBskyFeedDefs.FeedViewPost { + post: AppBskyFeedDefs.PostView; + feedContext?: string | undefined; + reason?: + | Typed + | Typed + | undefined; + reply?: AppBskyFeedDefs.ReplyRef | undefined; + $type?: string | undefined; - /** - * Get a hydrated feed from an actor's selected feed generator. Implemented by App View. - */ - async get( - params: AppBskyFeedGetFeed.Params, - options?: AppBskyFeedGetFeed.Input, - ): Promise> { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getFeed', { - ...(options ?? {}), - params: { - cursor, - ...params, - }, - }); - - return res.data; - }); - } - - /** - * Send information about interactions with feed items back to the feed generator that served them. - */ - async sendInteractions( - interactions: AppBskyFeedSendInteractions.Input['interactions'], - options: RPCOptions = {}, + constructor( + private client: Client, + payload: AppBskyFeedDefs.FeedViewPost, ) { - const res = await this.client.call('app.bsky.feed.sendInteractions', { - data: { interactions }, - ...options, - }); - - return res.data; - } + this.$type = payload.$type; + this.feedContext = payload.feedContext; + this.reason = payload.reason; + this.post = new Post(this.client, payload.post); - generator() { - return new FeedGenerator(this.client); + if (payload.reply) { + this.reply = { + ...payload.reply, + grandparentAuthor: payload.reply.grandparentAuthor + ? new BasicActorProfile(this.client, payload.reply.grandparentAuthor) + : undefined, + }; + } } } diff --git a/packages/client/src/feed/generator.ts b/packages/client/src/feed/generator.ts index 2c05d59..498bf0c 100644 --- a/packages/client/src/feed/generator.ts +++ b/packages/client/src/feed/generator.ts @@ -1,8 +1,13 @@ import type { + AppBskyActorDefs, + AppBskyFeedDefs, AppBskyFeedGetFeedGenerator, AppBskyFeedGetFeedGenerators, AppBskyFeedGetFeedSkeleton, + AppBskyRichtextFacet, + ComAtprotoLabelDefs, } from '@tsky/lexicons'; +import { BasicActorProfile } from '~/actor'; import type { Client } from '~/agent/client'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; @@ -74,3 +79,33 @@ export class FeedGenerator { }); } } + +export class FeedGeneratorView implements AppBskyFeedDefs.GeneratorView { + cid!: string; + creator: AppBskyActorDefs.ProfileView; + did!: `did:${string}`; + displayName!: string; + indexedAt!: string; + uri!: string; + acceptsInteractions?: boolean | undefined; + avatar?: string | undefined; + contentMode?: + | (string & {}) + | 'app.bsky.feed.defs#contentModeUnspecified' + | 'app.bsky.feed.defs#contentModeVideo' + | undefined; + description?: string | undefined; + descriptionFacets?: AppBskyRichtextFacet.Main[] | undefined; + labels?: ComAtprotoLabelDefs.Label[] | undefined; + likeCount?: number | undefined; + viewer?: AppBskyFeedDefs.GeneratorViewerState | undefined; + $type?: string | undefined; + + constructor( + private client: Client, + payload: AppBskyFeedDefs.GeneratorView, + ) { + Object.assign(this, payload); + this.creator = new BasicActorProfile(this.client, payload.creator); + } +} diff --git a/packages/client/src/list/list.ts b/packages/client/src/list/list.ts index 062fd1f..b387428 100644 --- a/packages/client/src/list/list.ts +++ b/packages/client/src/list/list.ts @@ -1,12 +1,19 @@ -import { ActorProfile } from '~/actor'; +import type { + AppBskyActorDefs, + AppBskyGraphDefs, + AppBskyRichtextFacet, + ComAtprotoLabelDefs, +} from '@tsky/lexicons'; +import { ActorProfile, BasicActorProfile } from '~/actor'; import type { Client } from '~/agent/client'; +import { Post } from '~/post'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; export class List { constructor( private client: Client, - private uri: string, + public uri: string, ) {} /** @@ -29,6 +36,7 @@ export class List { ...item, subject: new ActorProfile(this.client, item.subject), })), + list: new ListView(this.client, res.data.list), }; }); } @@ -47,7 +55,60 @@ export class List { ...options, }); - return res.data; + return { + ...res.data, + feed: res.data.feed.map((item) => ({ + ...item, + post: new Post(this.client, item.post), + reply: item.reply + ? { + ...item.reply, + grandparentAuthor: item.reply.grandparentAuthor + ? new BasicActorProfile( + this.client, + item.reply.grandparentAuthor, + ) + : undefined, + } + : undefined, + })), + }; }); } } + +export class ListBasicView + extends List + implements AppBskyGraphDefs.ListViewBasic +{ + cid!: string; + name!: string; + purpose!: AppBskyGraphDefs.ListPurpose; + avatar?: string | undefined; + indexedAt?: string | undefined; + labels?: ComAtprotoLabelDefs.Label[] | undefined; + listItemCount?: number | undefined; + viewer?: AppBskyGraphDefs.ListViewerState | undefined; + $type?: string | undefined; + + constructor(client: Client, payload: AppBskyGraphDefs.ListViewBasic) { + super(client, payload.uri); + Object.assign(this, payload); + } +} + +export class ListView + extends ListBasicView + implements AppBskyGraphDefs.ListView +{ + override indexedAt: string; + creator: AppBskyActorDefs.ProfileView; + description?: string | undefined; + descriptionFacets?: AppBskyRichtextFacet.Main[] | undefined; + + constructor(client: Client, payload: AppBskyGraphDefs.ListView) { + super(client, payload); + this.indexedAt = payload.indexedAt; + this.creator = new ActorProfile(client, payload.creator); + } +} diff --git a/packages/client/src/post/post.ts b/packages/client/src/post/post.ts index 1357867..7d1e24f 100644 --- a/packages/client/src/post/post.ts +++ b/packages/client/src/post/post.ts @@ -18,10 +18,10 @@ import type { RPCOptions } from '~/types'; import { Paginator, parseAtUri } from '~/utils'; export class Post implements AppBskyFeedDefs.PostView { - uri: string; + uri!: string; author: BasicActorProfile; - cid: string; - indexedAt: string; + cid!: string; + indexedAt!: string; record: unknown; embed?: | Typed diff --git a/packages/client/src/starterpack/starterpack.ts b/packages/client/src/starterpack/starterpack.ts index 6785ccc..21928ce 100644 --- a/packages/client/src/starterpack/starterpack.ts +++ b/packages/client/src/starterpack/starterpack.ts @@ -4,15 +4,17 @@ import type { AppBskyGraphDefs, ComAtprotoLabelDefs, } from '@tsky/lexicons'; -import { BasicActorProfile } from '~/actor'; +import { ActorProfile, BasicActorProfile } from '~/actor'; import type { Client } from '~/agent/client'; +import { FeedGeneratorView } from '~/feed/generator'; +import { ListBasicView } from '~/list'; class Starterpack { - cid: string; + cid!: string; creator: AppBskyActorDefs.ProfileViewBasic; - indexedAt: string; + indexedAt!: string; record: unknown; - uri: string; + uri!: string; joinedAllTimeCount?: number | undefined; joinedWeekCount?: number | undefined; labels?: ComAtprotoLabelDefs.Label[] | undefined; @@ -41,4 +43,23 @@ export class StarterPack feeds?: AppBskyFeedDefs.GeneratorView[]; list?: AppBskyGraphDefs.ListViewBasic; listItemsSample?: AppBskyGraphDefs.ListItemView[]; + + constructor(client: Client, payload: AppBskyGraphDefs.StarterPackView) { + super(client, payload); + + this.feeds = payload.feeds?.map( + (feed) => new FeedGeneratorView(client, feed), + ); + + if (payload.list) { + this.list = new ListBasicView(client, payload.list); + } + + if (payload.listItemsSample) { + this.listItemsSample = payload.listItemsSample.map((item) => ({ + ...item, + subject: new ActorProfile(client, item.subject), + })); + } + } } diff --git a/packages/client/src/user/index.ts b/packages/client/src/user/index.ts index c47a4c0..c15d967 100644 --- a/packages/client/src/user/index.ts +++ b/packages/client/src/user/index.ts @@ -1,5 +1,6 @@ import type { AppBskyFeedGetTimeline } from '@tsky/lexicons'; import { ActorWithProfileFunction } from '~/actor'; +import { FeedViewPost } from '~/feed'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; import { Mute } from './mute'; @@ -21,15 +22,20 @@ export class User extends ActorWithProfileFunction { options?: AppBskyFeedGetTimeline.Input, ): Promise> { return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getTimeline', { - ...(options ?? {}), - params: { - cursor, - ...params, - }, - }); + const res = await this.client + .get('app.bsky.feed.getTimeline', { + ...(options ?? {}), + params: { + cursor, + ...params, + }, + }) + .then((res) => res.data); - return res.data; + return { + ...res, + feed: res.feed.map((item) => new FeedViewPost(this.client, item)), + }; }); } @@ -38,12 +44,17 @@ export class User extends ActorWithProfileFunction { */ likes(limit?: number, options: RPCOptions = {}) { return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getActorLikes', { - params: { cursor, actor: this.did, limit }, - ...options, - }); + const res = await this.client + .get('app.bsky.feed.getActorLikes', { + params: { cursor, actor: this.did, limit }, + ...options, + }) + .then((res) => res.data); - return res.data; + return { + ...res, + feed: res.feed.map((item) => new FeedViewPost(this.client, item)), + }; }); } diff --git a/packages/client/src/user/muted/muted.ts b/packages/client/src/user/muted/muted.ts index 2a3f755..d7bd533 100644 --- a/packages/client/src/user/muted/muted.ts +++ b/packages/client/src/user/muted/muted.ts @@ -1,4 +1,6 @@ +import { DetailedActorProfile } from '~/actor'; import type { Client } from '~/agent/client'; +import { ListView } from '~/list'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; @@ -10,15 +12,20 @@ export class Muted { */ lists(limit?: number, options?: RPCOptions) { return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.graph.getListMutes', { - params: { - cursor, - limit, - }, - ...options, - }); + const res = await this.client + .get('app.bsky.graph.getListMutes', { + params: { + cursor, + limit, + }, + ...options, + }) + .then((res) => res.data); - return res.data; + return { + ...res, + lists: res.lists.map((list) => new ListView(this.client, list)), + }; }); } @@ -27,15 +34,22 @@ export class Muted { */ profiles(limit?: number, options?: RPCOptions) { return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.graph.getMutes', { - params: { - cursor, - limit, - }, - ...options, - }); + const res = await this.client + .get('app.bsky.graph.getMutes', { + params: { + cursor, + limit, + }, + ...options, + }) + .then((res) => res.data); - return res.data; + return { + ...res, + mutes: res.mutes.map( + (mute) => new DetailedActorProfile(this.client, mute), + ), + }; }); } } diff --git a/packages/client/src/user/suggestion/suggestion.ts b/packages/client/src/user/suggestion/suggestion.ts index 5aa10b8..34e1c89 100644 --- a/packages/client/src/user/suggestion/suggestion.ts +++ b/packages/client/src/user/suggestion/suggestion.ts @@ -1,4 +1,6 @@ +import { DetailedActorProfile } from '~/actor'; import type { Client } from '~/agent/client'; +import { FeedGeneratorView } from '~/feed/generator'; import type { RPCOptions } from '~/types'; import { Paginator } from '~/utils'; @@ -10,28 +12,44 @@ export class Suggestion { */ follow(limit?: number, options?: RPCOptions) { return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.actor.getSuggestions', { - params: { - cursor, - limit, - }, - ...options, - }); + const res = await this.client + .get('app.bsky.actor.getSuggestions', { + params: { + cursor, + limit, + }, + ...options, + }) + .then((res) => res.data); - return res.data; + return { + ...res, + actors: res.actors.map( + (actor) => new DetailedActorProfile(this.client, actor), + ), + }; }); } /** * Enumerates follows similar to a given account (actor). Expected use is to recommend additional accounts immediately after following one account. */ - afterFollowing(actor: string, options?: RPCOptions) { - return this.client.get('app.bsky.graph.getSuggestedFollowsByActor', { - params: { - actor, - }, - ...options, - }); + async afterFollowing(actor: string, options?: RPCOptions) { + const res = await this.client + .get('app.bsky.graph.getSuggestedFollowsByActor', { + params: { + actor, + }, + ...options, + }) + .then((res) => res.data); + + return { + ...res, + suggestions: res.suggestions.map( + (suggestion) => new DetailedActorProfile(this.client, suggestion), + ), + }; } /** @@ -39,12 +57,19 @@ export class Suggestion { */ feeds(limit?: number, options?: RPCOptions) { return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getSuggestedFeeds', { - params: { cursor, limit }, - ...options, - }); + const res = await this.client + .get('app.bsky.feed.getSuggestedFeeds', { + params: { cursor, limit }, + ...options, + }) + .then((res) => res.data); - return res.data; + return { + ...res, + feeds: res.feeds.map( + (feed) => new FeedGeneratorView(this.client, feed), + ), + }; }); } } From d2b9518ace952a99ac871b6fc42f3189f268dbf1 Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Mon, 3 Mar 2025 13:23:12 +0530 Subject: [PATCH 10/16] chore: minor changes --- packages/client/src/agent/client.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/client/src/agent/client.ts b/packages/client/src/agent/client.ts index 3ee3b3d..7a48c1f 100644 --- a/packages/client/src/agent/client.ts +++ b/packages/client/src/agent/client.ts @@ -89,7 +89,8 @@ export class Client { }, } as unknown as RPCOptions, ); - return response.data; + + return response.data as StrongRef; } /** @@ -120,7 +121,7 @@ export class Client { }, } as unknown as RPCOptions, ); - return response.data; + return response.data as StrongRef; } /** From 9f10ab682cc638c432d45b7d7c7b810dd532000d Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Tue, 4 Mar 2025 02:01:54 +0530 Subject: [PATCH 11/16] feat: connected the classes --- packages/client/package.json | 2 +- packages/client/src/actor/actor.ts | 236 --- packages/client/src/actor/index.ts | 1 - packages/client/src/agent.ts | 1261 +++++++++++++++++ packages/client/src/agent/agent.ts | 143 -- packages/client/src/agent/index.ts | 1 - packages/client/src/{agent => }/client.ts | 2 +- packages/client/src/feed/feed.test.ts | 36 - packages/client/src/feed/feed.ts | 34 - packages/client/src/feed/generator.ts | 111 -- packages/client/src/feed/index.ts | 1 - packages/client/src/list/index.ts | 1 - packages/client/src/list/list.ts | 114 -- packages/client/src/post/index.ts | 1 - packages/client/src/post/post.ts | 141 -- .../preferences => }/preferences.test.ts | 0 .../client/src/{user => }/profile.test.ts | 0 packages/client/src/search/index.ts | 1 - packages/client/src/search/search.ts | 55 - packages/client/src/starterpack/index.ts | 1 - .../client/src/starterpack/starterpack.ts | 65 - packages/client/src/{tsky => }/tsky.test.ts | 0 packages/client/src/{tsky => }/tsky.ts | 2 +- packages/client/src/tsky/index.ts | 1 - packages/client/src/tsky/types.ts | 10 - packages/client/src/types.ts | 11 + packages/client/src/user/index.ts | 76 - packages/client/src/user/mute/index.ts | 1 - packages/client/src/user/mute/mute.ts | 37 - packages/client/src/user/muted/index.ts | 1 - packages/client/src/user/muted/muted.ts | 55 - packages/client/src/user/preferences/index.ts | 1 - .../src/user/preferences/preferences.ts | 29 - packages/client/src/user/suggestion/index.ts | 1 - .../client/src/user/suggestion/suggestion.ts | 75 - packages/client/src/user/unmute/index.ts | 1 - packages/client/src/user/unmute/unmute.ts | 37 - packages/client/src/video/index.ts | 1 - packages/client/src/video/video.ts | 89 -- 39 files changed, 1275 insertions(+), 1360 deletions(-) delete mode 100644 packages/client/src/actor/actor.ts delete mode 100644 packages/client/src/actor/index.ts create mode 100644 packages/client/src/agent.ts delete mode 100644 packages/client/src/agent/agent.ts delete mode 100644 packages/client/src/agent/index.ts rename packages/client/src/{agent => }/client.ts (99%) delete mode 100644 packages/client/src/feed/feed.test.ts delete mode 100644 packages/client/src/feed/feed.ts delete mode 100644 packages/client/src/feed/generator.ts delete mode 100644 packages/client/src/feed/index.ts delete mode 100644 packages/client/src/list/index.ts delete mode 100644 packages/client/src/list/list.ts delete mode 100644 packages/client/src/post/index.ts delete mode 100644 packages/client/src/post/post.ts rename packages/client/src/{user/preferences => }/preferences.test.ts (100%) rename packages/client/src/{user => }/profile.test.ts (100%) delete mode 100644 packages/client/src/search/index.ts delete mode 100644 packages/client/src/search/search.ts delete mode 100644 packages/client/src/starterpack/index.ts delete mode 100644 packages/client/src/starterpack/starterpack.ts rename packages/client/src/{tsky => }/tsky.test.ts (100%) rename packages/client/src/{tsky => }/tsky.ts (91%) delete mode 100644 packages/client/src/tsky/index.ts delete mode 100644 packages/client/src/tsky/types.ts delete mode 100644 packages/client/src/user/index.ts delete mode 100644 packages/client/src/user/mute/index.ts delete mode 100644 packages/client/src/user/mute/mute.ts delete mode 100644 packages/client/src/user/muted/index.ts delete mode 100644 packages/client/src/user/muted/muted.ts delete mode 100644 packages/client/src/user/preferences/index.ts delete mode 100644 packages/client/src/user/preferences/preferences.ts delete mode 100644 packages/client/src/user/suggestion/index.ts delete mode 100644 packages/client/src/user/suggestion/suggestion.ts delete mode 100644 packages/client/src/user/unmute/index.ts delete mode 100644 packages/client/src/user/unmute/unmute.ts delete mode 100644 packages/client/src/video/index.ts delete mode 100644 packages/client/src/video/video.ts diff --git a/packages/client/package.json b/packages/client/package.json index 145abc8..02de143 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -44,4 +44,4 @@ "typescript": "catalog:", "vitest": "^3.0.6" } -} +} \ No newline at end of file diff --git a/packages/client/src/actor/actor.ts b/packages/client/src/actor/actor.ts deleted file mode 100644 index 669a6ec..0000000 --- a/packages/client/src/actor/actor.ts +++ /dev/null @@ -1,236 +0,0 @@ -import type { - AppBskyActorDefs, - AppBskyFeedGetAuthorFeed, - AppBskyGraphDefs, - At, - ComAtprotoLabelDefs, - ComAtprotoRepoStrongRef, -} from '@tsky/lexicons'; -import type { Client } from '~/agent/client'; -import { FeedGeneratorView } from '~/feed/generator'; -import { ListView } from '~/list'; -import type { RPCOptions } from '~/types'; -import { Paginator } from '~/utils'; - -export class Actor { - client: Client; - did: At.DID; - - constructor(client: Client, did: At.DID) { - this.client = client; - this.did = did; - } - - /** - * Get a list of starter packs created by the actor. - */ - starterPacks(limit?: number, options: RPCOptions = {}) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.graph.getActorStarterPacks', { - params: { cursor, actor: this.did, limit }, - ...options, - }); - - return res.data; - }); - } - - /** - * Enumerates accounts which follow a specified account (actor). - */ - followers(limit?: number, options: RPCOptions = {}) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.graph.getFollowers', { - params: { - cursor, - actor: this.did, - limit, - }, - ...options, - }); - - return { - ...res.data, - subject: new ActorProfile(this.client, res.data.subject), - followers: res.data.followers.map( - (follower) => new ActorProfile(this.client, follower), - ), - }; - }); - } - - /** - * Enumerates accounts which a specified account (actor) follows. - */ - follows(limit?: number, options: RPCOptions = {}) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.graph.getFollows', { - params: { - cursor, - actor: this.did, - limit, - }, - ...options, - }); - - return { - ...res.data, - subject: new ActorProfile(this.client, res.data.subject), - follows: res.data.follows.map( - (follow) => new ActorProfile(this.client, follow), - ), - }; - }); - } - - /** - * Enumerates the lists created by a specified account (actor). - */ - lists(limit?: number, options: RPCOptions = {}) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.graph.getLists', { - params: { - cursor, - actor: this.did, - limit, - }, - ...options, - }); - - return { - ...res.data, - lists: res.data.lists.map((list) => new ListView(this.client, list)), - }; - }); - } - - /** - * Enumerates public relationships between one account, and a list of other accounts. Does not require auth. - */ - async relationships(others?: string[], options?: RPCOptions) { - const res = await this.client.get('app.bsky.graph.getRelationships', { - params: { - actor: this.did, - others, - }, - ...options, - }); - - return { - ...res.data, - actor: res.data.actor - ? new Actor(this.client, res.data.actor) - : undefined, - }; - } - - /** - * Get a view of an actor's 'author feed' (post and reposts by the author). Does not require auth. - */ - feeds(limit?: number, options?: RPCOptions) { - return Paginator.init(async (cursor) => { - const res = await this.client - .get('app.bsky.feed.getActorFeeds', { - params: { cursor, actor: this.did, limit }, - ...options, - }) - .then((res) => res.data); - - return { - ...res, - feeds: res.feeds.map( - (feed) => new FeedGeneratorView(this.client, feed), - ), - }; - }); - } - - /** - * Get a list of feeds (feed generator records) created by the actor (in the actor's repo). - */ - feed( - params?: Omit, - options?: RPCOptions, - ) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getAuthorFeed', { - params: { cursor, ...params, actor: this.did }, - ...options, - }); - - return res.data; - }); - } -} - -// TODO: we can give this a better name -export class ActorWithProfileFunction extends Actor { - async profile() { - const data = await this.client - .get('app.bsky.actor.getProfile', { - params: { actor: this.did }, - }) - .then((res) => res.data); - - if ( - data.viewer?.knownFollowers?.followers && - data.viewer?.knownFollowers?.followers.length > 0 - ) { - data.viewer.knownFollowers.followers = - data.viewer.knownFollowers.followers.map( - (follower) => new BasicActorProfile(this.client, follower), - ); - } - - return data; - } -} - -export class BasicActorProfile - extends Actor - implements AppBskyActorDefs.ProfileViewBasic -{ - handle!: string; - associated?: AppBskyActorDefs.ProfileAssociated; - avatar?: string; - createdAt?: string; - displayName?: string; - labels?: ComAtprotoLabelDefs.Label[]; - viewer?: AppBskyActorDefs.ViewerState; - $type?: string; - - constructor(client: Client, actor: AppBskyActorDefs.ProfileViewBasic) { - super(client, actor.did); - Object.assign(this, actor); - } -} - -export class ActorProfile - extends BasicActorProfile - implements AppBskyActorDefs.ProfileView -{ - description?: string; - indexedAt?: string; - - constructor(client: Client, actor: AppBskyActorDefs.ProfileViewDetailed) { - super(client, actor); - Object.assign(this, actor); - } -} - -export class DetailedActorProfile - extends ActorProfile - implements AppBskyActorDefs.ProfileViewDetailed -{ - banner?: string; - followersCount?: number; - followsCount?: number; - joinedViaStarterPack?: AppBskyGraphDefs.StarterPackViewBasic; - pinnedPost?: ComAtprotoRepoStrongRef.Main; - postsCount?: number; - - constructor(client: Client, actor: AppBskyActorDefs.ProfileViewDetailed) { - super(client, actor); - Object.assign(this, actor); - } -} diff --git a/packages/client/src/actor/index.ts b/packages/client/src/actor/index.ts deleted file mode 100644 index bce1ce5..0000000 --- a/packages/client/src/actor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './actor'; diff --git a/packages/client/src/agent.ts b/packages/client/src/agent.ts new file mode 100644 index 0000000..8418e72 --- /dev/null +++ b/packages/client/src/agent.ts @@ -0,0 +1,1261 @@ +import { type CredentialManager, XRPC } from '@atcute/client'; +import type { + AppBskyActorDefs, + AppBskyActorPutPreferences, + AppBskyEmbedExternal, + AppBskyEmbedImages, + AppBskyEmbedRecord, + AppBskyEmbedRecordWithMedia, + AppBskyEmbedVideo, + AppBskyFeedDefs, + AppBskyFeedGetAuthorFeed, + AppBskyFeedGetFeed, + AppBskyFeedGetFeedGenerator, + AppBskyFeedGetFeedGenerators, + AppBskyFeedGetFeedSkeleton, + AppBskyFeedGetLikes, + AppBskyFeedGetPostThread, + AppBskyFeedGetQuotes, + AppBskyFeedGetRepostedBy, + AppBskyFeedGetTimeline, + AppBskyFeedSearchPosts, + AppBskyFeedSendInteractions, + AppBskyGraphDefs, + AppBskyGraphGetStarterPack, + AppBskyGraphGetStarterPacks, + AppBskyGraphSearchStarterPacks, + AppBskyRichtextFacet, + AppBskyVideoDefs, + AppBskyVideoUploadVideo, + At, + ComAtprotoLabelDefs, + Queries, + Typed, +} from '@tsky/lexicons'; +import { Client } from './client'; +import type { RPCOptions } from './types'; +import { Paginator, parseAtUri } from './utils'; + +export class Actor { + client: Client; + did: At.DID; + + constructor(client: Client, did: At.DID) { + this.client = client; + this.did = did; + } + + /** + * Get a list of starter packs created by the actor. + */ + starterPacks(limit?: number, options: RPCOptions = {}) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.graph.getActorStarterPacks', { + params: { cursor, actor: this.did, limit }, + ...options, + }) + .then((res) => res.data); + + data.starterPacks = data.starterPacks.map( + (starterPack) => new StarterpackBasicView(this.client, starterPack), + ); + + return data; + }); + } + + /** + * Enumerates accounts which follow a specified account (actor). + */ + followers(limit?: number, options: RPCOptions = {}) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.graph.getFollowers', { + params: { + cursor, + actor: this.did, + limit, + }, + ...options, + }) + .then((res) => res.data); + + data.subject = new ActorProfile(this.client, data.subject); + data.followers = data.followers.map( + (follower) => new ActorProfile(this.client, follower), + ); + + return data; + }); + } + + /** + * Enumerates accounts which a specified account (actor) follows. + */ + follows(limit?: number, options: RPCOptions = {}) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.graph.getFollows', { + params: { + cursor, + actor: this.did, + limit, + }, + ...options, + }) + .then((res) => res.data); + + data.subject = new ActorProfile(this.client, data.subject); + data.follows = data.follows.map( + (follow) => new ActorProfile(this.client, follow), + ); + + return data; + }); + } + + /** + * Enumerates the lists created by a specified account (actor). + */ + lists(limit?: number, options: RPCOptions = {}) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.graph.getLists', { + params: { + cursor, + actor: this.did, + limit, + }, + ...options, + }) + .then((res) => res.data); + + data.lists = data.lists.map((list) => new ListView(this.client, list)); + + return data; + }); + } + + /** + * Enumerates public relationships between one account, and a list of other accounts. Does not require auth. + */ + async relationships(others?: string[], options?: RPCOptions) { + const data = await this.client + .get('app.bsky.graph.getRelationships', { + params: { + actor: this.did, + others, + }, + ...options, + }) + .then((res) => res.data); + + return { + ...data, + actor: data.actor + ? new ActorLazyProfile(this.client, data.actor) + : undefined, + }; + } + + /** + * Get a view of an actor's 'author feed' (post and reposts by the author). Does not require auth. + */ + feeds(limit?: number, options?: RPCOptions) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.getActorFeeds', { + params: { cursor, actor: this.did, limit }, + ...options, + }) + .then((res) => res.data); + + data.feeds = data.feeds.map( + (feed) => new FeedGeneratorView(this.client, feed), + ); + + return data; + }); + } + + /** + * Get a list of feeds (feed generator records) created by the actor (in the actor's repo). + */ + feed( + params?: Omit, + options?: RPCOptions, + ) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.getAuthorFeed', { + params: { cursor, ...params, actor: this.did }, + ...options, + }) + .then((res) => res.data); + + data.feed = data.feed.map((item) => new FeedViewPost(this.client, item)); + + return data; + }); + } +} + +export class ActorLazyProfile extends Actor { + async profile() { + const data = await this.client + .get('app.bsky.actor.getProfile', { + params: { actor: this.did }, + }) + .then((res) => res.data); + + return new ActorProfile(this.client, data); + } +} + +export class ActorBasicProfile + extends Actor + implements AppBskyActorDefs.ProfileViewBasic +{ + handle: string; + associated?: AppBskyActorDefs.ProfileAssociated | undefined; + avatar?: string | undefined; + createdAt?: string | undefined; + displayName?: string | undefined; + labels?: ComAtprotoLabelDefs.Label[] | undefined; + viewer?: AppBskyActorDefs.ViewerState | undefined; + $type?: string | undefined; + + constructor(client: Client, actor: AppBskyActorDefs.ProfileViewBasic) { + super(client, actor.did); + this.handle = actor.handle; + this.associated = actor.associated; + this.avatar = actor.avatar; + this.createdAt = actor.createdAt; + this.displayName = actor.displayName; + this.labels = actor.labels; + this.$type = actor.$type; + + if (actor.viewer) { + this.viewer = actor.viewer; + + if (actor.viewer?.knownFollowers) { + actor.viewer.knownFollowers.followers = + actor.viewer.knownFollowers.followers.map( + (follower) => new ActorBasicProfile(client, follower), + ); + } + + if (actor.viewer?.blockingByList) { + actor.viewer.blockingByList = new ListBasicView( + client, + actor.viewer.blockingByList, + ); + } + } + } +} + +export class ActorProfile + extends ActorBasicProfile + implements AppBskyActorDefs.ProfileView +{ + description?: string; + indexedAt?: string; + + constructor(client: Client, actor: AppBskyActorDefs.ProfileViewDetailed) { + super(client, actor); + this.description = actor.description; + this.indexedAt = actor.indexedAt; + } +} + +export class List { + client: Client; + uri: string; + + constructor(client: Client, uri: string) { + this.client = client; + this.uri = uri; + } + + /** + * Gets a 'view' (with additional context) of a specified list. + */ + about(limit?: number, options?: RPCOptions) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.graph.getList', { + params: { + cursor, + list: this.uri, + limit, + }, + ...options, + }) + .then((res) => res.data); + + data.items = data.items.map((item) => { + item.subject = new ActorProfile(this.client, item.subject); + + return item; + }); + + data.list = new ListView(this.client, data.list); + + return data; + }); + } + + /** + * Get a feed of recent posts from a list (posts and reposts from any actors on the list). Does not require auth. + */ + feed(limit?: number, options?: RPCOptions) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.getListFeed', { + params: { + cursor, + list: this.uri, + limit, + }, + ...options, + }) + .then((res) => res.data); + + data.feed = data.feed.map((item) => new FeedViewPost(this.client, item)); + + return data; + }); + } +} + +export class ListBasicView + extends List + implements AppBskyGraphDefs.ListViewBasic +{ + cid: string; + name: string; + purpose: AppBskyGraphDefs.ListPurpose; + avatar?: string | undefined; + indexedAt?: string | undefined; + labels?: ComAtprotoLabelDefs.Label[] | undefined; + listItemCount?: number | undefined; + viewer?: AppBskyGraphDefs.ListViewerState | undefined; + $type?: string | undefined; + + constructor(client: Client, list: AppBskyGraphDefs.ListViewBasic) { + super(client, list.uri); + this.cid = list.cid; + this.name = list.name; + this.purpose = list.purpose; + this.avatar = list.avatar; + this.indexedAt = list.indexedAt; + this.labels = list.labels; + this.listItemCount = list.listItemCount; + this.viewer = list.viewer; + this.$type = list.$type; + } +} + +export class ListView + extends ListBasicView + implements AppBskyGraphDefs.ListView +{ + override indexedAt: string; + creator: AppBskyActorDefs.ProfileView; + description?: string | undefined; + descriptionFacets?: AppBskyRichtextFacet.Main[] | undefined; + + constructor(client: Client, list: AppBskyGraphDefs.ListView) { + super(client, list); + this.indexedAt = list.indexedAt; + this.creator = new ActorProfile(client, list.creator); + this.description = list.description; + this.descriptionFacets = list.descriptionFacets; + } +} + +export class Starterpack { + cid: string; + creator: AppBskyActorDefs.ProfileViewBasic; + indexedAt: string; + record: unknown; + uri: string; + joinedAllTimeCount?: number | undefined; + joinedWeekCount?: number | undefined; + labels?: ComAtprotoLabelDefs.Label[] | undefined; + $type?: string | undefined; + + constructor( + public client: Client, + payload: Omit, + ) { + this.cid = payload.cid; + this.creator = new ActorBasicProfile(this.client, payload.creator); + this.indexedAt = payload.indexedAt; + this.record = payload.record; + this.uri = payload.uri; + this.joinedAllTimeCount = payload.joinedAllTimeCount; + this.joinedWeekCount = payload.joinedWeekCount; + this.labels = payload.labels; + this.$type = payload.$type; + } +} + +export class StarterpackBasicView + extends Starterpack + implements AppBskyGraphDefs.StarterPackViewBasic +{ + listItemCount?: number | undefined; + + constructor(client: Client, payload: AppBskyGraphDefs.StarterPackViewBasic) { + super(client, payload); + this.listItemCount = payload.listItemCount; + } +} + +export class StarterpackView + extends Starterpack + implements AppBskyGraphDefs.StarterPackView +{ + feeds?: AppBskyFeedDefs.GeneratorView[] | undefined; + list?: AppBskyGraphDefs.ListViewBasic | undefined; + listItemsSample?: AppBskyGraphDefs.ListItemView[] | undefined; + + constructor(client: Client, payload: AppBskyGraphDefs.StarterPackView) { + super(client, payload); + this.feeds = payload.feeds?.map( + (feed) => new FeedGeneratorView(client, feed), + ); + + if (payload.list) { + this.list = new ListBasicView(client, payload.list); + } + + this.listItemsSample = payload.listItemsSample?.map((item) => { + item.subject = new ActorProfile(client, item.subject); + return item; + }); + } +} + +export class PostView implements AppBskyFeedDefs.PostView { + author: AppBskyActorDefs.ProfileViewBasic; + cid: string; + indexedAt: string; + record: unknown; + uri: string; + embed?: + | Typed + | Typed + | Typed + | Typed + | Typed + | undefined; + labels?: ComAtprotoLabelDefs.Label[] | undefined; + likeCount?: number | undefined; + quoteCount?: number | undefined; + replyCount?: number | undefined; + repostCount?: number | undefined; + threadgate?: AppBskyFeedDefs.ThreadgateView | undefined; + viewer?: AppBskyFeedDefs.ViewerState | undefined; + $type?: string | undefined; + + constructor( + public client: Client, + payload: AppBskyFeedDefs.PostView, + ) { + this.author = new ActorBasicProfile(this.client, payload.author); + this.cid = payload.cid; + this.indexedAt = payload.indexedAt; + this.record = payload.record; + this.uri = payload.uri; + this.embed = payload.embed; + this.labels = payload.labels; + this.likeCount = payload.likeCount; + this.quoteCount = payload.quoteCount; + this.replyCount = payload.replyCount; + this.repostCount = payload.repostCount; + this.threadgate = payload.threadgate; + this.viewer = payload.viewer; + this.$type = payload.$type; + } + + isOfCurrentUser() { + const { host: repo } = parseAtUri(this.uri); + return repo !== this.client.crenditials.session?.did; + } + + remove(options: RPCOptions = {}) { + return this.client.deleteRecord(this.uri, options); + } + + // TODO: method for liking, unliking, reposting, un-reposting, quoting, etc. + + /** + * Get posts in a thread. Does not require auth, but additional metadata and filtering will be applied for authed requests. + */ + async threads( + params: Omit = {}, + options: RPCOptions = {}, + ) { + return this.client + .get('app.bsky.feed.getPostThread', { + params: { uri: this.uri, ...params }, + ...options, + }) + .then((res) => res.data); + } + + /** + * Get like records which reference a subject (by AT-URI and CID). + */ + likes( + params: Omit = {}, + options: RPCOptions = {}, + ) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.getLikes', { + params: { cursor, uri: this.uri, ...params }, + ...options, + }) + .then((res) => res.data); + + data.likes = data.likes.map((like) => { + like.actor = new ActorBasicProfile(this.client, like.actor); + return like; + }); + + return data; + }); + } + + /** + * Get a list of quotes for a given post. + */ + quotes( + params: Omit = {}, + options: RPCOptions = {}, + ) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.getQuotes', { + params: { cursor, uri: this.uri, ...params }, + ...options, + }) + .then((res) => res.data); + + data.posts = data.posts.map((post) => new PostView(this.client, post)); + + return data; + }); + } + + /** + * Get a list of reposts for a given post. + */ + repostedBy( + params: Omit = {}, + options: RPCOptions = {}, + ) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.getRepostedBy', { + params: { cursor, uri: this.uri, ...params }, + ...options, + }) + .then((res) => res.data); + + data.repostedBy = data.repostedBy.map( + (repost) => new ActorProfile(this.client, repost), + ); + + return data; + }); + } + + /** + * Gets post views for a specified list of posts (by AT-URI). This is sometimes referred to as 'hydrating' a 'feed skeleton'. + */ + static async getMany( + client: Client, + posts: string[], + options: RPCOptions = {}, + ) { + const data = await client + .get('app.bsky.feed.getPosts', { + params: { uris: posts }, + ...options, + }) + .then((res) => res.data); + + data.posts = data.posts.map((post) => new PostView(client, post)); + + return data; + } +} + +export class Search { + constructor(private client: Client) {} + + /** + * Find posts matching search criteria, returning views of those posts. + */ + posts(params: AppBskyFeedSearchPosts.Params, options: RPCOptions = {}) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.searchPosts', { + params: { cursor, ...params }, + ...options, + }) + .then((res) => res.data); + + data.posts = data.posts.map((post) => new PostView(this.client, post)); + + return data; + }); + } + + /** + * Search for starter packs. + */ + starterpacks( + params: AppBskyGraphSearchStarterPacks.Params, + options?: RPCOptions, + ) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.graph.searchStarterPacks', { + params: { + cursor, + ...params, + }, + ...options, + }) + .then((res) => res.data); + + data.starterPacks = data.starterPacks.map( + (starterPack) => new StarterpackBasicView(this.client, starterPack), + ); + + return data; + }); + } +} + +export class FeedViewPost implements AppBskyFeedDefs.FeedViewPost { + post: AppBskyFeedDefs.PostView; + feedContext?: string | undefined; + reason?: + | Typed + | Typed + | undefined; + reply?: AppBskyFeedDefs.ReplyRef | undefined; + $type?: string | undefined; + + constructor( + public client: Client, + payload: AppBskyFeedDefs.FeedViewPost, + ) { + this.$type = payload.$type; + this.feedContext = payload.feedContext; + this.reason = payload.reason; + this.post = new PostView(this.client, payload.post); + + if (payload.reply) { + this.reply = { + ...payload.reply, + grandparentAuthor: payload.reply.grandparentAuthor + ? new ActorBasicProfile(this.client, payload.reply.grandparentAuthor) + : undefined, + }; + } + } +} + +export class FeedGenerator { + constructor(public client: Client) {} + + /** + * Get information about a feed generator, including policies and offered feed URIs. Does not require auth; implemented by Feed Generator services (not App View). + */ + async describe(options: RPCOptions = {}) { + return this.client + .get('app.bsky.feed.describeFeedGenerator', options) + .then((res) => res.data); + } + + /** + * Get information about a feed generator. Implemented by AppView. + */ + feed( + feed: string, + options: RPCOptions, + ): Promise; + /** + * Get information about a list of feed generators. + */ + feed( + feeds: string[], + options: RPCOptions, + ): Promise; + + async feed(feed: string | string[], options: RPCOptions) { + if (Array.isArray(feed)) { + const data = await this.client + .get('app.bsky.feed.getFeedGenerators', { + params: { + feeds: feed, + }, + ...options, + }) + .then((res) => res.data); + + return data.feeds; + } + + return this.client + .get('app.bsky.feed.getFeedGenerator', { + params: { feed }, + ...options, + }) + .then((res) => res.data); + } + + /** + * Get a skeleton of a feed provided by a feed generator. Auth is optional, depending on provider requirements, and provides the DID of the requester. Implemented by Feed Generator Service. + */ + skeleton( + params: AppBskyFeedGetFeedSkeleton.Params, + options: RPCOptions = {}, + ) { + return Paginator.init(async (cursor) => { + return this.client + .get('app.bsky.feed.getFeedSkeleton', { + params: { cursor, ...params }, + ...options, + }) + .then((res) => res.data); + }); + } +} + +export class FeedGeneratorView implements AppBskyFeedDefs.GeneratorView { + cid: string; + creator: AppBskyActorDefs.ProfileView; + did: At.DID; + displayName: string; + indexedAt: string; + uri: string; + acceptsInteractions?: boolean | undefined; + avatar?: string | undefined; + contentMode?: + | (string & {}) + | 'app.bsky.feed.defs#contentModeUnspecified' + | 'app.bsky.feed.defs#contentModeVideo' + | undefined; + description?: string | undefined; + descriptionFacets?: AppBskyRichtextFacet.Main[] | undefined; + labels?: ComAtprotoLabelDefs.Label[] | undefined; + likeCount?: number | undefined; + viewer?: AppBskyFeedDefs.GeneratorViewerState | undefined; + $type?: string | undefined; + + constructor( + public client: Client, + payload: AppBskyFeedDefs.GeneratorView, + ) { + this.cid = payload.cid; + this.creator = new ActorProfile(this.client, payload.creator); + this.did = payload.did; + this.displayName = payload.displayName; + this.indexedAt = payload.indexedAt; + this.uri = payload.uri; + this.acceptsInteractions = payload.acceptsInteractions; + this.avatar = payload.avatar; + this.contentMode = payload.contentMode; + this.description = payload.description; + this.descriptionFacets = payload.descriptionFacets; + this.labels = payload.labels; + this.likeCount = payload.likeCount; + this.viewer = payload.viewer; + this.$type = payload.$type; + } +} + +export class Preferences { + constructor(public client: Client) {} + + /** + * Get private preferences attached to the current account. Expected use is synchronization between multiple devices, and import/export during account migration. Requires auth. + */ + async get(options: RPCOptions = {}) { + const res = await this.client.get('app.bsky.actor.getPreferences', options); + + return res.data.preferences; + } + + /** + * Set the private preferences attached to the account. + */ + async set( + preferences: AppBskyActorPutPreferences.Input['preferences'], + options: RPCOptions = {}, + ) { + await this.client.call('app.bsky.actor.putPreferences', { + data: { preferences }, + ...options, + }); + } +} + +export class Muted { + constructor(public client: Client) {} + + /** + * Enumerates mod lists that the requesting account (actor) currently has muted. Requires auth. + */ + lists(limit?: number, options?: RPCOptions) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.graph.getListMutes', { + params: { + cursor, + limit, + }, + ...options, + }) + .then((res) => res.data); + + data.lists = data.lists.map((list) => new ListView(this.client, list)); + + return data; + }); + } + + /** + * Enumerates accounts that the requesting account (actor) currently has muted. Requires auth. + */ + profiles(limit?: number, options?: RPCOptions) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.graph.getMutes', { + params: { + cursor, + limit, + }, + ...options, + }) + .then((res) => res.data); + + data.mutes = data.mutes.map( + (mute) => new ActorProfile(this.client, mute), + ); + + return data; + }); + } +} + +export class Suggestion { + constructor(private client: Client) {} + + /** + * Get a list of suggested actors. Expected use is discovery of accounts to follow during new account onboarding. + */ + follow(limit?: number, options?: RPCOptions) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.actor.getSuggestions', { + params: { + cursor, + limit, + }, + ...options, + }) + .then((res) => res.data); + + data.actors = data.actors.map( + (actor) => new ActorProfile(this.client, actor), + ); + + return data; + }); + } + + /** + * Enumerates follows similar to a given account (actor). Expected use is to recommend additional accounts immediately after following one account. + */ + async afterFollowing(actor: string, options?: RPCOptions) { + const data = await this.client + .get('app.bsky.graph.getSuggestedFollowsByActor', { + params: { + actor, + }, + ...options, + }) + .then((res) => res.data); + + data.suggestions = data.suggestions.map( + (suggestion) => new ActorProfile(this.client, suggestion), + ); + + return data; + } + + /** + * Get a list of suggested feeds (feed generators) for the requesting account. + */ + feeds(limit?: number, options?: RPCOptions) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.getSuggestedFeeds', { + params: { cursor, limit }, + ...options, + }) + .then((res) => res.data); + + data.feeds = data.feeds.map( + (feed) => new FeedGeneratorView(this.client, feed), + ); + + return data; + }); + } +} + +export class User extends ActorLazyProfile { + get preferences() { + return new Preferences(this.client); + } + + /** + * Get a view of the requesting account's home timeline. This is expected to be some form of reverse-chronological feed. + */ + timeline( + params: AppBskyFeedGetTimeline.Params, + options?: AppBskyFeedGetTimeline.Input, + ): Promise> { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.getTimeline', { + ...(options ?? {}), + params: { + cursor, + ...params, + }, + }) + .then((res) => res.data); + + data.feed = data.feed.map((item) => new FeedViewPost(this.client, item)); + + return data; + }); + } + + /** + * Get a list of posts liked by the current user + */ + likes(limit?: number, options: RPCOptions = {}) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.getActorLikes', { + params: { cursor, actor: this.did, limit }, + ...options, + }) + .then((res) => res.data); + + data.feed = data.feed.map((item) => new FeedViewPost(this.client, item)); + + return data; + }); + } + + get muted() { + return new Muted(this.client); + } + + get suggestion() { + return new Suggestion(this.client); + } + + /** + * Creates a mute relationship for the specified account. Mutes are private in Bluesky. + */ + muteActor(identifier: string, options: RPCOptions = {}) { + return this.client.call('app.bsky.graph.muteActor', { + data: { actor: identifier }, + ...options, + }); + } + + /** + * Unmutes the specified account. + */ + unmuteActor(identifier: string, options: RPCOptions = {}) { + return this.client.call('app.bsky.graph.unmuteActor', { + data: { actor: identifier }, + ...options, + }); + } + + /** + * Mutes a thread preventing notifications from the thread and any of its children. Mutes are private in Bluesky. + */ + muteThread(identifier: string, options: RPCOptions = {}) { + return this.client.call('app.bsky.graph.muteThread', { + data: { root: identifier }, + ...options, + }); + } + + /** + * Unmutes the specified thread. + */ + unmuteThread(identifier: string, options: RPCOptions = {}) { + return this.client.call('app.bsky.graph.unmuteThread', { + data: { root: identifier }, + ...options, + }); + } + + /** + * Mute an entire list (specified by AT-URI) of actors. This creates a mute relationship for all actors + * on the specified list. Mutes are private on Bluesky. + */ + muteActorList(identifier: string, options: RPCOptions = {}) { + return this.client.call('app.bsky.graph.muteActorList', { + data: { list: identifier }, + ...options, + }); + } + + /** + * Unmute an entire list (specified by AT-URI) of actors. This removes the mute relationship for all actors + * on the specified list. + */ + unmuteActorList(identifier: string, options: RPCOptions = {}) { + return this.client.call('app.bsky.graph.unmuteActorList', { + data: { list: identifier }, + ...options, + }); + } +} + +export class Video { + constructor(private client: Client) {} + + /** + * Get video upload limits for the authenticated user. + */ + async limit(options: RPCOptions = {}) { + const res = await this.client.get( + 'app.bsky.video.getUploadLimits', + options, + ); + + return res.data; + } + + /** + * Get status details for a video processing job. + */ + async status(jobId: string, options?: RPCOptions) { + const res = await this.client.get('app.bsky.video.getJobStatus', { + params: { jobId }, + ...options, + }); + + return new JobStatus(this.client, res.data.jobStatus); + } + + /** + * Upload a video to be processed then stored on the PDS. + */ + async upload(data: AppBskyVideoUploadVideo.Input, options?: RPCOptions) { + const res = await this.client.call('app.bsky.video.uploadVideo', { + data, + ...options, + }); + + return new JobStatus(this.client, res.data.jobStatus); + } +} + +class JobStatus { + jobId: string; + did: string; + /** The state of the video processing job. All values not listed as a known value indicate that the job is in process. */ + state: 'JOB_STATE_COMPLETED' | 'JOB_STATE_FAILED' | (string & {}); + /** Progress within the current processing state. */ + progress?: number; + blob?: AppBskyVideoDefs.JobStatus['blob']; + error?: string; + message?: string; + + constructor( + private client: Client, + data: AppBskyVideoDefs.JobStatus, + ) { + this.jobId = data.jobId; + this.did = data.did; + + this.state = data.state; + + this.progress = data.progress; + this.blob = data.blob; + this.error = data.error; + this.message = data.message; + } + + /** + * Update status details for a video processing job. + */ + async refresh(options?: RPCOptions) { + const res = await this.client + .get('app.bsky.video.getJobStatus', { + params: { jobId: this.jobId }, + ...options, + }) + .then((res) => res.data.jobStatus); + + this.state = res.state; + + this.progress = res.progress; + this.blob = res.blob; + this.error = res.error; + this.message = res.message; + } +} + +export class Agent { + client: Client; + + constructor(private handler: CredentialManager) { + // Initialize the client + const xrpc = new XRPC({ handler: this.handler }); + this.client = new Client(xrpc, this.handler); + } + + get session() { + return this.handler.session; + } + + /** + * Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth. + */ + async actor(identifier: At.DID) { + return new ActorLazyProfile(this.client, identifier); + } + + /** + * Get a hydrated feed from an actor's selected feed generator. Implemented by App View. + */ + async feed( + params: AppBskyFeedGetFeed.Params, + options?: AppBskyFeedGetFeed.Input, + ) { + return Paginator.init(async (cursor) => { + const data = await this.client + .get('app.bsky.feed.getFeed', { + ...(options ?? {}), + params: { + cursor, + ...params, + }, + }) + .then((res) => res.data); + + data.feed = data.feed.map((item) => new FeedViewPost(this.client, item)); + + return data; + }); + } + + /** + * Send information about interactions with feed items back to the feed generator that served them. + */ + async sendInteractions( + interactions: AppBskyFeedSendInteractions.Input['interactions'], + options: RPCOptions = {}, + ) { + return this.client + .call('app.bsky.feed.sendInteractions', { + data: { interactions }, + ...options, + }) + .then((res) => res.data); + } + + get search() { + return new Search(this.client); + } + + get user() { + if (!this.session) { + throw new Error('There is no active session'); + } + + return new User(this.client, this.session.did); + } + + get video() { + if (!this.session) { + throw new Error('There is no active session'); + } + + return new Video(this.client); + } + + async posts(uris: string[], options?: RPCOptions) { + const data = await this.client + .get('app.bsky.feed.getPosts', { + params: { uris }, + ...options, + }) + .then((res) => res.data); + + return data.posts.map((post) => new PostView(this.client, post)); + } + + /** + * Gets a view of a starter pack. + */ + startpacks( + uri: string, + options?: RPCOptions, + ): Promise; + /** + * Get views for a list of starter packs. + */ + startpacks( + uris: string[], + options?: RPCOptions, + ): Promise; + + async startpacks(uris: string | string[], options: RPCOptions = {}) { + if (Array.isArray(uris)) { + const data = await this.client + .get('app.bsky.graph.getStarterPacks', { + params: { + uris, + }, + ...options, + }) + .then((res) => res.data); + + return data.starterPacks; + } + + const data = await this.client + .get('app.bsky.graph.getStarterPack', { + params: { starterPack: uris }, + ...options, + }) + .then((res) => res.data); + + return data; + } +} diff --git a/packages/client/src/agent/agent.ts b/packages/client/src/agent/agent.ts deleted file mode 100644 index c6f9be1..0000000 --- a/packages/client/src/agent/agent.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { type CredentialManager, XRPC } from '@atcute/client'; -import type { - AppBskyFeedGetFeed, - AppBskyFeedSendInteractions, - AppBskyGraphGetStarterPack, - AppBskyGraphGetStarterPacks, - At, - Queries, -} from '@tsky/lexicons'; -import { ActorWithProfileFunction } from '~/actor'; -import { FeedViewPost } from '~/feed'; -import { Post } from '~/post'; -import { Search } from '~/search'; -import type { RPCOptions } from '~/types'; -import { User } from '~/user'; -import { Paginator } from '~/utils'; -import { Video } from '~/video'; -import { Client } from './client'; - -export class Agent { - client: Client; - - constructor(private handler: CredentialManager) { - // Initialize the client - const xrpc = new XRPC({ handler: this.handler }); - this.client = new Client(xrpc, this.handler); - } - - get session() { - return this.handler.session; - } - - /** - * Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth. - */ - async actor(identifier: At.DID): Promise { - return new ActorWithProfileFunction(this.client, identifier); - } - - /** - * Get a hydrated feed from an actor's selected feed generator. Implemented by App View. - */ - async feed( - params: AppBskyFeedGetFeed.Params, - options?: AppBskyFeedGetFeed.Input, - ): Promise> { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getFeed', { - ...(options ?? {}), - params: { - cursor, - ...params, - }, - }); - - return { - ...res.data, - feed: res.data.feed.map((item) => new FeedViewPost(this.client, item)), - }; - }); - } - - /** - * Send information about interactions with feed items back to the feed generator that served them. - */ - async sendInteractions( - interactions: AppBskyFeedSendInteractions.Input['interactions'], - options: RPCOptions = {}, - ) { - const res = await this.client.call('app.bsky.feed.sendInteractions', { - data: { interactions }, - ...options, - }); - - return res.data; - } - - get search() { - return new Search(this.client); - } - - get user() { - if (!this.session) { - throw new Error('There is no active session'); - } - - return new User(this.client, this.session.did); - } - - get video() { - if (!this.session) { - throw new Error('There is no active session'); - } - - return new Video(this.client); - } - - async posts(uris: string[], options?: RPCOptions) { - const data = await this.client - .get('app.bsky.feed.getPosts', { - params: { uris }, - ...options, - }) - .then((res) => res.data); - - return data.posts.map((post) => new Post(this.client, post)); - } - - /** - * Gets a view of a starter pack. - */ - startpacks( - uri: string, - options?: RPCOptions, - ): Promise; - /** - * Get views for a list of starter packs. - */ - startpacks( - uris: string[], - options?: RPCOptions, - ): Promise; - - async startpacks(uris: string | string[], options: RPCOptions = {}) { - if (Array.isArray(uris)) { - const res = await this.client.get('app.bsky.graph.getStarterPacks', { - params: { - uris, - }, - ...options, - }); - - return res.data.starterPacks; - } - - const res = await this.client.get('app.bsky.graph.getStarterPack', { - params: { starterPack: uris }, - ...options, - }); - - return res.data; - } -} diff --git a/packages/client/src/agent/index.ts b/packages/client/src/agent/index.ts deleted file mode 100644 index bf1c0a4..0000000 --- a/packages/client/src/agent/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './agent'; diff --git a/packages/client/src/agent/client.ts b/packages/client/src/client.ts similarity index 99% rename from packages/client/src/agent/client.ts rename to packages/client/src/client.ts index 7a48c1f..cff0007 100644 --- a/packages/client/src/agent/client.ts +++ b/packages/client/src/client.ts @@ -7,7 +7,7 @@ import type { } from '@atcute/client'; import type { At, Procedures, Queries } from '@tsky/lexicons'; import { parseAtUri } from '~/utils'; -import type { RPCOptions as GenericReqOptions, StrongRef } from '../types'; +import type { RPCOptions as GenericReqOptions, StrongRef } from './types'; // From @atcute/client type OutputOf = T extends { diff --git a/packages/client/src/feed/feed.test.ts b/packages/client/src/feed/feed.test.ts deleted file mode 100644 index 99a7480..0000000 --- a/packages/client/src/feed/feed.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { createAgent } from '~/tsky'; - -const TEST_CREDENTIALS = { - alice: { - handle: 'alice.tsky.dev', - did: 'did:plc:jguhdmnjclquqf5lsvkyxqy3', - password: 'alice_and_bob', - }, - bob: { - handle: 'bob.tsky.dev', - did: 'did:plc:2ig7akkyfq256j42uxvc4g2h', - password: 'alice_and_bob', - }, -}; - -describe('feed', () => { - it('.getFeed()', async () => { - 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', - limit: 30, - }); - expect(paginator).toBeDefined(); - expect(paginator.values).toBeDefined(); - expect(paginator.values).toBeInstanceOf(Array); - expect(paginator.values.length).toBe(1); // we should get the first page from the paginator - expect(paginator.values[0].feed.length).toBeGreaterThan(0); // we found some birds posts ;) - expect(paginator.values[0].feed[0]).toHaveProperty('post'); - }); -}); diff --git a/packages/client/src/feed/feed.ts b/packages/client/src/feed/feed.ts deleted file mode 100644 index 44cd262..0000000 --- a/packages/client/src/feed/feed.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { AppBskyFeedDefs, Typed } from '@tsky/lexicons'; -import { BasicActorProfile } from '~/actor'; -import type { Client } from '~/agent/client'; -import { Post } from '~/post'; - -export class FeedViewPost implements AppBskyFeedDefs.FeedViewPost { - post: AppBskyFeedDefs.PostView; - feedContext?: string | undefined; - reason?: - | Typed - | Typed - | undefined; - reply?: AppBskyFeedDefs.ReplyRef | undefined; - $type?: string | undefined; - - constructor( - private client: Client, - payload: AppBskyFeedDefs.FeedViewPost, - ) { - this.$type = payload.$type; - this.feedContext = payload.feedContext; - this.reason = payload.reason; - this.post = new Post(this.client, payload.post); - - if (payload.reply) { - this.reply = { - ...payload.reply, - grandparentAuthor: payload.reply.grandparentAuthor - ? new BasicActorProfile(this.client, payload.reply.grandparentAuthor) - : undefined, - }; - } - } -} diff --git a/packages/client/src/feed/generator.ts b/packages/client/src/feed/generator.ts deleted file mode 100644 index 498bf0c..0000000 --- a/packages/client/src/feed/generator.ts +++ /dev/null @@ -1,111 +0,0 @@ -import type { - AppBskyActorDefs, - AppBskyFeedDefs, - AppBskyFeedGetFeedGenerator, - AppBskyFeedGetFeedGenerators, - AppBskyFeedGetFeedSkeleton, - AppBskyRichtextFacet, - ComAtprotoLabelDefs, -} from '@tsky/lexicons'; -import { BasicActorProfile } from '~/actor'; -import type { Client } from '~/agent/client'; -import type { RPCOptions } from '~/types'; -import { Paginator } from '~/utils'; - -export class FeedGenerator { - constructor(private client: Client) {} - - /** - * Get information about a feed generator, including policies and offered feed URIs. Does not require auth; implemented by Feed Generator services (not App View). - */ - async describe(options: RPCOptions = {}) { - const res = await this.client.get( - 'app.bsky.feed.describeFeedGenerator', - options, - ); - - return res.data; - } - - /** - * Get information about a feed generator. Implemented by AppView. - */ - feed( - feed: string, - options: RPCOptions, - ): Promise; - /** - * Get information about a list of feed generators. - */ - feed( - feeds: string[], - options: RPCOptions, - ): Promise; - - async feed(feed: string | string[], options: RPCOptions) { - if (Array.isArray(feed)) { - const res = await this.client.get('app.bsky.feed.getFeedGenerators', { - params: { - feeds: feed, - }, - ...options, - }); - - return res.data.feeds; - } - - const res = await this.client.get('app.bsky.feed.getFeedGenerator', { - params: { feed }, - ...options, - }); - - return res.data; - } - - /** - * Get a skeleton of a feed provided by a feed generator. Auth is optional, depending on provider requirements, and provides the DID of the requester. Implemented by Feed Generator Service. - */ - skeleton( - params: AppBskyFeedGetFeedSkeleton.Params, - options: RPCOptions = {}, - ) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getFeedSkeleton', { - params: { cursor, ...params }, - ...options, - }); - - return res.data; - }); - } -} - -export class FeedGeneratorView implements AppBskyFeedDefs.GeneratorView { - cid!: string; - creator: AppBskyActorDefs.ProfileView; - did!: `did:${string}`; - displayName!: string; - indexedAt!: string; - uri!: string; - acceptsInteractions?: boolean | undefined; - avatar?: string | undefined; - contentMode?: - | (string & {}) - | 'app.bsky.feed.defs#contentModeUnspecified' - | 'app.bsky.feed.defs#contentModeVideo' - | undefined; - description?: string | undefined; - descriptionFacets?: AppBskyRichtextFacet.Main[] | undefined; - labels?: ComAtprotoLabelDefs.Label[] | undefined; - likeCount?: number | undefined; - viewer?: AppBskyFeedDefs.GeneratorViewerState | undefined; - $type?: string | undefined; - - constructor( - private client: Client, - payload: AppBskyFeedDefs.GeneratorView, - ) { - Object.assign(this, payload); - this.creator = new BasicActorProfile(this.client, payload.creator); - } -} diff --git a/packages/client/src/feed/index.ts b/packages/client/src/feed/index.ts deleted file mode 100644 index 0b2a3ad..0000000 --- a/packages/client/src/feed/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './feed'; diff --git a/packages/client/src/list/index.ts b/packages/client/src/list/index.ts deleted file mode 100644 index 7182513..0000000 --- a/packages/client/src/list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './list'; diff --git a/packages/client/src/list/list.ts b/packages/client/src/list/list.ts deleted file mode 100644 index b387428..0000000 --- a/packages/client/src/list/list.ts +++ /dev/null @@ -1,114 +0,0 @@ -import type { - AppBskyActorDefs, - AppBskyGraphDefs, - AppBskyRichtextFacet, - ComAtprotoLabelDefs, -} from '@tsky/lexicons'; -import { ActorProfile, BasicActorProfile } from '~/actor'; -import type { Client } from '~/agent/client'; -import { Post } from '~/post'; -import type { RPCOptions } from '~/types'; -import { Paginator } from '~/utils'; - -export class List { - constructor( - private client: Client, - public uri: string, - ) {} - - /** - * Gets a 'view' (with additional context) of a specified list. - */ - about(limit?: number, options?: RPCOptions) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.graph.getList', { - params: { - cursor, - list: this.uri, - limit, - }, - ...options, - }); - - return { - ...res.data, - items: res.data.items.map((item) => ({ - ...item, - subject: new ActorProfile(this.client, item.subject), - })), - list: new ListView(this.client, res.data.list), - }; - }); - } - - /** - * Get a feed of recent posts from a list (posts and reposts from any actors on the list). Does not require auth. - */ - feed(limit?: number, options?: RPCOptions) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getListFeed', { - params: { - cursor, - list: this.uri, - limit, - }, - ...options, - }); - - return { - ...res.data, - feed: res.data.feed.map((item) => ({ - ...item, - post: new Post(this.client, item.post), - reply: item.reply - ? { - ...item.reply, - grandparentAuthor: item.reply.grandparentAuthor - ? new BasicActorProfile( - this.client, - item.reply.grandparentAuthor, - ) - : undefined, - } - : undefined, - })), - }; - }); - } -} - -export class ListBasicView - extends List - implements AppBskyGraphDefs.ListViewBasic -{ - cid!: string; - name!: string; - purpose!: AppBskyGraphDefs.ListPurpose; - avatar?: string | undefined; - indexedAt?: string | undefined; - labels?: ComAtprotoLabelDefs.Label[] | undefined; - listItemCount?: number | undefined; - viewer?: AppBskyGraphDefs.ListViewerState | undefined; - $type?: string | undefined; - - constructor(client: Client, payload: AppBskyGraphDefs.ListViewBasic) { - super(client, payload.uri); - Object.assign(this, payload); - } -} - -export class ListView - extends ListBasicView - implements AppBskyGraphDefs.ListView -{ - override indexedAt: string; - creator: AppBskyActorDefs.ProfileView; - description?: string | undefined; - descriptionFacets?: AppBskyRichtextFacet.Main[] | undefined; - - constructor(client: Client, payload: AppBskyGraphDefs.ListView) { - super(client, payload); - this.indexedAt = payload.indexedAt; - this.creator = new ActorProfile(client, payload.creator); - } -} diff --git a/packages/client/src/post/index.ts b/packages/client/src/post/index.ts deleted file mode 100644 index 336abe1..0000000 --- a/packages/client/src/post/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './post'; diff --git a/packages/client/src/post/post.ts b/packages/client/src/post/post.ts deleted file mode 100644 index 7d1e24f..0000000 --- a/packages/client/src/post/post.ts +++ /dev/null @@ -1,141 +0,0 @@ -import type { - AppBskyEmbedExternal, - AppBskyEmbedImages, - AppBskyEmbedRecord, - AppBskyEmbedRecordWithMedia, - AppBskyEmbedVideo, - AppBskyFeedDefs, - AppBskyFeedGetLikes, - AppBskyFeedGetPostThread, - AppBskyFeedGetQuotes, - AppBskyFeedGetRepostedBy, - ComAtprotoLabelDefs, - Typed, -} from '@tsky/lexicons'; -import { BasicActorProfile } from '~/actor'; -import type { Client } from '~/agent/client'; -import type { RPCOptions } from '~/types'; -import { Paginator, parseAtUri } from '~/utils'; - -export class Post implements AppBskyFeedDefs.PostView { - uri!: string; - author: BasicActorProfile; - cid!: string; - indexedAt!: string; - record: unknown; - embed?: - | Typed - | Typed - | Typed - | Typed - | Typed; - labels?: ComAtprotoLabelDefs.Label[]; - likeCount?: number; - quoteCount?: number; - replyCount?: number; - repostCount?: number; - threadgate?: AppBskyFeedDefs.ThreadgateView; - viewer?: AppBskyFeedDefs.ViewerState; - $type?: string; - - constructor( - private client: Client, - payload: AppBskyFeedDefs.PostView, - ) { - Object.assign(this, payload); - this.author = new BasicActorProfile(this.client, payload.author); - } - - isOfCurrentUser() { - const { host: repo } = parseAtUri(this.uri); - return repo !== this.client.crenditials.session?.did; - } - - remove(options: RPCOptions = {}) { - this.client.deleteRecord(this.uri, options); - } - - // TODO: method for liking, unliking, reposting, un-reposting, quoting, etc. - - /** - * Get posts in a thread. Does not require auth, but additional metadata and filtering will be applied for authed requests. - */ - async threads( - params: Omit = {}, - options: RPCOptions = {}, - ) { - const res = await this.client.get('app.bsky.feed.getPostThread', { - params: { uri: this.uri, ...params }, - ...options, - }); - - return res.data; - } - - /** - * Get like records which reference a subject (by AT-URI and CID). - */ - likes( - params: Omit = {}, - options: RPCOptions = {}, - ) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getLikes', { - params: { cursor, uri: this.uri, ...params }, - ...options, - }); - - return res.data; - }); - } - - /** - * Get a list of quotes for a given post. - */ - quotes( - params: Omit = {}, - options: RPCOptions = {}, - ) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getQuotes', { - params: { cursor, uri: this.uri, ...params }, - ...options, - }); - - return res.data; - }); - } - - /** - * Get a list of reposts for a given post. - */ - repostedBy( - params: Omit = {}, - options: RPCOptions = {}, - ) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.getRepostedBy', { - params: { cursor, uri: this.uri, ...params }, - ...options, - }); - - return res.data; - }); - } - - /** - * Gets post views for a specified list of posts (by AT-URI). This is sometimes referred to as 'hydrating' a 'feed skeleton'. - */ - static async getMany( - client: Client, - posts: string[], - options: RPCOptions = {}, - ) { - const res = await client.get('app.bsky.feed.getPosts', { - params: { uris: posts }, - ...options, - }); - - return res.data.posts; - } -} diff --git a/packages/client/src/user/preferences/preferences.test.ts b/packages/client/src/preferences.test.ts similarity index 100% rename from packages/client/src/user/preferences/preferences.test.ts rename to packages/client/src/preferences.test.ts diff --git a/packages/client/src/user/profile.test.ts b/packages/client/src/profile.test.ts similarity index 100% rename from packages/client/src/user/profile.test.ts rename to packages/client/src/profile.test.ts diff --git a/packages/client/src/search/index.ts b/packages/client/src/search/index.ts deleted file mode 100644 index 5a2bdeb..0000000 --- a/packages/client/src/search/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './search'; diff --git a/packages/client/src/search/search.ts b/packages/client/src/search/search.ts deleted file mode 100644 index a3c1b65..0000000 --- a/packages/client/src/search/search.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { - AppBskyFeedSearchPosts, - AppBskyGraphSearchStarterPacks, -} from '@tsky/lexicons'; -import type { Client } from '~/agent/client'; -import { Post } from '~/post'; -import { BasicStarterPack } from '~/starterpack'; -import type { RPCOptions } from '~/types'; -import { Paginator } from '~/utils'; - -export class Search { - constructor(private client: Client) {} - - /** - * Find posts matching search criteria, returning views of those posts. - */ - posts(params: AppBskyFeedSearchPosts.Params, options: RPCOptions = {}) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.feed.searchPosts', { - params: { cursor, ...params }, - ...options, - }); - - return { - ...res.data, - posts: res.data.posts.map((post) => new Post(this.client, post)), - }; - }); - } - - /** - * Search for starter packs. - */ - starterpacks( - params: AppBskyGraphSearchStarterPacks.Params, - options?: RPCOptions, - ) { - return Paginator.init(async (cursor) => { - const res = await this.client.get('app.bsky.graph.searchStarterPacks', { - params: { - cursor, - ...params, - }, - ...options, - }); - - return { - ...res.data, - starterPacks: res.data.starterPacks.map( - (starterPack) => new BasicStarterPack(this.client, starterPack), - ), - }; - }); - } -} diff --git a/packages/client/src/starterpack/index.ts b/packages/client/src/starterpack/index.ts deleted file mode 100644 index e55a57e..0000000 --- a/packages/client/src/starterpack/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './starterpack'; diff --git a/packages/client/src/starterpack/starterpack.ts b/packages/client/src/starterpack/starterpack.ts deleted file mode 100644 index 21928ce..0000000 --- a/packages/client/src/starterpack/starterpack.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { - AppBskyActorDefs, - AppBskyFeedDefs, - AppBskyGraphDefs, - ComAtprotoLabelDefs, -} from '@tsky/lexicons'; -import { ActorProfile, BasicActorProfile } from '~/actor'; -import type { Client } from '~/agent/client'; -import { FeedGeneratorView } from '~/feed/generator'; -import { ListBasicView } from '~/list'; - -class Starterpack { - cid!: string; - creator: AppBskyActorDefs.ProfileViewBasic; - indexedAt!: string; - record: unknown; - uri!: string; - joinedAllTimeCount?: number | undefined; - joinedWeekCount?: number | undefined; - labels?: ComAtprotoLabelDefs.Label[] | undefined; - $type?: string | undefined; - - constructor( - private client: Client, - payload: AppBskyGraphDefs.StarterPackView, - ) { - Object.assign(this, payload); - this.creator = new BasicActorProfile(this.client, payload.creator); - } -} - -export class BasicStarterPack - extends Starterpack - implements AppBskyGraphDefs.StarterPackViewBasic -{ - listItemCount?: number | undefined; -} - -export class StarterPack - extends Starterpack - implements AppBskyGraphDefs.StarterPackView -{ - feeds?: AppBskyFeedDefs.GeneratorView[]; - list?: AppBskyGraphDefs.ListViewBasic; - listItemsSample?: AppBskyGraphDefs.ListItemView[]; - - constructor(client: Client, payload: AppBskyGraphDefs.StarterPackView) { - super(client, payload); - - this.feeds = payload.feeds?.map( - (feed) => new FeedGeneratorView(client, feed), - ); - - if (payload.list) { - this.list = new ListBasicView(client, payload.list); - } - - if (payload.listItemsSample) { - this.listItemsSample = payload.listItemsSample.map((item) => ({ - ...item, - subject: new ActorProfile(client, item.subject), - })); - } - } -} diff --git a/packages/client/src/tsky/tsky.test.ts b/packages/client/src/tsky.test.ts similarity index 100% rename from packages/client/src/tsky/tsky.test.ts rename to packages/client/src/tsky.test.ts diff --git a/packages/client/src/tsky/tsky.ts b/packages/client/src/tsky.ts similarity index 91% rename from packages/client/src/tsky/tsky.ts rename to packages/client/src/tsky.ts index 60a6ba9..f78f784 100644 --- a/packages/client/src/tsky/tsky.ts +++ b/packages/client/src/tsky.ts @@ -3,7 +3,7 @@ import { type CredentialManagerOptions, } from '@atcute/client'; import { Agent } from '~/agent'; -import type { CreateAgentOptions } from './types'; +import type { CreateAgentOptions } from '~/types'; export async function createAgent( credentials?: CreateAgentOptions, diff --git a/packages/client/src/tsky/index.ts b/packages/client/src/tsky/index.ts deleted file mode 100644 index 615ea17..0000000 --- a/packages/client/src/tsky/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './tsky'; diff --git a/packages/client/src/tsky/types.ts b/packages/client/src/tsky/types.ts deleted file mode 100644 index 01c0118..0000000 --- a/packages/client/src/tsky/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { AtpSessionData } from '@atcute/client'; - -export type CreateAgentOptions = - | { - identifier: string; - password: string; - } - | { - session: AtpSessionData; - }; diff --git a/packages/client/src/types.ts b/packages/client/src/types.ts index 5c54345..3c33709 100644 --- a/packages/client/src/types.ts +++ b/packages/client/src/types.ts @@ -1,3 +1,14 @@ +import type { AtpSessionData } from '@atcute/client'; + +export type CreateAgentOptions = + | { + identifier: string; + password: string; + } + | { + session: AtpSessionData; + }; + export type RPCOptions = { signal?: AbortSignal; headers?: HeadersInit }; /** diff --git a/packages/client/src/user/index.ts b/packages/client/src/user/index.ts deleted file mode 100644 index c15d967..0000000 --- a/packages/client/src/user/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { AppBskyFeedGetTimeline } from '@tsky/lexicons'; -import { ActorWithProfileFunction } from '~/actor'; -import { FeedViewPost } from '~/feed'; -import type { RPCOptions } from '~/types'; -import { Paginator } from '~/utils'; -import { Mute } from './mute'; -import { Muted } from './muted'; -import { Preferences } from './preferences'; -import { Suggestion } from './suggestion'; -import { Unmute } from './unmute'; - -export class User extends ActorWithProfileFunction { - get preferences() { - return new Preferences(this.client); - } - - /** - * Get a view of the requesting account's home timeline. This is expected to be some form of reverse-chronological feed. - */ - timeline( - params: AppBskyFeedGetTimeline.Params, - options?: AppBskyFeedGetTimeline.Input, - ): Promise> { - return Paginator.init(async (cursor) => { - const res = await this.client - .get('app.bsky.feed.getTimeline', { - ...(options ?? {}), - params: { - cursor, - ...params, - }, - }) - .then((res) => res.data); - - return { - ...res, - feed: res.feed.map((item) => new FeedViewPost(this.client, item)), - }; - }); - } - - /** - * Get a list of posts liked by the current user - */ - likes(limit?: number, options: RPCOptions = {}) { - return Paginator.init(async (cursor) => { - const res = await this.client - .get('app.bsky.feed.getActorLikes', { - params: { cursor, actor: this.did, limit }, - ...options, - }) - .then((res) => res.data); - - return { - ...res, - feed: res.feed.map((item) => new FeedViewPost(this.client, item)), - }; - }); - } - - get muted() { - return new Muted(this.client); - } - - get suggestion() { - return new Suggestion(this.client); - } - - get mute() { - return new Mute(this.client); - } - - get unmute() { - return new Unmute(this.client); - } -} diff --git a/packages/client/src/user/mute/index.ts b/packages/client/src/user/mute/index.ts deleted file mode 100644 index c456722..0000000 --- a/packages/client/src/user/mute/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './mute'; diff --git a/packages/client/src/user/mute/mute.ts b/packages/client/src/user/mute/mute.ts deleted file mode 100644 index 6738591..0000000 --- a/packages/client/src/user/mute/mute.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { Client } from '~/agent/client'; -import type { RPCOptions } from '~/types'; - -export class Mute { - constructor(private client: Client) {} - - /** - * Creates a mute relationship for the specified account. Mutes are private in Bluesky. - */ - actor(identifier: string, options: RPCOptions = {}) { - return this.client.call('app.bsky.graph.muteActor', { - data: { actor: identifier }, - ...options, - }); - } - - /** - * Mutes a thread preventing notifications from the thread and any of its children. Mutes are private in Bluesky. - */ - thread(identifier: string, options: RPCOptions = {}) { - return this.client.call('app.bsky.graph.muteThread', { - data: { root: identifier }, - ...options, - }); - } - - /** - * Mute an entire list (specified by AT-URI) of actors. This creates a mute relationship for all actors - * on the specified list. Mutes are private on Bluesky. - */ - actorList(identifier: string, options: RPCOptions = {}) { - return this.client.call('app.bsky.graph.muteActorList', { - data: { list: identifier }, - ...options, - }); - } -} diff --git a/packages/client/src/user/muted/index.ts b/packages/client/src/user/muted/index.ts deleted file mode 100644 index c0af9b3..0000000 --- a/packages/client/src/user/muted/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './muted'; diff --git a/packages/client/src/user/muted/muted.ts b/packages/client/src/user/muted/muted.ts deleted file mode 100644 index d7bd533..0000000 --- a/packages/client/src/user/muted/muted.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { DetailedActorProfile } from '~/actor'; -import type { Client } from '~/agent/client'; -import { ListView } from '~/list'; -import type { RPCOptions } from '~/types'; -import { Paginator } from '~/utils'; - -export class Muted { - constructor(private client: Client) {} - - /** - * Enumerates mod lists that the requesting account (actor) currently has muted. Requires auth. - */ - lists(limit?: number, options?: RPCOptions) { - return Paginator.init(async (cursor) => { - const res = await this.client - .get('app.bsky.graph.getListMutes', { - params: { - cursor, - limit, - }, - ...options, - }) - .then((res) => res.data); - - return { - ...res, - lists: res.lists.map((list) => new ListView(this.client, list)), - }; - }); - } - - /** - * Enumerates accounts that the requesting account (actor) currently has muted. Requires auth. - */ - profiles(limit?: number, options?: RPCOptions) { - return Paginator.init(async (cursor) => { - const res = await this.client - .get('app.bsky.graph.getMutes', { - params: { - cursor, - limit, - }, - ...options, - }) - .then((res) => res.data); - - return { - ...res, - mutes: res.mutes.map( - (mute) => new DetailedActorProfile(this.client, mute), - ), - }; - }); - } -} diff --git a/packages/client/src/user/preferences/index.ts b/packages/client/src/user/preferences/index.ts deleted file mode 100644 index a17a3da..0000000 --- a/packages/client/src/user/preferences/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './preferences'; diff --git a/packages/client/src/user/preferences/preferences.ts b/packages/client/src/user/preferences/preferences.ts deleted file mode 100644 index c66b88a..0000000 --- a/packages/client/src/user/preferences/preferences.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { AppBskyActorPutPreferences } from '@tsky/lexicons'; -import type { Client } from '~/agent/client'; -import type { RPCOptions } from '~/types'; - -export class Preferences { - constructor(private client: Client) {} - - /** - * Get private preferences attached to the current account. Expected use is synchronization between multiple devices, and import/export during account migration. Requires auth. - */ - async get(options: RPCOptions = {}) { - const res = await this.client.get('app.bsky.actor.getPreferences', options); - - return res.data.preferences; - } - - /** - * Set the private preferences attached to the account. - */ - async set( - preferences: AppBskyActorPutPreferences.Input['preferences'], - options: RPCOptions = {}, - ) { - await this.client.call('app.bsky.actor.putPreferences', { - data: { preferences }, - ...options, - }); - } -} diff --git a/packages/client/src/user/suggestion/index.ts b/packages/client/src/user/suggestion/index.ts deleted file mode 100644 index 33fe965..0000000 --- a/packages/client/src/user/suggestion/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './suggestion'; diff --git a/packages/client/src/user/suggestion/suggestion.ts b/packages/client/src/user/suggestion/suggestion.ts deleted file mode 100644 index 34e1c89..0000000 --- a/packages/client/src/user/suggestion/suggestion.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { DetailedActorProfile } from '~/actor'; -import type { Client } from '~/agent/client'; -import { FeedGeneratorView } from '~/feed/generator'; -import type { RPCOptions } from '~/types'; -import { Paginator } from '~/utils'; - -export class Suggestion { - constructor(private client: Client) {} - - /** - * Get a list of suggested actors. Expected use is discovery of accounts to follow during new account onboarding. - */ - follow(limit?: number, options?: RPCOptions) { - return Paginator.init(async (cursor) => { - const res = await this.client - .get('app.bsky.actor.getSuggestions', { - params: { - cursor, - limit, - }, - ...options, - }) - .then((res) => res.data); - - return { - ...res, - actors: res.actors.map( - (actor) => new DetailedActorProfile(this.client, actor), - ), - }; - }); - } - - /** - * Enumerates follows similar to a given account (actor). Expected use is to recommend additional accounts immediately after following one account. - */ - async afterFollowing(actor: string, options?: RPCOptions) { - const res = await this.client - .get('app.bsky.graph.getSuggestedFollowsByActor', { - params: { - actor, - }, - ...options, - }) - .then((res) => res.data); - - return { - ...res, - suggestions: res.suggestions.map( - (suggestion) => new DetailedActorProfile(this.client, suggestion), - ), - }; - } - - /** - * Get a list of suggested feeds (feed generators) for the requesting account. - */ - feeds(limit?: number, options?: RPCOptions) { - return Paginator.init(async (cursor) => { - const res = await this.client - .get('app.bsky.feed.getSuggestedFeeds', { - params: { cursor, limit }, - ...options, - }) - .then((res) => res.data); - - return { - ...res, - feeds: res.feeds.map( - (feed) => new FeedGeneratorView(this.client, feed), - ), - }; - }); - } -} diff --git a/packages/client/src/user/unmute/index.ts b/packages/client/src/user/unmute/index.ts deleted file mode 100644 index 42ac0a4..0000000 --- a/packages/client/src/user/unmute/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './unmute'; diff --git a/packages/client/src/user/unmute/unmute.ts b/packages/client/src/user/unmute/unmute.ts deleted file mode 100644 index be4791a..0000000 --- a/packages/client/src/user/unmute/unmute.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { Client } from '~/agent/client'; -import type { RPCOptions } from '~/types'; - -export class Unmute { - constructor(private client: Client) {} - - /** - * Unmutes the specified account. - */ - actor(identifier: string, options: RPCOptions = {}) { - return this.client.call('app.bsky.graph.unmuteActor', { - data: { actor: identifier }, - ...options, - }); - } - - /** - * Unmutes the specified thread. - */ - thread(identifier: string, options: RPCOptions = {}) { - return this.client.call('app.bsky.graph.unmuteThread', { - data: { root: identifier }, - ...options, - }); - } - - /** - * Unmute an entire list (specified by AT-URI) of actors. This removes the mute relationship for all actors - * on the specified list. - */ - actorList(identifier: string, options: RPCOptions = {}) { - return this.client.call('app.bsky.graph.unmuteActorList', { - data: { list: identifier }, - ...options, - }); - } -} diff --git a/packages/client/src/video/index.ts b/packages/client/src/video/index.ts deleted file mode 100644 index 777af2d..0000000 --- a/packages/client/src/video/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './video'; diff --git a/packages/client/src/video/video.ts b/packages/client/src/video/video.ts deleted file mode 100644 index 849a5d4..0000000 --- a/packages/client/src/video/video.ts +++ /dev/null @@ -1,89 +0,0 @@ -import type { AppBskyVideoDefs, AppBskyVideoUploadVideo } from '@tsky/lexicons'; -import type { Client } from '~/agent/client'; -import type { RPCOptions } from '~/types'; - -export class Video { - constructor(private client: Client) {} - - /** - * Get video upload limits for the authenticated user. - */ - async limit(options: RPCOptions = {}) { - const res = await this.client.get( - 'app.bsky.video.getUploadLimits', - options, - ); - - return res.data; - } - - /** - * Get status details for a video processing job. - */ - async status(jobId: string, options?: RPCOptions) { - const res = await this.client.get('app.bsky.video.getJobStatus', { - params: { jobId }, - ...options, - }); - - return new JobStatus(this.client, res.data.jobStatus); - } - - /** - * Upload a video to be processed then stored on the PDS. - */ - async upload(data: AppBskyVideoUploadVideo.Input, options?: RPCOptions) { - const res = await this.client.call('app.bsky.video.uploadVideo', { - data, - ...options, - }); - - return new JobStatus(this.client, res.data.jobStatus); - } -} - -class JobStatus { - jobId: string; - did: string; - /** The state of the video processing job. All values not listed as a known value indicate that the job is in process. */ - state: 'JOB_STATE_COMPLETED' | 'JOB_STATE_FAILED' | (string & {}); - /** Progress within the current processing state. */ - progress?: number; - blob?: AppBskyVideoDefs.JobStatus['blob']; - error?: string; - message?: string; - - constructor( - private client: Client, - data: AppBskyVideoDefs.JobStatus, - ) { - this.jobId = data.jobId; - this.did = data.did; - - this.state = data.state; - - this.progress = data.progress; - this.blob = data.blob; - this.error = data.error; - this.message = data.message; - } - - /** - * Update status details for a video processing job. - */ - async refresh(options?: RPCOptions) { - const res = await this.client - .get('app.bsky.video.getJobStatus', { - params: { jobId: this.jobId }, - ...options, - }) - .then((res) => res.data.jobStatus); - - this.state = res.state; - - this.progress = res.progress; - this.blob = res.blob; - this.error = res.error; - this.message = res.message; - } -} From dc8f47985aeaae3d6dfc41405cf6616a324113c3 Mon Sep 17 00:00:00 2001 From: Aditya Mathur Date: Tue, 11 Mar 2025 17:03:44 +0530 Subject: [PATCH 12/16] feat: added an example for tsky client --- biome.json | 2 +- examples/user-profile/.gitignore | 24 + examples/user-profile/index.html | 13 + examples/user-profile/package.json | 27 + examples/user-profile/public/tsky-logo.png | Bin 0 -> 27632 bytes examples/user-profile/src/App.tsx | 165 ++ examples/user-profile/src/index.css | 1 + examples/user-profile/src/main.tsx | 10 + examples/user-profile/src/vite-env.d.ts | 1 + examples/user-profile/tsconfig.app.json | 26 + examples/user-profile/tsconfig.json | 7 + examples/user-profile/tsconfig.node.json | 24 + examples/user-profile/vite.config.ts | 8 + package.json | 1 - packages/client/src/agent.ts | 49 +- packages/client/src/index.ts | 1 + packages/client/src/preferences.test.ts | 12 +- packages/client/src/profile.test.ts | 12 +- packages/client/src/tsky.test.ts | 24 +- packages/client/src/tsky.ts | 10 +- pnpm-lock.yaml | 1682 +++++++++++++++++++- pnpm-workspace.yaml | 1 + 22 files changed, 2050 insertions(+), 50 deletions(-) create mode 100644 examples/user-profile/.gitignore create mode 100644 examples/user-profile/index.html create mode 100644 examples/user-profile/package.json create mode 100644 examples/user-profile/public/tsky-logo.png create mode 100644 examples/user-profile/src/App.tsx create mode 100644 examples/user-profile/src/index.css create mode 100644 examples/user-profile/src/main.tsx create mode 100644 examples/user-profile/src/vite-env.d.ts create mode 100644 examples/user-profile/tsconfig.app.json create mode 100644 examples/user-profile/tsconfig.json create mode 100644 examples/user-profile/tsconfig.node.json create mode 100644 examples/user-profile/vite.config.ts diff --git a/biome.json b/biome.json index 42b47ab..2990d82 100644 --- a/biome.json +++ b/biome.json @@ -4,7 +4,7 @@ "enabled": true }, "files": { - "include": ["docs", "packages/*"], + "include": ["docs", "packages/*", "examples/*"], "ignore": [ ".github", ".gitignore", diff --git a/examples/user-profile/.gitignore b/examples/user-profile/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/examples/user-profile/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/user-profile/index.html b/examples/user-profile/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/examples/user-profile/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/examples/user-profile/package.json b/examples/user-profile/package.json new file mode 100644 index 0000000..1aa4e50 --- /dev/null +++ b/examples/user-profile/package.json @@ -0,0 +1,27 @@ +{ + "name": "user-profile", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tsky/client": "workspace:*", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.0.12", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "globals": "^15.15.0", + "tailwindcss": "^4.0.12", + "typescript": "~5.7.2", + "typescript-eslint": "^8.24.1", + "vite": "^6.2.0" + } +} diff --git a/examples/user-profile/public/tsky-logo.png b/examples/user-profile/public/tsky-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0781db5433b591456b6984a6b0173953fead4eeb GIT binary patch literal 27632 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�vB4gG5~PQB@AqtWiVR}) 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 HiO>H7-UdxB literal 0 HcmV?d00001 diff --git a/examples/user-profile/src/App.tsx b/examples/user-profile/src/App.tsx new file mode 100644 index 0000000..838477f --- /dev/null +++ b/examples/user-profile/src/App.tsx @@ -0,0 +1,165 @@ +import { type ActorProfile, createAgent } from '@tsky/client'; +import { useEffect, useState } from 'react'; + +async function findUser(user: string) { + try { + const agent = await createAgent({ + options: { + service: 'https://public.api.bsky.app', + }, + }); + const actor = await agent.actor(user); + + return actor.profile(); + } catch (err) { + console.error(err); + } +} + +// did:plc:xdwatsttsxnl5h65mf3ddxbq + +function App() { + const [search, setSearch] = useState(); + const [user, setUser] = useState(); + + useEffect(() => { + if (search) { + findUser(search).then(setUser); + } + }, [search]); + + return ( +
+
+
{ + const value = values.get('search'); + + if (value) { + setSearch(value.toString()); + } + }} + className="flex gap-3 items-center" + > + + tsky logo + + + +
+ {user && ( +
+ banner +
+ +

{user.displayName}

+

+ @{user.handle} +

+
+

+ {user.followersCount} + + {' '} + Followers + +

+

+ {user.followsCount} + + {' '} + Following + +

+
+

{user.description}

+
+

+ Made with Tsky +

+ + + Bluesky Icon + + + +
+
+
+ )} +
+
+ ); +} + +export default App; diff --git a/examples/user-profile/src/index.css b/examples/user-profile/src/index.css new file mode 100644 index 0000000..a461c50 --- /dev/null +++ b/examples/user-profile/src/index.css @@ -0,0 +1 @@ +@import "tailwindcss"; \ No newline at end of file diff --git a/examples/user-profile/src/main.tsx b/examples/user-profile/src/main.tsx new file mode 100644 index 0000000..bef5202 --- /dev/null +++ b/examples/user-profile/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/examples/user-profile/src/vite-env.d.ts b/examples/user-profile/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/examples/user-profile/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/user-profile/tsconfig.app.json b/examples/user-profile/tsconfig.app.json new file mode 100644 index 0000000..358ca9b --- /dev/null +++ b/examples/user-profile/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/examples/user-profile/tsconfig.json b/examples/user-profile/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/examples/user-profile/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/examples/user-profile/tsconfig.node.json b/examples/user-profile/tsconfig.node.json new file mode 100644 index 0000000..db0becc --- /dev/null +++ b/examples/user-profile/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/user-profile/vite.config.ts b/examples/user-profile/vite.config.ts new file mode 100644 index 0000000..a2d3dc2 --- /dev/null +++ b/examples/user-profile/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(),tailwindcss(),], +}) diff --git a/package.json b/package.json index f0a988b..a1b6780 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "scripts": { "dev": "pnpm run -r dev", "build": "pnpm run -r build", - "prepare": "pnpm --filter !@tsky/docs build", "docs:dev": "pnpm run --filter @tsky/docs dev", "docs:build": "pnpm run --filter @tsky/docs build", "docs:preview": "pnpm run --filter @tsky/docs preview", diff --git a/packages/client/src/agent.ts b/packages/client/src/agent.ts index 8418e72..e30ab76 100644 --- a/packages/client/src/agent.ts +++ b/packages/client/src/agent.ts @@ -29,6 +29,7 @@ import type { AppBskyVideoUploadVideo, At, ComAtprotoLabelDefs, + ComAtprotoRepoStrongRef, Queries, Typed, } from '@tsky/lexicons'; @@ -199,6 +200,12 @@ export class Actor { return data; }); } + + toJSON() { + return { + did: this.did, + }; + } } export class ActorLazyProfile extends Actor { @@ -254,19 +261,59 @@ export class ActorBasicProfile } } } + + override toJSON() { + return { + ...super.toJSON(), + handle: this.handle, + associated: this.associated, + avatar: this.avatar, + createdAt: this.createdAt, + displayName: this.displayName, + labels: this.labels, + viewer: this.viewer, + $type: this.$type, + }; + } } export class ActorProfile extends ActorBasicProfile - implements AppBskyActorDefs.ProfileView + implements AppBskyActorDefs.ProfileViewDetailed { description?: string; indexedAt?: string; + followersCount?: number; + followsCount?: number; + postsCount?: number; + banner?: string | undefined; + joinedViaStarterPack?: AppBskyGraphDefs.StarterPackViewBasic | undefined; + pinnedPost?: ComAtprotoRepoStrongRef.Main | undefined; constructor(client: Client, actor: AppBskyActorDefs.ProfileViewDetailed) { super(client, actor); this.description = actor.description; this.indexedAt = actor.indexedAt; + this.followersCount = actor.followersCount; + this.followsCount = actor.followsCount; + this.postsCount = actor.postsCount; + this.banner = actor.banner; + this.joinedViaStarterPack = actor.joinedViaStarterPack; + this.pinnedPost = actor.pinnedPost; + } + + override toJSON() { + return { + ...super.toJSON(), + description: this.description, + indexedAt: this.indexedAt, + followersCount: this.followersCount, + followsCount: this.followsCount, + postsCount: this.postsCount, + banner: this.banner, + joinedViaStarterPack: this.joinedViaStarterPack, + pinnedPost: this.pinnedPost, + }; } } diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 2a1ee5f..c0fdb66 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1 +1,2 @@ export * from '~/tsky'; +export * from './agent'; diff --git a/packages/client/src/preferences.test.ts b/packages/client/src/preferences.test.ts index 2fb286a..ce2884f 100644 --- a/packages/client/src/preferences.test.ts +++ b/packages/client/src/preferences.test.ts @@ -17,8 +17,10 @@ const TEST_CREDENTIALS = { describe('preferences', () => { it('.get()', async () => { const agent = await createAgent({ - identifier: TEST_CREDENTIALS.alice.handle, - password: TEST_CREDENTIALS.alice.password, + credentials: { + identifier: TEST_CREDENTIALS.alice.handle, + password: TEST_CREDENTIALS.alice.password, + }, }); const preferences = await agent.user.preferences.get(); @@ -27,8 +29,10 @@ describe('preferences', () => { it('.set()', async () => { const agent = await createAgent({ - identifier: TEST_CREDENTIALS.alice.handle, - password: TEST_CREDENTIALS.alice.password, + credentials: { + identifier: TEST_CREDENTIALS.alice.handle, + password: TEST_CREDENTIALS.alice.password, + }, }); const payload = { diff --git a/packages/client/src/profile.test.ts b/packages/client/src/profile.test.ts index 19c618b..9f2fe84 100644 --- a/packages/client/src/profile.test.ts +++ b/packages/client/src/profile.test.ts @@ -17,8 +17,10 @@ const TEST_CREDENTIALS = { describe('profile', async () => { it("Getting alice's profile", async () => { const agent = await createAgent({ - identifier: TEST_CREDENTIALS.alice.handle, - password: TEST_CREDENTIALS.alice.password, + credentials: { + identifier: TEST_CREDENTIALS.alice.handle, + password: TEST_CREDENTIALS.alice.password, + }, }); const profile = await agent.user.profile(); @@ -29,8 +31,10 @@ describe('profile', async () => { it("Getting bob's profile", async () => { const agent = await createAgent({ - identifier: TEST_CREDENTIALS.bob.handle, - password: TEST_CREDENTIALS.bob.password, + credentials: { + identifier: TEST_CREDENTIALS.bob.handle, + password: TEST_CREDENTIALS.bob.password, + }, }); const profile = await agent.user.profile(); diff --git a/packages/client/src/tsky.test.ts b/packages/client/src/tsky.test.ts index 7eb2eb9..7f3f428 100644 --- a/packages/client/src/tsky.test.ts +++ b/packages/client/src/tsky.test.ts @@ -6,13 +6,13 @@ import { createAgent } from '~/tsky'; describe('createAgent', () => { it('can create agent for Alice', async () => { - const agent = await createAgent( - { + const agent = await createAgent({ + credentials: { identifier: 'alice.test', password: 'password', }, - { service: inject('testPdsUrl') }, - ); + options: { service: inject('testPdsUrl') }, + }); expect(agent.session).not.toBe(undefined); expect(agent.session?.handle).toBe('alice.test'); expect(agent.session?.email).toBe('alice.test@example.com'); @@ -21,22 +21,22 @@ describe('createAgent', () => { it('can resume from stored session', async () => { let session: AtpSessionData; { - const agent = await createAgent( - { + const agent = await createAgent({ + credentials: { identifier: 'alice.test', password: 'password', }, - { service: inject('testPdsUrl') }, - ); + options: { service: inject('testPdsUrl') }, + }); expect(agent.session).toBeDefined(); session = agent.session as AtpSessionData; } { - const agent = await createAgent( - { session }, - { service: inject('testPdsUrl') }, - ); + const agent = await createAgent({ + credentials: { session }, + options: { service: inject('testPdsUrl') }, + }); expect(agent.session).not.toBe(undefined); expect(agent.session?.handle).toBe('alice.test'); expect(agent.session?.email).toBe('alice.test@example.com'); diff --git a/packages/client/src/tsky.ts b/packages/client/src/tsky.ts index f78f784..5c32685 100644 --- a/packages/client/src/tsky.ts +++ b/packages/client/src/tsky.ts @@ -5,10 +5,12 @@ import { import { Agent } from '~/agent'; import type { CreateAgentOptions } from '~/types'; -export async function createAgent( - credentials?: CreateAgentOptions, - options?: CredentialManagerOptions, -) { +export async function createAgent(config: { + credentials?: CreateAgentOptions; + options?: CredentialManagerOptions; +}) { + const { credentials, options } = config; + const manager = new CredentialManager( options ?? { service: 'https://bsky.social' }, ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed9d517..81c143c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,7 +37,47 @@ importers: version: 1.1.2(typedoc-plugin-markdown@4.4.1(typedoc@0.27.6(typescript@5.7.2))) vitepress: specifier: ^1.5.0 - version: 1.5.0(@algolia/client-search@5.15.0)(@types/node@22.10.1)(axios@1.7.9)(postcss@8.4.49)(search-insights@2.17.3)(typescript@5.7.2) + version: 1.5.0(@algolia/client-search@5.15.0)(@types/node@22.10.1)(axios@1.7.9)(lightningcss@1.29.2)(postcss@8.5.3)(search-insights@2.17.3)(typescript@5.7.2) + + examples/user-profile: + dependencies: + '@tsky/client': + specifier: workspace:* + version: link:../../packages/client + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + devDependencies: + '@tailwindcss/vite': + specifier: ^4.0.12 + version: 4.0.12(vite@6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0)) + '@types/react': + specifier: ^19.0.10 + version: 19.0.10 + '@types/react-dom': + specifier: ^19.0.4 + version: 19.0.4(@types/react@19.0.10) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.3.4(vite@6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0)) + globals: + specifier: ^15.15.0 + version: 15.15.0 + tailwindcss: + specifier: ^4.0.12 + version: 4.0.12 + typescript: + specifier: ~5.7.2 + version: 5.7.2 + typescript-eslint: + specifier: ^8.24.1 + version: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2) + vite: + specifier: ^6.2.0 + version: 6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0) packages/client: dependencies: @@ -53,7 +93,7 @@ importers: version: link:../lexicons '@vitest/coverage-istanbul': specifier: ^3.0.6 - version: 3.0.6(vitest@3.0.6(@types/node@22.10.1)) + version: 3.0.6(vitest@3.0.6(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0)) globals: specifier: ^15.12.0 version: 15.12.0 @@ -68,7 +108,7 @@ importers: version: 5.7.2 vitest: specifier: ^3.0.6 - version: 3.0.6(@types/node@22.10.1) + version: 3.0.6(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0) packages/internal/dev-env: dependencies: @@ -502,6 +542,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-plugin-utils@7.26.5': + resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.25.9': resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} @@ -528,6 +572,18 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/plugin-transform-react-jsx-self@7.25.9': + resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.25.9': + resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/template@7.25.9': resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} @@ -671,6 +727,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.1': + resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} @@ -683,6 +745,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.1': + resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} @@ -695,6 +763,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.1': + resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} @@ -707,6 +781,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.1': + resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} @@ -719,6 +799,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.1': + resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} @@ -731,6 +817,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.1': + resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} @@ -743,6 +835,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.1': + resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} @@ -755,6 +853,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.1': + resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} @@ -767,6 +871,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.1': + resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} @@ -779,6 +889,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.1': + resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} @@ -791,6 +907,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.1': + resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} @@ -803,6 +925,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.1': + resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} @@ -815,6 +943,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.1': + resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} @@ -827,6 +961,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.1': + resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} @@ -839,6 +979,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.1': + resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} @@ -851,6 +997,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.1': + resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} @@ -863,6 +1015,18 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.1': + resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.1': + resolution: {integrity: sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} @@ -875,12 +1039,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.1': + resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.23.1': resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.25.1': + resolution: {integrity: sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} @@ -893,6 +1069,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.1': + resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} @@ -905,6 +1087,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.1': + resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} @@ -917,6 +1105,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.1': + resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} @@ -929,6 +1123,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.1': + resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} @@ -941,6 +1141,50 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.1': + resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.5.0': + resolution: {integrity: sha512-RoV8Xs9eNwiDvhv7M+xcL4PWyRyIXRY/FLp3buU4h1EYfdF7unWUy3dOjPqb3C7rMUewIcqwW850PgS8h1o1yg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.19.2': + resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.1.0': + resolution: {integrity: sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.12.0': + resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.0': + resolution: {integrity: sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.22.0': + resolution: {integrity: sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.7': + resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@gerrit0/mini-shiki@1.27.2': resolution: {integrity: sha512-GeWyHz8ao2gBiUW4OJnQDxXQnFgZQwwQk05t/CVVgNBN7/rK8XZ7xY6YhLVv9tH3VppWWmr9DCl3MwemB/i+Og==} @@ -963,6 +1207,26 @@ packages: '@hapi/hoek@11.0.7': resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==} + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.2': + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} + engines: {node: '>=18.18'} + '@iconify-json/simple-icons@1.2.13': resolution: {integrity: sha512-rRQjMoIt/kPfaD+fnBC9YZQpso3hkn8xPeadl+YWhscJ5SVUCdB9oTeR9VIpt+/5Yi8vEkh2UOWFPq4lz3ee2A==} @@ -1208,91 +1472,186 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.35.0': + resolution: {integrity: sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.27.4': resolution: {integrity: sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.35.0': + resolution: {integrity: sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.27.4': resolution: {integrity: sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.35.0': + resolution: {integrity: sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.27.4': resolution: {integrity: sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.35.0': + resolution: {integrity: sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.27.4': resolution: {integrity: sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.35.0': + resolution: {integrity: sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.27.4': resolution: {integrity: sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.35.0': + resolution: {integrity: sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.27.4': resolution: {integrity: sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.35.0': + resolution: {integrity: sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.27.4': resolution: {integrity: sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.35.0': + resolution: {integrity: sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.27.4': resolution: {integrity: sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.35.0': + resolution: {integrity: sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.27.4': resolution: {integrity: sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.35.0': + resolution: {integrity: sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.35.0': + resolution: {integrity: sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.27.4': resolution: {integrity: sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.35.0': + resolution: {integrity: sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.27.4': resolution: {integrity: sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.35.0': + resolution: {integrity: sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.27.4': resolution: {integrity: sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.35.0': + resolution: {integrity: sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.27.4': resolution: {integrity: sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.35.0': + resolution: {integrity: sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.27.4': resolution: {integrity: sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.35.0': + resolution: {integrity: sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==} + cpu: [x64] + os: [linux] + '@rollup/rollup-win32-arm64-msvc@4.27.4': resolution: {integrity: sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.35.0': + resolution: {integrity: sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.27.4': resolution: {integrity: sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.35.0': + resolution: {integrity: sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.27.4': resolution: {integrity: sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.35.0': + resolution: {integrity: sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==} + cpu: [x64] + os: [win32] + '@shikijs/core@1.24.0': resolution: {integrity: sha512-6pvdH0KoahMzr6689yh0QJ3rCgF4j1XsXRHNEeEN6M4xJTfQ6QPWrmHzIddotg+xPJUPEPzYzYCKzpYyhTI6Gw==} @@ -1532,9 +1891,99 @@ packages: resolution: {integrity: sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==} engines: {node: '>=18.0.0'} + '@tailwindcss/node@4.0.12': + resolution: {integrity: sha512-a6J11K1Ztdln9OrGfoM75/hChYPcHYGNYimqciMrvKXRmmPaS8XZTHhdvb5a3glz4Kd4ZxE1MnuFE2c0fGGmtg==} + + '@tailwindcss/oxide-android-arm64@4.0.12': + resolution: {integrity: sha512-dAXCaemu3mHLXcA5GwGlQynX8n7tTdvn5i1zAxRvZ5iC9fWLl5bGnjZnzrQqT7ttxCvRwdVf3IHUnMVdDBO/kQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.0.12': + resolution: {integrity: sha512-vPNI+TpJQ7sizselDXIJdYkx9Cu6JBdtmRWujw9pVIxW8uz3O2PjgGGzL/7A0sXI8XDjSyRChrUnEW9rQygmJQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.0.12': + resolution: {integrity: sha512-RL/9jM41Fdq4Efr35C5wgLx98BirnrfwuD+zgMFK6Ir68HeOSqBhW9jsEeC7Y/JcGyPd3MEoJVIU4fAb7YLg7A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.0.12': + resolution: {integrity: sha512-7WzWiax+LguJcMEimY0Q4sBLlFXu1tYxVka3+G2M9KmU/3m84J3jAIV4KZWnockbHsbb2XgrEjtlJKVwHQCoRA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.12': + resolution: {integrity: sha512-X9LRC7jjE1QlfIaBbXjY0PGeQP87lz5mEfLSVs2J1yRc9PSg1tEPS9NBqY4BU9v5toZgJgzKeaNltORyTs22TQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.0.12': + resolution: {integrity: sha512-i24IFNq2402zfDdoWKypXz0ZNS2G4NKaA82tgBlE2OhHIE+4mg2JDb5wVfyP6R+MCm5grgXvurcIcKWvo44QiQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.0.12': + resolution: {integrity: sha512-LmOdshJBfAGIBG0DdBWhI0n5LTMurnGGJCHcsm9F//ISfsHtCnnYIKgYQui5oOz1SUCkqsMGfkAzWyNKZqbGNw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.0.12': + resolution: {integrity: sha512-OSK667qZRH30ep8RiHbZDQfqkXjnzKxdn0oRwWzgCO8CoTxV+MvIkd0BWdQbYtYuM1wrakARV/Hwp0eA/qzdbw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.0.12': + resolution: {integrity: sha512-uylhWq6OWQ8krV8Jk+v0H/3AZKJW6xYMgNMyNnUbbYXWi7hIVdxRKNUB5UvrlC3RxtgsK5EAV2i1CWTRsNcAnA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-win32-arm64-msvc@4.0.12': + resolution: {integrity: sha512-XDLnhMoXZEEOir1LK43/gHHwK84V1GlV8+pAncUAIN2wloeD+nNciI9WRIY/BeFTqES22DhTIGoilSO39xDb2g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.0.12': + resolution: {integrity: sha512-I/BbjCLpKDQucvtn6rFuYLst1nfFwSMYyPzkx/095RE+tuzk5+fwXuzQh7T3fIBTcbn82qH/sFka7yPGA50tLw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.0.12': + resolution: {integrity: sha512-DWb+myvJB9xJwelwT9GHaMc1qJj6MDXRDR0CS+T8IdkejAtu8ctJAgV4r1drQJLPeS7mNwq2UHW2GWrudTf63A==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.0.12': + resolution: {integrity: sha512-JM3gp601UJiryIZ9R2bSqalzcOy15RCybQ1Q+BJqDEwVyo4LkWKeqQAcrpHapWXY31OJFTuOUVBFDWMhzHm2Bg==} + peerDependencies: + vite: ^5.2.0 || ^6 + '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/bn.js@5.1.6': resolution: {integrity: sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==} @@ -1547,6 +1996,9 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} @@ -1562,6 +2014,14 @@ packages: '@types/node@22.10.1': resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==} + '@types/react-dom@19.0.4': + resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==} + peerDependencies: + '@types/react': ^19.0.0 + + '@types/react@19.0.10': + resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==} + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -1571,9 +2031,62 @@ packages: '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + '@typescript-eslint/eslint-plugin@8.26.1': + resolution: {integrity: sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.26.1': + resolution: {integrity: sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.26.1': + resolution: {integrity: sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.26.1': + resolution: {integrity: sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.26.1': + resolution: {integrity: sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.26.1': + resolution: {integrity: sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.26.1': + resolution: {integrity: sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.26.1': + resolution: {integrity: sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@vitejs/plugin-react@4.3.4': + resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@vitejs/plugin-vue@5.2.1': resolution: {integrity: sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1711,6 +2224,19 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + algoliasearch@5.15.0: resolution: {integrity: sha512-Yf3Swz1s63hjvBVZ/9f2P1Uu48GjmjCN+Esxb6MAONMGtZB1fRX8/S1AhUTtsuTlcGovbYLxpHgc7wEzstDZBw==} engines: {node: '>= 14.0.0'} @@ -1789,6 +2315,9 @@ packages: bowser@2.11.0: resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} @@ -1829,6 +2358,10 @@ packages: resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + caniuse-lite@1.0.30001695: resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==} @@ -1850,6 +2383,10 @@ packages: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} @@ -1908,6 +2445,9 @@ packages: resolution: {integrity: sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==} engines: {node: '>= 0.8.0'} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -1983,6 +2523,9 @@ packages: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -2070,6 +2613,10 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} @@ -2102,12 +2649,59 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.25.1: + resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@8.3.0: + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.22.0: + resolution: {integrity: sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -2115,6 +2709,10 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} @@ -2147,10 +2745,19 @@ packages: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-printf@1.6.10: resolution: {integrity: sha512-GwTgG9O4FVIdShhbVF3JxOgSBY2+ePGsu2V/UONgoCPzF9VY6ZdBMKsHKCYQHZwNk3qNouUolRDsgVxcVA5G1w==} engines: {node: '>=10.0'} @@ -2166,6 +2773,10 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + file-type@16.5.4: resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} engines: {node: '>=10'} @@ -2181,6 +2792,17 @@ packages: resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + focus-trap@7.6.2: resolution: {integrity: sha512-9FhUxK1hVju2+AiQIDJ5Dd//9R2n2RAfJ0qfhF4IHGHgcoEUTMpbTeG/zbEuwaiYXfuAH6XE0/aCyxDdRM+W5w==} @@ -2250,6 +2872,10 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true @@ -2263,14 +2889,25 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + globals@15.12.0: resolution: {integrity: sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==} engines: {node: '>=18'} + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -2339,6 +2976,18 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2423,17 +3072,34 @@ packages: resolution: {integrity: sha512-oSwM7q8PTHQWuZAlp995iPpPJ4Vkl7qT0ZRD+9duL9j2oBy6KcTfyxc8mEuHJYC+z/kbps80aJLkaNzTOrf/kw==} engines: {node: 20 || >=22} + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + jose@5.9.6: resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} hasBin: true + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -2446,6 +3112,9 @@ packages: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kysely@0.22.0: resolution: {integrity: sha512-ZE3qWtnqLOalodzfK5QUEcm7AEulhxsPNuKaGFsC3XiqO92vMLm+mAHk/NnbSIOtC4RmGm0nsv700i8KDp1gfQ==} engines: {node: '>=14.0.0'} @@ -2454,15 +3123,90 @@ packages: resolution: {integrity: sha512-TH+b56pVXQq0tsyooYLeNfV11j6ih7D50dyN8tkM0e7ndiUH28Nziojiog3qRFlmEj9XePYdZUrNJ2079Qjdow==} engines: {node: '>=14.0.0'} + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-darwin-arm64@1.29.2: + resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.29.2: + resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.29.2: + resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.29.2: + resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.29.2: + resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.29.2: + resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.29.2: + resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.29.2: + resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.29.2: + resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.29.2: + resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.29.2: + resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} + engines: {node: '>= 12.0.0'} + linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} lodash.isarguments@3.1.0: resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + loupe@3.1.2: resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} @@ -2577,6 +3321,9 @@ packages: resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} engines: {node: 20 || >=22} + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -2631,6 +3378,9 @@ packages: napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -2690,10 +3440,22 @@ packages: oniguruma-to-es@0.7.0: resolution: {integrity: sha512-HRaRh09cE0gRS3+wi2zxekB+I5L8C/gN60S+vb11eADHUaB/q4u8wGGOX3GvwvitG8ixaeycZfeoyruKQzUgNg==} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + p-finally@1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + p-queue@6.6.2: resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} engines: {node: '>=8'} @@ -2709,10 +3471,18 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2817,6 +3587,10 @@ packages: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -2841,6 +3615,10 @@ packages: engines: {node: '>=10'} hasBin: true + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + prettier@3.4.2: resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} engines: {node: '>=14'} @@ -2902,6 +3680,19 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-dom@19.0.0: + resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} + peerDependencies: + react: ^19.0.0 + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react@19.0.0: + resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} + engines: {node: '>=0.10.0'} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -2935,6 +3726,10 @@ packages: regex@5.0.2: resolution: {integrity: sha512-/pczGbKIQgfTMRV0XjABvc5RzLqQmwqxLHdQao2RTXPk+pmTXB2P0IaUHYdYyk412YLwUIkaeMd5T+RzVgTqnQ==} + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -2967,6 +3762,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.35.0: + resolution: {integrity: sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2983,6 +3783,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.25.0: + resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} + search-insights@2.17.3: resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} @@ -3120,6 +3923,10 @@ packages: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} @@ -3142,6 +3949,13 @@ packages: tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tailwindcss@4.0.12: + resolution: {integrity: sha512-bT0hJo91FtncsAMSsMzUkoo/iEU0Xs5xgFgVC9XmdM9bw5MhZuQFjPNl6wxAE0SiQF/YTZJa+PndGWYSDtuxAg==} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + tar-fs@2.1.2: resolution: {integrity: sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==} @@ -3197,6 +4011,12 @@ packages: trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + ts-api-utils@2.0.1: + resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -3215,6 +4035,10 @@ packages: typanion@3.14.0: resolution: {integrity: sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + type-fest@2.19.0: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} @@ -3244,6 +4068,13 @@ packages: peerDependencies: typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x + typescript-eslint@8.26.1: + resolution: {integrity: sha512-t/oIs9mYyrwZGRpDv3g+3K6nZ5uhKEMt2oNmAPwaY4/ye0+EH4nXIPYNtkYFS6QHm+1DFg34DbglYBz5P9Xysg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + typescript@5.7.2: resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} engines: {node: '>=14.17'} @@ -3295,6 +4126,9 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -3355,6 +4189,46 @@ packages: terser: optional: true + vite@6.2.1: + resolution: {integrity: sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitepress@1.5.0: resolution: {integrity: sha512-q4Q/G2zjvynvizdB3/bupdYkCJe2umSAMv9Ju4d92E6/NXJ59z70xB0q5p/4lpRyAwflDsbwy1mLV9Q5+nlB+g==} hasBin: true @@ -3424,6 +4298,10 @@ packages: engines: {node: '>=8'} hasBin: true + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} @@ -3466,6 +4344,10 @@ packages: engines: {node: '>= 14'} hasBin: true + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + zod@3.24.1: resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} @@ -4449,6 +5331,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-plugin-utils@7.26.5': {} + '@babel/helper-string-parser@7.25.9': {} '@babel/helper-validator-identifier@7.25.9': {} @@ -4468,6 +5352,16 @@ snapshots: dependencies: '@babel/types': 7.26.5 + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/template@7.25.9': dependencies: '@babel/code-frame': 7.26.2 @@ -4616,141 +5510,260 @@ snapshots: '@esbuild/aix-ppc64@0.23.1': optional: true + '@esbuild/aix-ppc64@0.25.1': + optional: true + '@esbuild/android-arm64@0.21.5': optional: true '@esbuild/android-arm64@0.23.1': optional: true + '@esbuild/android-arm64@0.25.1': + optional: true + '@esbuild/android-arm@0.21.5': optional: true '@esbuild/android-arm@0.23.1': optional: true + '@esbuild/android-arm@0.25.1': + optional: true + '@esbuild/android-x64@0.21.5': optional: true '@esbuild/android-x64@0.23.1': optional: true + '@esbuild/android-x64@0.25.1': + optional: true + '@esbuild/darwin-arm64@0.21.5': optional: true '@esbuild/darwin-arm64@0.23.1': optional: true + '@esbuild/darwin-arm64@0.25.1': + optional: true + '@esbuild/darwin-x64@0.21.5': optional: true '@esbuild/darwin-x64@0.23.1': optional: true + '@esbuild/darwin-x64@0.25.1': + optional: true + '@esbuild/freebsd-arm64@0.21.5': optional: true '@esbuild/freebsd-arm64@0.23.1': optional: true + '@esbuild/freebsd-arm64@0.25.1': + optional: true + '@esbuild/freebsd-x64@0.21.5': optional: true '@esbuild/freebsd-x64@0.23.1': optional: true + '@esbuild/freebsd-x64@0.25.1': + optional: true + '@esbuild/linux-arm64@0.21.5': optional: true '@esbuild/linux-arm64@0.23.1': optional: true + '@esbuild/linux-arm64@0.25.1': + optional: true + '@esbuild/linux-arm@0.21.5': optional: true '@esbuild/linux-arm@0.23.1': optional: true + '@esbuild/linux-arm@0.25.1': + optional: true + '@esbuild/linux-ia32@0.21.5': optional: true '@esbuild/linux-ia32@0.23.1': optional: true + '@esbuild/linux-ia32@0.25.1': + optional: true + '@esbuild/linux-loong64@0.21.5': optional: true '@esbuild/linux-loong64@0.23.1': optional: true + '@esbuild/linux-loong64@0.25.1': + optional: true + '@esbuild/linux-mips64el@0.21.5': optional: true '@esbuild/linux-mips64el@0.23.1': optional: true + '@esbuild/linux-mips64el@0.25.1': + optional: true + '@esbuild/linux-ppc64@0.21.5': optional: true '@esbuild/linux-ppc64@0.23.1': optional: true + '@esbuild/linux-ppc64@0.25.1': + optional: true + '@esbuild/linux-riscv64@0.21.5': optional: true '@esbuild/linux-riscv64@0.23.1': optional: true + '@esbuild/linux-riscv64@0.25.1': + optional: true + '@esbuild/linux-s390x@0.21.5': optional: true '@esbuild/linux-s390x@0.23.1': optional: true + '@esbuild/linux-s390x@0.25.1': + optional: true + '@esbuild/linux-x64@0.21.5': optional: true '@esbuild/linux-x64@0.23.1': optional: true + '@esbuild/linux-x64@0.25.1': + optional: true + + '@esbuild/netbsd-arm64@0.25.1': + optional: true + '@esbuild/netbsd-x64@0.21.5': optional: true '@esbuild/netbsd-x64@0.23.1': optional: true + '@esbuild/netbsd-x64@0.25.1': + optional: true + '@esbuild/openbsd-arm64@0.23.1': optional: true + '@esbuild/openbsd-arm64@0.25.1': + optional: true + '@esbuild/openbsd-x64@0.21.5': optional: true '@esbuild/openbsd-x64@0.23.1': optional: true + '@esbuild/openbsd-x64@0.25.1': + optional: true + '@esbuild/sunos-x64@0.21.5': optional: true '@esbuild/sunos-x64@0.23.1': optional: true + '@esbuild/sunos-x64@0.25.1': + optional: true + '@esbuild/win32-arm64@0.21.5': optional: true '@esbuild/win32-arm64@0.23.1': optional: true + '@esbuild/win32-arm64@0.25.1': + optional: true + '@esbuild/win32-ia32@0.21.5': optional: true '@esbuild/win32-ia32@0.23.1': optional: true + '@esbuild/win32-ia32@0.25.1': + optional: true + '@esbuild/win32-x64@0.21.5': optional: true '@esbuild/win32-x64@0.23.1': optional: true + '@esbuild/win32-x64@0.25.1': + optional: true + + '@eslint-community/eslint-utils@4.5.0(eslint@9.22.0(jiti@2.4.2))': + dependencies: + eslint: 9.22.0(jiti@2.4.2) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.19.2': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.1.0': {} + + '@eslint/core@0.12.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.0': + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.22.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.2.7': + dependencies: + '@eslint/core': 0.12.0 + levn: 0.4.1 + '@gerrit0/mini-shiki@1.27.2': dependencies: '@shikijs/engine-oniguruma': 1.29.1 @@ -4778,6 +5791,19 @@ snapshots: '@hapi/hoek@11.0.7': {} + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.2': {} + '@iconify-json/simple-icons@1.2.13': dependencies: '@iconify/types': 2.0.0 @@ -4984,57 +6010,114 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.27.4': optional: true + '@rollup/rollup-android-arm-eabi@4.35.0': + optional: true + '@rollup/rollup-android-arm64@4.27.4': optional: true + '@rollup/rollup-android-arm64@4.35.0': + optional: true + '@rollup/rollup-darwin-arm64@4.27.4': optional: true + '@rollup/rollup-darwin-arm64@4.35.0': + optional: true + '@rollup/rollup-darwin-x64@4.27.4': optional: true + '@rollup/rollup-darwin-x64@4.35.0': + optional: true + '@rollup/rollup-freebsd-arm64@4.27.4': optional: true + '@rollup/rollup-freebsd-arm64@4.35.0': + optional: true + '@rollup/rollup-freebsd-x64@4.27.4': optional: true + '@rollup/rollup-freebsd-x64@4.35.0': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.27.4': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.35.0': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.27.4': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.35.0': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.27.4': optional: true + '@rollup/rollup-linux-arm64-gnu@4.35.0': + optional: true + '@rollup/rollup-linux-arm64-musl@4.27.4': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.27.4': + '@rollup/rollup-linux-arm64-musl@4.35.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.35.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.27.4': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.35.0': optional: true '@rollup/rollup-linux-riscv64-gnu@4.27.4': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.35.0': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.27.4': optional: true + '@rollup/rollup-linux-s390x-gnu@4.35.0': + optional: true + '@rollup/rollup-linux-x64-gnu@4.27.4': optional: true + '@rollup/rollup-linux-x64-gnu@4.35.0': + optional: true + '@rollup/rollup-linux-x64-musl@4.27.4': optional: true + '@rollup/rollup-linux-x64-musl@4.35.0': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.27.4': optional: true + '@rollup/rollup-win32-arm64-msvc@4.35.0': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.27.4': optional: true + '@rollup/rollup-win32-ia32-msvc@4.35.0': + optional: true + '@rollup/rollup-win32-x64-msvc@4.27.4': optional: true + '@rollup/rollup-win32-x64-msvc@4.35.0': + optional: true + '@shikijs/core@1.24.0': dependencies: '@shikijs/engine-javascript': 1.24.0 @@ -5409,8 +6492,90 @@ snapshots: '@smithy/types': 4.1.0 tslib: 2.8.1 + '@tailwindcss/node@4.0.12': + dependencies: + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + tailwindcss: 4.0.12 + + '@tailwindcss/oxide-android-arm64@4.0.12': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.0.12': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.0.12': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.0.12': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.12': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.0.12': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.0.12': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.0.12': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.0.12': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.0.12': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.0.12': + optional: true + + '@tailwindcss/oxide@4.0.12': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.0.12 + '@tailwindcss/oxide-darwin-arm64': 4.0.12 + '@tailwindcss/oxide-darwin-x64': 4.0.12 + '@tailwindcss/oxide-freebsd-x64': 4.0.12 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.12 + '@tailwindcss/oxide-linux-arm64-gnu': 4.0.12 + '@tailwindcss/oxide-linux-arm64-musl': 4.0.12 + '@tailwindcss/oxide-linux-x64-gnu': 4.0.12 + '@tailwindcss/oxide-linux-x64-musl': 4.0.12 + '@tailwindcss/oxide-win32-arm64-msvc': 4.0.12 + '@tailwindcss/oxide-win32-x64-msvc': 4.0.12 + + '@tailwindcss/vite@4.0.12(vite@6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0))': + dependencies: + '@tailwindcss/node': 4.0.12 + '@tailwindcss/oxide': 4.0.12 + lightningcss: 1.29.2 + tailwindcss: 4.0.12 + vite: 6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0) + '@tokenizer/token@0.3.0': {} + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.26.5 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.26.5 + '@types/bn.js@5.1.6': dependencies: '@types/node': 22.10.1 @@ -5425,6 +6590,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/json-schema@7.0.15': {} + '@types/linkify-it@5.0.0': {} '@types/markdown-it@14.1.2': @@ -5442,20 +6609,116 @@ snapshots: dependencies: undici-types: 6.20.0 + '@types/react-dom@19.0.4(@types/react@19.0.10)': + dependencies: + '@types/react': 19.0.10 + + '@types/react@19.0.10': + dependencies: + csstype: 3.1.3 + '@types/resolve@1.20.2': {} '@types/unist@3.0.3': {} '@types/web-bluetooth@0.0.20': {} + '@typescript-eslint/eslint-plugin@8.26.1(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.26.1 + '@typescript-eslint/type-utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2) + '@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.26.1 + eslint: 9.22.0(jiti@2.4.2) + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 2.0.1(typescript@5.7.2) + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.26.1 + '@typescript-eslint/types': 8.26.1 + '@typescript-eslint/typescript-estree': 8.26.1(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.26.1 + debug: 4.4.0 + eslint: 9.22.0(jiti@2.4.2) + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.26.1': + dependencies: + '@typescript-eslint/types': 8.26.1 + '@typescript-eslint/visitor-keys': 8.26.1 + + '@typescript-eslint/type-utils@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2)': + dependencies: + '@typescript-eslint/typescript-estree': 8.26.1(typescript@5.7.2) + '@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2) + debug: 4.4.0 + eslint: 9.22.0(jiti@2.4.2) + ts-api-utils: 2.0.1(typescript@5.7.2) + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.26.1': {} + + '@typescript-eslint/typescript-estree@8.26.1(typescript@5.7.2)': + dependencies: + '@typescript-eslint/types': 8.26.1 + '@typescript-eslint/visitor-keys': 8.26.1 + debug: 4.4.0 + fast-glob: 3.3.2 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 2.0.1(typescript@5.7.2) + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2)': + dependencies: + '@eslint-community/eslint-utils': 4.5.0(eslint@9.22.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.26.1 + '@typescript-eslint/types': 8.26.1 + '@typescript-eslint/typescript-estree': 8.26.1(typescript@5.7.2) + eslint: 9.22.0(jiti@2.4.2) + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.26.1': + dependencies: + '@typescript-eslint/types': 8.26.1 + eslint-visitor-keys: 4.2.0 + '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-vue@5.2.1(vite@5.4.11(@types/node@22.10.1))(vue@3.5.13(typescript@5.7.2))': + '@vitejs/plugin-react@4.3.4(vite@6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0))': + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.2 + vite: 6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@5.2.1(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.2))(vue@3.5.13(typescript@5.7.2))': dependencies: - vite: 5.4.11(@types/node@22.10.1) + vite: 5.4.11(@types/node@22.10.1)(lightningcss@1.29.2) vue: 3.5.13(typescript@5.7.2) - '@vitest/coverage-istanbul@3.0.6(vitest@3.0.6(@types/node@22.10.1))': + '@vitest/coverage-istanbul@3.0.6(vitest@3.0.6(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0))': dependencies: '@istanbuljs/schema': 0.1.3 debug: 4.4.0 @@ -5467,7 +6730,7 @@ snapshots: magicast: 0.3.5 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.6(@types/node@22.10.1) + vitest: 3.0.6(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0) transitivePeerDependencies: - supports-color @@ -5478,13 +6741,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.6(vite@5.4.11(@types/node@22.10.1))': + '@vitest/mocker@3.0.6(vite@6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0))': dependencies: '@vitest/spy': 3.0.6 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 5.4.11(@types/node@22.10.1) + vite: 6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0) '@vitest/pretty-format@3.0.6': dependencies: @@ -5623,6 +6886,19 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 + acorn-jsx@5.3.2(acorn@8.14.1): + dependencies: + acorn: 8.14.1 + + acorn@8.14.1: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + algoliasearch@5.15.0: dependencies: '@algolia/client-abtesting': 5.15.0 @@ -5720,6 +6996,11 @@ snapshots: bowser@2.11.0: {} + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 @@ -5766,6 +7047,8 @@ snapshots: call-bind-apply-helpers: 1.0.1 get-intrinsic: 1.2.7 + callsites@3.1.0: {} + caniuse-lite@1.0.30001695: {} cbor-extract@2.2.0: @@ -5796,6 +7079,11 @@ snapshots: loupe: 3.1.2 pathval: 2.0.0 + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + character-entities-html4@2.1.0: {} character-entities-legacy@3.0.0: {} @@ -5852,6 +7140,8 @@ snapshots: transitivePeerDependencies: - supports-color + concat-map@0.0.1: {} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -5903,6 +7193,8 @@ snapshots: deep-extend@0.6.0: {} + deep-is@0.1.4: {} + deepmerge@4.3.1: {} delay@5.0.0: {} @@ -5979,6 +7271,11 @@ snapshots: dependencies: once: 1.4.0 + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + entities@2.2.0: {} entities@4.5.0: {} @@ -6046,16 +7343,115 @@ snapshots: '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 + esbuild@0.25.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.1 + '@esbuild/android-arm': 0.25.1 + '@esbuild/android-arm64': 0.25.1 + '@esbuild/android-x64': 0.25.1 + '@esbuild/darwin-arm64': 0.25.1 + '@esbuild/darwin-x64': 0.25.1 + '@esbuild/freebsd-arm64': 0.25.1 + '@esbuild/freebsd-x64': 0.25.1 + '@esbuild/linux-arm': 0.25.1 + '@esbuild/linux-arm64': 0.25.1 + '@esbuild/linux-ia32': 0.25.1 + '@esbuild/linux-loong64': 0.25.1 + '@esbuild/linux-mips64el': 0.25.1 + '@esbuild/linux-ppc64': 0.25.1 + '@esbuild/linux-riscv64': 0.25.1 + '@esbuild/linux-s390x': 0.25.1 + '@esbuild/linux-x64': 0.25.1 + '@esbuild/netbsd-arm64': 0.25.1 + '@esbuild/netbsd-x64': 0.25.1 + '@esbuild/openbsd-arm64': 0.25.1 + '@esbuild/openbsd-x64': 0.25.1 + '@esbuild/sunos-x64': 0.25.1 + '@esbuild/win32-arm64': 0.25.1 + '@esbuild/win32-ia32': 0.25.1 + '@esbuild/win32-x64': 0.25.1 + escalade@3.2.0: {} escape-html@1.0.3: {} + escape-string-regexp@4.0.0: {} + + eslint-scope@8.3.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint@9.22.0(jiti@2.4.2): + dependencies: + '@eslint-community/eslint-utils': 4.5.0(eslint@9.22.0(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.19.2 + '@eslint/config-helpers': 0.1.0 + '@eslint/core': 0.12.0 + '@eslint/eslintrc': 3.3.0 + '@eslint/js': 9.22.0 + '@eslint/plugin-kit': 0.2.7 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.2 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + escape-string-regexp: 4.0.0 + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.4.2 + transitivePeerDependencies: + - supports-color + + espree@10.3.0: + dependencies: + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) + eslint-visitor-keys: 4.2.0 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + estree-walker@2.0.2: {} estree-walker@3.0.3: dependencies: '@types/estree': 1.0.6 + esutils@2.0.3: {} + etag@1.8.1: {} event-target-shim@5.0.1: {} @@ -6108,6 +7504,8 @@ snapshots: transitivePeerDependencies: - supports-color + fast-deep-equal@3.1.3: {} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -6116,6 +7514,10 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fast-printf@1.6.10: {} fast-redact@3.5.0: {} @@ -6128,6 +7530,10 @@ snapshots: dependencies: reusify: 1.0.4 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + file-type@16.5.4: dependencies: readable-web-to-node-stream: 3.0.3 @@ -6152,6 +7558,18 @@ snapshots: transitivePeerDependencies: - supports-color + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + focus-trap@7.6.2: dependencies: tabbable: 6.2.0 @@ -6214,6 +7632,10 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + glob@10.4.5: dependencies: foreground-child: 3.3.0 @@ -6234,10 +7656,16 @@ snapshots: globals@11.12.0: {} + globals@14.0.0: {} + globals@15.12.0: {} + globals@15.15.0: {} + gopd@1.2.0: {} + graceful-fs@4.2.11: {} + graphemer@1.4.0: {} handlebars@4.7.8: @@ -6329,6 +7757,15 @@ snapshots: ieee754@1.2.1: {} + ignore@5.3.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + inherits@2.0.4: {} ini@1.3.8: {} @@ -6420,12 +7857,24 @@ snapshots: dependencies: '@isaacs/cliui': 8.0.2 + jiti@2.4.2: {} + jose@5.9.6: {} js-tokens@4.0.0: {} + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + jsesc@3.1.0: {} + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} key-encoder@2.0.3: @@ -6439,18 +7888,78 @@ snapshots: dependencies: tsscmp: 1.0.6 + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + kysely@0.22.0: {} kysely@0.23.5: {} + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-darwin-arm64@1.29.2: + optional: true + + lightningcss-darwin-x64@1.29.2: + optional: true + + lightningcss-freebsd-x64@1.29.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.29.2: + optional: true + + lightningcss-linux-arm64-gnu@1.29.2: + optional: true + + lightningcss-linux-arm64-musl@1.29.2: + optional: true + + lightningcss-linux-x64-gnu@1.29.2: + optional: true + + lightningcss-linux-x64-musl@1.29.2: + optional: true + + lightningcss-win32-arm64-msvc@1.29.2: + optional: true + + lightningcss-win32-x64-msvc@1.29.2: + optional: true + + lightningcss@1.29.2: + dependencies: + detect-libc: 2.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.29.2 + lightningcss-darwin-x64: 1.29.2 + lightningcss-freebsd-x64: 1.29.2 + lightningcss-linux-arm-gnueabihf: 1.29.2 + lightningcss-linux-arm64-gnu: 1.29.2 + lightningcss-linux-arm64-musl: 1.29.2 + lightningcss-linux-x64-gnu: 1.29.2 + lightningcss-linux-x64-musl: 1.29.2 + lightningcss-win32-arm64-msvc: 1.29.2 + lightningcss-win32-x64-msvc: 1.29.2 + linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + lodash.defaults@4.2.0: {} lodash.isarguments@3.1.0: {} + lodash.merge@4.6.2: {} + loupe@3.1.2: {} loupe@3.1.3: {} @@ -6560,6 +8069,10 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -6597,6 +8110,8 @@ snapshots: napi-build-utils@2.0.0: {} + natural-compare@1.4.0: {} + negotiator@0.6.3: {} negotiator@0.6.4: {} @@ -6644,8 +8159,25 @@ snapshots: regex: 5.0.2 regex-recursion: 4.3.0 + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + p-finally@1.0.0: {} + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + p-queue@6.6.2: dependencies: eventemitter3: 4.0.7 @@ -6661,8 +8193,14 @@ snapshots: package-json-from-dist@1.0.1: {} + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + parseurl@1.3.3: {} + path-exists@4.0.0: {} + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -6777,6 +8315,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.3: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postgres-array@2.0.0: {} postgres-bytea@1.0.0: {} @@ -6804,6 +8348,8 @@ snapshots: tar-fs: 2.1.2 tunnel-agent: 0.6.0 + prelude-ls@1.2.1: {} + prettier@3.4.2: {} process-warning@3.0.0: {} @@ -6858,6 +8404,15 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-dom@19.0.0(react@19.0.0): + dependencies: + react: 19.0.0 + scheduler: 0.25.0 + + react-refresh@0.14.2: {} + + react@19.0.0: {} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -6895,6 +8450,8 @@ snapshots: dependencies: regex-utilities: 2.3.0 + resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} resolve@1.22.8: @@ -6946,6 +8503,31 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.27.4 fsevents: 2.3.3 + rollup@4.35.0: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.35.0 + '@rollup/rollup-android-arm64': 4.35.0 + '@rollup/rollup-darwin-arm64': 4.35.0 + '@rollup/rollup-darwin-x64': 4.35.0 + '@rollup/rollup-freebsd-arm64': 4.35.0 + '@rollup/rollup-freebsd-x64': 4.35.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.35.0 + '@rollup/rollup-linux-arm-musleabihf': 4.35.0 + '@rollup/rollup-linux-arm64-gnu': 4.35.0 + '@rollup/rollup-linux-arm64-musl': 4.35.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.35.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.35.0 + '@rollup/rollup-linux-riscv64-gnu': 4.35.0 + '@rollup/rollup-linux-s390x-gnu': 4.35.0 + '@rollup/rollup-linux-x64-gnu': 4.35.0 + '@rollup/rollup-linux-x64-musl': 4.35.0 + '@rollup/rollup-win32-arm64-msvc': 4.35.0 + '@rollup/rollup-win32-ia32-msvc': 4.35.0 + '@rollup/rollup-win32-x64-msvc': 4.35.0 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -6961,6 +8543,8 @@ snapshots: safer-buffer@2.1.2: {} + scheduler@0.25.0: {} + search-insights@2.17.3: {} semver-compare@1.0.0: {} @@ -7141,6 +8725,8 @@ snapshots: strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} + strnum@1.0.5: {} strtok3@6.3.0: @@ -7160,6 +8746,10 @@ snapshots: tabbable@6.2.0: {} + tailwindcss@4.0.12: {} + + tapable@2.2.1: {} + tar-fs@2.1.2: dependencies: chownr: 1.1.4 @@ -7219,6 +8809,10 @@ snapshots: trim-lines@3.0.1: {} + ts-api-utils@2.0.1(typescript@5.7.2): + dependencies: + typescript: 5.7.2 + tslib@2.8.1: {} tsscmp@1.0.6: {} @@ -7236,6 +8830,10 @@ snapshots: typanion@3.14.0: {} + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + type-fest@2.19.0: {} type-is@1.6.18: @@ -7264,6 +8862,16 @@ snapshots: typescript: 5.7.2 yaml: 2.7.0 + typescript-eslint@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2): + dependencies: + '@typescript-eslint/eslint-plugin': 8.26.1(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2) + '@typescript-eslint/parser': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2) + '@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.2) + eslint: 9.22.0(jiti@2.4.2) + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + typescript@5.7.2: {} uc.micro@2.1.0: {} @@ -7314,6 +8922,10 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + util-deprecate@1.0.2: {} utils-merge@1.0.1: {} @@ -7334,15 +8946,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@3.0.6(@types/node@22.10.1): + vite-node@3.0.6(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 5.4.11(@types/node@22.10.1) + vite: 6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0) transitivePeerDependencies: - '@types/node' + - jiti - less - lightningcss - sass @@ -7351,8 +8964,10 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml - vite@5.4.11(@types/node@22.10.1): + vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.2): dependencies: esbuild: 0.21.5 postcss: 8.4.49 @@ -7360,8 +8975,22 @@ snapshots: optionalDependencies: '@types/node': 22.10.1 fsevents: 2.3.3 + lightningcss: 1.29.2 + + vite@6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0): + dependencies: + esbuild: 0.25.1 + postcss: 8.5.3 + rollup: 4.35.0 + optionalDependencies: + '@types/node': 22.10.1 + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.29.2 + tsx: 4.19.2 + yaml: 2.7.0 - vitepress@1.5.0(@algolia/client-search@5.15.0)(@types/node@22.10.1)(axios@1.7.9)(postcss@8.4.49)(search-insights@2.17.3)(typescript@5.7.2): + vitepress@1.5.0(@algolia/client-search@5.15.0)(@types/node@22.10.1)(axios@1.7.9)(lightningcss@1.29.2)(postcss@8.5.3)(search-insights@2.17.3)(typescript@5.7.2): dependencies: '@docsearch/css': 3.8.0 '@docsearch/js': 3.8.0(@algolia/client-search@5.15.0)(search-insights@2.17.3) @@ -7370,7 +8999,7 @@ snapshots: '@shikijs/transformers': 1.24.0 '@shikijs/types': 1.24.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.1(vite@5.4.11(@types/node@22.10.1))(vue@3.5.13(typescript@5.7.2)) + '@vitejs/plugin-vue': 5.2.1(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.2))(vue@3.5.13(typescript@5.7.2)) '@vue/devtools-api': 7.6.7 '@vue/shared': 3.5.13 '@vueuse/core': 11.3.0(vue@3.5.13(typescript@5.7.2)) @@ -7379,10 +9008,10 @@ snapshots: mark.js: 8.11.1 minisearch: 7.1.1 shiki: 1.24.0 - vite: 5.4.11(@types/node@22.10.1) + vite: 5.4.11(@types/node@22.10.1)(lightningcss@1.29.2) vue: 3.5.13(typescript@5.7.2) optionalDependencies: - postcss: 8.4.49 + postcss: 8.5.3 transitivePeerDependencies: - '@algolia/client-search' - '@types/node' @@ -7411,10 +9040,10 @@ snapshots: - typescript - universal-cookie - vitest@3.0.6(@types/node@22.10.1): + vitest@3.0.6(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0): dependencies: '@vitest/expect': 3.0.6 - '@vitest/mocker': 3.0.6(vite@5.4.11(@types/node@22.10.1)) + '@vitest/mocker': 3.0.6(vite@6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0)) '@vitest/pretty-format': 3.0.6 '@vitest/runner': 3.0.6 '@vitest/snapshot': 3.0.6 @@ -7430,12 +9059,13 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 5.4.11(@types/node@22.10.1) - vite-node: 3.0.6(@types/node@22.10.1) + vite: 6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0) + vite-node: 3.0.6(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.1 transitivePeerDependencies: + - jiti - less - lightningcss - msw @@ -7445,6 +9075,8 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml vue-demi@0.14.10(vue@3.5.13(typescript@5.7.2)): dependencies: @@ -7469,6 +9101,8 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + word-wrap@1.2.5: {} + wordwrap@1.0.0: {} wrap-ansi@7.0.0: @@ -7495,6 +9129,8 @@ snapshots: yaml@2.7.0: {} + yocto-queue@0.1.0: {} + zod@3.24.1: {} zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index bef6bad..b469d94 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,6 @@ packages: - packages/* + - examples/* - packages/internal/* - docs/ catalog: From 4bfcaf22d57bcc66d4bbd804c99bc1626c999222 Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Wed, 12 Mar 2025 01:22:06 +0530 Subject: [PATCH 13/16] fix: corrected the example --- examples/user-profile/package.json | 3 ++- examples/user-profile/src/App.tsx | 17 ++++++++++++----- package.json | 3 ++- packages/client/src/agent.ts | 9 +++++++++ packages/lexicons/src/lib/lexicons.ts | 6 ++++-- pnpm-lock.yaml | 3 +++ 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/examples/user-profile/package.json b/examples/user-profile/package.json index 1aa4e50..3833232 100644 --- a/examples/user-profile/package.json +++ b/examples/user-profile/package.json @@ -15,6 +15,7 @@ }, "devDependencies": { "@tailwindcss/vite": "^4.0.12", + "@tsky/lexicons": "workspace:*", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react": "^4.3.4", @@ -24,4 +25,4 @@ "typescript-eslint": "^8.24.1", "vite": "^6.2.0" } -} +} \ No newline at end of file diff --git a/examples/user-profile/src/App.tsx b/examples/user-profile/src/App.tsx index 838477f..dee8992 100644 --- a/examples/user-profile/src/App.tsx +++ b/examples/user-profile/src/App.tsx @@ -1,14 +1,23 @@ import { type ActorProfile, createAgent } from '@tsky/client'; +import type { At } from '@tsky/lexicons'; import { useEffect, useState } from 'react'; -async function findUser(user: string) { +async function getUserProfile(identity: string) { try { const agent = await createAgent({ options: { service: 'https://public.api.bsky.app', }, }); - const actor = await agent.actor(user); + + let did = identity; + + if (!did.startsWith('did:')) { + const _id = await agent.resolveDIDFromHandle(identity); + did = _id.did; + } + + const actor = await agent.actor(did as At.DID); return actor.profile(); } catch (err) { @@ -16,15 +25,13 @@ async function findUser(user: string) { } } -// did:plc:xdwatsttsxnl5h65mf3ddxbq - function App() { const [search, setSearch] = useState(); const [user, setUser] = useState(); useEffect(() => { if (search) { - findUser(search).then(setUser); + getUserProfile(search).then(setUser); } }, [search]); diff --git a/package.json b/package.json index a1b6780..4ff1ca9 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "scripts": { "dev": "pnpm run -r dev", "build": "pnpm run -r build", + "prepare": "pnpm --filter !@tsky/docs build", "docs:dev": "pnpm run --filter @tsky/docs dev", "docs:build": "pnpm run --filter @tsky/docs build", "docs:preview": "pnpm run --filter @tsky/docs preview", @@ -34,4 +35,4 @@ "biome check --write --" ] } -} +} \ No newline at end of file diff --git a/packages/client/src/agent.ts b/packages/client/src/agent.ts index e30ab76..c92078d 100644 --- a/packages/client/src/agent.ts +++ b/packages/client/src/agent.ts @@ -1305,4 +1305,13 @@ export class Agent { return data; } + + async resolveDIDFromHandle(handle: string, options: RPCOptions = {}) { + return this.client + .get('com.atproto.identity.resolveHandle', { + params: { handle }, + ...options, + }) + .then((res) => res.data); + } } diff --git a/packages/lexicons/src/lib/lexicons.ts b/packages/lexicons/src/lib/lexicons.ts index fb37506..78b0861 100644 --- a/packages/lexicons/src/lib/lexicons.ts +++ b/packages/lexicons/src/lib/lexicons.ts @@ -5,9 +5,9 @@ * @module * Contains type declarations for Bluesky lexicons * @generated - * Generated on: 2025-03-01T03:33:31.541Z + * Generated on: 2025-03-11T19:48:30.798Z * Version: main - * Source: https://github.com/bluesky-social/atproto/tree/38320191e559f8b928c6e951a9b4a6207240bfc1/lexicons + * Source: https://github.com/bluesky-social/atproto/tree/18fbfa00057dda9ef4eba77d8b4e87994893c952/lexicons */ /** Base type with optional type field */ @@ -612,6 +612,7 @@ export declare namespace AppBskyEmbedRecordWithMedia { export declare namespace AppBskyEmbedVideo { interface Main extends TypedBase { + /** The mp4 video file. May be up to 100mb, formerly limited to 50mb. */ video: At.Blob; /** * Alt text description of the video, for accessibility. @@ -5468,6 +5469,7 @@ export declare namespace ToolsOzoneTeamListMembers { * \@default 50 */ limit?: number; + q?: string; roles?: string[]; } type Input = undefined; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81c143c..8bdeb1d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,6 +54,9 @@ importers: '@tailwindcss/vite': specifier: ^4.0.12 version: 4.0.12(vite@6.2.1(@types/node@22.10.1)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.2)(yaml@2.7.0)) + '@tsky/lexicons': + specifier: workspace:* + version: link:../../packages/lexicons '@types/react': specifier: ^19.0.10 version: 19.0.10 From 0b744b463c8801ff7b0058b1e04dba0c28fe6a42 Mon Sep 17 00:00:00 2001 From: Aditya Mathur Date: Wed, 12 Mar 2025 11:25:31 +0530 Subject: [PATCH 14/16] fix: resolved linting issues --- examples/user-profile/src/main.tsx | 18 ++++++++++++------ examples/user-profile/tsconfig.app.json | 4 ---- examples/user-profile/tsconfig.node.json | 4 ---- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/examples/user-profile/src/main.tsx b/examples/user-profile/src/main.tsx index bef5202..a0a3f66 100644 --- a/examples/user-profile/src/main.tsx +++ b/examples/user-profile/src/main.tsx @@ -1,10 +1,16 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App.tsx'; -createRoot(document.getElementById('root')!).render( +const root = document.getElementById('root'); + +if (!root) { + throw new Error('No root element found'); +} + +createRoot(root).render( , -) +); diff --git a/examples/user-profile/tsconfig.app.json b/examples/user-profile/tsconfig.app.json index 358ca9b..1903283 100644 --- a/examples/user-profile/tsconfig.app.json +++ b/examples/user-profile/tsconfig.app.json @@ -6,16 +6,12 @@ "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, - - /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "isolatedModules": true, "moduleDetection": "force", "noEmit": true, "jsx": "react-jsx", - - /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, diff --git a/examples/user-profile/tsconfig.node.json b/examples/user-profile/tsconfig.node.json index db0becc..1dba6de 100644 --- a/examples/user-profile/tsconfig.node.json +++ b/examples/user-profile/tsconfig.node.json @@ -5,15 +5,11 @@ "lib": ["ES2023"], "module": "ESNext", "skipLibCheck": true, - - /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "isolatedModules": true, "moduleDetection": "force", "noEmit": true, - - /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, From f14caebcccd0ce94a443aed889e58fcbd5b3a3d4 Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Wed, 12 Mar 2025 11:31:53 +0530 Subject: [PATCH 15/16] chore: formatted the code --- examples/user-profile/src/index.css | 2 +- examples/user-profile/vite.config.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/user-profile/src/index.css b/examples/user-profile/src/index.css index a461c50..f1d8c73 100644 --- a/examples/user-profile/src/index.css +++ b/examples/user-profile/src/index.css @@ -1 +1 @@ -@import "tailwindcss"; \ No newline at end of file +@import "tailwindcss"; diff --git a/examples/user-profile/vite.config.ts b/examples/user-profile/vite.config.ts index a2d3dc2..6e7f528 100644 --- a/examples/user-profile/vite.config.ts +++ b/examples/user-profile/vite.config.ts @@ -1,8 +1,8 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import tailwindcss from '@tailwindcss/vite' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import tailwindcss from '@tailwindcss/vite'; // https://vite.dev/config/ export default defineConfig({ - plugins: [react(),tailwindcss(),], -}) + plugins: [react(), tailwindcss()], +}); From 76a8b46c4f56d0dd1c516da6bf21ebef256328d5 Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Wed, 12 Mar 2025 11:34:24 +0530 Subject: [PATCH 16/16] fix(client): corrected the test-utils file --- packages/client/test-utils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/client/test-utils.ts b/packages/client/test-utils.ts index ae82b44..3d322b2 100644 --- a/packages/client/test-utils.ts +++ b/packages/client/test-utils.ts @@ -11,13 +11,13 @@ const testAgents: Record = { }; function createTestAgent(handle: Handle) { - return createAgent( - { + return createAgent({ + credentials: { identifier: handle, password: 'password', }, - { service: inject('testPdsUrl') }, - ); + options: { service: inject('testPdsUrl') }, + }); } /**