@atproto/lex-client 0.3.6 → 0.4.0
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 +28 -0
- package/README.md +4 -3
- package/dist/client.d.ts +4 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6 -12
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/applyWrites.d.ts +2 -2
- package/dist/lexicons/com/atproto/repo/applyWrites.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/applyWrites.defs.d.ts +23 -23
- package/dist/lexicons/com/atproto/repo/applyWrites.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/applyWrites.defs.js +13 -64
- package/dist/lexicons/com/atproto/repo/applyWrites.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/applyWrites.js +2 -2
- package/dist/lexicons/com/atproto/repo/applyWrites.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.d.ts +2 -2
- package/dist/lexicons/com/atproto/repo/createRecord.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts +2 -2
- package/dist/lexicons/com/atproto/repo/createRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.defs.js +6 -24
- package/dist/lexicons/com/atproto/repo/createRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/createRecord.js +2 -2
- package/dist/lexicons/com/atproto/repo/createRecord.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/defs.d.ts +1 -1
- package/dist/lexicons/com/atproto/repo/defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/defs.defs.d.ts +4 -4
- package/dist/lexicons/com/atproto/repo/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/defs.defs.js +3 -7
- package/dist/lexicons/com/atproto/repo/defs.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/defs.js +1 -1
- package/dist/lexicons/com/atproto/repo/defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/deleteRecord.d.ts +2 -2
- package/dist/lexicons/com/atproto/repo/deleteRecord.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts +2 -2
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js +6 -19
- package/dist/lexicons/com/atproto/repo/deleteRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/deleteRecord.js +2 -2
- package/dist/lexicons/com/atproto/repo/deleteRecord.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/getRecord.d.ts +2 -2
- package/dist/lexicons/com/atproto/repo/getRecord.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts +1 -1
- package/dist/lexicons/com/atproto/repo/getRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/getRecord.defs.js +5 -14
- package/dist/lexicons/com/atproto/repo/getRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/getRecord.js +2 -2
- package/dist/lexicons/com/atproto/repo/getRecord.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/listRecords.d.ts +2 -2
- package/dist/lexicons/com/atproto/repo/listRecords.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts +5 -5
- package/dist/lexicons/com/atproto/repo/listRecords.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/listRecords.defs.js +5 -22
- package/dist/lexicons/com/atproto/repo/listRecords.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/listRecords.js +2 -2
- package/dist/lexicons/com/atproto/repo/listRecords.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/putRecord.d.ts +2 -2
- package/dist/lexicons/com/atproto/repo/putRecord.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts +2 -2
- package/dist/lexicons/com/atproto/repo/putRecord.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/putRecord.defs.js +6 -25
- package/dist/lexicons/com/atproto/repo/putRecord.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/putRecord.js +2 -2
- package/dist/lexicons/com/atproto/repo/putRecord.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.d.ts +2 -2
- package/dist/lexicons/com/atproto/repo/uploadBlob.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js +4 -6
- package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/repo/uploadBlob.js +2 -2
- package/dist/lexicons/com/atproto/repo/uploadBlob.js.map +1 -1
- package/dist/lexicons/com/atproto/repo.d.ts +8 -8
- package/dist/lexicons/com/atproto/repo.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/repo.js +8 -8
- package/dist/lexicons/com/atproto/repo.js.map +1 -1
- package/dist/lexicons/com/atproto/sync/getBlob.d.ts +2 -2
- package/dist/lexicons/com/atproto/sync/getBlob.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/sync/getBlob.defs.d.ts +1 -1
- package/dist/lexicons/com/atproto/sync/getBlob.defs.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/sync/getBlob.defs.js +5 -14
- package/dist/lexicons/com/atproto/sync/getBlob.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/sync/getBlob.js +2 -2
- package/dist/lexicons/com/atproto/sync/getBlob.js.map +1 -1
- package/dist/lexicons/com/atproto/sync.d.ts +1 -1
- package/dist/lexicons/com/atproto/sync.d.ts.map +1 -1
- package/dist/lexicons/com/atproto/sync.js +1 -1
- package/dist/lexicons/com/atproto/sync.js.map +1 -1
- package/dist/lexicons/com/atproto.d.ts +2 -2
- package/dist/lexicons/com/atproto.d.ts.map +1 -1
- package/dist/lexicons/com/atproto.js +2 -2
- package/dist/lexicons/com/atproto.js.map +1 -1
- package/dist/lexicons/com.d.ts +1 -1
- package/dist/lexicons/com.d.ts.map +1 -1
- package/dist/lexicons/com.js +1 -1
- package/dist/lexicons/com.js.map +1 -1
- package/dist/lexicons/index.d.ts +1 -1
- package/dist/lexicons/index.d.ts.map +1 -1
- package/dist/lexicons/index.js +1 -1
- package/dist/lexicons/index.js.map +1 -1
- package/dist/write-operation-builder.d.ts +1 -1
- package/dist/write-operation-builder.d.ts.map +1 -1
- package/dist/write-operation-builder.js +4 -4
- package/dist/write-operation-builder.js.map +1 -1
- package/package.json +14 -7
- package/dist/agent.d.ts +0 -127
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -41
- package/dist/agent.js.map +0 -1
- package/dist/errors.d.ts +0 -277
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -388
- package/dist/errors.js.map +0 -1
- package/dist/response.d.ts +0 -124
- package/dist/response.d.ts.map +0 -1
- package/dist/response.js +0 -178
- package/dist/response.js.map +0 -1
- package/dist/types.d.ts +0 -54
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -16
- package/dist/types.js.map +0 -1
- package/dist/util.d.ts +0 -90
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js +0 -210
- package/dist/util.js.map +0 -1
- package/dist/www-authenticate.d.ts +0 -35
- package/dist/www-authenticate.d.ts.map +0 -1
- package/dist/www-authenticate.js +0 -54
- package/dist/www-authenticate.js.map +0 -1
- package/dist/xrpc.d.ts +0 -194
- package/dist/xrpc.d.ts.map +0 -1
- package/dist/xrpc.js +0 -235
- package/dist/xrpc.js.map +0 -1
package/dist/response.js
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { jsonToLex } from '@atproto/lex-json';
|
|
2
|
-
import { XrpcAuthenticationError, XrpcInvalidResponseError, XrpcResponseError, XrpcResponseValidationError, } from './errors.js';
|
|
3
|
-
import { isEncodingString } from './types.js';
|
|
4
|
-
const CONTENT_TYPE_BINARY = 'application/octet-stream';
|
|
5
|
-
const CONTENT_TYPE_JSON = 'application/json';
|
|
6
|
-
/**
|
|
7
|
-
* Small container for XRPC response.
|
|
8
|
-
*
|
|
9
|
-
* @implements {ResultSuccess<XrpcResponse<M>>} for convenience in result handling contexts.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
*
|
|
13
|
-
* ```typescript
|
|
14
|
-
* import { app } from '#/lexicons'
|
|
15
|
-
* import { XrpcResponse } from '@atproto/lex-client'
|
|
16
|
-
*
|
|
17
|
-
* const fetchResponse = await fetch('https://example.com/xrpc/app.bsky.feed.getTimeline')
|
|
18
|
-
*
|
|
19
|
-
* const response = await XrpcResponse.fromFetchResponse(
|
|
20
|
-
* app.bsky.feed.getTimeline.main,
|
|
21
|
-
* fetchResponse,
|
|
22
|
-
* )
|
|
23
|
-
*
|
|
24
|
-
* // Fully typed (validated) response body, according to the method's output schema
|
|
25
|
-
* const { cursor, feed } = response.body
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export class XrpcResponse {
|
|
29
|
-
/** @see {@link ResultSuccess.value} */
|
|
30
|
-
get value() {
|
|
31
|
-
return this;
|
|
32
|
-
}
|
|
33
|
-
constructor(method, status, headers, payload) {
|
|
34
|
-
this.method = method;
|
|
35
|
-
this.status = status;
|
|
36
|
-
this.headers = headers;
|
|
37
|
-
this.payload = payload;
|
|
38
|
-
/** @see {@link ResultSuccess.success} */
|
|
39
|
-
this.success = true;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Whether the response payload was parsed as {@link LexValue} (`true`) or is
|
|
43
|
-
* in binary form {@link Uint8Array} (`false`).
|
|
44
|
-
*/
|
|
45
|
-
get isParsed() {
|
|
46
|
-
return this.method.output.encoding === CONTENT_TYPE_JSON;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* The Content-Type encoding of the response (e.g., 'application/json').
|
|
50
|
-
* Returns `undefined` if the response has no body.
|
|
51
|
-
*/
|
|
52
|
-
get encoding() {
|
|
53
|
-
return this.payload?.encoding;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* The parsed response body.
|
|
57
|
-
*
|
|
58
|
-
* For 'application/json' responses, this is the parsed and validated LexValue.
|
|
59
|
-
* For binary responses, this is a Uint8Array.
|
|
60
|
-
* Returns `undefined` if the response has no body.
|
|
61
|
-
*/
|
|
62
|
-
get body() {
|
|
63
|
-
return this.payload?.body;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* @throws {XrpcResponseError} in case of (valid) XRPC error responses. Use
|
|
67
|
-
* {@link XrpcResponseError.matchesSchemaErrors} to narrow the error type based on
|
|
68
|
-
* the method's declared error schema. This can be narrowed further as a
|
|
69
|
-
* {@link XrpcAuthenticationError} if the error is an authentication error.
|
|
70
|
-
* @throws {XrpcInvalidResponseError} when the response is not a valid XRPC
|
|
71
|
-
* response, or if the response does not conform to the method's schema.
|
|
72
|
-
*/
|
|
73
|
-
static async fromFetchResponse(method, response, options) {
|
|
74
|
-
// @NOTE The body MUST either be read or canceled to avoid resource leaks.
|
|
75
|
-
// Since nothing should cause an exception before "readPayload" is
|
|
76
|
-
// called, we can safely not use a try/finally here.
|
|
77
|
-
// Always turn 4xx/5xx responses into XrpcResponseError
|
|
78
|
-
if (response.status >= 400) {
|
|
79
|
-
const payload = await readPayload(method, response, {
|
|
80
|
-
// Always parse errors in non-strict mode
|
|
81
|
-
parse: { strict: false },
|
|
82
|
-
});
|
|
83
|
-
if (response.status === 401) {
|
|
84
|
-
throw new XrpcAuthenticationError(method, response, payload);
|
|
85
|
-
}
|
|
86
|
-
throw new XrpcResponseError(method, response, payload);
|
|
87
|
-
}
|
|
88
|
-
// @NOTE redirect is set to 'follow', so we shouldn't get 3xx responses here
|
|
89
|
-
if (response.status < 200 || response.status >= 300) {
|
|
90
|
-
await response.body?.cancel();
|
|
91
|
-
throw new XrpcInvalidResponseError(method, response, undefined, `Unexpected status code ${response.status}`);
|
|
92
|
-
}
|
|
93
|
-
const payload = await readPayload(method, response, {
|
|
94
|
-
// Parse response if there is a schema, or if the encoding is
|
|
95
|
-
// "application/json"
|
|
96
|
-
parse: method.output.schema || method.output.encoding === CONTENT_TYPE_JSON
|
|
97
|
-
? { strict: options?.strictResponseProcessing ?? true }
|
|
98
|
-
: // If there is no declared output encoding, we'll parse the output (in loose mode)
|
|
99
|
-
method.output.encoding == null
|
|
100
|
-
? { strict: false }
|
|
101
|
-
: false,
|
|
102
|
-
});
|
|
103
|
-
if (!method.output.matchesEncoding(payload?.encoding)) {
|
|
104
|
-
throw new XrpcInvalidResponseError(method, response, payload, `Expected ${stringifyEncoding(method.output.encoding)} response (got ${stringifyEncoding(payload?.encoding)})`);
|
|
105
|
-
}
|
|
106
|
-
// Response is successful (2xx). Validate payload (data and encoding) against schema.
|
|
107
|
-
if (method.output.encoding != null) {
|
|
108
|
-
// If the schema specifies an output, verify that the response properly
|
|
109
|
-
// matches the expected format (encoding and schema, if present). If no
|
|
110
|
-
// output is specified, any payload could be returned.
|
|
111
|
-
// Needed for type safety. Should never happen since matchesEncoding()
|
|
112
|
-
// should return not succeed if there is a schema encoding but no payload.
|
|
113
|
-
if (!payload)
|
|
114
|
-
throw new Error('Expected payload');
|
|
115
|
-
// Assert valid response body.
|
|
116
|
-
if (method.output.schema && options?.validateResponse !== false) {
|
|
117
|
-
const result = method.output.schema.safeParse(payload.body, {
|
|
118
|
-
strict: options?.strictResponseProcessing ?? true,
|
|
119
|
-
});
|
|
120
|
-
if (!result.success) {
|
|
121
|
-
throw new XrpcResponseValidationError(method, response, payload, result.reason);
|
|
122
|
-
}
|
|
123
|
-
const parsedPayload = {
|
|
124
|
-
body: result.value,
|
|
125
|
-
encoding: payload.encoding,
|
|
126
|
-
};
|
|
127
|
-
return new XrpcResponse(method, response.status, response.headers, parsedPayload);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return new XrpcResponse(method, response.status, response.headers, payload);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* @note this function always consumes the response body
|
|
135
|
-
*/
|
|
136
|
-
async function readPayload(method, response, options) {
|
|
137
|
-
try {
|
|
138
|
-
// @TODO Should we limit the maximum response size here (this could also be
|
|
139
|
-
// done by the FetchHandler)?
|
|
140
|
-
const encoding = response.headers
|
|
141
|
-
.get('content-type')
|
|
142
|
-
?.split(';')[0]
|
|
143
|
-
.trim()
|
|
144
|
-
.toLowerCase();
|
|
145
|
-
// Response content-type is undefined
|
|
146
|
-
if (!encoding) {
|
|
147
|
-
// If the body is empty, return undefined (= no payload)
|
|
148
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
149
|
-
if (arrayBuffer.byteLength === 0)
|
|
150
|
-
return undefined;
|
|
151
|
-
// If we got data despite no content-type, treat it as binary
|
|
152
|
-
return {
|
|
153
|
-
encoding: CONTENT_TYPE_BINARY,
|
|
154
|
-
body: new Uint8Array(arrayBuffer),
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
if (!isEncodingString(encoding)) {
|
|
158
|
-
throw new TypeError(`Invalid content-type "${encoding}" in response`);
|
|
159
|
-
}
|
|
160
|
-
if (options?.parse && encoding === CONTENT_TYPE_JSON) {
|
|
161
|
-
// @NOTE See ./response.bench.ts to comparison of different parsing approaches.
|
|
162
|
-
const json = await response.json();
|
|
163
|
-
const body = jsonToLex(json, options.parse);
|
|
164
|
-
return { encoding, body };
|
|
165
|
-
}
|
|
166
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
167
|
-
return { encoding, body: new Uint8Array(arrayBuffer) };
|
|
168
|
-
}
|
|
169
|
-
catch (cause) {
|
|
170
|
-
const message = 'Unable to parse response payload';
|
|
171
|
-
const messageDetail = cause instanceof TypeError ? cause.message : undefined;
|
|
172
|
-
throw new XrpcInvalidResponseError(method, response, undefined, messageDetail ? `${message}: ${messageDetail}` : message, { cause });
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
function stringifyEncoding(encoding) {
|
|
176
|
-
return encoding ? `"${encoding}"` : 'no payload';
|
|
177
|
-
}
|
|
178
|
-
//# sourceMappingURL=response.js.map
|
package/dist/response.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"response.js","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"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","sourcesContent":["import type { LexParseOptions } from '@atproto/lex-json'\nimport { jsonToLex } from '@atproto/lex-json'\nimport type {\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 { isEncodingString } from './types.js'\nimport type { EncodingString, XrpcUnknownResponsePayload } 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<ArrayBuffer>\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<ArrayBuffer>\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\n | { body: LexValue | Uint8Array<ArrayBuffer>; 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.\n *\n * @implements {ResultSuccess<XrpcResponse<M>>} for convenience in result handling contexts.\n *\n * @example\n *\n * ```typescript\n * import { app } from '#/lexicons'\n * import { XrpcResponse } from '@atproto/lex-client'\n *\n * const fetchResponse = await fetch('https://example.com/xrpc/app.bsky.feed.getTimeline')\n *\n * const response = await XrpcResponse.fromFetchResponse(\n * app.bsky.feed.getTimeline.main,\n * fetchResponse,\n * )\n *\n * // Fully typed (validated) response body, according to the method's output schema\n * const { cursor, feed } = response.body\n * ```\n */\nexport class XrpcResponse<M extends Procedure | Query> implements ResultSuccess<\n 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(): boolean {\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 See ./response.bench.ts to comparison of different parsing approaches.\n const json = await response.json()\n const body = jsonToLex(json, options.parse)\n return { encoding, body }\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
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { type DidString, type LexValue, type UnknownString } from '@atproto/lex-schema';
|
|
2
|
-
export type { DidString, LexValue, UnknownString };
|
|
3
|
-
/**
|
|
4
|
-
* Service identifier fragment for DID service endpoints.
|
|
5
|
-
*
|
|
6
|
-
* Common values include 'atproto_labeler' for labeling services,
|
|
7
|
-
* or custom service identifiers.
|
|
8
|
-
*/
|
|
9
|
-
export type DidServiceIdentifier = 'atproto_labeler' | UnknownString;
|
|
10
|
-
/**
|
|
11
|
-
* A full service proxy identifier combining a DID with a service fragment.
|
|
12
|
-
*
|
|
13
|
-
* Used to route requests through a specific service endpoint.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```typescript
|
|
17
|
-
* const service: Service = 'did:web:api.bsky.app#bsky_appview'
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
export type Service = `${DidString}#${DidServiceIdentifier}`;
|
|
21
|
-
export declare const isService: (value: string) => value is Service;
|
|
22
|
-
/**
|
|
23
|
-
* Valid input types for binary request bodies.
|
|
24
|
-
*
|
|
25
|
-
* These types can be used as the body for procedures that expect
|
|
26
|
-
* non-JSON content (e.g., blob uploads, binary data).
|
|
27
|
-
*
|
|
28
|
-
* @example Uploading a blob
|
|
29
|
-
* ```typescript
|
|
30
|
-
* const imageData: BinaryBodyInit = new Uint8Array(buffer)
|
|
31
|
-
* await client.uploadBlob(imageData, { encoding: 'image/png' })
|
|
32
|
-
* ```
|
|
33
|
-
*
|
|
34
|
-
* @example Streaming upload
|
|
35
|
-
* ```typescript
|
|
36
|
-
* const stream: BinaryBodyInit = someReadableStream
|
|
37
|
-
* await client.xrpc(uploadMethod, { body: stream })
|
|
38
|
-
* ```
|
|
39
|
-
*
|
|
40
|
-
* @example File upload in browser
|
|
41
|
-
* ```typescript
|
|
42
|
-
* const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement
|
|
43
|
-
* const file: BinaryBodyInit = fileInput.files[0]
|
|
44
|
-
* await client.xrpc(uploadMethod, { body: file })
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export type BinaryBodyInit = string | Blob | Uint8Array | ArrayBuffer | ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>;
|
|
48
|
-
export type EncodingString = `${string}/${string}`;
|
|
49
|
-
export declare function isEncodingString(contentType: string): contentType is EncodingString;
|
|
50
|
-
export type XrpcUnknownResponsePayload<TBinary extends BinaryBodyInit = Uint8Array> = {
|
|
51
|
-
encoding: EncodingString;
|
|
52
|
-
body: LexValue | TBinary;
|
|
53
|
-
};
|
|
54
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,aAAa,EAEnB,MAAM,qBAAqB,CAAA;AAE5B,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,eAAO,MAAM,SAAS,UAAW,MAAM,KAAG,KAAK,IAAI,OAOlD,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,IAAI,GACJ,UAAU,GACV,WAAW,GACX,cAAc,CAAC,UAAU,CAAC,GAC1B,aAAa,CAAC,UAAU,CAAC,CAAA;AAE7B,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
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { isDidString, } from '@atproto/lex-schema';
|
|
2
|
-
export const isService = (value) => {
|
|
3
|
-
const hashIndex = value.indexOf('#');
|
|
4
|
-
if (hashIndex < 7)
|
|
5
|
-
return false; // "did:a:b#" / no "#" in value
|
|
6
|
-
if (hashIndex > value.length - 3)
|
|
7
|
-
return false; // "#x"
|
|
8
|
-
if (value.includes('#', hashIndex + 1))
|
|
9
|
-
return false;
|
|
10
|
-
const did = value.slice(0, hashIndex);
|
|
11
|
-
return isDidString(did);
|
|
12
|
-
};
|
|
13
|
-
export function isEncodingString(contentType) {
|
|
14
|
-
return contentType.includes('/');
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,WAAW,GACZ,MAAM,qBAAqB,CAAA;AAwB5B,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAa,EAAoB,EAAE;IAC3D,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACpC,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,KAAK,CAAA,CAAC,+BAA+B;IAC/D,IAAI,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAA,CAAC,OAAO;IACtD,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IACpD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;IACrC,OAAO,WAAW,CAAC,GAAG,CAAC,CAAA;AACzB,CAAC,CAAA;AAqCD,MAAM,UAAU,gBAAgB,CAC9B,WAAmB;IAEnB,OAAO,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;AAClC,CAAC","sourcesContent":["import {\n type DidString,\n type LexValue,\n type UnknownString,\n isDidString,\n} 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\nexport const isService = (value: string): value is Service => {\n const hashIndex = value.indexOf('#')\n if (hashIndex < 7) return false // \"did:a:b#\" / no \"#\" in value\n if (hashIndex > value.length - 3) return false // \"#x\"\n if (value.includes('#', hashIndex + 1)) return false\n const did = value.slice(0, hashIndex)\n return isDidString(did)\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 | string\n | Blob\n | Uint8Array\n | ArrayBuffer\n | ReadableStream<Uint8Array>\n | AsyncIterable<Uint8Array>\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
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import type { InferRecordKey, LexiconRecordKey, RecordSchema } from '@atproto/lex-schema';
|
|
2
|
-
import type { DidString, Service } from './types.ts';
|
|
3
|
-
type NonReadonly<T> = {
|
|
4
|
-
-readonly [K in keyof T]: T[K];
|
|
5
|
-
};
|
|
6
|
-
export type WithDefaults<TOptions extends Record<string, unknown>, TDefaults extends Record<string, unknown>[]> = TDefaults extends [infer First, ...infer Rest extends any[]] ? TOptions & WithDefaults<NonReadonly<First>, Rest> : TOptions;
|
|
7
|
-
export declare function applyDefaults<TOptions extends Readonly<Record<string, unknown>>, TDefaults extends Readonly<Record<string, unknown>>[]>(options: TOptions, ...defaults: TDefaults): WithDefaults<TOptions, TDefaults>;
|
|
8
|
-
/**
|
|
9
|
-
* Type guard to check if a value is {@link Blob}-like.
|
|
10
|
-
*
|
|
11
|
-
* Handles both native Blobs and polyfilled Blob implementations
|
|
12
|
-
* (e.g., fetch-blob from node-fetch).
|
|
13
|
-
*
|
|
14
|
-
* @param value - The value to check
|
|
15
|
-
* @returns `true` if the value is a Blob or Blob-like object
|
|
16
|
-
*/
|
|
17
|
-
export declare function isBlobLike(value: unknown): value is Blob;
|
|
18
|
-
export declare function isAsyncIterable<T>(value: T): value is unknown extends T ? T & AsyncIterable<unknown> : Extract<T, AsyncIterable<any>>;
|
|
19
|
-
export declare function asUint8ArrayArrayBuffer(bytes: Uint8Array): Uint8Array<ArrayBuffer>;
|
|
20
|
-
export type XrpcRequestHeadersOptions = {
|
|
21
|
-
/**
|
|
22
|
-
* Additional custom HTTP headers to include in the request.
|
|
23
|
-
*
|
|
24
|
-
* @note "atproto-proxy" and "atproto-accept-labelers" headers might change
|
|
25
|
-
* depending on the `service` and `labelers` options, respectively, if they
|
|
26
|
-
* are provided (which is always the case when using {@link Client.xrpc}).
|
|
27
|
-
*/
|
|
28
|
-
headers?: HeadersInit;
|
|
29
|
-
/**
|
|
30
|
-
* Labeler DIDs to request labels from for content moderation.
|
|
31
|
-
*
|
|
32
|
-
* When `undefined`, will default to the client instance's default. When
|
|
33
|
-
* `null`, it will cause any existing `atproto-accept-labelers` header
|
|
34
|
-
* (including one provided through the
|
|
35
|
-
* {@link XrpcRequestHeadersOptions.headers} option) to be removed.
|
|
36
|
-
*/
|
|
37
|
-
labelers?: null | Iterable<DidString>;
|
|
38
|
-
/**
|
|
39
|
-
* Labeler DIDs to request labels from for content moderation. Values here
|
|
40
|
-
* will always be applied with the ";redact" suffix, which indicates that the
|
|
41
|
-
* client is requesting that the labeler redact content that is deemed
|
|
42
|
-
* inappropriate.
|
|
43
|
-
*/
|
|
44
|
-
appLabelers?: null | Iterable<DidString>;
|
|
45
|
-
/**
|
|
46
|
-
* Service proxy identifier for routing requests through a specific service.
|
|
47
|
-
*
|
|
48
|
-
* When `undefined`, will default to the client instance's default. When not
|
|
49
|
-
* used against a client instance (e.g., when using the {@link xrpc} helper
|
|
50
|
-
* function), the default is to not alter the `atproto-proxy` header (e.g. if
|
|
51
|
-
* one is provided in the {@link XrpcRequestHeadersOptions.headers}).
|
|
52
|
-
*
|
|
53
|
-
* When defined (as either `null` or a string), it will override any
|
|
54
|
-
* `atproto-proxy` header provided in the
|
|
55
|
-
* {@link XrpcRequestHeadersOptions.headers} option.
|
|
56
|
-
*/
|
|
57
|
-
service?: null | Service;
|
|
58
|
-
};
|
|
59
|
-
/**
|
|
60
|
-
* Builds HTTP headers for AT Protocol requests.
|
|
61
|
-
*
|
|
62
|
-
* Adds or removes the following headers based on the provided options:
|
|
63
|
-
* - `atproto-proxy`: Service routing header (if service is specified)
|
|
64
|
-
* - `atproto-accept-labelers`: Comma-separated list of labeler DIDs
|
|
65
|
-
*
|
|
66
|
-
* @see {@link XrpcRequestHeadersOptions}
|
|
67
|
-
* @returns A new Headers object with AT Protocol headers added
|
|
68
|
-
*/
|
|
69
|
-
export declare function buildXrpcRequestHeaders({ service, labelers, appLabelers, headers: headersInit, }: XrpcRequestHeadersOptions): Headers;
|
|
70
|
-
export declare function toReadableStream(data: AsyncIterable<Uint8Array>): ReadableStream<Uint8Array>;
|
|
71
|
-
export declare function toReadableStreamPonyfill(data: AsyncIterable<Uint8Array>): ReadableStream<Uint8Array>;
|
|
72
|
-
export type RecordKeyOptions<T extends RecordSchema, AlsoOptionalWhenRecordKeyIs extends LexiconRecordKey = never> = T['key'] extends `literal:${string}` | AlsoOptionalWhenRecordKeyIs ? {
|
|
73
|
-
rkey?: InferRecordKey<T>;
|
|
74
|
-
} : {
|
|
75
|
-
rkey: InferRecordKey<T>;
|
|
76
|
-
};
|
|
77
|
-
export declare function getDefaultRecordKey<const T extends RecordSchema>(schema: T): undefined | InferRecordKey<T>;
|
|
78
|
-
export declare function getLiteralRecordKey<const T extends RecordSchema>(schema: T): InferRecordKey<T>;
|
|
79
|
-
export declare function mergeHeaders(defaultHeaders: HeadersInit, requestHeaders: HeadersInit): Headers;
|
|
80
|
-
/**
|
|
81
|
-
* Uses the native `AbortSignal.throwIfAborted()` when available, falling back
|
|
82
|
-
* for older implementations such as React Native's `abort-controller`.
|
|
83
|
-
*/
|
|
84
|
-
export declare function throwIfAborted(signal?: AbortSignal): void;
|
|
85
|
-
export declare function wait(ms: number, { signal }?: {
|
|
86
|
-
signal?: AbortSignal;
|
|
87
|
-
}): Promise<void>;
|
|
88
|
-
export declare function trim(value: string): string;
|
|
89
|
-
export {};
|
|
90
|
-
//# sourceMappingURL=util.d.ts.map
|
package/dist/util.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|
package/dist/util.js
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
export function applyDefaults(options, ...defaults) {
|
|
2
|
-
const combined = { ...options };
|
|
3
|
-
// @NOTE We make sure that options with an explicit `undefined` value get the
|
|
4
|
-
// default, since spreading doesn't override with `undefined`. When multiple
|
|
5
|
-
// defaults define the same key, the first defined value wins.
|
|
6
|
-
for (const def of defaults) {
|
|
7
|
-
for (const key of Object.keys(def)) {
|
|
8
|
-
if (combined[key] === undefined) {
|
|
9
|
-
combined[key] = def[key];
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return combined;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Type guard to check if a value is {@link Blob}-like.
|
|
17
|
-
*
|
|
18
|
-
* Handles both native Blobs and polyfilled Blob implementations
|
|
19
|
-
* (e.g., fetch-blob from node-fetch).
|
|
20
|
-
*
|
|
21
|
-
* @param value - The value to check
|
|
22
|
-
* @returns `true` if the value is a Blob or Blob-like object
|
|
23
|
-
*/
|
|
24
|
-
export function isBlobLike(value) {
|
|
25
|
-
if (value == null)
|
|
26
|
-
return false;
|
|
27
|
-
if (typeof value !== 'object')
|
|
28
|
-
return false;
|
|
29
|
-
if (typeof Blob === 'function' && value instanceof Blob)
|
|
30
|
-
return true;
|
|
31
|
-
// Support for Blobs provided by libraries that don't use the native Blob
|
|
32
|
-
// (e.g. fetch-blob from node-fetch).
|
|
33
|
-
// https://github.com/node-fetch/fetch-blob/blob/a1a182e5978811407bef4ea1632b517567dda01f/index.js#L233-L244
|
|
34
|
-
const tag = value[Symbol.toStringTag];
|
|
35
|
-
if (tag === 'Blob' || tag === 'File') {
|
|
36
|
-
return 'stream' in value && typeof value.stream === 'function';
|
|
37
|
-
}
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
export function isAsyncIterable(value) {
|
|
41
|
-
return (value != null && typeof value[Symbol.asyncIterator] === 'function');
|
|
42
|
-
}
|
|
43
|
-
export function asUint8ArrayArrayBuffer(bytes) {
|
|
44
|
-
// If the Uint8Array is already backed by a non-shared ArrayBuffer, we can use
|
|
45
|
-
// it directly.
|
|
46
|
-
if (bytes.buffer instanceof ArrayBuffer) {
|
|
47
|
-
return bytes;
|
|
48
|
-
}
|
|
49
|
-
// Otherwise, we need to create a new ArrayBuffer and copy the data.
|
|
50
|
-
return new Uint8Array(bytes);
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Builds HTTP headers for AT Protocol requests.
|
|
54
|
-
*
|
|
55
|
-
* Adds or removes the following headers based on the provided options:
|
|
56
|
-
* - `atproto-proxy`: Service routing header (if service is specified)
|
|
57
|
-
* - `atproto-accept-labelers`: Comma-separated list of labeler DIDs
|
|
58
|
-
*
|
|
59
|
-
* @see {@link XrpcRequestHeadersOptions}
|
|
60
|
-
* @returns A new Headers object with AT Protocol headers added
|
|
61
|
-
*/
|
|
62
|
-
export function buildXrpcRequestHeaders({ service, labelers, appLabelers, headers: headersInit, }) {
|
|
63
|
-
const headers = new Headers(headersInit);
|
|
64
|
-
// If provided, the "service" option overrides any existing "atproto-proxy"
|
|
65
|
-
// header. If `null`, the header is removed entirely.
|
|
66
|
-
if (service !== undefined) {
|
|
67
|
-
if (service === null) {
|
|
68
|
-
headers.delete('atproto-proxy');
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
headers.set('atproto-proxy', service);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
// Labelers are combined from the "appLabelers" and "labelers" options, as
|
|
75
|
-
// well as any existing "atproto-accept-labelers" header. The "appLabelers"
|
|
76
|
-
// are always added with the ";redact" suffix, while the "labelers" are added
|
|
77
|
-
// as-is. Existing labelers headers are not preserved if the "labelers" option
|
|
78
|
-
// is explicitly set to null.
|
|
79
|
-
const combinedLabelers = new Set();
|
|
80
|
-
if (appLabelers) {
|
|
81
|
-
for (const labeler of appLabelers) {
|
|
82
|
-
combinedLabelers.add(`${labeler};redact`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (labelers) {
|
|
86
|
-
for (const labeler of labelers) {
|
|
87
|
-
combinedLabelers.add(labeler);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (labelers !== null) {
|
|
91
|
-
const headersLabelers = headers.get('atproto-accept-labelers');
|
|
92
|
-
if (headersLabelers) {
|
|
93
|
-
for (const labeler of headersLabelers
|
|
94
|
-
.split(',')
|
|
95
|
-
.map(trim)
|
|
96
|
-
.filter(Boolean)) {
|
|
97
|
-
if (labeler)
|
|
98
|
-
combinedLabelers.add(labeler);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (combinedLabelers.size > 0) {
|
|
103
|
-
headers.set('atproto-accept-labelers', Array.from(combinedLabelers).join(', '));
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
headers.delete('atproto-accept-labelers');
|
|
107
|
-
}
|
|
108
|
-
return headers;
|
|
109
|
-
}
|
|
110
|
-
export function toReadableStream(data) {
|
|
111
|
-
// Use the native ReadableStream.from() if available.
|
|
112
|
-
/* v8 ignore next -- @preserve */
|
|
113
|
-
if ('from' in ReadableStream && typeof ReadableStream.from === 'function') {
|
|
114
|
-
return ReadableStream.from(data);
|
|
115
|
-
}
|
|
116
|
-
/* v8 ignore next -- @preserve */
|
|
117
|
-
return toReadableStreamPonyfill(data);
|
|
118
|
-
}
|
|
119
|
-
export function toReadableStreamPonyfill(data) {
|
|
120
|
-
let iterator;
|
|
121
|
-
return new ReadableStream({
|
|
122
|
-
async pull(controller) {
|
|
123
|
-
try {
|
|
124
|
-
iterator ??= data[Symbol.asyncIterator]();
|
|
125
|
-
const result = await iterator.next();
|
|
126
|
-
if (result.done)
|
|
127
|
-
controller.close();
|
|
128
|
-
else
|
|
129
|
-
controller.enqueue(result.value);
|
|
130
|
-
}
|
|
131
|
-
catch (err) {
|
|
132
|
-
controller.error(err);
|
|
133
|
-
iterator = undefined;
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
async cancel() {
|
|
137
|
-
await iterator?.return?.();
|
|
138
|
-
iterator = undefined;
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
export function getDefaultRecordKey(schema) {
|
|
143
|
-
// Let the server generate the TID
|
|
144
|
-
if (schema.key === 'tid')
|
|
145
|
-
return undefined;
|
|
146
|
-
if (schema.key === 'any')
|
|
147
|
-
return undefined;
|
|
148
|
-
return getLiteralRecordKey(schema);
|
|
149
|
-
}
|
|
150
|
-
export function getLiteralRecordKey(schema) {
|
|
151
|
-
if (schema.key.startsWith('literal:')) {
|
|
152
|
-
return schema.key.slice(8);
|
|
153
|
-
}
|
|
154
|
-
throw new TypeError(`An "rkey" must be provided for record key type "${schema.key}" (${schema.$type})`);
|
|
155
|
-
}
|
|
156
|
-
export function mergeHeaders(defaultHeaders, requestHeaders) {
|
|
157
|
-
// We don't want to alter the original Headers objects, so we create a new one
|
|
158
|
-
const result = new Headers(defaultHeaders);
|
|
159
|
-
const overrides = requestHeaders instanceof Headers
|
|
160
|
-
? requestHeaders
|
|
161
|
-
: new Headers(requestHeaders);
|
|
162
|
-
for (const [key, value] of overrides.entries()) {
|
|
163
|
-
result.set(key, value);
|
|
164
|
-
}
|
|
165
|
-
return result;
|
|
166
|
-
}
|
|
167
|
-
function getAbortReason(signal) {
|
|
168
|
-
if ('reason' in signal)
|
|
169
|
-
return signal.reason;
|
|
170
|
-
return typeof DOMException !== 'undefined'
|
|
171
|
-
? new DOMException('Aborted', 'AbortError')
|
|
172
|
-
: new Error('Aborted');
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Uses the native `AbortSignal.throwIfAborted()` when available, falling back
|
|
176
|
-
* for older implementations such as React Native's `abort-controller`.
|
|
177
|
-
*/
|
|
178
|
-
export function throwIfAborted(signal) {
|
|
179
|
-
if (!signal)
|
|
180
|
-
return;
|
|
181
|
-
if (typeof signal.throwIfAborted === 'function') {
|
|
182
|
-
signal.throwIfAborted();
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (signal.aborted)
|
|
186
|
-
throw getAbortReason(signal);
|
|
187
|
-
}
|
|
188
|
-
export function wait(ms, { signal } = {}) {
|
|
189
|
-
return new Promise((resolve, reject) => {
|
|
190
|
-
throwIfAborted(signal);
|
|
191
|
-
const cleanup = () => {
|
|
192
|
-
clearTimeout(timeout);
|
|
193
|
-
signal?.removeEventListener('abort', onAbort);
|
|
194
|
-
};
|
|
195
|
-
const timeout = setTimeout(() => {
|
|
196
|
-
cleanup();
|
|
197
|
-
resolve();
|
|
198
|
-
}, ms);
|
|
199
|
-
const onAbort = () => {
|
|
200
|
-
cleanup();
|
|
201
|
-
// @NOTE the signal exists because this listener came from it.
|
|
202
|
-
reject(getAbortReason(signal));
|
|
203
|
-
};
|
|
204
|
-
signal?.addEventListener('abort', onAbort);
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
export function trim(value) {
|
|
208
|
-
return value.trim();
|
|
209
|
-
}
|
|
210
|
-
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n InferRecordKey,\n LexiconRecordKey,\n RecordSchema,\n} from '@atproto/lex-schema'\nimport type { DidString, Service } from './types.ts'\n\ntype NonReadonly<T> = { -readonly [K in keyof T]: T[K] }\n\nexport type WithDefaults<\n TOptions extends Record<string, unknown>,\n TDefaults extends Record<string, unknown>[],\n> = TDefaults extends [infer First, ...infer Rest extends any[]]\n ? TOptions & WithDefaults<NonReadonly<First>, Rest>\n : TOptions\n\nexport function applyDefaults<\n TOptions extends Readonly<Record<string, unknown>>,\n TDefaults extends Readonly<Record<string, unknown>>[],\n>(\n options: TOptions,\n ...defaults: TDefaults\n): WithDefaults<TOptions, TDefaults> {\n const combined: Record<string, unknown> = { ...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`. When multiple\n // defaults define the same key, the first defined value wins.\n for (const def of defaults) {\n for (const key of Object.keys(def) as (keyof typeof def)[]) {\n if (combined[key] === undefined) {\n combined[key] = def[key]\n }\n }\n }\n\n return combined as WithDefaults<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 function asUint8ArrayArrayBuffer(\n bytes: Uint8Array,\n): Uint8Array<ArrayBuffer> {\n // If the Uint8Array is already backed by a non-shared ArrayBuffer, we can use\n // it directly.\n if (bytes.buffer instanceof ArrayBuffer) {\n return bytes as Uint8Array<ArrayBuffer>\n }\n\n // Otherwise, we need to create a new ArrayBuffer and copy the data.\n return new Uint8Array(bytes)\n}\n\nexport type XrpcRequestHeadersOptions = {\n /**\n * Additional custom HTTP headers to include in the request.\n *\n * @note \"atproto-proxy\" and \"atproto-accept-labelers\" headers might change\n * depending on the `service` and `labelers` options, respectively, if they\n * are provided (which is always the case when using {@link Client.xrpc}).\n */\n headers?: HeadersInit\n\n /**\n * Labeler DIDs to request labels from for content moderation.\n *\n * When `undefined`, will default to the client instance's default. When\n * `null`, it will cause any existing `atproto-accept-labelers` header\n * (including one provided through the\n * {@link XrpcRequestHeadersOptions.headers} option) to be removed.\n */\n labelers?: null | Iterable<DidString>\n\n /**\n * Labeler DIDs to request labels from for content moderation. Values here\n * will always be applied with the \";redact\" suffix, which indicates that the\n * client is requesting that the labeler redact content that is deemed\n * inappropriate.\n */\n appLabelers?: null | Iterable<DidString>\n\n /**\n * Service proxy identifier for routing requests through a specific service.\n *\n * When `undefined`, will default to the client instance's default. When not\n * used against a client instance (e.g., when using the {@link xrpc} helper\n * function), the default is to not alter the `atproto-proxy` header (e.g. if\n * one is provided in the {@link XrpcRequestHeadersOptions.headers}).\n *\n * When defined (as either `null` or a string), it will override any\n * `atproto-proxy` header provided in the\n * {@link XrpcRequestHeadersOptions.headers} option.\n */\n service?: null | Service\n}\n\n/**\n * Builds HTTP headers for AT Protocol requests.\n *\n * Adds or removes the following headers based on the provided options:\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 service,\n labelers,\n appLabelers,\n headers: headersInit,\n}: XrpcRequestHeadersOptions): Headers {\n const headers = new Headers(headersInit)\n\n // If provided, the \"service\" option overrides any existing \"atproto-proxy\"\n // header. If `null`, the header is removed entirely.\n if (service !== undefined) {\n if (service === null) {\n headers.delete('atproto-proxy')\n } else {\n headers.set('atproto-proxy', service)\n }\n }\n\n // Labelers are combined from the \"appLabelers\" and \"labelers\" options, as\n // well as any existing \"atproto-accept-labelers\" header. The \"appLabelers\"\n // are always added with the \";redact\" suffix, while the \"labelers\" are added\n // as-is. Existing labelers headers are not preserved if the \"labelers\" option\n // is explicitly set to null.\n const combinedLabelers = new Set<string>()\n\n if (appLabelers) {\n for (const labeler of appLabelers) {\n combinedLabelers.add(`${labeler};redact`)\n }\n }\n\n if (labelers) {\n for (const labeler of labelers) {\n combinedLabelers.add(labeler)\n }\n }\n\n if (labelers !== null) {\n const headersLabelers = headers.get('atproto-accept-labelers')\n if (headersLabelers) {\n for (const labeler of headersLabelers\n .split(',')\n .map(trim)\n .filter(Boolean)) {\n if (labeler) combinedLabelers.add(labeler)\n }\n }\n }\n\n if (combinedLabelers.size > 0) {\n headers.set(\n 'atproto-accept-labelers',\n Array.from(combinedLabelers).join(', '),\n )\n } else {\n headers.delete('atproto-accept-labelers')\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<Uint8Array>({\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\nexport type RecordKeyOptions<\n T extends RecordSchema,\n AlsoOptionalWhenRecordKeyIs extends LexiconRecordKey = never,\n> = T['key'] extends `literal:${string}` | AlsoOptionalWhenRecordKeyIs\n ? { rkey?: InferRecordKey<T> }\n : { rkey: InferRecordKey<T> }\n\nexport function getDefaultRecordKey<const T extends RecordSchema>(\n schema: T,\n): undefined | InferRecordKey<T> {\n // Let the server generate the TID\n if (schema.key === 'tid') return undefined\n if (schema.key === 'any') return undefined\n\n return getLiteralRecordKey(schema)\n}\n\nexport function getLiteralRecordKey<const T extends RecordSchema>(\n schema: T,\n): InferRecordKey<T> {\n if (schema.key.startsWith('literal:')) {\n return schema.key.slice(8) as InferRecordKey<T>\n }\n\n throw new TypeError(\n `An \"rkey\" must be provided for record key type \"${schema.key}\" (${schema.$type})`,\n )\n}\n\nexport function mergeHeaders(\n defaultHeaders: HeadersInit,\n requestHeaders: HeadersInit,\n): Headers {\n // We don't want to alter the original Headers objects, so we create a new one\n const result = new Headers(defaultHeaders)\n\n const overrides =\n requestHeaders instanceof Headers\n ? requestHeaders\n : new Headers(requestHeaders)\n\n for (const [key, value] of overrides.entries()) {\n result.set(key, value)\n }\n\n return result\n}\n\nfunction getAbortReason(signal: AbortSignal): unknown {\n if ('reason' in signal) return signal.reason\n return typeof DOMException !== 'undefined'\n ? new DOMException('Aborted', 'AbortError')\n : new Error('Aborted')\n}\n\n/**\n * Uses the native `AbortSignal.throwIfAborted()` when available, falling back\n * for older implementations such as React Native's `abort-controller`.\n */\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (!signal) return\n if (typeof signal.throwIfAborted === 'function') {\n signal.throwIfAborted()\n return\n }\n if (signal.aborted) throw getAbortReason(signal)\n}\n\nexport function wait(\n ms: number,\n { signal }: { signal?: AbortSignal } = {},\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n throwIfAborted(signal)\n\n const cleanup = () => {\n clearTimeout(timeout)\n signal?.removeEventListener('abort', onAbort)\n }\n\n const timeout = setTimeout(() => {\n cleanup()\n resolve()\n }, ms)\n\n const onAbort = () => {\n cleanup()\n // @NOTE the signal exists because this listener came from it.\n reject(getAbortReason(signal!))\n }\n\n signal?.addEventListener('abort', onAbort)\n })\n}\n\nexport function trim(value: string): string {\n return value.trim()\n}\n"]}
|