Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions docs/guide/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ Trotsky is currently limited to the following features:
**StepActorLikes** | :white_check_mark: | Get an actor's likes. | ```Trotsky.init(agent).actor('bsky.app').likes().each()```
**StepActorLists** | :white_check_mark: | Get an actor's lists. | ```Trotsky.init(agent).actor('bsky.app').lists().each()```
**StepActorMute** | :white_check_mark: | Mute an actor. | ```Trotsky.init(agent).actor('bsky.app').mute()```
**StepActorPosts** | :white_check_mark: | Get an actor's posts | ```Trotsky.init(agent).actor('bsky.app').posts().each()```
**StepActors** | :white_check_mark: | Get a list of actors by their DIDs or handles. | ```Trotsky.init(agent).actors(['bsky.app', 'trotsky.pirhoo.com']).each()```
**StepActorStarterPacks** | :x: | Get an actor starter packs. |
**StepActorPosts** | :white_check_mark: | Get an actor's posts | ```Trotsky.init(agent).actor('bsky.app').posts().each()```
**StepActors** | :white_check_mark: | Get a list of actors by their DIDs or handles. | ```Trotsky.init(agent).actors(['bsky.app', 'trotsky.pirhoo.com']).each()```
**StepActorStarterPacks** | :white_check_mark: | Get an actor's starter packs. | ```Trotsky.init(agent).actor('bsky.app').starterPacks().each()```
**StepActorStreamPosts** | :test_tube: | Stream an actor's posts. | ```Trotsky.init(agent).actor('bsky.app').streamPosts().each()```
**StepActorUnblock** | :white_check_mark: | Unblock an actor. | ```Trotsky.init(agent).actor('bsky.app').unblock()```
**StepActorUnfollow** | :white_check_mark: | Unfollow an actor. | ```Trotsky.init(agent).actor('bsky.app').unfollow()```
Expand All @@ -29,10 +29,10 @@ Trotsky is currently limited to the following features:
**StepPostReply** | :white_check_mark: | Reply to a post. | ```Trotsky.init(agent).post("at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l").reply({ text: "Well done!" })```
**StepPostRepost** | :white_check_mark: | Repost a post. | ```Trotsky.init(agent).post("at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l").repost()```
**StepPosts** | :white_check_mark: | Get a list of post by their URIs. | ```Trotsky.init(agent).posts(["at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l"]).each()```
**StepSearchPosts** | :white_check_mark: | Search posts. | ```Trotsky.init(agent).searchPosts({ q: "Mapo Tofu" }).each()```
**StepSearchStarterPacks** | :x: | Search starter packs. |
**StepStarterPack** | :x: | Get a start pack by its URI. |
**StepStarterPacks** | :x: | Get a list of starter packs by their URIs. |
**StepSearchPosts** | :white_check_mark: | Search posts. | ```Trotsky.init(agent).searchPosts({ q: "Mapo Tofu" }).each()```
**StepSearchStarterPacks** | :white_check_mark: | Search starter packs. | ```Trotsky.init(agent).searchStarterPacks({ q: "tech" }).each()```
**StepStarterPack** | :white_check_mark: | Get a starter pack by its URI. | ```Trotsky.init(agent).starterPack("at://did:plc:example/app.bsky.graph.starterpack/packid")```
**StepStarterPacks** | :white_check_mark: | Get a list of starter packs by their URIs. | ```Trotsky.init(agent).starterPacks([uri1, uri2]).each()```
**StepStreamPosts** | :test_tube: | Use the firehose to stream posts. | ```Trotsky.init(agent).streamPosts().each()```
**StepTimeline** | :x: | Get the timeline. |

Expand Down
90 changes: 90 additions & 0 deletions lib/core/StepActorStarterPacks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import type { AppBskyGraphGetActorStarterPacks } from "@atproto/api"

import { StepStarterPacks, type StepStarterPacksOutput } from "./StepStarterPacks"
import type { StepActor } from "./StepActor"
import type { StepActorOutput } from "./StepActor"

/**
* Type representing the output of the starter packs retrieved by {@link StepActorStarterPacks}.
* @public
*/
export type StepActorStarterPacksOutput = StepStarterPacksOutput

/**
* Type representing the query parameters for retrieving actor starter packs.
* @public
*/
export type StepActorStarterPacksQueryParams = AppBskyGraphGetActorStarterPacks.QueryParams

/**
* Type representing the cursor for paginated queries.
* @public
*/
export type StepActorStarterPacksQueryParamsCursor = StepActorStarterPacksQueryParams["cursor"] | undefined

