@atproto/lex-client 0.0.16 → 0.0.18
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/CHANGELOG.md +32 -0
- package/dist/client.d.ts +24 -21
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +14 -7
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +22 -22
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +62 -37
- package/dist/errors.js.map +1 -1
- package/dist/response.d.ts +66 -7
- package/dist/response.d.ts.map +1 -1
- package/dist/response.js +90 -69
- package/dist/response.js.map +1 -1
- package/dist/types.d.ts +8 -37
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +14 -27
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +15 -6
- package/dist/util.js.map +1 -1
- package/dist/xrpc.d.ts +40 -15
- package/dist/xrpc.d.ts.map +1 -1
- package/dist/xrpc.js +4 -2
- package/dist/xrpc.js.map +1 -1
- package/package.json +6 -6
- package/src/client.ts +83 -31
- package/src/errors.test.ts +243 -32
- package/src/errors.ts +91 -52
- package/src/response.ts +229 -102
- package/src/types.ts +17 -40
- package/src/util.test.ts +11 -11
- package/src/util.ts +33 -36
- package/src/xrpc.test.ts +691 -142
- package/src/xrpc.ts +73 -29
package/dist/response.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.js","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"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","sourcesContent":["import { lexParse } from '@atproto/lex-json'\nimport {\n InferMethodOutputEncoding,\n Procedure,\n Query,\n ResultSuccess,\n} from '@atproto/lex-schema'\nimport {\n XrpcAuthenticationError,\n XrpcInvalidResponseError,\n XrpcResponseError,\n XrpcUpstreamError,\n isXrpcErrorPayload,\n} from './errors.js'\nimport { XrpcResponseBody, XrpcResponsePayload } from './util.js'\n\nconst CONTENT_TYPE_BINARY = 'application/octet-stream'\nconst CONTENT_TYPE_JSON = 'application/json'\n\nexport type { XrpcResponseBody, XrpcResponsePayload }\n\n/**\n * Small container for XRPC response data.\n *\n * @implements {ResultSuccess<XrpcResponse<M>>} for convenience in result handling contexts.\n */\nexport class XrpcResponse<M extends Procedure | Query>\n implements ResultSuccess<XrpcResponse<M>>\n{\n /** @see {@link ResultSuccess.success} */\n readonly success = true as const\n\n /** @see {@link ResultSuccess.value} */\n get value(): this {\n return this\n }\n\n constructor(\n readonly method: M,\n readonly status: number,\n readonly headers: Headers,\n readonly payload: XrpcResponsePayload<M>,\n ) {}\n\n /**\n * Whether the response payload was parsed as {@link LexValue} (`true`) or is\n * in binary form {@link Uint8Array} (`false`).\n */\n get isParsed() {\n return this.method.output.encoding === CONTENT_TYPE_JSON\n }\n\n /**\n * The Content-Type encoding of the response (e.g., 'application/json').\n * Returns `undefined` if the response has no body.\n */\n get encoding() {\n return this.payload?.encoding as InferMethodOutputEncoding<M>\n }\n\n /**\n * The parsed response body.\n *\n * For 'application/json' responses, this is the parsed and validated LexValue.\n * For binary responses, this is a Uint8Array.\n * Returns `undefined` if the response has no body.\n */\n get body() {\n return this.payload?.body as XrpcResponseBody<M>\n }\n\n /**\n * @throws {XrpcResponseError} in case of (valid) XRPC error responses. Use\n * {@link XrpcResponseError.matchesSchemaErrors} to narrow the error type based on\n * the method's declared error schema. This can be narrowed further as a\n * {@link XrpcAuthenticationError} if the error is an authentication error.\n * @throws {XrpcUpstreamError} when the response is not a valid XRPC\n * response, or if the response does not conform to the method's schema.\n */\n static async fromFetchResponse<const M extends Procedure | Query>(\n method: M,\n response: Response,\n options?: { validateResponse?: boolean },\n ): Promise<XrpcResponse<M>> {\n // @NOTE The body MUST either be read or canceled to avoid resource leaks.\n // Since nothing should cause an exception before \"readPayload\" is\n // called, we can safely not use a try/finally here.\n\n // @NOTE redirect is set to 'follow', so we shouldn't get 3xx responses here\n if (response.status < 200 || response.status >= 300) {\n // Always parse json for error responses\n const payload = await readPayload(response, { parse: true }).catch(\n (cause) => {\n throw new XrpcUpstreamError(\n method,\n response,\n null,\n 'Unable to parse response payload',\n { cause },\n )\n },\n )\n\n // Properly formatted XRPC error response ?\n if (response.status >= 400 && isXrpcErrorPayload(payload)) {\n throw response.status === 401\n ? new XrpcAuthenticationError<M>(method, response, payload)\n : new XrpcResponseError<M>(method, response, payload)\n }\n\n // Invalid XRPC response (we probably did not hit an XRPC implementation)\n throw new XrpcUpstreamError(\n method,\n response,\n payload,\n response.status >= 500\n ? 'Upstream server encountered an error'\n : response.status >= 400\n ? 'Invalid response payload'\n : 'Invalid response status code',\n )\n }\n\n // Only parse json if the schema expects it\n const payload = await readPayload(response, {\n parse: method.output.encoding === CONTENT_TYPE_JSON,\n }).catch((cause) => {\n throw new XrpcUpstreamError(\n method,\n response,\n null,\n 'Unable to parse response payload',\n { cause },\n )\n })\n\n // Response is successful (2xx). Validate payload (data and encoding) against schema.\n if (method.output.encoding == null) {\n // Schema expects no payload\n if (payload) {\n throw new XrpcUpstreamError(\n method,\n response,\n payload,\n `Expected response with no body, got ${payload.encoding}`,\n )\n }\n } else {\n // Schema expects a payload\n if (!payload || !method.output.matchesEncoding(payload.encoding)) {\n throw new XrpcUpstreamError(\n method,\n response,\n payload,\n payload\n ? `Expected ${method.output.encoding} response, got ${payload.encoding}`\n : `Expected non-empty response with content-type ${method.output.encoding}`,\n )\n }\n\n // Assert valid response body.\n if (method.output.schema && options?.validateResponse !== false) {\n const result = method.output.schema.safeParse(payload.body)\n\n if (!result.success) {\n throw new XrpcInvalidResponseError(\n method,\n response,\n payload,\n result.reason,\n )\n }\n }\n }\n\n return new XrpcResponse<M>(\n method,\n response.status,\n response.headers,\n payload as XrpcResponsePayload<M>,\n )\n }\n}\n\n/**\n * @note this function always consumes the response body\n */\nasync function readPayload(\n response: Response,\n options?: { parse?: boolean },\n): Promise<XrpcResponsePayload> {\n // @TODO Should we limit the maximum response size here (this could also be\n // done by the FetchHandler)?\n\n const encoding = response.headers\n .get('content-type')\n ?.split(';')[0]\n .trim()\n .toLowerCase()\n\n // Response content-type is undefined\n if (!encoding) {\n // If the body is empty, return undefined (= no payload)\n const body = await response.arrayBuffer()\n if (body.byteLength === 0) return undefined\n\n // If we got data despite no content-type, treat it as binary\n return {\n encoding: CONTENT_TYPE_BINARY,\n body: new Uint8Array(body),\n }\n }\n\n if (options?.parse && encoding === CONTENT_TYPE_JSON) {\n // @NOTE It might be worth returning the raw bytes here (Uint8Array) and\n // perform the lex parsing using cborg/json, allowing to do\n // bytes->LexValue in one step instead of bytes->text->JSON->LexValue.\n // This would require adding encode/decode utilities to lex-json (similar\n // to @ipld/dag-json)\n const text = await response.text()\n\n // @NOTE Using `lexParse(text)` (instead of `jsonToLex(json)`) here as\n // using a reviver function during JSON.parse should be faster than\n // parsing to JSON then converting to Lex (?)\n\n // @TODO verify statement above\n return { encoding, body: lexParse(text) }\n }\n\n return { encoding, body: new Uint8Array(await response.arrayBuffer()) }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"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","sourcesContent":["import { LexParseOptions, lexParse } from '@atproto/lex-json'\nimport {\n InferMethodOutputEncoding,\n InferOutput,\n LexValue,\n Payload,\n Procedure,\n Query,\n ResultSuccess,\n Validator,\n} from '@atproto/lex-schema'\nimport {\n XrpcAuthenticationError,\n XrpcInvalidResponseError,\n XrpcResponseError,\n XrpcResponseValidationError,\n} from './errors.js'\nimport {\n EncodingString,\n XrpcUnknownResponsePayload,\n isEncodingString,\n} from './types.js'\n\nconst CONTENT_TYPE_BINARY = 'application/octet-stream'\nconst CONTENT_TYPE_JSON = 'application/json'\n\n// @NOTE the output schema is used in \"parse\" mode (safeParse), which means that\n// defaults will be applied and coercions will be performed, so we need to use\n// InferOutput here to get the final parsed type, not Infer/InferInput. For this\n// reason, we cannot use InferMethodOutputBody and InferMethodOutput from\n// lex-schema here.\n\ntype InferEncodingType<TEncoding extends string> = TEncoding extends '*/*'\n ? EncodingString\n : TEncoding extends `${infer T extends string}/*`\n ? `${T}/${string}`\n : TEncoding\n\ntype InferBodyType<\n TEncoding extends string,\n TSchema,\n> = TSchema extends Validator\n ? InferOutput<TSchema>\n : TEncoding extends `application/json`\n ? LexValue\n : Uint8Array\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> =\n M['output'] extends Payload<infer TEncoding, infer TSchema>\n ? TEncoding extends string\n ? InferBodyType<TEncoding, TSchema>\n : undefined | LexValue | Uint8Array\n : never\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<M extends Procedure | Query> =\n M['output'] extends Payload<infer TEncoding, infer TSchema>\n ? TEncoding extends string\n ? {\n encoding: InferEncodingType<TEncoding>\n body: InferBodyType<TEncoding, TSchema>\n }\n : // If the schema does not specify an output encoding, anything could be\n // returned, including no payload at all (undefined).\n undefined | { body: LexValue | Uint8Array; encoding: string }\n : never\n\nexport type XrpcResponseOptions = {\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 * Whether to strictly process response payloads according to Lex encoding\n * rules. By default, the client will reject responses with invalid Lex data\n * (floats and invalid $bytes / $link objects).\n *\n * Setting this option to `false` will allow the client to accept such\n * responses in a non-strict mode, where invalid Lex data will be returned\n * as-is (e.g., floats will not be rejected, and invalid $bytes / $link\n * objects will not be converted to Uint8Array / Cid). When in non-strict\n * mode, the validation will also be relaxed when validating the response\n * against the method's output schema, allowing values that do not strictly\n * conform to the schema (e.g. datetime strings that are not valid RFC3339\n * format, blobs that are not of the right size/mime-type, etc.) to be\n * accepted as long as their basic structure is correct.\n *\n * When validation is enabled (the default), the values defined through the\n * method schema will be enforced, ensuring that the client can still process\n * the response even if the server returns invalid Lex data.\n *\n * @default true\n * @see {@link LexParseOptions.strict}\n */\n strictResponseProcessing?: boolean\n}\n\n/**\n * Small container for XRPC response data.\n *\n * @implements {ResultSuccess<XrpcResponse<M>>} for convenience in result handling contexts.\n */\nexport class XrpcResponse<M extends Procedure | Query>\n implements ResultSuccess<XrpcResponse<M>>\n{\n /** @see {@link ResultSuccess.success} */\n readonly success = true as const\n\n /** @see {@link ResultSuccess.value} */\n get value(): this {\n return this\n }\n\n constructor(\n readonly method: M,\n readonly status: number,\n readonly headers: Headers,\n readonly payload: XrpcResponsePayload<M>,\n ) {}\n\n /**\n * Whether the response payload was parsed as {@link LexValue} (`true`) or is\n * in binary form {@link Uint8Array} (`false`).\n */\n get isParsed() {\n return this.method.output.encoding === CONTENT_TYPE_JSON\n }\n\n /**\n * The Content-Type encoding of the response (e.g., 'application/json').\n * Returns `undefined` if the response has no body.\n */\n get encoding() {\n return this.payload?.encoding as InferMethodOutputEncoding<M>\n }\n\n /**\n * The parsed response body.\n *\n * For 'application/json' responses, this is the parsed and validated LexValue.\n * For binary responses, this is a Uint8Array.\n * Returns `undefined` if the response has no body.\n */\n get body() {\n return this.payload?.body as XrpcResponseBody<M>\n }\n\n /**\n * @throws {XrpcResponseError} in case of (valid) XRPC error responses. Use\n * {@link XrpcResponseError.matchesSchemaErrors} to narrow the error type based on\n * the method's declared error schema. This can be narrowed further as a\n * {@link XrpcAuthenticationError} if the error is an authentication error.\n * @throws {XrpcInvalidResponseError} when the response is not a valid XRPC\n * response, or if the response does not conform to the method's schema.\n */\n static async fromFetchResponse<const M extends Procedure | Query>(\n method: M,\n response: Response,\n options?: XrpcResponseOptions,\n ): Promise<XrpcResponse<M>> {\n // @NOTE The body MUST either be read or canceled to avoid resource leaks.\n // Since nothing should cause an exception before \"readPayload\" is\n // called, we can safely not use a try/finally here.\n\n // Always turn 4xx/5xx responses into XrpcResponseError\n if (response.status >= 400) {\n const payload = await readPayload(method, response, {\n // Always parse errors in non-strict mode\n parse: { strict: false },\n })\n\n if (response.status === 401) {\n throw new XrpcAuthenticationError<M>(method, response, payload)\n }\n\n throw new XrpcResponseError<M>(method, response, payload)\n }\n\n // @NOTE redirect is set to 'follow', so we shouldn't get 3xx responses here\n if (response.status < 200 || response.status >= 300) {\n await response.body?.cancel()\n\n throw new XrpcInvalidResponseError(\n method,\n response,\n undefined,\n `Unexpected status code ${response.status}`,\n )\n }\n\n const payload = await readPayload(method, response, {\n // Parse response if there is a schema, or if the encoding is\n // \"application/json\"\n parse:\n method.output.schema || method.output.encoding === CONTENT_TYPE_JSON\n ? { strict: options?.strictResponseProcessing ?? true }\n : // If there is no declared output encoding, we'll parse the output (in loose mode)\n method.output.encoding == null\n ? { strict: false }\n : false,\n })\n\n if (!method.output.matchesEncoding(payload?.encoding)) {\n throw new XrpcInvalidResponseError(\n method,\n response,\n payload,\n `Expected ${stringifyEncoding(method.output.encoding)} response (got ${stringifyEncoding(payload?.encoding)})`,\n )\n }\n\n // Response is successful (2xx). Validate payload (data and encoding) against schema.\n if (method.output.encoding != null) {\n // If the schema specifies an output, verify that the response properly\n // matches the expected format (encoding and schema, if present). If no\n // output is specified, any payload could be returned.\n\n // Needed for type safety. Should never happen since matchesEncoding()\n // should return not succeed if there is a schema encoding but no payload.\n if (!payload) throw new Error('Expected payload')\n\n // Assert valid response body.\n if (method.output.schema && options?.validateResponse !== false) {\n const result = method.output.schema.safeParse(payload.body, {\n strict: options?.strictResponseProcessing ?? true,\n })\n\n if (!result.success) {\n throw new XrpcResponseValidationError(\n method,\n response,\n payload,\n result.reason,\n )\n }\n\n const parsedPayload = {\n body: result.value,\n encoding: payload.encoding,\n } as XrpcResponsePayload<M>\n\n return new XrpcResponse<M>(\n method,\n response.status,\n response.headers,\n parsedPayload,\n )\n }\n }\n\n return new XrpcResponse<M>(\n method,\n response.status,\n response.headers,\n payload as XrpcResponsePayload<M>,\n )\n }\n}\n\ntype ReadPayloadOptions = {\n /**\n * Whether to parse the response body as JSON and convert it to LexValue.\n *\n * @default false\n */\n parse?: false | LexParseOptions\n}\n\n/**\n * @note this function always consumes the response body\n */\nasync function readPayload(\n method: Query | Procedure,\n response: Response,\n options?: ReadPayloadOptions,\n): Promise<undefined | XrpcUnknownResponsePayload> {\n try {\n // @TODO Should we limit the maximum response size here (this could also be\n // done by the FetchHandler)?\n\n const encoding = response.headers\n .get('content-type')\n ?.split(';')[0]\n .trim()\n .toLowerCase()\n\n // Response content-type is undefined\n if (!encoding) {\n // If the body is empty, return undefined (= no payload)\n const arrayBuffer = await response.arrayBuffer()\n if (arrayBuffer.byteLength === 0) return undefined\n\n // If we got data despite no content-type, treat it as binary\n return {\n encoding: CONTENT_TYPE_BINARY,\n body: new Uint8Array(arrayBuffer),\n }\n }\n\n if (!isEncodingString(encoding)) {\n throw new TypeError(`Invalid content-type \"${encoding}\" in response`)\n }\n\n if (options?.parse && encoding === CONTENT_TYPE_JSON) {\n // @NOTE It might be worth returning the raw bytes here (Uint8Array) and\n // perform the lex parsing using cborg/json, allowing to do\n // bytes->LexValue in one step instead of bytes->text->JSON->LexValue.\n // This would require adding encode/decode utilities to lex-json (similar\n // to @ipld/dag-json)\n const text = await response.text()\n\n // @NOTE Using `lexParse(text)` (instead of `jsonToLex(json)`) here as\n // using a reviver function during JSON.parse should be faster than\n // parsing to JSON then converting to Lex (?)\n\n // @TODO verify statement above\n return { encoding, body: lexParse(text, options.parse) }\n }\n\n const arrayBuffer = await response.arrayBuffer()\n return { encoding, body: new Uint8Array(arrayBuffer) }\n } catch (cause) {\n const message = 'Unable to parse response payload'\n const messageDetail = cause instanceof TypeError ? cause.message : undefined\n throw new XrpcInvalidResponseError(\n method,\n response,\n undefined,\n messageDetail ? `${message}: ${messageDetail}` : message,\n { cause },\n )\n }\n}\n\nfunction stringifyEncoding(encoding: string | undefined) {\n return encoding ? `\"${encoding}\"` : 'no payload'\n}\n"]}
|
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
|
package/dist/types.d.ts.map
CHANGED
|
@@ -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;
|
|
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
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 *
|
|
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,
|
|
2
|
-
|
|
3
|
-
|
|
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,17 +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
|
-
* @
|
|
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
|
|
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>;
|
|
49
36
|
export declare function toReadableStreamPonyfill(data: AsyncIterable<Uint8Array>): ReadableStream<Uint8Array>;
|
|
50
37
|
//# sourceMappingURL=util.d.ts.map
|
package/dist/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
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,10 +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.
|
|
6
|
+
exports.buildXrpcRequestHeaders = buildXrpcRequestHeaders;
|
|
6
7
|
exports.toReadableStream = toReadableStream;
|
|
7
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
|
+
}
|
|
8
20
|
/**
|
|
9
21
|
* Type guard to check if a value is {@link Blob}-like.
|
|
10
22
|
*
|
|
@@ -40,13 +52,10 @@ function isAsyncIterable(value) {
|
|
|
40
52
|
* - `atproto-proxy`: Service routing header (if service is specified)
|
|
41
53
|
* - `atproto-accept-labelers`: Comma-separated list of labeler DIDs
|
|
42
54
|
*
|
|
43
|
-
* @
|
|
44
|
-
* @param options.headers - Base headers to include
|
|
45
|
-
* @param options.service - Service proxy identifier
|
|
46
|
-
* @param options.labelers - Labeler DIDs to request labels from
|
|
55
|
+
* @see {@link XrpcRequestHeadersOptions}
|
|
47
56
|
* @returns A new Headers object with AT Protocol headers added
|
|
48
57
|
*/
|
|
49
|
-
function
|
|
58
|
+
function buildXrpcRequestHeaders(options) {
|
|
50
59
|
const headers = new Headers(options?.headers);
|
|
51
60
|
if (options.service && !headers.has('atproto-proxy')) {
|
|
52
61
|
headers.set('atproto-proxy', options.service);
|
package/dist/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;
|
|
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
|
|
6
|
-
|
|
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
|
|
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
|
|
32
|
-
*
|
|
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> =
|
|
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
|
*
|
package/dist/xrpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,IAAI,EAEJ,MAAM,EAEN,SAAS,EACT,KAAK,EACL,UAAU,EACV,YAAY,EAEb,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,KAAK,EAAE,YAAY,EAAc,MAAM,YAAY,CAAA;AAC5D,OAAO,EAAE,WAAW,EAAiC,MAAM,aAAa,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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"}
|
package/dist/xrpc.js
CHANGED
|
@@ -34,13 +34,15 @@ async function xrpcSafe(agentOpts, ns, options = {}) {
|
|
|
34
34
|
}
|
|
35
35
|
function xrpcRequestUrl(method, options) {
|
|
36
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
|
|
37
39
|
const queryString = method.parameters
|
|
38
40
|
?.toURLSearchParams(options.params ?? {})
|
|
39
41
|
.toString();
|
|
40
42
|
return queryString ? `${path}?${queryString}` : path;
|
|
41
43
|
}
|
|
42
44
|
function xrpcRequestInit(schema, options) {
|
|
43
|
-
const headers = (0, util_js_1.
|
|
45
|
+
const headers = (0, util_js_1.buildXrpcRequestHeaders)(options);
|
|
44
46
|
// Tell the server what type of response we're expecting
|
|
45
47
|
if (schema.output.encoding) {
|
|
46
48
|
headers.set('accept', schema.output.encoding);
|
|
@@ -123,7 +125,7 @@ function xrpcProcedureInput(method, options, encodingHint) {
|
|
|
123
125
|
function buildPayload(schema, body, encodingHint) {
|
|
124
126
|
if (schema.encoding === undefined) {
|
|
125
127
|
if (body !== undefined) {
|
|
126
|
-
throw new TypeError(`
|
|
128
|
+
throw new TypeError(`Endpoint expects no payload`);
|
|
127
129
|
}
|
|
128
130
|
return null;
|
|
129
131
|
}
|
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, XrpcFetchError, 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).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: 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(`Endpoint expects no payload`)\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.
|
|
3
|
+
"version": "0.0.18",
|
|
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.
|
|
41
|
-
"@atproto/lex-json": "^0.0.
|
|
42
|
-
"@atproto/lex-schema": "^0.0.
|
|
40
|
+
"@atproto/lex-data": "^0.0.14",
|
|
41
|
+
"@atproto/lex-json": "^0.0.14",
|
|
42
|
+
"@atproto/lex-schema": "^0.0.17"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"vitest": "^4.0.16",
|
|
46
|
-
"@atproto/lex-cbor": "^0.0.
|
|
47
|
-
"@atproto/lex-builder": "^0.0.
|
|
46
|
+
"@atproto/lex-cbor": "^0.0.15",
|
|
47
|
+
"@atproto/lex-builder": "^0.0.20"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"prebuild": "node ./scripts/lex-build.mjs",
|