@atproto/lex-client 0.0.15 → 0.0.17

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/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { DidString, UnknownString } from '@atproto/lex-schema';
2
- export type { DidString, UnknownString };
1
+ import { DidString, LexValue, UnknownString } from '@atproto/lex-schema';
2
+ export type { DidString, LexValue, UnknownString };
3
3
  /**
4
4
  * Service identifier fragment for DID service endpoints.
5
5
  *
@@ -18,41 +18,6 @@ export type DidServiceIdentifier = 'atproto_labeler' | UnknownString;
18
18
  * ```
19
19
  */
20
20
  export type Service = `${DidString}#${DidServiceIdentifier}`;
21
- /**
22
- * Common options available for all XRPC calls.
23
- *
24
- * These options can be passed to any method that makes XRPC requests,
25
- * including `xrpc()`, `call()`, and record operations.
26
- */
27
- export type CallOptions = {
28
- /** Labeler DIDs to request labels from for content moderation. */
29
- labelers?: Iterable<DidString>;
30
- /** AbortSignal to cancel the request. */
31
- signal?: AbortSignal;
32
- /** Additional HTTP headers to include in the request. */
33
- headers?: HeadersInit;
34
- /** Service proxy identifier for routing requests through a specific service. */
35
- service?: Service;
36
- /**
37
- * Whether to validate the request against the method's input schema. Enabling
38
- * this can help catch errors early but may have a performance cost. This
39
- * would typically only be set to `true` in development or debugging
40
- * scenarios.
41
- *
42
- * @default false
43
- */
44
- validateRequest?: boolean;
45
- /**
46
- * Whether to validate the response against the method's output schema.
47
- * Disabling this can improve performance but may lead to runtime errors if
48
- * the response does not conform to the expected schema. Only set this to
49
- * `false` if you are certain that the upstream service will always return
50
- * valid responses.
51
- *
52
- * @default true
53
- */
54
- validateResponse?: boolean;
55
- };
56
21
  /**
57
22
  * Valid input types for binary request bodies.
58
23
  *
@@ -79,4 +44,10 @@ export type CallOptions = {
79
44
  * ```
80
45
  */
81
46
  export type BinaryBodyInit = Uint8Array | ArrayBuffer | Blob | ReadableStream<Uint8Array> | AsyncIterable<Uint8Array> | string;
47
+ export type EncodingString = `${string}/${string}`;
48
+ export declare function isEncodingString(contentType: string): contentType is EncodingString;
49
+ export type XrpcUnknownResponsePayload<TBinary extends BinaryBodyInit = Uint8Array> = {
50
+ encoding: EncodingString;
51
+ body: LexValue | TBinary;
52
+ };
82
53
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAE9D,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA;AAExC;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,aAAa,CAAA;AAEpE;;;;;;;;;GASG;AACH,MAAM,MAAM,OAAO,GAAG,GAAG,SAAS,IAAI,oBAAoB,EAAE,CAAA;AAE5D;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAA;IAC9B,yCAAyC;IACzC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,yDAAyD;IACzD,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IAEzB;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,cAAc,GACtB,UAAU,GACV,WAAW,GACX,IAAI,GACJ,cAAc,CAAC,UAAU,CAAC,GAC1B,aAAa,CAAC,UAAU,CAAC,GACzB,MAAM,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAA;AAElD;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,aAAa,CAAA;AAEpE;;;;;;;;;GASG;AACH,MAAM,MAAM,OAAO,GAAG,GAAG,SAAS,IAAI,oBAAoB,EAAE,CAAA;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,cAAc,GACtB,UAAU,GACV,WAAW,GACX,IAAI,GACJ,cAAc,CAAC,UAAU,CAAC,GAC1B,aAAa,CAAC,UAAU,CAAC,GACzB,MAAM,CAAA;AAEV,MAAM,MAAM,cAAc,GAAG,GAAG,MAAM,IAAI,MAAM,EAAE,CAAA;AAElD,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,GAClB,WAAW,IAAI,cAAc,CAE/B;AAED,MAAM,MAAM,0BAA0B,CACpC,OAAO,SAAS,cAAc,GAAG,UAAU,IACzC;IACF,QAAQ,EAAE,cAAc,CAAA;IACxB,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAA;CACzB,CAAA"}
package/dist/types.js CHANGED
@@ -1,3 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isEncodingString = isEncodingString;
4
+ function isEncodingString(contentType) {
5
+ return contentType.includes('/');
6
+ }
3
7
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import { DidString, UnknownString } from '@atproto/lex-schema'\n\nexport type { DidString, UnknownString }\n\n/**\n * Service identifier fragment for DID service endpoints.\n *\n * Common values include 'atproto_labeler' for labeling services,\n * or custom service identifiers.\n */\nexport type DidServiceIdentifier = 'atproto_labeler' | UnknownString\n\n/**\n * A full service proxy identifier combining a DID with a service fragment.\n *\n * Used to route requests through a specific service endpoint.\n *\n * @example\n * ```typescript\n * const service: Service = 'did:web:api.bsky.app#bsky_appview'\n * ```\n */\nexport type Service = `${DidString}#${DidServiceIdentifier}`\n\n/**\n * Common options available for all XRPC calls.\n *\n * These options can be passed to any method that makes XRPC requests,\n * including `xrpc()`, `call()`, and record operations.\n */\nexport type CallOptions = {\n /** Labeler DIDs to request labels from for content moderation. */\n labelers?: Iterable<DidString>\n /** AbortSignal to cancel the request. */\n signal?: AbortSignal\n /** Additional HTTP headers to include in the request. */\n headers?: HeadersInit\n /** Service proxy identifier for routing requests through a specific service. */\n service?: Service\n\n /**\n * Whether to validate the request against the method's input schema. Enabling\n * this can help catch errors early but may have a performance cost. This\n * would typically only be set to `true` in development or debugging\n * scenarios.\n *\n * @default false\n */\n validateRequest?: boolean\n\n /**\n * Whether to validate the response against the method's output schema.\n * Disabling this can improve performance but may lead to runtime errors if\n * the response does not conform to the expected schema. Only set this to\n * `false` if you are certain that the upstream service will always return\n * valid responses.\n *\n * @default true\n */\n validateResponse?: boolean\n}\n\n/**\n * Valid input types for binary request bodies.\n *\n * These types can be used as the body for procedures that expect\n * non-JSON content (e.g., blob uploads, binary data).\n *\n * @example Uploading a blob\n * ```typescript\n * const imageData: BinaryBodyInit = new Uint8Array(buffer)\n * await client.uploadBlob(imageData, { encoding: 'image/png' })\n * ```\n *\n * @example Streaming upload\n * ```typescript\n * const stream: BinaryBodyInit = someReadableStream\n * await client.xrpc(uploadMethod, { body: stream })\n * ```\n *\n * @example File upload in browser\n * ```typescript\n * const fileInput = document.querySelector('input[type=\"file\"]') as HTMLInputElement\n * const file: BinaryBodyInit = fileInput.files[0]\n * await client.xrpc(uploadMethod, { body: file })\n * ```\n */\nexport type BinaryBodyInit =\n | Uint8Array\n | ArrayBuffer\n | Blob\n | ReadableStream<Uint8Array>\n | AsyncIterable<Uint8Array>\n | string\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;AA2DA,4CAIC;AAJD,SAAgB,gBAAgB,CAC9B,WAAmB;IAEnB,OAAO,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;AAClC,CAAC","sourcesContent":["import { DidString, LexValue, UnknownString } from '@atproto/lex-schema'\n\nexport type { DidString, LexValue, UnknownString }\n\n/**\n * Service identifier fragment for DID service endpoints.\n *\n * Common values include 'atproto_labeler' for labeling services,\n * or custom service identifiers.\n */\nexport type DidServiceIdentifier = 'atproto_labeler' | UnknownString\n\n/**\n * A full service proxy identifier combining a DID with a service fragment.\n *\n * Used to route requests through a specific service endpoint.\n *\n * @example\n * ```typescript\n * const service: Service = 'did:web:api.bsky.app#bsky_appview'\n * ```\n */\nexport type Service = `${DidString}#${DidServiceIdentifier}`\n\n/**\n * Valid input types for binary request bodies.\n *\n * These types can be used as the body for procedures that expect\n * non-JSON content (e.g., blob uploads, binary data).\n *\n * @example Uploading a blob\n * ```typescript\n * const imageData: BinaryBodyInit = new Uint8Array(buffer)\n * await client.uploadBlob(imageData, { encoding: 'image/png' })\n * ```\n *\n * @example Streaming upload\n * ```typescript\n * const stream: BinaryBodyInit = someReadableStream\n * await client.xrpc(uploadMethod, { body: stream })\n * ```\n *\n * @example File upload in browser\n * ```typescript\n * const fileInput = document.querySelector('input[type=\"file\"]') as HTMLInputElement\n * const file: BinaryBodyInit = fileInput.files[0]\n * await client.xrpc(uploadMethod, { body: file })\n * ```\n */\nexport type BinaryBodyInit =\n | Uint8Array\n | ArrayBuffer\n | Blob\n | ReadableStream<Uint8Array>\n | AsyncIterable<Uint8Array>\n | string\n\nexport type EncodingString = `${string}/${string}`\n\nexport function isEncodingString(\n contentType: string,\n): contentType is EncodingString {\n return contentType.includes('/')\n}\n\nexport type XrpcUnknownResponsePayload<\n TBinary extends BinaryBodyInit = Uint8Array,\n> = {\n encoding: EncodingString\n body: LexValue | TBinary\n}\n"]}
package/dist/util.d.ts CHANGED
@@ -1,21 +1,7 @@
1
- import { DidString, InferMethodOutput, InferMethodOutputBody, Procedure, Query } from '@atproto/lex-schema';
2
- /**
3
- * The body type of an XRPC response, inferred from the method's output schema.
4
- *
5
- * For JSON responses, this is the parsed LexValue. For binary responses,
6
- * this is a Uint8Array.
7
- *
8
- * @typeParam M - The XRPC method type (Procedure or Query)
9
- */
10
- export type XrpcResponseBody<M extends Procedure | Query = Procedure | Query> = InferMethodOutputBody<M, Uint8Array>;
11
- /**
12
- * The full payload type of an XRPC response, including body and encoding.
13
- *
14
- * Returns `null` for methods that have no output.
15
- *
16
- * @typeParam M - The XRPC method type (Procedure or Query)
17
- */
18
- export type XrpcResponsePayload<M extends Procedure | Query = Procedure | Query> = InferMethodOutput<M, Uint8Array>;
1
+ import type { DidString, Service } from './types.js';
2
+ export declare function applyDefaults<TDefaults extends Record<string, unknown>, TOptions extends {
3
+ [K in keyof TDefaults]?: TDefaults[K];
4
+ }>(options: TOptions, defaults: TDefaults): TOptions & TDefaults;
19
5
  /**
20
6
  * Type guard to check if a value is {@link Blob}-like.
21
7
  *
@@ -27,6 +13,14 @@ export type XrpcResponsePayload<M extends Procedure | Query = Procedure | Query>
27
13
  */