/**
* Represents a step for retrieving an actor's starter packs using the Bluesky API.
* Supports paginated retrieval of starter packs.
*
* @typeParam P - Type of the parent step, extending {@link StepActor}.
* @typeParam C - Type of the context object, extending {@link StepActorOutput}.
* @typeParam O - Type of the output object, extending {@link StepActorStarterPacksOutput}.
*
* @example
* Get an actor's starter packs:
* ```ts
* await Trotsky.init(agent)
* .actor("alice.bsky.social")
* .starterPacks()
* .each()
* .tap((step) => {
* console.log(`Pack: ${step.context.uri}`)
* console.log(`Creator: @${step.context.creator.handle}`)
* console.log(`Members: ${step.context.listItemCount || 0}`)
* })
* .run()
* ```
*
* @example
* Follow members from an actor's popular starter packs:
* ```ts
* await Trotsky.init(agent)
* .actor("bob.bsky.social")
* .starterPacks()
* .each()
* .when((step) => (step?.context?.joinedAllTimeCount || 0) > 50)
* .tap((step) => {
* console.log(`Popular pack: ${step.context.uri}`)
* })
* .run()
* ```
*
* @public
*/
export class StepActorStarterPacks<P = StepActor, C extends StepActorOutput = StepActorOutput, O extends StepActorStarterPacksOutput = StepActorStarterPacksOutput> extends StepStarterPacks<P, C, O> {

/**
* Applies pagination to retrieve starter packs and sets the output.
* Fetches paginated results using the agent and appends them to the output.
*/
async applyPagination () {
this.output = await this.paginate<O, AppBskyGraphGetActorStarterPacks.Response>("starterPacks", (cursor) => {
return this.agent.app.bsky.graph.getActorStarterPacks(this.queryParams(cursor))
})
}

/**
* Generates query parameters for retrieving starter packs, including the optional cursor.
* @param cursor - The cursor for paginated queries.
* @returns The query parameters for retrieving starter packs.
* @throws
* Error if no context is found.
*/
queryParams (cursor: StepActorStarterPacksQueryParamsCursor): StepActorStarterPacksQueryParams {
if (!this.context) {
throw new Error("No context found for StepActorStarterPacks")
}

return { "actor": this.context.did, cursor }
}
}
4 changes: 2 additions & 2 deletions lib/core/StepActorStreamPosts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type PostRecord } from "@atproto/api"
import { type AppBskyFeedPostRecord } from "@atproto/api"
import { StepStreamPosts, type StepActorOutput } from "../trotsky"
import { buildEventEmitter, JetstreamMessageCommit } from "./utils/jetstream"

Expand All @@ -7,7 +7,7 @@ import { buildEventEmitter, JetstreamMessageCommit } from "./utils/jetstream"
* Typically represents the streamed output of a single post event.
* @public
*/
export type StepActorStreamPostsOutput = JetstreamMessageCommit & { "record": Partial<PostRecord> }
export type StepActorStreamPostsOutput = JetstreamMessageCommit & { "record": Partial<AppBskyFeedPostRecord> }

/**
* @experimental
Expand Down
120 changes: 120 additions & 0 deletions lib/core/StepSearchStarterPacks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import type { AppBskyGraphSearchStarterPacks, AtpAgent } from "@atproto/api"

import { StepStarterPacks } from "../trotsky"

/**
* Represents the output of a search starter packs step, consisting of an array of starter packs.
* @public
*/
export type StepSearchStarterPacksOutput = AppBskyGraphSearchStarterPacks.OutputSchema["starterPacks"]

/**
* Represents the query parameters for searching starter packs.
* @public
*/
export type StepSearchStarterPacksQueryParams = AppBskyGraphSearchStarterPacks.QueryParams

/**
* Represents the cursor for paginating through search starter pack results.
* @public
*/
export type StepSearchStarterPacksQueryParamsCursor = StepSearchStarterPacksQueryParams["cursor"] | undefined

/**
* Represents a step for searching starter packs on Bluesky, with support for pagination.
*
* @remarks
* Note: The `searchStarterPacks` API endpoint may not be available on all Bluesky servers.
* As of November 2025, this API is available in test environments but not yet deployed to
* the main Bluesky network. When the API is not available, it will throw an XRPCNotSupported error.
*
* @typeParam P - The parent type of this step.
* @typeParam C - The child context type, defaulting to `null`.
* @typeParam O - The output type, defaulting to {@link StepSearchStarterPacksOutput}.
*
* @example
* Search for starter packs and display them:
* ```ts
* await Trotsky.init(agent)
* .searchStarterPacks({ q: "typescript" })
* .take(10)
* .each()
* .tap((step) => {
* console.log(`Pack URI: ${step.context.uri}`)
* console.log(`Creator: @${step.context.creator.handle}`)
* console.log(`Members: ${step.context.listItemCount || 0}`)
* })
* .run()
* ```
*
* @example
* Filter starter packs by join count:
* ```ts
* await Trotsky.init(agent)
* .searchStarterPacks({ q: "tech" })
* .take(20)
* .each()
* .when((step) => (step?.context?.joinedAllTimeCount || 0) > 100)
* .tap((step) => {
* console.log(`Popular pack: ${step.context.uri}`)
* })
* .run()
* ```
*
* @public
*/
export class StepSearchStarterPacks<P, C = null, O extends StepSearchStarterPacksOutput = StepSearchStarterPacksOutput> extends StepStarterPacks<P, C, O> {

/**
* The initial query parameters for the search.
*/
_queryParams: StepSearchStarterPacksQueryParams

/**
* Initializes the StepSearchStarterPacks instance with the given agent, parent, and query parameters.
*
* @param agent - The AT protocol agent used for API calls.
* @param parent - The parent step in the chain.
* @param queryParams - The initial query parameters for the search.
*/
constructor (agent: AtpAgent, parent: P, queryParams: StepSearchStarterPacksQueryParams) {
super(agent, parent)
this._queryParams = queryParams
}

/**
* Clones the current step and returns a new instance with the same parameters.
* @param rest - Additional parameters to pass to the cloned step. This is useful for child class overriding the clone.
* @returns A new {@link StepSearchStarterPacks} instance.
*/
override clone (...rest: unknown[]) {
const step = super.clone(...rest)
step._queryParams = this._queryParams
return step
}

/**
* Applies the pagination logic to retrieve starter packs based on the search parameters.
* Results are stored in the `output` property.
*
* @override
*/
async applyPagination () {
this.output = await this.paginate<O, AppBskyGraphSearchStarterPacks.Response>("starterPacks", (cursor) => {
return this
.agent
.app.bsky.graph
.searchStarterPacks(this.queryParams(cursor))
})
}

/**
* Constructs the query parameters for the current pagination step.
*
* @param cursor - The cursor indicating the current position in the paginated results.
* @returns The query parameters for the search.
*/
queryParams (cursor: StepSearchStarterPacksQueryParamsCursor): StepSearchStarterPacksQueryParams {
return { ...this._queryParams, cursor }
}
}
111 changes: 111 additions & 0 deletions lib/core/StepStarterPack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type { AtUri, AtpAgent, AppBskyGraphGetStarterPack, AppBskyGraphDefs } from "@atproto/api"

