Suite of AT Protocol TypeScript libraries built on web standards
21
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: xrpc in client

+139 -3
+39
xrpc/client.ts
··· 56 56 this.headers.clear(); 57 57 } 58 58 59 + xrpc<const M extends XrpcMethodLike>( 60 + input: M, 61 + ): Promise<XRPCResponse>; 62 + xrpc<const M extends XrpcMethodLike, const O>( 63 + input: M, 64 + options: O & XrpcCallCompatibleOptions<M, O>, 65 + ): Promise<XRPCResponse>; 66 + async xrpc<const M extends XrpcMethodLike>( 67 + input: M, 68 + options: XrpcCallOptions<M> = {} as XrpcCallOptions<M>, 69 + ): Promise<XRPCResponse> { 70 + return await this.performXrpc(input, options); 71 + } 72 + 73 + xrpcSafe<const M extends XrpcMethodLike>( 74 + input: M, 75 + ): Promise<XRPCError | XRPCResponse>; 76 + xrpcSafe<const M extends XrpcMethodLike, const O>( 77 + input: M, 78 + options: O & XrpcCallCompatibleOptions<M, O>, 79 + ): Promise<XRPCError | XRPCResponse>; 80 + async xrpcSafe<const M extends XrpcMethodLike>( 81 + input: M, 82 + options: XrpcCallOptions<M> = {} as XrpcCallOptions<M>, 83 + ): Promise<XRPCError | XRPCResponse> { 84 + try { 85 + return await this.performXrpc(input, options); 86 + } catch (err) { 87 + return XRPCError.from(err); 88 + } 89 + } 90 + 59 91 call<const M extends XrpcMethodLike>( 60 92 input: M, 61 93 ): Promise<XRPCResponse>; ··· 66 98 async call<const M extends XrpcMethodLike>( 67 99 input: M, 68 100 options: XrpcCallOptions<M> = {} as XrpcCallOptions<M>, 101 + ): Promise<XRPCResponse> { 102 + return await this.xrpc(input, options); 103 + } 104 + 105 + private async performXrpc<const M extends XrpcMethodLike>( 106 + input: M, 107 + options: XrpcCallOptions<M>, 69 108 ): Promise<XRPCResponse> { 70 109 const method = getXrpcMethod(input); 71 110 const params = this.getValidatedParams(method, options);
+1 -1
xrpc/mod.ts
··· 36 36 * pingLexicon, 37 37 * ]) 38 38 * 39 - * const res1 = await client.call('io.example.ping', { 39 + * const res1 = await client.xrpc('io.example.ping', { 40 40 * message: 'hello world', 41 41 * }) 42 42 * res1.body // => {message: 'hello world'}
+97
xrpc/tests/client_test.ts
··· 27 27 assertEquals(result.data, { value: "ok" }); 28 28 }); 29 29 30 + Deno.test("calls query with xrpc", async () => { 31 + const method = l.query( 32 + "io.example.query", 33 + l.params({ limit: l.optional(l.integer()) }), 34 + l.jsonPayload({ value: l.string() }), 35 + ); 36 + 37 + const client = new Client((url, init) => { 38 + assertEquals(url, "/xrpc/io.example.query?limit=9"); 39 + assertEquals(init.method, "get"); 40 + return Promise.resolve(Response.json({ value: "ok" })); 41 + }); 42 + 43 + const result = await client.xrpc(method, { 44 + params: { limit: 9 }, 45 + }); 46 + 47 + assertEquals(result.data, { value: "ok" }); 48 + }); 49 + 50 + Deno.test("narrows xrpcSafe success results on success flag", async () => { 51 + const method = l.query( 52 + "io.example.query", 53 + l.params({ limit: l.optional(l.integer()) }), 54 + l.jsonPayload({ value: l.string() }), 55 + ); 56 + 57 + const client = new Client((url, init) => { 58 + assertEquals(url, "/xrpc/io.example.query?limit=8"); 59 + assertEquals(init.method, "get"); 60 + return Promise.resolve(Response.json({ value: "ok" })); 61 + }); 62 + 63 + const result = await client.xrpcSafe(method, { 64 + params: { limit: 8 }, 65 + }); 66 + 67 + assertEquals(result.success, true); 68 + 69 + if (result.success) { 70 + assertEquals(result.data, { value: "ok" }); 71 + } else { 72 + throw new Error(result.error); 73 + } 74 + }); 75 + 76 + Deno.test("keeps call as a compatibility alias for xrpc", async () => { 77 + const method = l.query( 78 + "io.example.query", 79 + l.params({ limit: l.optional(l.integer()) }), 80 + l.jsonPayload({ value: l.string() }), 81 + ); 82 + 83 + const client = new Client((url, init) => { 84 + assertEquals(url, "/xrpc/io.example.query?limit=4"); 85 + assertEquals(init.method, "get"); 86 + return Promise.resolve(Response.json({ value: "ok" })); 87 + }); 88 + 89 + const result = await client.call(method, { 90 + params: { limit: 4 }, 91 + }); 92 + 93 + assertEquals(result.data, { value: "ok" }); 94 + }); 95 + 30 96 Deno.test("serializes params using schema transforms", async () => { 31 97 const method = l.query( 32 98 "io.example.query", ··· 199 265 }, 200 266 XRPCInvalidResponseError, 201 267 ); 268 + }); 269 + 270 + Deno.test("returns xrpc errors from xrpcSafe", async () => { 271 + const method = l.query( 272 + "io.example.query", 273 + l.params({ limit: l.optional(l.integer()) }), 274 + l.jsonPayload({ value: l.string() }), 275 + ); 276 + 277 + const client = new Client(() => 278 + Promise.resolve( 279 + Response.json( 280 + { error: "BadRequest", message: "nope" }, 281 + { status: 400 }, 282 + ), 283 + ) 284 + ); 285 + 286 + const result = await client.xrpcSafe(method, { 287 + params: { limit: 1 }, 288 + }); 289 + 290 + assertEquals(result.success, false); 291 + 292 + if (!result.success) { 293 + assertEquals(result.success, false); 294 + assertEquals(result.error, "BadRequest"); 295 + assertEquals(result.message, "nope"); 296 + } else { 297 + throw new Error(JSON.stringify(result.data)); 298 + } 202 299 }); 203 300 204 301 Deno.test("accepts formatted strings in json request bodies", async () => {
+2 -2
xrpc/types.ts
··· 480 480 * Response type of a successful XRPC request. 481 481 */ 482 482 export class XRPCResponse { 483 - success = true; 483 + readonly success: true = true; 484 484 485 485 constructor( 486 486 public data: any, ··· 492 492 * Response type of a failed XRPC request with details of the error. 493 493 */ 494 494 export class XRPCError extends Error { 495 - success = false; 495 + readonly success: false = false; 496 496 497 497 public status: ResponseType; 498 498