28
14
  export declare function isBlobLike(value: unknown): value is Blob;
29
15
  export declare function isAsyncIterable<T>(value: T): value is unknown extends T ? T & AsyncIterable<unknown> : Extract<T, AsyncIterable<any>>;
16
+ export type XrpcRequestHeadersOptions = {
17
+ /** Additional HTTP headers to include in the request. */
18
+ headers?: HeadersInit;
19
+ /** Labeler DIDs to request labels from for content moderation. */
20
+ labelers?: Iterable<DidString>;
21
+ /** Service proxy identifier for routing requests through a specific service. */
22
+ service?: Service;
23
+ };
30
24
  /**
31
25
  * Builds HTTP headers for AT Protocol requests.
32
26
  *
@@ -34,16 +28,10 @@ export declare function isAsyncIterable<T>(value: T): value is unknown extends T
34
28
  * - `atproto-proxy`: Service routing header (if service is specified)
35
29
  * - `atproto-accept-labelers`: Comma-separated list of labeler DIDs
36
30
  *
37
- * @param options - Header building options
38
- * @param options.headers - Base headers to include
39
- * @param options.service - Service proxy identifier
40
- * @param options.labelers - Labeler DIDs to request labels from
31
+ * @see {@link XrpcRequestHeadersOptions}
41
32
  * @returns A new Headers object with AT Protocol headers added
42
33
  */
43
- export declare function buildAtprotoHeaders(options: {
44
- headers?: HeadersInit;
45
- service?: `${DidString}#${string}`;
46
- labelers?: Iterable<DidString>;
47
- }): Headers;
34
+ export declare function buildXrpcRequestHeaders(options: XrpcRequestHeadersOptions): Headers;
48
35
  export declare function toReadableStream(data: AsyncIterable<Uint8Array>): ReadableStream<Uint8Array>;
36
+ export declare function toReadableStreamPonyfill(data: AsyncIterable<Uint8Array>): ReadableStream<Uint8Array>;
49
37
  //# sourceMappingURL=util.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,qBAAqB,EACrB,SAAS,EACT,KAAK,EACN,MAAM,qBAAqB,CAAA;AAE5B;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,SAAS,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,IAC1E,qBAAqB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;AAEtC;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,CAC7B,CAAC,SAAS,SAAS,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,IAC7C,iBAAiB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;AAEpC;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAexD;AAED,wBAAgB,eAAe,CAAC,CAAC,EAC/B,KAAK,EAAE,CAAC,GACP,KAAK,IAAI,OAAO,SAAS,CAAC,GACzB,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,GAC1B,OAAO,CAAC,CAAC,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAIjC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,OAAO,CAAC,EAAE,GAAG,SAAS,IAAI,MAAM,EAAE,CAAA;IAClC,QAAQ,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAA;CAC/B,GAAG,OAAO,CAiBV;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,GAC9B,cAAc,CAAC,UAAU,CAAC,CAwB5B"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEpD,wBAAgB,aAAa,CAC3B,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzC,QAAQ,SAAS;KACd,CAAC,IAAI,MAAM,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;CACtC,EACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAY9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAexD;AAED,wBAAgB,eAAe,CAAC,CAAC,EAC/B,KAAK,EAAE,CAAC,GACP,KAAK,IAAI,OAAO,SAAS,CAAC,GACzB,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,GAC1B,OAAO,CAAC,CAAC,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAIjC;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,yDAAyD;IACzD,OAAO,CAAC,EAAE,WAAW,CAAA;IAErB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAA;IAE9B,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAiBT;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,GAC9B,cAAc,CAAC,UAAU,CAAC,CAU5B;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,GAC9B,cAAc,CAAC,UAAU,CAAC,CAmB5B"}
package/dist/util.js CHANGED
@@ -1,9 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyDefaults = applyDefaults;
3
4
  exports.isBlobLike = isBlobLike;
4
5
  exports.isAsyncIterable = isAsyncIterable;