import { Step, type StepBuilder } from "../trotsky"
import type { Resolvable } from "./utils/resolvable"
import { resolveValue } from "./utils/resolvable"

/**
* Defines the query parameters for retrieving a starter pack.
* @public
*/
export type StepStarterPackQueryParams = AppBskyGraphGetStarterPack.QueryParams

/**
* Represents a starter pack's URI, which can be a string or an {@link AtUri}.
* @public
*/
export type StepStarterPackUri = string | AtUri

/**
* Represents the output of a retrieved starter pack, including its URI, CID, record data, creator, and associated lists/feeds.
* @public
*/
export type StepStarterPackOutput = AppBskyGraphDefs.StarterPackView

/**
* Represents a step for retrieving a starter pack by its URI.
*
* @typeParam P - The parent type of this step, defaulting to {@link StepBuilder}.
* @typeParam C - The child context type, defaulting to `null`.
* @typeParam O - The output type, defaulting to {@link StepStarterPackOutput}.
*
* @example
* Get a specific starter pack:
* ```ts
* await Trotsky.init(agent)
* .starterPack("at://did:plc:example/app.bsky.graph.starterpack/packid")
* .tap((step) => {
* console.log(`Creator: ${step.context.creator.handle}`)
* console.log(`List: ${step.context.list?.name}`)
* console.log(`Joined this week: ${step.context.joinedWeekCount}`)
* })
* .run()
* ```
*
* @example
* Get starter pack details and display feeds:
* ```ts
* await Trotsky.init(agent)
* .starterPack("at://did:plc:example/app.bsky.graph.starterpack/packid")
* .tap((step) => {
* console.log(`Feeds in pack: ${step.context.feeds?.length || 0}`)
* step.context.feeds?.forEach(feed => {
* console.log(`- ${feed.displayName}`)
* })
* })
* .run()
* ```
*
* @public
*/
export class StepStarterPack<P = StepBuilder, C = null, O extends StepStarterPackOutput = StepStarterPackOutput> extends Step<P, C, O> {

/**
* The URI of the starter pack to retrieve, which can be resolved dynamically at runtime.
* @internal
*/
_uri: Resolvable<StepStarterPackUri>

/**
* Initializes the StepStarterPack instance with the given agent, parent, and URI.
*
* @param agent - The AT protocol agent used for API calls.
* @param parent - The parent step in the chain.
* @param uri - The URI of the starter pack to retrieve, possibly resolvable at runtime.
*/
constructor (agent: AtpAgent, parent: P, uri: Resolvable<StepStarterPackUri>) {
super(agent, parent)
this._uri = uri
}

/**
* Clones the current step and returns a new instance with the same parameters.
* @param rest - Additional parameters to pass to the cloned step. This is useful for child class overriding the clone.
* @returns A new {@link StepStarterPack} instance.
*/
override clone (...rest: unknown[]) {
return super.clone(this._uri, ...rest)
}

/**
* Applies the step logic to retrieve the starter pack and sets the output to the retrieved starter pack's data.
* If no starter pack is found, the output is set to `null`.
*
* @override
*/
async apply () {
const { "data": { starterPack } } = await this.agent.app.bsky.graph.getStarterPack(await this.queryParams())
this.output = starterPack as O ?? null
}

/**
* Resolves the query parameters for retrieving the starter pack, including its URI.
*
* @returns A promise that resolves to the query parameters.
*/
async queryParams (): Promise<StepStarterPackQueryParams> {
const uri = await resolveValue<StepStarterPackUri>(this, this._uri)
const starterPack = uri.toString?.() ?? uri.toString()
return { starterPack }
}
}
Loading