Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
24 KiB
@atproto/xrpc
0.7.5
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.5.1
0.7.4
Patch Changes
-
#4108
f9dc9aa4cThanks @matthieusieben! - Explicitly set theredirect: "follow"infetch()calls -
Updated dependencies [
f9dc9aa4c]:- @atproto/lexicon@0.5.0
0.7.3
Patch Changes
- Updated dependencies [
2104d9033]:- @atproto/lexicon@0.4.14
0.7.2
Patch Changes
- Updated dependencies [
331a356ce]:- @atproto/lexicon@0.4.13
0.7.1
Patch Changes
- Updated dependencies [
8ef976d38]:- @atproto/lexicon@0.4.12
0.7.0
Minor Changes
-
#3792
f36ab48d9Thanks @matthieusieben! - RenameResponseType.AuthRequiredintoResponseType.AuthenticationRequiredto match actual error name. -
#3792
f36ab48d9Thanks @matthieusieben! - Remove un-necessaryResponseTypeNamesin favor ofResponseType.
Patch Changes
-
#3792
f36ab48d9Thanks @matthieusieben! - Add missingNotAcceptablekey inResponseTypeStrings -
Updated dependencies [
cc485d296]:- @atproto/lexicon@0.4.11
0.6.12
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.4.10
0.6.11
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.4.9
0.6.10
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.4.8
0.6.9
Patch Changes
0.6.8
Patch Changes
-
#3220
61dc0d60eThanks @matthieusieben! - Apply new linting rules regarding import order -
Updated dependencies [
61dc0d60e]:- @atproto/lexicon@0.4.6
0.6.7
Patch Changes
- #3456
fb64d50eeThanks @matthieusieben! - Explicitly allow "undefined" values inheaders
0.6.6
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.4.5
0.6.5
Patch Changes
- Updated dependencies [
9fd65ba0f]:- @atproto/lexicon@0.4.4
0.6.4
Patch Changes
- Updated dependencies [
bac9be2d3]:- @atproto/lexicon@0.4.3
0.6.3
Patch Changes
-
#2770
a07b21151Thanks @matthieusieben! - Add NotAcceptable response type -
Updated dependencies [
87a1f2426]:- @atproto/lexicon@0.4.2
0.6.2
Patch Changes
- #2464
98711a147Thanks @matthieusieben! - Add UnsupportedMediaType response type
0.6.1
Patch Changes
-
#2714
d9ffa3c46Thanks @matthieusieben! - Improve handling of fetchHandler errors when turning them intoXrpcError. -
#2714
d9ffa3c46Thanks @matthieusieben! - Add ability to instantiate XrpcClient from FetchHandlerObject type -
#2714
d9ffa3c46Thanks @matthieusieben! - Add global headers toXrpcClientinstances
0.6.0
Minor Changes
-
#2483
b934b396bThanks @matthieusieben!Motivation
The motivation for these changes is the need to make the
@atproto/apipackage compatible with OAuth session management. We don't have OAuth client support "launched" and documented quite yet, so you can keep using the current app password authentication system. When we do "launch" OAuth support and begin encouraging its usage in the near future (see the OAuth Roadmap), these changes will make it easier to migrate.In addition, the redesigned session management system fixes a bug that could cause the session data to become invalid when Agent clones are created (e.g. using
agent.withProxy()).New Features
We've restructured the
XrpcClientHTTP fetch handler to be specified during the instantiation of the XRPC client, through the constructor, instead of using a default implementation (which was statically defined).With this refactor, the XRPC client is now more modular and reusable. Session management, retries, cryptographic signing, and other request-specific logic can be implemented in the fetch handler itself rather than by the calling code.
A new abstract class named
Agent, has been added to@atproto/api. This class will be the base class for all Bluesky agents classes in the@atprotoecosystem. It is meant to be extended by implementations that provide session management and fetch handling.As you adapt your code to these changes, make sure to use the
Agenttype wherever you expect to receive an agent, and use theAtpAgenttype (class) only to instantiate your client. The reason for this is to be forward compatible with the OAuth agent implementation that will also extendAgent, and notAtpAgent.import { Agent, AtpAgent } from "@atproto/api"; async function setupAgent( service: string, username: string, password: string, ): Promise<Agent> { const agent = new AtpAgent({ service, persistSession: (evt, session) => { // handle session update }, }); await agent.login(username, password); return agent; }import { Agent } from "@atproto/api"; async function doStuffWithAgent(agent: Agent, arg: string) { return agent.resolveHandle(arg); }import { Agent, AtpAgent } from "@atproto/api"; class MyClass { agent: Agent; constructor() { this.agent = new AtpAgent(); } }Breaking changes
Most of the changes introduced in this version are backward-compatible. However, there are a couple of breaking changes you should be aware of:
- Customizing
fetch: The ability to customize thefetch: FetchHandlerproperty of@atproto/xrpc'sClientand@atproto/api'sAtpAgentclasses has been removed. Previously, thefetchproperty could be set to a function that would be used as the fetch handler for that instance, and was initialized to a default fetch handler. That property is still accessible in a read-only fashion through thefetchHandlerproperty and can only be set during the instance creation. Attempting to set/get thefetchproperty will now result in an error. - The
fetch()method, as well as WhatWG compliantRequestandHeadersconstructors, must be globally available in your environment. Use a polyfill if necessary. - The
AtpBaseClienthas been removed. TheAtpServiceClienthas been renamedAtpBaseClient. Any code using either of these classes will need to be updated. - Instead of wrapping an
XrpcClientin itsxrpcproperty, theAtpBaseClient(formerlyAtpServiceClient) class - created throughlex-cli- now extends theXrpcClientclass. This means that a client instance now passes theinstanceof XrpcClientcheck. Thexrpcproperty now returns the instance itself and has been deprecated. setSessionPersistHandleris no longer available on theAtpAgentorBskyAgentclasses. The session handler can only be set though thepersistSessionoptions of theAtpAgentconstructor.- The new class hierarchy is as follows:
BskyAgentextendsAtpAgent: but add no functionality (hence its deprecation).AtpAgentextendsAgent: adds password based session management.AgentextendsAtpBaseClient: this abstract class that adds syntactic sugar methodsapp.bskylexicons. It also adds abstract session management methods and adds atproto specific utilities (labelers&proxyheaders, cloning capability)AtpBaseClientextendsXrpcClient: automatically code that adds fully typed lexicon defined namespaces (instance.app.bsky.feed.getPosts()) to theXrpcClient.XrpcClientis the base class.
Non-breaking changes
- The
com.*andapp.*namespaces have been made directly available to everyAgentinstances.
Deprecations
- The default export of the
@atproto/xrpcpackage has been deprecated. Use named exports instead. - The
ClientandServiceClientclasses are now deprecated. They are replaced by a singleXrpcClientclass. - The default export of the
@atproto/apipackage has been deprecated. Use named exports instead. - The
BskyAgenthas been deprecated. Use theAtpAgentclass instead. - The
xrpcproperty of theAtpClientinstances has been deprecated. The instance itself should be used as the XRPC client. - The
apiproperty of theAtpAgentandBskyAgentinstances has been deprecated. Use the instance itself instead.
Migration
The
@atproto/apipackageIf you were relying on the
AtpBaseClientsolely to perform validation, use this:Before After import { AtpBaseClient, ComAtprotoSyncSubscribeRepos } from "@atproto/api"; const baseClient = new AtpBaseClient(); baseClient.xrpc.lex.assertValidXrpcMessage("io.example.doStuff", { // ... });import { lexicons } from "@atproto/api"; lexicons.assertValidXrpcMessage("io.example.doStuff", { // ... });If you are extending the
BskyAgentto perform customsessionmanipulation, define your ownAgentsubclass instead:Before After import { BskyAgent } from "@atproto/api"; class MyAgent extends BskyAgent { private accessToken?: string; async createOrRefreshSession(identifier: string, password: string) { // custom logic here this.accessToken = "my-access-jwt"; } async doStuff() { return this.call("io.example.doStuff", { headers: { Authorization: this.accessToken && `Bearer ${this.accessToken}`, }, }); } }import { Agent } from "@atproto/api"; class MyAgent extends Agent { private accessToken?: string; public did?: string; constructor(private readonly service: string | URL) { super({ service, headers: { Authorization: () => this.accessToken ? `Bearer ${this.accessToken}` : null, }, }); } clone(): MyAgent { const agent = new MyAgent(this.service); agent.accessToken = this.accessToken; agent.did = this.did; return this.copyInto(agent); } async createOrRefreshSession(identifier: string, password: string) { // custom logic here this.did = "did:example:123"; this.accessToken = "my-access-jwt"; } }If you are monkey patching the
xrpcservice client to perform client-side rate limiting, you can now do this in theFetchHandlerfunction:Before After import { BskyAgent } from "@atproto/api"; import { RateLimitThreshold } from "rate-limit-threshold"; const agent = new BskyAgent(); const limiter = new RateLimitThreshold(3000, 300_000); const origCall = agent.api.xrpc.call; agent.api.xrpc.call = async function (...args) { await limiter.wait(); return origCall.call(this, ...args); };import { AtpAgent } from "@atproto/api"; import { RateLimitThreshold } from "rate-limit-threshold"; class LimitedAtpAgent extends AtpAgent { constructor(options: AtpAgentOptions) { const fetch: typeof globalThis.fetch = options.fetch ?? globalThis.fetch; const limiter = new RateLimitThreshold(3000, 300_000); super({ ...options, fetch: async (...args) => { await limiter.wait(); return fetch(...args); }, }); } }If you configure a static
fetchhandler on theBskyAgentclass - for example to modify the headers of every request - you can now do this by providing your ownfetchfunction:Before After import { BskyAgent, defaultFetchHandler } from "@atproto/api"; BskyAgent.configure({ fetch: async (httpUri, httpMethod, httpHeaders, httpReqBody) => { const ua = httpHeaders["User-Agent"]; httpHeaders["User-Agent"] = ua ? `${ua} ${userAgent}` : userAgent; return defaultFetchHandler(httpUri, httpMethod, httpHeaders, httpReqBody); }, });import { AtpAgent } from "@atproto/api"; class MyAtpAgent extends AtpAgent { constructor(options: AtpAgentOptions) { const fetch = options.fetch ?? globalThis.fetch; super({ ...options, fetch: async (url, init) => { const headers = new Headers(init.headers); const ua = headersList.get("User-Agent"); headersList.set("User-Agent", ua ? `${ua} ${userAgent}` : userAgent); return fetch(url, { ...init, headers }); }, }); } }The
@atproto/xrpcpackageThe
ClientandServiceClientclasses are now deprecated. If you need a lexicon based client, you should update the code to use theXrpcClientclass instead.The deprecated
ServiceClientclass now extends the newXrpcClientclass. Because of this, thefetchFetchHandlercan no longer be configured on theClientinstances (including the default export of the package). If you are not relying on thefetchFetchHandler, the new changes should have no impact on your code. Beware that the deprecated classes will eventually be removed in a future version.Since its use has completely changed, the
FetchHandlertype has also completely changed. The newFetchHandlertype is now a function that receives aurlpathname and aRequestInitobject and returns aPromise<Response>. This function is responsible for making the actual request to the server.export type FetchHandler = ( this: void, /** * The URL (pathname + query parameters) to make the request to, without the * origin. The origin (protocol, hostname, and port) must be added by this * {@link FetchHandler}, typically based on authentication or other factors. */ url: string, init: RequestInit, ) => Promise<Response>;A noticeable change that has been introduced is that the
urifield of theServiceClientclass has not been ported to the newXrpcClientclass. It is now the responsibility of theFetchHandlerto determine the full URL to make the request to. The same goes for theheaders, which should now be set through theFetchHandlerfunction.If you do rely on the legacy
Client.fetchproperty to perform custom logic upon request, you will need to migrate your code to use the newXrpcClientclass. TheXrpcClientclass has a similar API to the oldServiceClientclass, but with a few differences:-
The
Client+ServiceClientduality was removed in favor of a singleXrpcClientclass. This means that:- There no longer exists a centralized lexicon registry. If you need a global
lexicon registry, you can maintain one yourself using a
new Lexicons(from@atproto/lexicon). - The
FetchHandleris no longer a statically defined property of theClientclass. Instead, it is passed as an argument to theXrpcClientconstructor.
- There no longer exists a centralized lexicon registry. If you need a global
lexicon registry, you can maintain one yourself using a
-
The
XrpcClientconstructor now requires aFetchHandlerfunction as the first argument, and an optionalLexiconinstance as the second argument. -
The
setHeaderandunsetHeadermethods were not ported to the newXrpcClientclass. If you need to set or unset headers, you should do so in theFetchHandlerfunction provided in the constructor arg.
Before After import client, { defaultFetchHandler } from "@atproto/xrpc"; client.fetch = function ( httpUri: string, httpMethod: string, httpHeaders: Headers, httpReqBody: unknown, ) { // Custom logic here return defaultFetchHandler(httpUri, httpMethod, httpHeaders, httpReqBody); }; client.addLexicon({ lexicon: 1, id: "io.example.doStuff", defs: {}, }); const instance = client.service("http://my-service.com"); instance.setHeader("my-header", "my-value"); await instance.call("io.example.doStuff");import { XrpcClient } from "@atproto/xrpc"; const instance = new XrpcClient( async (url, init) => { const headers = new Headers(init.headers); headers.set("my-header", "my-value"); // Custom logic here const fullUrl = new URL(url, "http://my-service.com"); return fetch(fullUrl, { ...init, headers }); }, [ { lexicon: 1, id: "io.example.doStuff", defs: {}, }, ], ); await instance.call("io.example.doStuff");If your fetch handler does not require any "custom logic", and all you need is an
XrpcClientthat makes its HTTP requests towards a static service URL, the previous example can be simplified to:import { XrpcClient } from "@atproto/xrpc"; const instance = new XrpcClient("http://my-service.com", [ { lexicon: 1, id: "io.example.doStuff", defs: {}, }, ]);If you need to add static headers to all requests, you can instead instantiate the
XrpcClientas follows:import { XrpcClient } from "@atproto/xrpc"; const instance = new XrpcClient( { service: "http://my-service.com", headers: { "my-header": "my-value", }, }, [ { lexicon: 1, id: "io.example.doStuff", defs: {}, }, ], );If you need the headers or service url to be dynamic, you can define them using functions:
import { XrpcClient } from "@atproto/xrpc"; const instance = new XrpcClient( { service: () => "http://my-service.com", headers: { "my-header": () => "my-value", "my-ignored-header": () => null, // ignored }, }, [ { lexicon: 1, id: "io.example.doStuff", defs: {}, }, ], ); - Customizing
-
#2483
b934b396bThanks @matthieusieben! - Add the ability to usefetch()compatibleBodyInitbody when making XRPC calls.
Patch Changes
0.5.0
Minor Changes
- #2169
f689bd51aThanks @matthieusieben! - Build system rework, stop bundling dependencies.
Patch Changes
- Updated dependencies [
f689bd51a]:- @atproto/lexicon@0.4.0
0.4.3
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.3.3
0.4.2
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.3.2
0.4.1
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.3.1
0.4.0
Minor Changes
- #1801
ce49743dThanks @gaearon! - Methods that accepts lexicons now takeLexiconDoctype instead ofunknown
Patch Changes
-
#1788
84e2d4d2Thanks @bnewbold! - update license to "MIT or Apache2" -
Updated dependencies [
ce49743d,84e2d4d2]:- @atproto/lexicon@0.3.0
0.3.3
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.2.3
0.3.2
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.2.2
0.3.1
Patch Changes
- Updated dependencies []:
- @atproto/lexicon@0.2.1