5
- exports.buildAtprotoHeaders = buildAtprotoHeaders;
6
+ exports.buildXrpcRequestHeaders = buildXrpcRequestHeaders;
6
7
  exports.toReadableStream = toReadableStream;
8
+ exports.toReadableStreamPonyfill = toReadableStreamPonyfill;
9
+ function applyDefaults(options, defaults) {
10
+ const combined = { ...options };
11
+ // @NOTE We make sure that options with an explicit `undefined` value get the
12
+ // default, since spreading doesn't override with `undefined`.
13
+ for (const key of Object.keys(defaults)) {
14
+ if (options[key] === undefined) {
15
+ combined[key] = defaults[key];
16
+ }
17
+ }
18
+ return combined;
19
+ }
7
20
  /**
8
21
  * Type guard to check if a value is {@link Blob}-like.
9
22
  *
@@ -39,13 +52,10 @@ function isAsyncIterable(value) {
39
52
  * - `atproto-proxy`: Service routing header (if service is specified)
40
53
  * - `atproto-accept-labelers`: Comma-separated list of labeler DIDs
41
54
  *
42
- * @param options - Header building options
43
- * @param options.headers - Base headers to include
44
- * @param options.service - Service proxy identifier
45
- * @param options.labelers - Labeler DIDs to request labels from
55
+ * @see {@link XrpcRequestHeadersOptions}
46
56
  * @returns A new Headers object with AT Protocol headers added
47
57
  */
