@atcute/client 1.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -7
- package/dist/{middlewares/auth.d.ts → credential-manager.d.ts} +16 -14
- package/dist/credential-manager.js +177 -0
- package/dist/credential-manager.js.map +1 -0
- package/dist/fetch-handler.d.ts +12 -0
- package/dist/fetch-handler.js +13 -0
- package/dist/fetch-handler.js.map +1 -0
- package/dist/index.d.ts +3 -177
- package/dist/index.js +3 -206
- package/dist/index.js.map +1 -1
- package/dist/lexicons.d.ts +38 -14
- package/dist/rpc.d.ts +96 -0
- package/dist/rpc.js +173 -0
- package/dist/rpc.js.map +1 -0
- package/dist/utils/did.d.ts +3 -1
- package/dist/utils/did.js +3 -1
- package/dist/utils/did.js.map +1 -1
- package/dist/utils/http.d.ts +7 -0
- package/dist/utils/http.js +20 -0
- package/dist/utils/http.js.map +1 -0
- package/package.json +7 -4
- package/dist/middlewares/auth.js +0 -150
- package/dist/middlewares/auth.js.map +0 -1
- package/dist/middlewares/mod.d.ts +0 -24
- package/dist/middlewares/mod.js +0 -24
- package/dist/middlewares/mod.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,22 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
lightweight and cute API client for AT Protocol.
|
|
4
4
|
|
|
5
|
-
- Small,
|
|
5
|
+
- Small, the bare minimum is ~1 kB gzipped with the full package at ~2.4 kB gzipped.
|
|
6
6
|
- No validations, type definitions match actual HTTP responses.
|
|
7
7
|
|
|
8
8
|
This package only contains the base AT Protocol lexicons and endpoints, along with an authentication middleware.
|
|
9
9
|
For Bluesky-related lexicons, see `@atcute/bluesky` package.
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
|
-
import { XRPC } from '@atcute/client';
|
|
13
|
-
import { AtpAuth } from '@atcute/client/middlewares/auth';
|
|
12
|
+
import { XRPC, CredentialManager } from '@atcute/client';
|
|
14
13
|
|
|
15
|
-
const
|
|
16
|
-
const
|
|
14
|
+
const manager = new CredentialManager({ service: 'https://bsky.social' });
|
|
15
|
+
const rpc = new XRPC({ handler: manager });
|
|
17
16
|
|
|
18
|
-
await
|
|
17
|
+
await manager.login({ identifier: 'example.com', password: 'ofki-yrwl-hmcc-cvau' });
|
|
19
18
|
|
|
20
|
-
console.log(
|
|
19
|
+
console.log(manager.session);
|
|
21
20
|
// -> { refreshJwt: 'eyJhb...', ... }
|
|
22
21
|
|
|
23
22
|
const { data } = await rpc.get('com.atproto.identity.resolveHandle', {
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Contains a middleware that handles authentication to a personal data server.
|
|
4
|
-
*/
|
|
5
|
-
import { type XRPC } from '../index.js';
|
|
6
|
-
import type { At } from '../lexicons.js';
|
|
1
|
+
import type { At } from './lexicons.js';
|
|
2
|
+
import { type FetchHandlerObject } from './fetch-handler.js';
|
|
7
3
|
/** Interface for the decoded access token, for convenience */
|
|
8
4
|
export interface AtpAccessJwt {
|
|
9
5
|
/** Access token scope, app password returns a different scope. */
|
|
@@ -53,21 +49,27 @@ export interface AtpSessionData {
|
|
|
53
49
|
/** Possible reason for why the account is inactive */
|
|
54
50
|
inactiveStatus?: string;
|
|
55
51
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
export interface CredentialManagerOptions {
|
|
53
|
+
/** PDS server URL */
|
|
54
|
+
service: string;
|
|
55
|
+
/** Custom fetch function */
|
|
56
|
+
fetch?: typeof globalThis.fetch;
|
|
57
|
+
/** Function that gets called if the session turned out to have expired during an XRPC request */
|
|
59
58
|
onExpired?: (session: AtpSessionData) => void;
|
|
60
|
-
/**
|
|
59
|
+
/** Function that gets called if the session has been refreshed during an XRPC request */
|
|
61
60
|
onRefresh?: (session: AtpSessionData) => void;
|
|
62
|
-
/**
|
|
61
|
+
/** Function that gets called if the session object has been refreshed */
|
|
63
62
|
onSessionUpdate?: (session: AtpSessionData) => void;
|
|
64
63
|
}
|
|
65
|
-
|
|
66
|
-
export declare class AtpAuth {
|
|
64
|
+
export declare class CredentialManager implements FetchHandlerObject {
|
|
67
65
|
#private;
|
|
66
|
+
readonly serviceUrl: string;
|
|
67
|
+
fetch: typeof fetch;
|
|
68
68
|
/** Current session state */
|
|
69
69
|
session?: AtpSessionData;
|
|
70
|
-
constructor(
|
|
70
|
+
constructor({ service, onExpired, onRefresh, onSessionUpdate, fetch: _fetch, }: CredentialManagerOptions);
|
|
71
|
+
get dispatchUrl(): string;
|
|
72
|
+
handle(pathname: string, init: RequestInit): Promise<Response>;
|
|
71
73
|
/**
|
|
72
74
|
* Resume a saved session
|
|
73
75
|
* @param session Session information, taken from `AtpAuth#session` after login
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { simpleFetchHandler } from './fetch-handler.js';
|
|
2
|
+
import { XRPC, XRPCError } from './rpc.js';
|
|
3
|
+
import { getPdsEndpoint } from './utils/did.js';
|
|
4
|
+
import { decodeJwt } from './utils/jwt.js';
|
|
5
|
+
export class CredentialManager {
|
|
6
|
+
#server;
|
|
7
|
+
#refreshSessionPromise;
|
|
8
|
+
#onExpired;
|
|
9
|
+
#onRefresh;
|
|
10
|
+
#onSessionUpdate;
|
|
11
|
+
constructor({ service, onExpired, onRefresh, onSessionUpdate, fetch: _fetch = fetch, }) {
|
|
12
|
+
this.serviceUrl = service;
|
|
13
|
+
this.fetch = _fetch;
|
|
14
|
+
this.#server = new XRPC({ handler: simpleFetchHandler({ service: service, fetch: _fetch }) });
|
|
15
|
+
this.#onRefresh = onRefresh;
|
|
16
|
+
this.#onExpired = onExpired;
|
|
17
|
+
this.#onSessionUpdate = onSessionUpdate;
|
|
18
|
+
}
|
|
19
|
+
get dispatchUrl() {
|
|
20
|
+
return this.session?.pdsUri ?? this.serviceUrl;
|
|
21
|
+
}
|
|
22
|
+
async handle(pathname, init) {
|
|
23
|
+
await this.#refreshSessionPromise;
|
|
24
|
+
const url = new URL(pathname, this.dispatchUrl);
|
|
25
|
+
const request = new Request(url, init);
|
|
26
|
+
if (!this.session || request.headers.has('authorization')) {
|
|
27
|
+
return (0, this.fetch)(request);
|
|
28
|
+
}
|
|
29
|
+
request.headers.set('authorization', `Bearer ${this.session.accessJwt}`);
|
|
30
|
+
const initialResponse = await (0, this.fetch)(request);
|
|
31
|
+
const isExpired = await isExpiredTokenResponse(initialResponse);
|
|
32
|
+
if (!isExpired) {
|
|
33
|
+
return initialResponse;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
await this.#refreshSession();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return initialResponse;
|
|
40
|
+
}
|
|
41
|
+
// Return initial response if:
|
|
42
|
+
// - refreshSession returns expired
|
|
43
|
+
// - Body stream has been consumed
|
|
44
|
+
if (!this.session || init.body instanceof ReadableStream) {
|
|
45
|
+
return initialResponse;
|
|
46
|
+
}
|
|
47
|
+
request.headers.set('authorization', `Bearer ${this.session.accessJwt}`);
|
|
48
|
+
return await (0, this.fetch)(request);
|
|
49
|
+
}
|
|
50
|
+
#refreshSession() {
|
|
51
|
+
return (this.#refreshSessionPromise ||= this.#refreshSessionInner().finally(() => (this.#refreshSessionPromise = undefined)));
|
|
52
|
+
}
|
|
53
|
+
async #refreshSessionInner() {
|
|
54
|
+
const currentSession = this.session;
|
|
55
|
+
if (!currentSession) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const { data } = await this.#server.call('com.atproto.server.refreshSession', {
|
|
60
|
+
headers: {
|
|
61
|
+
authorization: `Bearer ${currentSession.refreshJwt}`,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
this.#updateSession({ ...currentSession, ...data });
|
|
65
|
+
this.#onRefresh?.(this.session);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (err instanceof XRPCError) {
|
|
69
|
+
const kind = err.kind;
|
|
70
|
+
if (kind === 'ExpiredToken' || kind === 'InvalidToken') {
|
|
71
|
+
this.session = undefined;
|
|
72
|
+
this.#onExpired?.(currentSession);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
#updateSession(raw) {
|
|
78
|
+
const didDoc = raw.didDoc;
|
|
79
|
+
let pdsUri;
|
|
80
|
+
if (didDoc) {
|
|
81
|
+
pdsUri = getPdsEndpoint(didDoc);
|
|
82
|
+
}
|
|
83
|
+
const newSession = {
|
|
84
|
+
accessJwt: raw.accessJwt,
|
|
85
|
+
refreshJwt: raw.refreshJwt,
|
|
86
|
+
handle: raw.handle,
|
|
87
|
+
did: raw.did,
|
|
88
|
+
pdsUri: pdsUri,
|
|
89
|
+
email: raw.email,
|
|
90
|
+
emailConfirmed: raw.emailConfirmed,
|
|
91
|
+
emailAuthFactor: raw.emailConfirmed,
|
|
92
|
+
active: raw.active ?? true,
|
|
93
|
+
inactiveStatus: raw.status,
|
|
94
|
+
};
|
|
95
|
+
this.session = newSession;
|
|
96
|
+
this.#onSessionUpdate?.(newSession);
|
|
97
|
+
return newSession;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Resume a saved session
|
|
101
|
+
* @param session Session information, taken from `AtpAuth#session` after login
|
|
102
|
+
*/
|
|
103
|
+
async resume(session) {
|
|
104
|
+
const now = Date.now() / 1000 + 60 * 5;
|
|
105
|
+
const refreshToken = decodeJwt(session.refreshJwt);
|
|
106
|
+
if (now >= refreshToken.exp) {
|
|
107
|
+
throw new XRPCError(401, { kind: 'InvalidToken' });
|
|
108
|
+
}
|
|
109
|
+
const accessToken = decodeJwt(session.accessJwt);
|
|
110
|
+
this.session = session;
|
|
111
|
+
if (now >= accessToken.exp) {
|
|
112
|
+
await this.#refreshSession();
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const promise = this.#server.get('com.atproto.server.getSession', {
|
|
116
|
+
headers: {
|
|
117
|
+
authorization: `Bearer ${session.accessJwt}`,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
promise.then((response) => {
|
|
121
|
+
const existing = this.session;
|
|
122
|
+
const next = response.data;
|
|
123
|
+
if (!existing) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
this.#updateSession({ ...existing, ...next });
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (!this.session) {
|
|
130
|
+
throw new XRPCError(401, { kind: 'InvalidToken' });
|
|
131
|
+
}
|
|
132
|
+
return this.session;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Perform a login operation
|
|
136
|
+
* @param options Login options
|
|
137
|
+
* @returns Session data that can be saved for later
|
|
138
|
+
*/
|
|
139
|
+
async login(options) {
|
|
140
|
+
// Reset the session
|
|
141
|
+
this.session = undefined;
|
|
142
|
+
const res = await this.#server.call('com.atproto.server.createSession', {
|
|
143
|
+
data: {
|
|
144
|
+
identifier: options.identifier,
|
|
145
|
+
password: options.password,
|
|
146
|
+
authFactorToken: options.code,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
return this.#updateSession(res.data);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const isExpiredTokenResponse = async (response) => {
|
|
153
|
+
if (response.status !== 400) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
if (extractContentType(response.headers) !== 'application/json') {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
// {"error":"ExpiredToken","message":"Token has expired"}
|
|
160
|
+
// {"error":"ExpiredToken","message":"Token is expired"}
|
|
161
|
+
if (extractContentLength(response.headers) > 54 * 1.5) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
const { error, message } = await response.clone().json();
|
|
166
|
+
return error === 'ExpiredToken' && (typeof message === 'string' || message === undefined);
|
|
167
|
+
}
|
|
168
|
+
catch { }
|
|
169
|
+
return false;
|
|
170
|
+
};
|
|
171
|
+
const extractContentType = (headers) => {
|
|
172
|
+
return headers.get('content-type')?.split(';')[0]?.trim();
|
|
173
|
+
};
|
|
174
|
+
const extractContentLength = (headers) => {
|
|
175
|
+
return Number(headers.get('content-length') ?? ';');
|
|
176
|
+
};
|
|
177
|
+
//# sourceMappingURL=credential-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-manager.js","sourceRoot":"","sources":["../lib/credential-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Fetch handler function */
|
|
2
|
+
export type FetchHandler = (pathname: string, init: RequestInit) => Promise<Response>;
|
|
3
|
+
/** Fetch handler in an object */
|
|
4
|
+
export interface FetchHandlerObject {
|
|
5
|
+
handle(this: FetchHandlerObject, pathname: string, init: RequestInit): Promise<Response>;
|
|
6
|
+
}
|
|
7
|
+
export declare const buildFetchHandler: (handler: FetchHandler | FetchHandlerObject) => FetchHandler;
|
|
8
|
+
export interface SimpleFetchHandlerOptions {
|
|
9
|
+
service: string | URL;
|
|
10
|
+
fetch?: typeof globalThis.fetch;
|
|
11
|
+
}
|
|
12
|
+
export declare const simpleFetchHandler: ({ service, fetch: _fetch, }: SimpleFetchHandlerOptions) => FetchHandler;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const buildFetchHandler = (handler) => {
|
|
2
|
+
if (typeof handler === 'object') {
|
|
3
|
+
return handler.handle.bind(handler);
|
|
4
|
+
}
|
|
5
|
+
return handler;
|
|
6
|
+
};
|
|
7
|
+
export const simpleFetchHandler = ({ service, fetch: _fetch = fetch, }) => {
|
|
8
|
+
return async (pathname, init) => {
|
|
9
|
+
const url = new URL(pathname, service);
|
|
10
|
+
return _fetch(url, init);
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=fetch-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-handler.js","sourceRoot":"","sources":["../lib/fetch-handler.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAA0C,EAAgB,EAAE;IAC7F,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAOF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,EAClC,OAAO,EACP,KAAK,EAAE,MAAM,GAAG,KAAK,GACM,EAAgB,EAAE;IAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;QAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC;AACH,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,177 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*/
|
|
5
|
-
import type { At, Procedures, Queries } from './lexicons.ts';
|
|
6
|
-
export type Headers = Record<string, string>;
|
|
7
|
-
/** Possible response status from an XRPC service, status <100 is used for the library itself. */
|
|
8
|
-
export declare const enum ResponseType {
|
|
9
|
-
/** Unknown error from the library */
|
|
10
|
-
Unknown = 1,
|
|
11
|
-
/** The server returned an invalid response */
|
|
12
|
-
InvalidResponse = 2,
|
|
13
|
-
/** Successful response from the service */
|
|
14
|
-
Success = 200,
|
|
15
|
-
/** Request was considered invalid by the service */
|
|
16
|
-
InvalidRequest = 400,
|
|
17
|
-
/** Service requires an authentication token */
|
|
18
|
-
AuthRequired = 401,
|
|
19
|
-
/** Request is forbidden by the service */
|
|
20
|
-
Forbidden = 403,
|
|
21
|
-
/** Not a XRPC service */
|
|
22
|
-
XRPCNotSupported = 404,
|
|
23
|
-
/** Payload is considered too large by the service */
|
|
24
|
-
PayloadTooLarge = 413,
|
|
25
|
-
/** Ratelimit was exceeded */
|
|
26
|
-
RateLimitExceeded = 429,
|
|
27
|
-
/** Internal server error */
|
|
28
|
-
InternalServerError = 500,
|
|
29
|
-
/** Method hasn't been implemented */
|
|
30
|
-
MethodNotImplemented = 501,
|
|
31
|
-
/** Failure by an upstream service */
|
|
32
|
-
UpstreamFailure = 502,
|
|
33
|
-
/** Not enough resources */
|
|
34
|
-
NotEnoughResouces = 503,
|
|
35
|
-
/** Timeout from upstream service */
|
|
36
|
-
UpstreamTimeout = 504
|
|
37
|
-
}
|
|
38
|
-
/** XRPC response status which are recoverable (network error) */
|
|
39
|
-
export declare const RECOVERABLE_RESPONSE_STATUS: number[];
|
|
40
|
-
/** Request type, either query (GET) or procedure (POST) */
|
|
41
|
-
export type RequestType = 'get' | 'post';
|
|
42
|
-
/** XRPC that gets passed around middlewares and eventually to the service. */
|
|
43
|
-
export interface XRPCRequest {
|
|
44
|
-
service: string;
|
|
45
|
-
type: RequestType;
|
|
46
|
-
nsid: string;
|
|
47
|
-
headers: Headers;
|
|
48
|
-
params: Record<string, unknown>;
|
|
49
|
-
encoding?: string;
|
|
50
|
-
data?: FormData | Blob | ArrayBufferView | Record<string, unknown>;
|
|
51
|
-
signal?: AbortSignal;
|
|
52
|
-
}
|
|
53
|
-
/** Response from XRPC service */
|
|
54
|
-
export interface XRPCResponse<T = any> {
|
|
55
|
-
data: T;
|
|
56
|
-
headers: Headers;
|
|
57
|
-
}
|
|
58
|
-
/** Options for constructing an XRPC error */
|
|
59
|
-
export interface XRPCErrorOptions {
|
|
60
|
-
kind?: string;
|
|
61
|
-
message?: string;
|
|
62
|
-
headers?: Headers;
|
|
63
|
-
cause?: unknown;
|
|
64
|
-
}
|
|
65
|
-
/** Error coming from the XRPC service */
|
|
66
|
-
export declare class XRPCError extends Error {
|
|
67
|
-
name: string;
|
|
68
|
-
/** Response status */
|
|
69
|
-
status: number;
|
|
70
|
-
/** Response headers */
|
|
71
|
-
headers: Headers;
|
|
72
|
-
/** Error kind */
|
|
73
|
-
kind?: string;
|
|
74
|
-
constructor(status: number, { kind, message, headers, cause }?: XRPCErrorOptions);
|
|
75
|
-
}
|
|
76
|
-
/** Response returned from middlewares and XRPC service */
|
|
77
|
-
export interface XRPCFetchReturn {
|
|
78
|
-
status: number;
|
|
79
|
-
headers: Headers;
|
|
80
|
-
body: unknown;
|
|
81
|
-
}
|
|
82
|
-
/** Fetch function */
|
|
83
|
-
export type XRPCFetch = (req: XRPCRequest) => Promise<XRPCFetchReturn>;
|
|
84
|
-
/** Function that constructs a middleware */
|
|
85
|
-
export type XRPCHook = (next: XRPCFetch) => XRPCFetch;
|
|
86
|
-
/** Options for constructing an XRPC class */
|
|
87
|
-
export interface XRPCOptions {
|
|
88
|
-
service: string;
|
|
89
|
-
}
|
|
90
|
-
/** Base options for the query/procedure request */
|
|
91
|
-
interface BaseRPCOptions {
|
|
92
|
-
/** `Content-Type` encoding for the input, defaults to `application/json` if passing a JSON object */
|
|
93
|
-
encoding?: string;
|
|
94
|
-
/** Request headers to make */
|
|
95
|
-
headers?: Headers;
|
|
96
|
-
/** Signal for aborting the request */
|
|
97
|
-
signal?: AbortSignal;
|
|
98
|
-
}
|
|
99
|
-
/** Options for the query/procedure request */
|
|
100
|
-
export type RPCOptions<T> = BaseRPCOptions & (T extends {
|
|
101
|
-
params: any;
|
|
102
|
-
} ? {
|
|
103
|
-
params: T['params'];
|
|
104
|
-
} : {}) & (T extends {
|
|
105
|
-
input: any;
|
|
106
|
-
} ? {
|
|
107
|
-
data: T['input'];
|
|
108
|
-
} : {});
|
|
109
|
-
type OutputOf<T> = T extends {
|
|
110
|
-
output: any;
|
|
111
|
-
} ? T['output'] : never;
|
|
112
|
-
/** The client that sends out requests. */
|
|
113
|
-
export declare class XRPC {
|
|
114
|
-
#private;
|
|
115
|
-
/** The service it should connect to */
|
|
116
|
-
service: string;
|
|
117
|
-
/** XRPC fetch handler */
|
|
118
|
-
fetch: XRPCFetch;
|
|
119
|
-
constructor(options: XRPCOptions);
|
|
120
|
-
/**
|
|
121
|
-
* Adds a hook to intercept XRPC requests.
|
|
122
|
-
* Hooks are executed from last-registered to first-registered
|
|
123
|
-
* @param fn Hook function
|
|
124
|
-
*/
|
|
125
|
-
hook(fn: XRPCHook): void;
|
|
126
|
-
/**
|
|
127
|
-
* Makes a query (GET) request
|
|
128
|
-
* @param nsid Namespace ID of a query endpoint
|
|
129
|
-
* @param options Options to include like parameters
|
|
130
|
-
* @returns The response of the request
|
|
131
|
-
*/
|
|
132
|
-
get<K extends keyof Queries>(nsid: K, options: RPCOptions<Queries[K]>): Promise<XRPCResponse<OutputOf<Queries[K]>>>;
|
|
133
|
-
/**
|
|
134
|
-
* Makes a procedure (POST) request
|
|
135
|
-
* @param nsid Namespace ID of a procedure endpoint
|
|
136
|
-
* @param options Options to include like input body or parameters
|
|
137
|
-
* @returns The response of the request
|
|
138
|
-
*/
|
|
139
|
-
call<K extends keyof Procedures>(nsid: K, options: RPCOptions<Procedures[K]>): Promise<XRPCResponse<OutputOf<Procedures[K]>>>;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Clones an XRPC instance
|
|
143
|
-
* @param rpc Base instance
|
|
144
|
-
* @returns The cloned instance
|
|
145
|
-
*/
|
|
146
|
-
export declare const clone: (rpc: XRPC) => XRPC;
|
|
147
|
-
/**
|
|
148
|
-
* Clones an existing XRPC instance, with a proxy on top.
|
|
149
|
-
* @param rpc Base instance
|
|
150
|
-
* @param opts Proxying options
|
|
151
|
-
* @returns Cloned instance with a proxy added
|
|
152
|
-
*/
|
|
153
|
-
export declare const withProxy: (rpc: XRPC, opts: ProxyOptions) => XRPC;
|
|
154
|
-
/** Known endpoint types for proxying */
|
|
155
|
-
export type ProxyType = 'atproto_labeler' | 'bsky_fg';
|
|
156
|
-
/** Options for proxying a request */
|
|
157
|
-
export interface ProxyOptions {
|
|
158
|
-
/** Service it should proxy requests to */
|
|
159
|
-
service: At.DID;
|
|
160
|
-
/** The endpoint to connect */
|
|
161
|
-
type: ProxyType | (string & {});
|
|
162
|
-
}
|
|
163
|
-
/** Default fetch handler */
|
|
164
|
-
export declare const fetchHandler: XRPCFetch;
|
|
165
|
-
/**
|
|
166
|
-
* Check if provided value is an error object
|
|
167
|
-
* @param value Response value
|
|
168
|
-
* @param names If provided, also checks if the error name matches what you expect
|
|
169
|
-
* @returns A boolean on the check
|
|
170
|
-
*/
|
|
171
|
-
export declare const isErrorResponse: (value: any, names?: string[]) => value is ErrorResponseBody;
|
|
172
|
-
/** Response body from a thrown query/procedure */
|
|
173
|
-
export interface ErrorResponseBody {
|
|
174
|
-
error?: string;
|
|
175
|
-
message?: string;
|
|
176
|
-
}
|
|
177
|
-
export {};
|
|
1
|
+
export * from './rpc.js';
|
|
2
|
+
export * from './fetch-handler.js';
|
|
3
|
+
export * from './credential-manager.js';
|