48
- function buildAtprotoHeaders(options) {
58
+ function buildXrpcRequestHeaders(options) {
49
59
  const headers = new Headers(options?.headers);
50
60
  if (options.service && !headers.has('atproto-proxy')) {
51
61
  headers.set('atproto-proxy', options.service);
@@ -59,9 +69,14 @@ function buildAtprotoHeaders(options) {
59
69
  }
60
70
  function toReadableStream(data) {
61
71
  // Use the native ReadableStream.from() if available.
72
+ /* v8 ignore next -- @preserve */
62
73
  if ('from' in ReadableStream && typeof ReadableStream.from === 'function') {
63
74
  return ReadableStream.from(data);
64
75
  }
76
+ /* v8 ignore next -- @preserve */
77
+ return toReadableStreamPonyfill(data);
78
+ }
79
+ function toReadableStreamPonyfill(data) {
65
80
  let iterator;
66
81
  return new ReadableStream({
67
82
  async pull(controller) {
package/dist/util.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;AAuCA,gCAeC;AAED,0CAQC;AAeD,kDAqBC;AAED,4CA0BC;AAlGD;;;;;;;;GAQG;AACH,SAAgB,UAAU,CAAC,KAAc;IACvC,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,KAAK,CAAA;IAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3C,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,IAAI,CAAA;IAEpE,yEAAyE;IACzE,qCAAqC;IACrC,4GAA4G;IAE5G,MAAM,GAAG,GAAI,KAAa,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;IAC9C,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACrC,OAAO,QAAQ,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU,CAAA;IAChE,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAgB,eAAe,CAC7B,KAAQ;IAIR,OAAO,CACL,KAAK,IAAI,IAAI,IAAI,OAAQ,KAAa,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,UAAU,CAC5E,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,mBAAmB,CAAC,OAInC;IACC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAE7C,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/C,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CACT,yBAAyB,EACzB,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE,CAAC;aAClE,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,IAAI,CAAC,CACd,CAAA;IACH,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,SAAgB,gBAAgB,CAC9B,IAA+B;IAE/B,qDAAqD;IACrD,IAAI,MAAM,IAAI,cAAc,IAAI,OAAO,cAAc,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC1E,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClC,CAAC;IAED,IAAI,QAA+C,CAAA;IACnD,OAAO,IAAI,cAAc,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,UAAU;YACnB,IAAI,CAAC;gBACH,QAAQ,KAAK,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAA;gBACzC,MAAM,MAAM,GAAG,MAAM,QAAS,CAAC,IAAI,EAAE,CAAA;gBACrC,IAAI,MAAM,CAAC,IAAI;oBAAE,UAAU,CAAC,KAAK,EAAE,CAAA;;oBAC9B,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;gBACrB,QAAQ,GAAG,SAAS,CAAA;YACtB,CAAC;QACH,CAAC;QACD,KAAK,CAAC,MAAM;YACV,MAAM,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAA;YAC1B,QAAQ,GAAG,SAAS,CAAA;QACtB,CAAC;KACF,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import {\n DidString,\n InferMethodOutput,\n InferMethodOutputBody,\n Procedure,\n Query,\n} from '@atproto/lex-schema'\n\n/**\n * The body type of an XRPC response, inferred from the method's output schema.\n *\n * For JSON responses, this is the parsed LexValue. For binary responses,\n * this is a Uint8Array.\n *\n * @typeParam M - The XRPC method type (Procedure or Query)\n */\nexport type XrpcResponseBody<M extends Procedure | Query = Procedure | Query> =\n InferMethodOutputBody<M, Uint8Array>\n\n/**\n * The full payload type of an XRPC response, including body and encoding.\n *\n * Returns `null` for methods that have no output.\n *\n * @typeParam M - The XRPC method type (Procedure or Query)\n */\nexport type XrpcResponsePayload<\n M extends Procedure | Query = Procedure | Query,\n> = InferMethodOutput<M, Uint8Array>\n\n/**\n * Type guard to check if a value is {@link Blob}-like.\n *\n * Handles both native Blobs and polyfilled Blob implementations\n * (e.g., fetch-blob from node-fetch).\n *\n * @param value - The value to check\n * @returns `true` if the value is a Blob or Blob-like object\n */\nexport function isBlobLike(value: unknown): value is Blob {\n if (value == null) return false\n if (typeof value !== 'object') return false\n if (typeof Blob === 'function' && value instanceof Blob) return true\n\n // Support for Blobs provided by libraries that don't use the native Blob\n // (e.g. fetch-blob from node-fetch).\n // https://github.com/node-fetch/fetch-blob/blob/a1a182e5978811407bef4ea1632b517567dda01f/index.js#L233-L244\n\n const tag = (value as any)[Symbol.toStringTag]\n if (tag === 'Blob' || tag === 'File') {\n return 'stream' in value && typeof value.stream === 'function'\n }\n\n return false\n}\n\nexport function isAsyncIterable<T>(\n value: T,\n): value is unknown extends T\n ? T & AsyncIterable<unknown>\n : Extract<T, AsyncIterable<any>> {\n return (\n value != null && typeof (value as any)[Symbol.asyncIterator] === 'function'\n )\n}\n\n/**\n * Builds HTTP headers for AT Protocol requests.\n *\n * Adds the following headers when applicable:\n * - `atproto-proxy`: Service routing header (if service is specified)\n * - `atproto-accept-labelers`: Comma-separated list of labeler DIDs\n *\n * @param options - Header building options\n * @param options.headers - Base headers to include\n * @param options.service - Service proxy identifier\n * @param options.labelers - Labeler DIDs to request labels from\n * @returns A new Headers object with AT Protocol headers added\n */\nexport function buildAtprotoHeaders(options: {\n headers?: HeadersInit\n service?: `${DidString}#${string}`\n labelers?: Iterable<DidString>\n}): Headers {\n const headers = new Headers(options?.headers)\n\n if (options.service && !headers.has('atproto-proxy')) {\n headers.set('atproto-proxy', options.service)\n }\n\n if (options.labelers) {\n headers.set(\n 'atproto-accept-labelers',\n [...options.labelers, headers.get('atproto-accept-labelers')?.trim()]\n .filter(Boolean)\n .join(', '),\n )\n }\n\n return headers\n}\n\nexport function toReadableStream(\n data: AsyncIterable<Uint8Array>,\n): ReadableStream<Uint8Array> {\n // Use the native ReadableStream.from() if available.\n if ('from' in ReadableStream && typeof ReadableStream.from === 'function') {\n return ReadableStream.from(data)\n }\n\n let iterator: AsyncIterator<Uint8Array> | undefined\n return new ReadableStream({\n async pull(controller) {\n try {\n iterator ??= data[Symbol.asyncIterator]()\n const result = await iterator!.next()\n if (result.done) controller.close()\n else controller.enqueue(result.value)\n } catch (err) {\n controller.error(err)\n iterator = undefined\n }\n },\n async cancel() {\n await iterator?.return?.()\n iterator = undefined\n },\n })\n}\n"]}
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DidString, Service } from './types.js'\n\nexport function applyDefaults<\n TDefaults extends Record<string, unknown>,\n TOptions extends {\n [K in keyof TDefaults]?: TDefaults[K]\n },\n>(options: TOptions, defaults: TDefaults): TOptions & TDefaults {\n const combined: Partial<TDefaults> = { ...options }\n\n // @NOTE We make sure that options with an explicit `undefined` value get the\n // default, since spreading doesn't override with `undefined`.\n for (const key of Object.keys(defaults) as (keyof typeof defaults)[]) {\n if (options[key] === undefined) {\n combined[key] = defaults[key]\n }\n }\n\n return combined as TOptions & TDefaults\n}\n\n/**\n * Type guard to check if a value is {@link Blob}-like.\n *\n * Handles both native Blobs and polyfilled Blob implementations\n * (e.g., fetch-blob from node-fetch).\n *\n * @param value - The value to check\n * @returns `true` if the value is a Blob or Blob-like object\n */\nexport function isBlobLike(value: unknown): value is Blob {\n if (value == null) return false\n if (typeof value !== 'object') return false\n if (typeof Blob === 'function' && value instanceof Blob) return true\n\n // Support for Blobs provided by libraries that don't use the native Blob\n // (e.g. fetch-blob from node-fetch).\n // https://github.com/node-fetch/fetch-blob/blob/a1a182e5978811407bef4ea1632b517567dda01f/index.js#L233-L244\n\n const tag = (value as any)[Symbol.toStringTag]\n if (tag === 'Blob' || tag === 'File') {\n return 'stream' in value && typeof value.stream === 'function'\n }\n\n return false\n}\n\nexport function isAsyncIterable<T>(\n value: T,\n): value is unknown extends T\n ? T & AsyncIterable<unknown>\n : Extract<T, AsyncIterable<any>> {\n return (\n value != null && typeof (value as any)[Symbol.asyncIterator] === 'function'\n )\n}\n\nexport type XrpcRequestHeadersOptions = {\n /** Additional HTTP headers to include in the request. */\n headers?: HeadersInit\n\n /** Labeler DIDs to request labels from for content moderation. */\n labelers?: Iterable<DidString>\n\n /** Service proxy identifier for routing requests through a specific service. */\n service?: Service\n}\n\n/**\n * Builds HTTP headers for AT Protocol requests.\n *\n * Adds the following headers when applicable:\n * - `atproto-proxy`: Service routing header (if service is specified)\n * - `atproto-accept-labelers`: Comma-separated list of labeler DIDs\n *\n * @see {@link XrpcRequestHeadersOptions}\n * @returns A new Headers object with AT Protocol headers added\n */\nexport function buildXrpcRequestHeaders(\n options: XrpcRequestHeadersOptions,\n): Headers {\n const headers = new Headers(options?.headers)\n\n if (options.service && !headers.has('atproto-proxy')) {\n headers.set('atproto-proxy', options.service)\n }\n\n if (options.labelers) {\n headers.set(\n 'atproto-accept-labelers',\n [...options.labelers, headers.get('atproto-accept-labelers')?.trim()]\n .filter(Boolean)\n .join(', '),\n )\n }\n\n return headers\n}\n\nexport function toReadableStream(\n data: AsyncIterable<Uint8Array>,\n): ReadableStream<Uint8Array> {\n // Use the native ReadableStream.from() if available.\n\n /* v8 ignore next -- @preserve */\n if ('from' in ReadableStream && typeof ReadableStream.from === 'function') {\n return ReadableStream.from(data)\n }\n\n /* v8 ignore next -- @preserve */\n return toReadableStreamPonyfill(data)\n}\n\nexport function toReadableStreamPonyfill(\n data: AsyncIterable<Uint8Array>,\n): ReadableStream<Uint8Array> {\n let iterator: AsyncIterator<Uint8Array> | undefined\n return new ReadableStream({\n async pull(controller) {\n try {\n iterator ??= data[Symbol.asyncIterator]()\n const result = await iterator!.next()\n if (result.done) controller.close()\n else controller.enqueue(result.value)\n } catch (err) {\n controller.error(err)\n iterator = undefined\n }\n },\n async cancel() {\n await iterator?.return?.()\n iterator = undefined\n },\n })\n}\n"]}
package/dist/xrpc.d.ts CHANGED
@@ -1,40 +1,49 @@
1
1
  import { InferInput, InferPayload, Main, Params, Procedure, Query, Restricted, Subscription } from '@atproto/lex-schema';
2
2
  import { Agent, AgentOptions } from './agent.js';
3
3
  import { XrpcFailure } from './errors.js';
4
- import { XrpcResponse } from './response.js';
5
- import { BinaryBodyInit, CallOptions } from './types.js';
6
- type XrpcParamsOptions<P extends Params> = NonNullable<unknown> extends P ? {
7
- params?: P;
8
- } : {
9
- params: P;
10
- };
4
+ import { XrpcResponse, XrpcResponseOptions } from './response.js';
5
+ import { BinaryBodyInit } from './types.js';
6
+ import { XrpcRequestHeadersOptions } from './util.js';
11
7
  /**
12
8
  * The query/path parameters type for an XRPC method, inferred from its schema.
13
9
  *
14
10
  * @typeParam M - The XRPC method type (Procedure, Query, or Subscription)
15
11
  */
16
12
  export type XrpcRequestParams<M extends Procedure | Query | Subscription> = InferInput<M['parameters']>;
13
+ type XrpcRequestParamsOptions<P extends Params> = NonNullable<unknown> extends P ? {
14
+ params?: P;
15
+ } : {
16
+ params: P;
17
+ };
17
18
  type XrpcRequestPayload<M extends Procedure | Query> = M extends Procedure ? InferPayload<M['input'], BinaryBodyInit> : undefined;
18
- type XrpcInputOptions<In> = In extends {
19
+ type XrpcRequestPayloadOptions<TPayload> = TPayload extends {
19
20
  body: infer B;
20
21
  encoding: infer E;
21
22
  } ? {
22
23
  body: B;
24
+ /**
25
+ * mime type hint for binary bodies
26
+ *
27
+ * Only needed for endpoints that accept binary input (e.g. file uploads)
28
+ * when the body is a Blob-like object without a type (e.g. fetch-blob's
29
+ * Blob). If the body is a Blob-like object with a type, that type will be
30
+ * used as the content-type header instead of this option.
31
+ *
32
+ * @default "application/octet-stream"
33
+ */
23
34
  encoding?: E;
24
35
  } : {
25
36
  body?: undefined;
26
37
  encoding?: undefined;
27
38
  };
28
39
  /**
29
- * Options for making an XRPC request.
40
+ * Options for making an XRPC request, based on the method schema.
30
41
  *
31
- * Combines {@link CallOptions} with method-specific params and body requirements.
32
- * The type system ensures required params/body are provided based on the method schema.
42
+ * Combines {@link XrpcRequestOptions} and {@link XrpcResponseOptions} with
43
+ * method-specific params and body requirements. The type system ensures
44
+ * required params/body are provided based on the method schema.
33
45
  *
34
46
  * @typeParam M - The XRPC method type (Procedure or Query)
35
- * @see {@link CallOptions} for general request options like signal and validateRequest
36
- * @see {@link XrpcParamsOptions} for method-specific query parameters
37
- * @see {@link XrpcInputOptions} for method-specific body and encoding requirements
38
47
  *
39
48
  * @example Query with params
40
49
  * ```typescript
@@ -50,7 +59,23 @@ type XrpcInputOptions<In> = In extends {
50
59
  * }
51
60
  * ```
52
61
  */
53
- export type XrpcOptions<M extends Procedure | Query = Procedure | Query> = CallOptions & XrpcInputOptions<XrpcRequestPayload<M>> & XrpcParamsOptions<XrpcRequestParams<M>>;
62
+ export type XrpcOptions<M extends Procedure | Query = Procedure | Query> = XrpcRequestOptions<M> & XrpcResponseOptions;
63
+ export type XrpcRequestOptions<M extends Procedure | Query = Procedure | Query> = XrpcRequestProcessingOptions & XrpcRequestHeadersOptions & XrpcRequestPayloadOptions<XrpcRequestPayload<M>> & XrpcRequestParamsOptions<XrpcRequestParams<M>>;
64
+ export type XrpcRequestProcessingOptions = {
65
+ /**
66
+ * AbortSignal to cancel the request.
67
+ */
68
+ signal?: AbortSignal;
69
+ /**
70
+ * Whether to validate the request against the method's input schema. Enabling
71
+ * this can help catch errors early but may have a performance cost. This
72
+ * would typically only be set to `true` in development or debugging
73
+ * scenarios.
74
+ *
75
+ * @default false
76
+ */
77
+ validateRequest?: boolean;
78
+ };
54
79
  /**
55
80
  * Makes an XRPC request and throws on failure.
56
81
  *
@@ -1 +1 @@
1
- {"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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"}
package/dist/xrpc.js CHANGED
@@ -23,7 +23,9 @@ async function xrpcSafe(agentOpts, ns, options = {}) {
23
23
  const agent = (0, agent_js_1.buildAgent)(agentOpts);
24
24
  const url = xrpcRequestUrl(method, options);
25
25
  const request = xrpcRequestInit(method, options);
26
- const response = await agent.fetchHandler(url, request);
26
+ const response = await agent.fetchHandler(url, request).catch((cause) => {
27
+ throw new errors_js_1.XrpcFetchError(method, cause);
28
+ });
27
29
  return await response_js_1.XrpcResponse.fromFetchResponse(method, response, options);
28
30
  }
29
31
  catch (cause) {
@@ -32,13 +34,15 @@ async function xrpcSafe(agentOpts, ns, options = {}) {
32
34
  }
33
35
  function xrpcRequestUrl(method, options) {
34
36
  const path = `/xrpc/${method.nsid}`;
37
+ // @NOTE param.toURLSearchParams() will always validate the params in order to
38
+ // apply default values, so we can't disable it with options.validateRequest
35
39
  const queryString = method.parameters
36
40
  ?.toURLSearchParams(options.params ?? {})
37
41
  .toString();
38
42
  return queryString ? `${path}?${queryString}` : path;
39
43
  }
40
44
  function xrpcRequestInit(schema, options) {
41
- const headers = (0, util_js_1.buildAtprotoHeaders)(options);
45
+ const headers = (0, util_js_1.buildXrpcRequestHeaders)(options);
42
46
  // Tell the server what type of response we're expecting
43
47
  if (schema.output.encoding) {
44
48
  headers.set('accept', schema.output.encoding);
package/dist/xrpc.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"xrpc.js","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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","sourcesContent":["import { LexValue, isLexScalar, isPlainObject } from '@atproto/lex-data'\nimport { lexStringify } from '@atproto/lex-json'\nimport {\n InferInput,\n InferPayload,\n Main,\n NsidString,\n Params,\n Payload,\n Procedure,\n Query,\n Restricted,\n Subscription,\n getMain,\n} from '@atproto/lex-schema'\nimport { Agent, AgentOptions, buildAgent } from './agent.js'\nimport { XrpcFailure, asXrpcFailure } from './errors.js'\nimport { XrpcResponse } from './response.js'\nimport { BinaryBodyInit, CallOptions } from './types.js'\nimport {\n buildAtprotoHeaders,\n isAsyncIterable,\n isBlobLike,\n toReadableStream,\n} from './util.js'\n\n// If all params are optional, allow omitting the params object\ntype XrpcParamsOptions<P extends Params> =\n NonNullable<unknown> extends P ? { params?: P } : { params: P }\n\n/**\n * The query/path parameters type for an XRPC method, inferred from its schema.\n *\n * @typeParam M - The XRPC method type (Procedure, Query, or Subscription)\n */\nexport type XrpcRequestParams<M extends Procedure | Query | Subscription> =\n InferInput<M['parameters']>\n\ntype XrpcRequestPayload<M extends Procedure | Query> = M extends Procedure\n ? InferPayload<M['input'], BinaryBodyInit>\n : undefined\n\ntype XrpcInputOptions<In> = In extends { body: infer B; encoding: infer E }\n ? // encoding will be inferred from the schema at runtime if not provided\n { body: B; encoding?: E }\n : { body?: undefined; encoding?: undefined }\n\n/**\n * Options for making an XRPC request.\n *\n * Combines {@link CallOptions} with method-specific params and body requirements.\n * The type system ensures required params/body are provided based on the method schema.\n *\n * @typeParam M - The XRPC method type (Procedure or Query)\n * @see {@link CallOptions} for general request options like signal and validateRequest\n * @see {@link XrpcParamsOptions} for method-specific query parameters\n * @see {@link XrpcInputOptions} for method-specific body and encoding requirements\n *\n * @example Query with params\n * ```typescript\n * const options: XrpcOptions<typeof app.bsky.feed.getTimeline.main> = {\n * params: { limit: 50 }\n * }\n * ```\n *\n * @example Procedure with body\n * ```typescript\n * const options: XrpcOptions<typeof com.atproto.repo.createRecord.main> = {\n * body: { repo: did, collection: 'app.bsky.feed.post', record: { ... } }\n * }\n * ```\n */\nexport type XrpcOptions<M extends Procedure | Query = Procedure | Query> =\n CallOptions &\n XrpcInputOptions<XrpcRequestPayload<M>> &\n XrpcParamsOptions<XrpcRequestParams<M>>\n\n/**\n * Makes an XRPC request and throws on failure.\n *\n * This is the low-level function for making XRPC calls.\n *\n * @param agent - The {@link Agent} to use for making the request\n * @param ns - The lexicon method definition\n * @param options - Request {@link XrpcOptions options} (params, body, headers, etc.)\n * @returns The successful {@link XrpcResponse}\n * @throws {XrpcFailure} When the request fails\n *\n * @example\n * ```typescript\n * const response = await xrpc('https://bsky.network', com.atproto.identity.resolveHandle, {\n * params: { handle: \"atproto.com\" }\n * })\n * ```\n *\n * @example\n * ```typescript\n * const response = await xrpc(agent, app.bsky.feed.getTimeline.main, {\n * params: { limit: 50 }\n * })\n * ```\n */\nexport async function xrpc<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: NonNullable<unknown> extends XrpcOptions<M>\n ? Main<M>\n : Restricted<'This XRPC method requires an \"options\" argument'>,\n): Promise<XrpcResponse<M>>\nexport async function xrpc<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: Main<M>,\n options: XrpcOptions<M>,\n): Promise<XrpcResponse<M>>\nexport async function xrpc<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: Main<M>,\n options: XrpcOptions<M> = {} as XrpcOptions<M>,\n): Promise<XrpcResponse<M>> {\n const response = await xrpcSafe<M>(agentOpts, ns, options)\n if (response.success) return response\n else throw response\n}\n\n/**\n * Union type representing either a successful response or a failure.\n *\n * Both {@link XrpcResponse} and {@link XrpcFailure} have a `success` property\n * that can be used to discriminate between them.\n *\n * @typeParam M - The XRPC method type\n */\nexport type XrpcResult<M extends Procedure | Query> =\n | XrpcResponse<M>\n | XrpcFailure<M>\n\n/**\n * Makes an XRPC request without throwing on failure.\n *\n * Returns a discriminated union that can be checked via the `success` property.\n * This is useful for handling errors without try/catch blocks. This also allow\n * failure results to be typed with the method schema, which can provide better\n * type safety when handling errors (e.g. checking for specific error codes).\n *\n * @param agent - The {@link Agent} to use for making the request\n * @param ns - The lexicon method definition\n * @param options - Request {@link XrpcOptions options} (params, body, headers, etc.)\n * @returns Either a successful {@link XrpcResponse} or an {@link XrpcFailure}\n *\n * @example\n * ```typescript\n * const result = await xrpcSafe('https://example.com', app.bsky.actor.getProfile, {\n * params: { actor: 'alice.bsky.social' }\n * })\n *\n * if (result.success) {\n * console.log(result.body.displayName)\n * } else {\n * console.error('Request failed:', result.error)\n * }\n * ```\n */\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: NonNullable<unknown> extends XrpcOptions<M>\n ? Main<M>\n : Restricted<'This XRPC method requires an \"options\" argument'>,\n): Promise<XrpcResult<M>>\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: Main<M>,\n options: XrpcOptions<M>,\n): Promise<XrpcResult<M>>\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: Main<M>,\n options: XrpcOptions<M> = {} as XrpcOptions<M>,\n): Promise<XrpcResult<M>> {\n options.signal?.throwIfAborted()\n const method: M = getMain(ns)\n try {\n const agent = buildAgent(agentOpts)\n const url = xrpcRequestUrl(method, options)\n const request = xrpcRequestInit(method, options)\n const response = await agent.fetchHandler(url, request)\n return await XrpcResponse.fromFetchResponse<M>(method, response, options)\n } catch (cause) {\n return asXrpcFailure(method, cause)\n }\n}\n\nfunction xrpcRequestUrl<M extends Procedure | Query | Subscription>(\n method: M,\n options: CallOptions & { params?: Params },\n): `/xrpc/${NsidString}${'' | `?${string}`}` {\n const path = `/xrpc/${method.nsid}` as const\n\n const queryString = method.parameters\n ?.toURLSearchParams(options.params ?? {})\n .toString()\n\n return queryString ? (`${path}?${queryString}` as const) : path\n}\n\nfunction xrpcRequestInit<T extends Procedure | Query>(\n schema: T,\n options: CallOptions & {\n body?: LexValue | BinaryBodyInit\n encoding?: string\n },\n): RequestInit & { duplex?: 'half' } {\n const headers = buildAtprotoHeaders(options)\n\n // Tell the server what type of response we're expecting\n if (schema.output.encoding) {\n headers.set('accept', schema.output.encoding)\n }\n\n // Caller should not set content-type header\n if (headers.has('content-type')) {\n const contentType = headers.get('content-type')\n throw new TypeError(`Unexpected content-type header (${contentType})`)\n }\n\n // Requests with body\n if ('input' in schema) {\n const encodingHint = options.encoding\n const input = xrpcProcedureInput(schema, options, encodingHint)\n\n if (input) {\n headers.set('content-type', input.encoding)\n } else if (encodingHint != null) {\n throw new TypeError(`Unexpected encoding hint (${encodingHint})`)\n }\n\n return {\n duplex: 'half',\n redirect: 'follow',\n referrerPolicy: 'strict-origin-when-cross-origin', // (default)\n mode: 'cors', // (default)\n signal: options.signal,\n method: 'POST',\n headers,\n body: input?.body,\n }\n }\n\n // Requests without body\n return {\n duplex: 'half',\n redirect: 'follow',\n referrerPolicy: 'strict-origin-when-cross-origin', // (default)\n mode: 'cors', // (default)\n signal: options.signal,\n method: 'GET',\n headers,\n }\n}\n\nfunction xrpcProcedureInput(\n method: Procedure,\n options: CallOptions & { body?: LexValue | BinaryBodyInit },\n encodingHint?: string,\n): null | { body: BodyInit; encoding: string } {\n const { input } = method\n const { body } = options\n\n if (options.validateRequest) {\n input.schema?.check(body)\n }\n\n // Special handling for endpoints expecting application/json input\n if (input.encoding === 'application/json') {\n // @NOTE **NOT** using isLexValue here to avoid deep checks in order to\n // distinguish between LexValue and BinaryBodyInit.\n if (!isLexScalar(body) && !isPlainObject(body) && !Array.isArray(body)) {\n throw new TypeError(`Expected LexValue body, got ${typeof body}`)\n }\n\n return buildPayload(input, lexStringify(body), encodingHint)\n }\n\n // Other encodings will be sent unaltered (ie. as binary data)\n switch (typeof body) {\n case 'undefined':\n case 'string':\n return buildPayload(input, body, encodingHint)\n case 'object': {\n if (body === null) break\n if (\n ArrayBuffer.isView(body) ||\n body instanceof ArrayBuffer ||\n body instanceof ReadableStream\n ) {\n return buildPayload(input, body, encodingHint)\n } else if (isAsyncIterable(body)) {\n return buildPayload(input, toReadableStream(body), encodingHint)\n } else if (isBlobLike(body)) {\n return buildPayload(input, body, encodingHint || body.type)\n }\n }\n }\n\n throw new TypeError(\n `Invalid ${typeof body} body for ${input.encoding} encoding`,\n )\n}\n\nfunction buildPayload(\n schema: Payload,\n body: undefined | BodyInit,\n encodingHint?: string,\n): null | { body: BodyInit; encoding: string } {\n if (schema.encoding === undefined) {\n if (body !== undefined) {\n throw new TypeError(\n `Cannot send a ${typeof body} body with undefined encoding`,\n )\n }\n\n return null\n }\n\n if (body === undefined) {\n // This error would be returned by the server, but we can catch it earlier\n // to avoid un-necessary requests. Note that a content-length of 0 does not\n // necessary mean that the body is \"empty\" (e.g. an empty txt file).\n throw new TypeError(`A request body is expected but none was provided`)\n }\n\n const encoding = buildEncoding(schema, encodingHint)\n return { encoding, body }\n}\n\nfunction buildEncoding(schema: Payload, encodingHint?: string): string {\n // Should never happen (required for type safety)\n if (!schema.encoding) {\n throw new TypeError('Unexpected payload')\n }\n\n if (encodingHint?.length) {\n if (!schema.matchesEncoding(encodingHint)) {\n throw new TypeError(\n `Cannot send a body with content-type \"${encodingHint}\" for \"${schema.encoding}\" encoding`,\n )\n }\n return encodingHint\n }\n\n // Fallback\n\n if (schema.encoding === '*/*') {\n return 'application/octet-stream'\n }\n\n if (schema.encoding.startsWith('text/')) {\n return schema.encoding.includes('*')\n ? 'text/plain; charset=utf-8'\n : `${schema.encoding}; charset=utf-8`\n }\n\n if (!schema.encoding.includes('*')) {\n return schema.encoding\n }\n\n throw new TypeError(\n `Unable to determine payload encoding. Please provide a 'content-type' header matching ${schema.encoding}.`,\n )\n}\n"]}
1
+ {"version":3,"file":"xrpc.js","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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","sourcesContent":["import { LexValue, isLexScalar, isPlainObject } from '@atproto/lex-data'\nimport { lexStringify } from '@atproto/lex-json'\nimport {\n InferInput,\n InferPayload,\n Main,\n NsidString,\n Params,\n Payload,\n Procedure,\n Query,\n Restricted,\n Subscription,\n getMain,\n} from '@atproto/lex-schema'\nimport { Agent, AgentOptions, buildAgent } from './agent.js'\nimport { XrpcFailure, XrpcFetchError, asXrpcFailure } from './errors.js'\nimport { XrpcResponse, XrpcResponseOptions } from './response.js'\nimport { BinaryBodyInit } from './types.js'\nimport {\n XrpcRequestHeadersOptions,\n buildXrpcRequestHeaders,\n isAsyncIterable,\n isBlobLike,\n toReadableStream,\n} from './util.js'\n\n/**\n * The query/path parameters type for an XRPC method, inferred from its schema.\n *\n * @typeParam M - The XRPC method type (Procedure, Query, or Subscription)\n */\nexport type XrpcRequestParams<M extends Procedure | Query | Subscription> =\n InferInput<M['parameters']>\n\n// If all params are optional, allow omitting the params object\ntype XrpcRequestParamsOptions<P extends Params> =\n NonNullable<unknown> extends P ? { params?: P } : { params: P }\n\ntype XrpcRequestPayload<M extends Procedure | Query> = M extends Procedure\n ? InferPayload<M['input'], BinaryBodyInit>\n : undefined\n\ntype XrpcRequestPayloadOptions<TPayload> = TPayload extends {\n body: infer B\n encoding: infer E\n}\n ? {\n body: B\n\n /**\n * mime type hint for binary bodies\n *\n * Only needed for endpoints that accept binary input (e.g. file uploads)\n * when the body is a Blob-like object without a type (e.g. fetch-blob's\n * Blob). If the body is a Blob-like object with a type, that type will be\n * used as the content-type header instead of this option.\n *\n * @default \"application/octet-stream\"\n */\n encoding?: E\n }\n : { body?: undefined; encoding?: undefined }\n\n/**\n * Options for making an XRPC request, based on the method schema.\n *\n * Combines {@link XrpcRequestOptions} and {@link XrpcResponseOptions} with\n * method-specific params and body requirements. The type system ensures\n * required params/body are provided based on the method schema.\n *\n * @typeParam M - The XRPC method type (Procedure or Query)\n *\n * @example Query with params\n * ```typescript\n * const options: XrpcOptions<typeof app.bsky.feed.getTimeline.main> = {\n * params: { limit: 50 }\n * }\n * ```\n *\n * @example Procedure with body\n * ```typescript\n * const options: XrpcOptions<typeof com.atproto.repo.createRecord.main> = {\n * body: { repo: did, collection: 'app.bsky.feed.post', record: { ... } }\n * }\n * ```\n */\nexport type XrpcOptions<M extends Procedure | Query = Procedure | Query> =\n XrpcRequestOptions<M> & XrpcResponseOptions\n\nexport type XrpcRequestOptions<\n M extends Procedure | Query = Procedure | Query,\n> = XrpcRequestProcessingOptions &\n XrpcRequestHeadersOptions &\n XrpcRequestPayloadOptions<XrpcRequestPayload<M>> &\n XrpcRequestParamsOptions<XrpcRequestParams<M>>\n\nexport type XrpcRequestProcessingOptions = {\n /**\n * AbortSignal to cancel the request.\n */\n signal?: AbortSignal\n\n /**\n * Whether to validate the request against the method's input schema. Enabling\n * this can help catch errors early but may have a performance cost. This\n * would typically only be set to `true` in development or debugging\n * scenarios.\n *\n * @default false\n */\n validateRequest?: boolean\n}\n\n/**\n * Makes an XRPC request and throws on failure.\n *\n * This is the low-level function for making XRPC calls.\n *\n * @param agent - The {@link Agent} to use for making the request\n * @param ns - The lexicon method definition\n * @param options - Request {@link XrpcOptions options} (params, body, headers, etc.)\n * @returns The successful {@link XrpcResponse}\n * @throws {XrpcFailure} When the request fails\n *\n * @example\n * ```typescript\n * const response = await xrpc('https://bsky.network', com.atproto.identity.resolveHandle, {\n * params: { handle: \"atproto.com\" }\n * })\n * ```\n *\n * @example\n * ```typescript\n * const response = await xrpc(agent, app.bsky.feed.getTimeline.main, {\n * params: { limit: 50 }\n * })\n * ```\n */\nexport async function xrpc<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: NonNullable<unknown> extends XrpcOptions<M>\n ? Main<M>\n : Restricted<'This XRPC method requires an \"options\" argument'>,\n): Promise<XrpcResponse<M>>\nexport async function xrpc<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: Main<M>,\n options: XrpcOptions<M>,\n): Promise<XrpcResponse<M>>\nexport async function xrpc<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: Main<M>,\n options: XrpcOptions<M> = {} as XrpcOptions<M>,\n): Promise<XrpcResponse<M>> {\n const response = await xrpcSafe<M>(agentOpts, ns, options)\n if (response.success) return response\n else throw response\n}\n\n/**\n * Union type representing either a successful response or a failure.\n *\n * Both {@link XrpcResponse} and {@link XrpcFailure} have a `success` property\n * that can be used to discriminate between them.\n *\n * @typeParam M - The XRPC method type\n */\nexport type XrpcResult<M extends Procedure | Query> =\n | XrpcResponse<M>\n | XrpcFailure<M>\n\n/**\n * Makes an XRPC request without throwing on failure.\n *\n * Returns a discriminated union that can be checked via the `success` property.\n * This is useful for handling errors without try/catch blocks. This also allow\n * failure results to be typed with the method schema, which can provide better\n * type safety when handling errors (e.g. checking for specific error codes).\n *\n * @param agent - The {@link Agent} to use for making the request\n * @param ns - The lexicon method definition\n * @param options - Request {@link XrpcOptions options} (params, body, headers, etc.)\n * @returns Either a successful {@link XrpcResponse} or an {@link XrpcFailure}\n *\n * @example\n * ```typescript\n * const result = await xrpcSafe('https://example.com', app.bsky.actor.getProfile, {\n * params: { actor: 'alice.bsky.social' }\n * })\n *\n * if (result.success) {\n * console.log(result.body.displayName)\n * } else {\n * console.error('Request failed:', result.error)\n * }\n * ```\n */\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: NonNullable<unknown> extends XrpcOptions<M>\n ? Main<M>\n : Restricted<'This XRPC method requires an \"options\" argument'>,\n): Promise<XrpcResult<M>>\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: Main<M>,\n options: XrpcOptions<M>,\n): Promise<XrpcResult<M>>\nexport async function xrpcSafe<const M extends Query | Procedure>(\n agentOpts: Agent | AgentOptions,\n ns: Main<M>,\n options: XrpcOptions<M> = {} as XrpcOptions<M>,\n): Promise<XrpcResult<M>> {\n options.signal?.throwIfAborted()\n const method: M = getMain(ns)\n try {\n const agent = buildAgent(agentOpts)\n const url = xrpcRequestUrl(method, options)\n const request = xrpcRequestInit(method, options)\n const response = await agent.fetchHandler(url, request).catch((cause) => {\n throw new XrpcFetchError(method, cause)\n })\n return await XrpcResponse.fromFetchResponse<M>(method, response, options)\n } catch (cause) {\n return asXrpcFailure(method, cause)\n }\n}\n\nfunction xrpcRequestUrl<M extends Procedure | Query | Subscription>(\n method: M,\n options: { params?: Params },\n): `/xrpc/${NsidString}${'' | `?${string}`}` {\n const path = `/xrpc/${method.nsid}` as const\n\n // @NOTE param.toURLSearchParams() will always validate the params in order to\n // apply default values, so we can't disable it with options.validateRequest\n\n const queryString = method.parameters\n ?.toURLSearchParams(options.params ?? {})\n .toString()\n\n return queryString ? (`${path}?${queryString}` as const) : path\n}\n\nfunction xrpcRequestInit<T extends Procedure | Query>(\n schema: T,\n options: XrpcRequestProcessingOptions &\n XrpcRequestHeadersOptions &\n XrpcProcedureInputOptions & {\n encoding?: string\n },\n): RequestInit & { duplex?: 'half' } {\n const headers = buildXrpcRequestHeaders(options)\n\n // Tell the server what type of response we're expecting\n if (schema.output.encoding) {\n headers.set('accept', schema.output.encoding)\n }\n\n // Caller should not set content-type header\n if (headers.has('content-type')) {\n const contentType = headers.get('content-type')\n throw new TypeError(`Unexpected content-type header (${contentType})`)\n }\n\n // Requests with body\n if ('input' in schema) {\n const encodingHint = options.encoding\n const input = xrpcProcedureInput(schema, options, encodingHint)\n\n if (input) {\n headers.set('content-type', input.encoding)\n } else if (encodingHint != null) {\n throw new TypeError(`Unexpected encoding hint (${encodingHint})`)\n }\n\n return {\n duplex: 'half',\n redirect: 'follow',\n referrerPolicy: 'strict-origin-when-cross-origin', // (default)\n mode: 'cors', // (default)\n signal: options.signal,\n method: 'POST',\n headers,\n body: input?.body,\n }\n }\n\n // Requests without body\n return {\n duplex: 'half',\n redirect: 'follow',\n referrerPolicy: 'strict-origin-when-cross-origin', // (default)\n mode: 'cors', // (default)\n signal: options.signal,\n method: 'GET',\n headers,\n }\n}\n\ntype XrpcProcedureInputOptions = {\n body?: LexValue | BinaryBodyInit\n validateRequest?: boolean\n}\n\nfunction xrpcProcedureInput(\n method: Procedure,\n options: XrpcProcedureInputOptions,\n encodingHint?: string,\n): null | { body: BodyInit; encoding: string } {\n const { input } = method\n const { body } = options\n\n if (options.validateRequest) {\n input.schema?.check(body)\n }\n\n // Special handling for endpoints expecting application/json input\n if (input.encoding === 'application/json') {\n // @NOTE **NOT** using isLexValue here to avoid deep checks in order to\n // distinguish between LexValue and BinaryBodyInit.\n if (!isLexScalar(body) && !isPlainObject(body) && !Array.isArray(body)) {\n throw new TypeError(`Expected LexValue body, got ${typeof body}`)\n }\n\n return buildPayload(input, lexStringify(body), encodingHint)\n }\n\n // Other encodings will be sent unaltered (ie. as binary data)\n switch (typeof body) {\n case 'undefined':\n case 'string':\n return buildPayload(input, body, encodingHint)\n case 'object': {\n if (body === null) break\n if (\n ArrayBuffer.isView(body) ||\n body instanceof ArrayBuffer ||\n body instanceof ReadableStream\n ) {\n return buildPayload(input, body, encodingHint)\n } else if (isAsyncIterable(body)) {\n return buildPayload(input, toReadableStream(body), encodingHint)\n } else if (isBlobLike(body)) {\n return buildPayload(input, body, encodingHint || body.type)\n }\n }\n }\n\n throw new TypeError(\n `Invalid ${typeof body} body for ${input.encoding} encoding`,\n )\n}\n\nfunction buildPayload(\n schema: Payload,\n body: undefined | BodyInit,\n encodingHint?: string,\n): null | { body: BodyInit; encoding: string } {\n if (schema.encoding === undefined) {\n if (body !== undefined) {\n throw new TypeError(\n `Cannot send a ${typeof body} body with undefined encoding`,\n )\n }\n\n return null\n }\n\n if (body === undefined) {\n // This error would be returned by the server, but we can catch it earlier\n // to avoid un-necessary requests. Note that a content-length of 0 does not\n // necessary mean that the body is \"empty\" (e.g. an empty txt file).\n throw new TypeError(`A request body is expected but none was provided`)\n }\n\n const encoding = buildEncoding(schema, encodingHint)\n return { encoding, body }\n}\n\nfunction buildEncoding(schema: Payload, encodingHint?: string): string {\n // Should never happen (required for type safety)\n if (!schema.encoding) {\n throw new TypeError('Unexpected payload')\n }\n\n if (encodingHint?.length) {\n if (!schema.matchesEncoding(encodingHint)) {\n throw new TypeError(\n `Cannot send a body with content-type \"${encodingHint}\" for \"${schema.encoding}\" encoding`,\n )\n }\n return encodingHint\n }\n\n // Fallback\n\n if (schema.encoding === '*/*') {\n return 'application/octet-stream'\n }\n\n if (schema.encoding.startsWith('text/')) {\n return schema.encoding.includes('*')\n ? 'text/plain; charset=utf-8'\n : `${schema.encoding}; charset=utf-8`\n }\n\n if (!schema.encoding.includes('*')) {\n return schema.encoding\n }\n\n throw new TypeError(\n `Unable to determine payload encoding. Please provide a 'content-type' header matching ${schema.encoding}.`,\n )\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lex-client",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "license": "MIT",
5
5
  "description": "HTTP client for interacting with Lexicon based APIs",
6
6
  "keywords": [
@@ -37,14 +37,14 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "tslib": "^2.8.1",
40
- "@atproto/lex-data": "^0.0.13",
41
- "@atproto/lex-json": "^0.0.13",
42
- "@atproto/lex-schema": "^0.0.14"
40
+ "@atproto/lex-data": "^0.0.14",
41
+ "@atproto/lex-json": "^0.0.14",
42
+ "@atproto/lex-schema": "^0.0.16"
43
43
  },
44
44
  "devDependencies": {
45
45
  "vitest": "^4.0.16",
46
- "@atproto/lex-cbor": "^0.0.14",
47
- "@atproto/lex-builder": "^0.0.17"
46
+ "@atproto/lex-cbor": "^0.0.15",
47
+ "@atproto/lex-builder": "^0.0.19"
48
48
  },
49
49
  "scripts": {
50
50
  "prebuild": "node ./scripts/lex-build.mjs",